Add the /donate page
Just doing some house-cleaning on easy pages that need converted before DTI Classic can retire!
This commit is contained in:
parent
2b486ea218
commit
07e2c0f7b1
9 changed files with 567 additions and 305 deletions
105
pages/donate.tsx
Normal file
105
pages/donate.tsx
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
import DonationsPage from "../src/app/DonationsPage";
|
||||||
|
// @ts-ignore: doesn't understand module.exports
|
||||||
|
import connectToDb from "../src/server/db";
|
||||||
|
|
||||||
|
type Props = { campaigns: Campaign[] };
|
||||||
|
|
||||||
|
type Campaign = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
donationFeatures: DonationFeature[];
|
||||||
|
};
|
||||||
|
type DonationFeature = {
|
||||||
|
id: string;
|
||||||
|
donorName: string | null;
|
||||||
|
outfit: { id: string; name: string; updatedAt: string } | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function DonationsPageWrapper({ campaigns }: Props) {
|
||||||
|
return <DonationsPage campaigns={campaigns} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getStaticProps loads the donation info and organizes it into a convenient
|
||||||
|
* structure for the page props.
|
||||||
|
*
|
||||||
|
* This happens at build time! This data basically hasn't changed since 2017,
|
||||||
|
* so we'd rather have the performance benefits and reliability of just
|
||||||
|
* building it up-front than making this an SSR thing. (It also makes it harder
|
||||||
|
* for someone to add a surprise prank message years later when we're not
|
||||||
|
* paying attention, by editing the outfit title—it would still probably make
|
||||||
|
* it onto the site eventually, but not until the next build, which should be
|
||||||
|
* discouraging. But nobody's ever tried to prank this page before, so, shrug!)
|
||||||
|
*
|
||||||
|
* I also just went with a direct DB query, to avoid putting any of this in our
|
||||||
|
* GraphQL schema when it's really super-duper *just* for this page and *just*
|
||||||
|
* going to be requested in super-duper *one* way.
|
||||||
|
*/
|
||||||
|
export async function getStaticProps() {
|
||||||
|
const db = await connectToDb();
|
||||||
|
|
||||||
|
const [rows]: [QueryRow[]] = await db.query({
|
||||||
|
sql: `
|
||||||
|
SELECT
|
||||||
|
donation_features.id,
|
||||||
|
donations.donor_name,
|
||||||
|
campaigns.id, campaigns.name,
|
||||||
|
outfits.id, outfits.name, outfits.updated_at
|
||||||
|
FROM donation_features
|
||||||
|
INNER JOIN donations ON donations.id = donation_features.donation_id
|
||||||
|
INNER JOIN campaigns ON campaigns.id = donations.campaign_id
|
||||||
|
LEFT JOIN outfits ON outfits.id = donation_features.outfit_id
|
||||||
|
ORDER BY campaigns.created_at DESC, donations.created_at ASC;
|
||||||
|
`,
|
||||||
|
nestTables: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reorganize the query rows into campaign objects with lists of donation
|
||||||
|
// features.
|
||||||
|
const campaigns: Campaign[] = [];
|
||||||
|
for (const row of rows) {
|
||||||
|
// Find the campaign for this feature in our campaigns list, or add it if
|
||||||
|
// it's not present yet.
|
||||||
|
let campaign = campaigns.find((c) => c.id === String(row.campaigns.id));
|
||||||
|
if (campaign == null) {
|
||||||
|
campaign = {
|
||||||
|
id: String(row.campaigns.id),
|
||||||
|
name: row.campaigns.name,
|
||||||
|
donationFeatures: [],
|
||||||
|
};
|
||||||
|
campaigns.push(campaign);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reformat the outfit and donation feature into safer and more
|
||||||
|
// serializable forms.
|
||||||
|
const outfit =
|
||||||
|
row.outfits.id != null
|
||||||
|
? {
|
||||||
|
id: String(row.outfits.id),
|
||||||
|
name: row.outfits.name,
|
||||||
|
updatedAt: row.outfits.updated_at.toISOString(),
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
const donationFeature: DonationFeature = {
|
||||||
|
id: String(row.donation_features.id),
|
||||||
|
donorName: row.donations.donor_name,
|
||||||
|
outfit,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add this donation feature to the campaign.
|
||||||
|
campaign.donationFeatures.push(donationFeature);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: { campaigns },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryRow = {
|
||||||
|
donation_features: { id: number };
|
||||||
|
donations: { donor_name: string | null };
|
||||||
|
campaigns: { id: string; name: string };
|
||||||
|
outfits:
|
||||||
|
| { id: number; name: string; updated_at: Date }
|
||||||
|
| { id: null; name: null; updated_at: null };
|
||||||
|
};
|
|
@ -1,8 +1,11 @@
|
||||||
USE openneo_impress;
|
USE openneo_impress;
|
||||||
|
|
||||||
-- Public data tables: read
|
-- Public data tables: read
|
||||||
|
GRANT SELECT ON campaigns TO impress2020;
|
||||||
GRANT SELECT ON colors TO impress2020;
|
GRANT SELECT ON colors TO impress2020;
|
||||||
GRANT SELECT ON color_translations TO impress2020;
|
GRANT SELECT ON color_translations TO impress2020;
|
||||||
|
GRANT SELECT ON donation_features TO impress2020;
|
||||||
|
GRANT SELECT ON donations TO impress2020;
|
||||||
GRANT SELECT ON items TO impress2020;
|
GRANT SELECT ON items TO impress2020;
|
||||||
GRANT SELECT ON item_translations TO impress2020;
|
GRANT SELECT ON item_translations TO impress2020;
|
||||||
GRANT SELECT ON modeling_logs TO impress2020;
|
GRANT SELECT ON modeling_logs TO impress2020;
|
||||||
|
|
127
src/app/DonationsPage.js
Normal file
127
src/app/DonationsPage.js
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
import React from "react";
|
||||||
|
import Head from "next/head";
|
||||||
|
import { Heading1, Heading2 } from "./util";
|
||||||
|
import TextContent from "./components/TextContent";
|
||||||
|
|
||||||
|
import FastlyLogoImg from "./images/fastly-logo.svg";
|
||||||
|
import { Box, Wrap, WrapItem } from "@chakra-ui/react";
|
||||||
|
import Image from "next/image";
|
||||||
|
import { OutfitCard } from "./UserOutfitsPage";
|
||||||
|
|
||||||
|
function DonationsPage({ campaigns }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>Donations | Dress to Impress</title>
|
||||||
|
</Head>
|
||||||
|
<TextContent>
|
||||||
|
<Heading1 marginBottom="4">Our donors</Heading1>
|
||||||
|
<p>
|
||||||
|
Dress to Impress has been around a long time—from back when Matchu was
|
||||||
|
in middle school! Without a real source of income, we used to depend a
|
||||||
|
lot on community donations to keep the site running.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Since then, life has changed a lot, and we're able to comfortably fund
|
||||||
|
Dress to Impress out-of-pocket. But we're still very grateful to the
|
||||||
|
donors who got us through those tougher years! Here's a showcase of
|
||||||
|
their outfits 💖
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{/* Thanking Fastly somewhere in a sponsors page on our site is a
|
||||||
|
* condition of the program, so here we are! But also it's a great
|
||||||
|
* deal and I mean what I say! */}
|
||||||
|
We're also grateful to <FastlyLogoLink />, who offer us some CDN
|
||||||
|
hosting services under their non-profit & open-source partner
|
||||||
|
program! They help us load all the big images around the site much
|
||||||
|
faster! Thank you!
|
||||||
|
</p>
|
||||||
|
</TextContent>
|
||||||
|
{campaigns.map((campaign) => (
|
||||||
|
<CampaignSection key={campaign.id} campaign={campaign} />
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CampaignSection({ campaign }) {
|
||||||
|
const { donationFeatures, name } = campaign;
|
||||||
|
const featuresWithNames = donationFeatures.filter(
|
||||||
|
(f) => f.donorName?.length > 0
|
||||||
|
);
|
||||||
|
const allDonorNames = new Set(featuresWithNames.map((f) => f.donorName));
|
||||||
|
const donorNamesWithOutfits = new Set(
|
||||||
|
featuresWithNames.filter((f) => f.outfit != null).map((f) => f.donorName)
|
||||||
|
);
|
||||||
|
const donorNamesWithoutOutfits = [...allDonorNames]
|
||||||
|
.filter((n) => !donorNamesWithOutfits.has(n))
|
||||||
|
.sort((a, b) => a.localeCompare(b));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box marginBottom="8">
|
||||||
|
<Heading2 marginBottom="4">{name} donors</Heading2>
|
||||||
|
<Wrap spacing="4" justify="space-around">
|
||||||
|
{donationFeatures
|
||||||
|
.filter((f) => f.outfit != null)
|
||||||
|
.map((donationFeature) => (
|
||||||
|
<WrapItem key={donationFeature.outfit.id}>
|
||||||
|
<DonationOutfitCard
|
||||||
|
outfit={donationFeature.outfit}
|
||||||
|
donorName={donationFeature.donorName || "Anonymous"}
|
||||||
|
/>
|
||||||
|
</WrapItem>
|
||||||
|
))}
|
||||||
|
</Wrap>
|
||||||
|
{donorNamesWithoutOutfits.length > 0 && (
|
||||||
|
<Box
|
||||||
|
textAlign="center"
|
||||||
|
fontStyle="italic"
|
||||||
|
maxWidth="800px"
|
||||||
|
marginX="auto"
|
||||||
|
marginTop="8"
|
||||||
|
>
|
||||||
|
<strong>And a few more:</strong> {donorNamesWithoutOutfits.join(", ")}
|
||||||
|
. <strong>Thank you!</strong>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DonationOutfitCard({ outfit, donorName }) {
|
||||||
|
return (
|
||||||
|
<OutfitCard
|
||||||
|
outfit={outfit}
|
||||||
|
caption={
|
||||||
|
<Box>
|
||||||
|
<Box fontSize="sm">Thank you, {donorName}!</Box>
|
||||||
|
<Box fontSize="xs">{outfit.name}</Box>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
alt={`Outfit thumbnail: ${outfit.name}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function FastlyLogoLink() {
|
||||||
|
return (
|
||||||
|
<a href="https://www.fastly.com/">
|
||||||
|
<Box
|
||||||
|
display="inline-block"
|
||||||
|
verticalAlign="middle"
|
||||||
|
filter="saturate(90%)"
|
||||||
|
marginBottom="-2px"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={FastlyLogoImg}
|
||||||
|
width={40}
|
||||||
|
height={16}
|
||||||
|
alt="Fastly"
|
||||||
|
title="Fastly"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DonationsPage;
|
|
@ -31,6 +31,9 @@ function GlobalFooter() {
|
||||||
<Link href="/privacy" passHref>
|
<Link href="/privacy" passHref>
|
||||||
<ChakraLink>Privacy Policy (Sep 2022)</ChakraLink>
|
<ChakraLink>Privacy Policy (Sep 2022)</ChakraLink>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link href="/donate" passHref>
|
||||||
|
<ChakraLink>Donors</ChakraLink>
|
||||||
|
</Link>
|
||||||
<ChakraLink href={classicDTIUrl}>Classic DTI</ChakraLink>
|
<ChakraLink href={classicDTIUrl}>Classic DTI</ChakraLink>
|
||||||
</HStack>
|
</HStack>
|
||||||
<Box as="p" opacity="0.75">
|
<Box as="p" opacity="0.75">
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { css } from "@emotion/react";
|
|
||||||
import { VStack } from "@chakra-ui/react";
|
import { VStack } from "@chakra-ui/react";
|
||||||
|
|
||||||
import { Heading1, Heading2, Heading3 } from "./util";
|
import { Heading1, Heading2, Heading3 } from "./util";
|
||||||
import { useAuthModeFeatureFlag } from "./components/useCurrentUser";
|
import { useAuthModeFeatureFlag } from "./components/useCurrentUser";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
|
import TextContent from "./components/TextContent";
|
||||||
|
|
||||||
function PrivacyPolicyPage() {
|
function PrivacyPolicyPage() {
|
||||||
const [authMode] = useAuthModeFeatureFlag();
|
const [authMode] = useAuthModeFeatureFlag();
|
||||||
|
@ -15,34 +15,18 @@ function PrivacyPolicyPage() {
|
||||||
<title>Privacy Policy | Dress to Impress</title>
|
<title>Privacy Policy | Dress to Impress</title>
|
||||||
</Head>
|
</Head>
|
||||||
<Heading1 marginBottom="4">Our privacy policy</Heading1>
|
<Heading1 marginBottom="4">Our privacy policy</Heading1>
|
||||||
<VStack
|
<TextContent maxWidth="800px">
|
||||||
spacing="4"
|
<VStack spacing="4" alignItems="flex-start">
|
||||||
alignItems="flex-start"
|
|
||||||
css={css`
|
|
||||||
max-width: 800px;
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
h2,
|
|
||||||
h3 {
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<section>
|
<section>
|
||||||
<p>
|
<p>
|
||||||
Hi, friends! Dress to Impress collects certain personal data. Here's
|
Hi, friends! Dress to Impress collects certain personal data.
|
||||||
how we use it!
|
Here's how we use it!
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
First off, we'll <em>never</em> sell your private data, ever. It'll
|
First off, we'll <em>never</em> sell your private data, ever.
|
||||||
only be available to you and our small trusted staff—and we'll only
|
It'll only be available to you and our small trusted staff—and
|
||||||
use it to serve you directly, debug site issues, and help you share
|
we'll only use it to serve you directly, debug site issues, and
|
||||||
your creations with others.
|
help you share your creations with others.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
{authMode === "auth0" && (
|
{authMode === "auth0" && (
|
||||||
|
@ -55,10 +39,10 @@ function PrivacyPolicyPage() {
|
||||||
account creation and login.
|
account creation and login.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
We made this decision because authentication is difficult to write
|
We made this decision because authentication is difficult to
|
||||||
and maintain securely. We felt that Auth0 was the smoothest and
|
write and maintain securely. We felt that Auth0 was the
|
||||||
most secure experience we could offer, especially as a small team
|
smoothest and most secure experience we could offer, especially
|
||||||
of volunteers{" "}
|
as a small team of volunteers{" "}
|
||||||
<span role="img" aria-label="Sweat smile emoji">
|
<span role="img" aria-label="Sweat smile emoji">
|
||||||
😅
|
😅
|
||||||
</span>
|
</span>
|
||||||
|
@ -67,15 +51,15 @@ function PrivacyPolicyPage() {
|
||||||
<a href="https://auth0.com/legal/ss-tos">
|
<a href="https://auth0.com/legal/ss-tos">
|
||||||
Auth0's terms of service
|
Auth0's terms of service
|
||||||
</a>{" "}
|
</a>{" "}
|
||||||
commit to treating your user data as confidential information, not
|
commit to treating your user data as confidential information,
|
||||||
to be shared with anyone else, and only to be used as part of
|
not to be shared with anyone else, and only to be used as part
|
||||||
Dress to Impress. (The details are in Sections 6 and 7!)
|
of Dress to Impress. (The details are in Sections 6 and 7!)
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
When signing up, Auth0 will ask for a username, password, and
|
When signing up, Auth0 will ask for a username, password, and
|
||||||
email address. They store your password as a <em>hash</em> (which,
|
email address. They store your password as a <em>hash</em>{" "}
|
||||||
colloquially, is like a one-way encryption), rather than as the
|
(which, colloquially, is like a one-way encryption), rather than
|
||||||
plain password itself.
|
as the plain password itself.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Some user accounts were created before we moved to Auth0. For
|
Some user accounts were created before we moved to Auth0. For
|
||||||
|
@ -89,8 +73,8 @@ function PrivacyPolicyPage() {
|
||||||
<Heading2>Analytics and logging</Heading2>
|
<Heading2>Analytics and logging</Heading2>
|
||||||
<p>
|
<p>
|
||||||
To understand how people use our site, we use a service called{" "}
|
To understand how people use our site, we use a service called{" "}
|
||||||
<a href="https://plausible.io/">Plausible</a>. Every time you visit
|
<a href="https://plausible.io/">Plausible</a>. Every time you
|
||||||
a page, we send them a{" "}
|
visit a page, we send them a{" "}
|
||||||
<a href="https://plausible.io/data-policy">
|
<a href="https://plausible.io/data-policy">
|
||||||
small packet of information
|
small packet of information
|
||||||
</a>
|
</a>
|
||||||
|
@ -105,29 +89,29 @@ function PrivacyPolicyPage() {
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
We also use a service called <a href="https://sentry.io/">Sentry</a>{" "}
|
We also use a service called{" "}
|
||||||
to track errors. When you encounter an error on our site, we send a
|
<a href="https://sentry.io/">Sentry</a> to track errors. When you
|
||||||
copy of it to our Sentry account, to help us debug it later. This
|
encounter an error on our site, we send a copy of it to our Sentry
|
||||||
might sometimes include personal data, but Sentry will only share it
|
account, to help us debug it later. This might sometimes include
|
||||||
with us.{" "}
|
personal data, but Sentry will only share it with us.{" "}
|
||||||
<a href="https://sentry.io/legal/dpa/2.0.0/">
|
<a href="https://sentry.io/legal/dpa/2.0.0/">
|
||||||
Here's their data policy.
|
Here's their data policy.
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
We also use <a href="https://www.linode.com/">Linode</a> and{" "}
|
We also use <a href="https://www.linode.com/">Linode</a> and{" "}
|
||||||
<a href="https://www.fastly.com/">Fastly</a> for web hosting. Linode
|
<a href="https://www.fastly.com/">Fastly</a> for web hosting.
|
||||||
stores our database, and handles most web traffic dealing with
|
Linode stores our database, and handles most web traffic dealing
|
||||||
personal data. Personal data also travels through Fastly's servers
|
with personal data. Personal data also travels through Fastly's
|
||||||
temporarily, but they only store aggregate usage logs for us, not
|
servers temporarily, but they only store aggregate usage logs for
|
||||||
any personally-identifying data.
|
us, not any personally-identifying data.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<Heading2>Creations and contributions</Heading2>
|
<Heading2>Creations and contributions</Heading2>
|
||||||
<p>
|
<p>
|
||||||
People use Dress to Impress to create, share, and communicate! Some
|
People use Dress to Impress to create, share, and communicate!
|
||||||
of these things are public, some are private, and some are
|
Some of these things are public, some are private, and some are
|
||||||
configurable.
|
configurable.
|
||||||
</p>
|
</p>
|
||||||
<Heading3>Outfits</Heading3>
|
<Heading3>Outfits</Heading3>
|
||||||
|
@ -140,8 +124,8 @@ function PrivacyPolicyPage() {
|
||||||
share outfits by URL without logging in.
|
share outfits by URL without logging in.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
When you save an outfit to your account, it's somewhat private, but
|
When you save an outfit to your account, it's somewhat private,
|
||||||
somewhat public.
|
but somewhat public.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
It's private in the sense that there is no central place where
|
It's private in the sense that there is no central place where
|
||||||
|
@ -155,7 +139,8 @@ function PrivacyPolicyPage() {
|
||||||
<p>
|
<p>
|
||||||
We might change this in the future, to make the URLs hard to guess
|
We might change this in the future, to make the URLs hard to guess
|
||||||
and <em>genuinely</em> private. Until then, we advise users to not
|
and <em>genuinely</em> private. Until then, we advise users to not
|
||||||
to include sensitive data in the outfits they save to their account.
|
to include sensitive data in the outfits they save to their
|
||||||
|
account.
|
||||||
</p>
|
</p>
|
||||||
<Heading3>Item lists</Heading3>
|
<Heading3>Item lists</Heading3>
|
||||||
<p>
|
<p>
|
||||||
|
@ -163,8 +148,8 @@ function PrivacyPolicyPage() {
|
||||||
and want, by saving item lists to their account.
|
and want, by saving item lists to their account.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
These lists are private by default, but can be configured to either
|
These lists are private by default, but can be configured to
|
||||||
be "public" or "trading" as well.
|
either be "public" or "trading" as well.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
The "public" status means that anyone who knows your Dress to
|
The "public" status means that anyone who knows your Dress to
|
||||||
|
@ -182,9 +167,9 @@ function PrivacyPolicyPage() {
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Sometimes, this will download new public outfit data that we've
|
Sometimes, this will download new public outfit data that we've
|
||||||
never seen before. For example, you might show us a Draik (a species
|
never seen before. For example, you might show us a Draik (a
|
||||||
of Neopet) wearing a new item, and we don't have data for a Draik
|
species of Neopet) wearing a new item, and we don't have data for
|
||||||
wearing that item yet.
|
a Draik wearing that item yet.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
When that happens, we'll extract that specific piece of data from
|
When that happens, we'll extract that specific piece of data from
|
||||||
|
@ -193,26 +178,27 @@ function PrivacyPolicyPage() {
|
||||||
"modeling".
|
"modeling".
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
When you model new data for us, it's separated from your pet. Users
|
When you model new data for us, it's separated from your pet.
|
||||||
can't discover what pet modeled a certain piece of data, or what
|
Users can't discover what pet modeled a certain piece of data, or
|
||||||
else that pet was wearing.
|
what else that pet was wearing.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
But, if you're logged in when modeling, we'll publicly credit your
|
But, if you're logged in when modeling, we'll publicly credit your
|
||||||
account for the new "contribution". This will appear in a number of
|
account for the new "contribution". This will appear in a number
|
||||||
places, including a list of the most recent contributions, and it
|
of places, including a list of the most recent contributions, and
|
||||||
will add points to your account that contribute to a public high
|
it will add points to your account that contribute to a public
|
||||||
score list. This will publicly display your username.
|
high score list. This will publicly display your username.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Right now, modeling contributions from logged-in users are always
|
Right now, modeling contributions from logged-in users are always
|
||||||
public. This is a limitation of our system, and we might change it
|
public. This is a limitation of our system, and we might change it
|
||||||
in the future! For now, if you would like to have your public
|
in the future! For now, if you would like to have your public
|
||||||
contributions removed from the site, please use the contact link at
|
contributions removed from the site, please use the contact link
|
||||||
the bottom of the page.
|
at the bottom of the page.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
</VStack>
|
</VStack>
|
||||||
|
</TextContent>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { css } from "@emotion/react";
|
|
||||||
import { VStack } from "@chakra-ui/react";
|
import { VStack } from "@chakra-ui/react";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
|
|
||||||
import { Heading1, Heading2 } from "./util";
|
import { Heading1, Heading2 } from "./util";
|
||||||
|
import TextContent from "./components/TextContent";
|
||||||
|
|
||||||
function TermsOfUsePage() {
|
function TermsOfUsePage() {
|
||||||
return (
|
return (
|
||||||
|
@ -11,30 +11,14 @@ function TermsOfUsePage() {
|
||||||
<title>Terms of Use | Dress to Impres</title>
|
<title>Terms of Use | Dress to Impres</title>
|
||||||
</Head>
|
</Head>
|
||||||
<Heading1 marginBottom="4">Our terms of use</Heading1>
|
<Heading1 marginBottom="4">Our terms of use</Heading1>
|
||||||
<VStack
|
<TextContent maxWidth="800px">
|
||||||
spacing="4"
|
<VStack spacing="4" alignItems="flex-start">
|
||||||
alignItems="flex-start"
|
|
||||||
css={css`
|
|
||||||
max-width: 800px;
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
h2,
|
|
||||||
h3 {
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<section>
|
<section>
|
||||||
<p>
|
<p>
|
||||||
Hi, friends! Here's some information about how Dress to Impress is
|
Hi, friends! Here's some information about how Dress to Impress is
|
||||||
meant to be used. The rules here aren't very formal, but we hope
|
meant to be used. The rules here aren't very formal, but we hope
|
||||||
they're clear, and we take them very seriously. Thank you for taking
|
they're clear, and we take them very seriously. Thank you for
|
||||||
the time to read!
|
taking the time to read!
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
|
@ -58,67 +42,69 @@ function TermsOfUsePage() {
|
||||||
<section>
|
<section>
|
||||||
<Heading2>What you can post on this service</Heading2>
|
<Heading2>What you can post on this service</Heading2>
|
||||||
<p>
|
<p>
|
||||||
<strong>Keep it Neoboard-safe.</strong> Neopets.com allows links to
|
<strong>Keep it Neoboard-safe.</strong> Neopets.com allows links
|
||||||
Dress to Impress, so everything needs to be safe for Neopians of all
|
to Dress to Impress, so everything needs to be safe for Neopians
|
||||||
ages! Please keep all content "PG" and appropriate for young
|
of all ages! Please keep all content "PG" and appropriate for
|
||||||
community members, just like you do on Neopets.com. (That said, the
|
young community members, just like you do on Neopets.com. (That
|
||||||
rules on the Neoboards haven't always been morally right, such as
|
said, the rules on the Neoboards haven't always been morally
|
||||||
when LGBTQIA+ discussion was banned. We'll always diverge from those
|
right, such as when LGBTQIA+ discussion was banned. We'll always
|
||||||
rules when it's ethically appropriate!)
|
diverge from those rules when it's ethically appropriate!)
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>Don't sell things for real money here.</strong> We don't
|
<strong>Don't sell things for real money here.</strong> We don't
|
||||||
have the capacity to validate who is and isn't a legitimate seller,
|
have the capacity to validate who is and isn't a legitimate
|
||||||
so we err on the side of safety and ban <em>all</em> sales. If
|
seller, so we err on the side of safety and ban <em>all</em>{" "}
|
||||||
you're selling something, please do it in a community where trust
|
sales. If you're selling something, please do it in a community
|
||||||
and reputation can be managed more appropriately, and please make
|
where trust and reputation can be managed more appropriately, and
|
||||||
sure it's in line with Neopets's terms.
|
please make sure it's in line with Neopets's terms.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<Heading2>How you can use our data</Heading2>
|
<Heading2>How you can use our data</Heading2>
|
||||||
<p>
|
<p>
|
||||||
<strong>Be thoughtful using Neopets's data.</strong> While Dress to
|
<strong>Be thoughtful using Neopets's data.</strong> While Dress
|
||||||
Impress has a license to distribute Neopets data and images, we
|
to Impress has a license to distribute Neopets data and images, we
|
||||||
aren't authorized to extend all of the same permissions to you.
|
aren't authorized to extend all of the same permissions to you.
|
||||||
Please think carefully about how you use Neopets's art and data you
|
Please think carefully about how you use Neopets's art and data
|
||||||
find on this site, and make sure you're complying with their
|
you find on this site, and make sure you're complying with their
|
||||||
licensing agreements and fair use laws, especially for derived works
|
licensing agreements and fair use laws, especially for derived
|
||||||
like outfits. But personal use, and usage that stays on our site,
|
works like outfits. But personal use, and usage that stays on our
|
||||||
are always okay!
|
site, are always okay!
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>Be thoughtful using user-generated data.</strong> Some data
|
<strong>Be thoughtful using user-generated data.</strong> Some
|
||||||
posted to Dress to Impress is generated by our users, like their
|
data posted to Dress to Impress is generated by our users, like
|
||||||
outfits and item lists. When you post those to Dress to Impress, you
|
their outfits and item lists. When you post those to Dress to
|
||||||
grant us a license to redistribute them with attribution as part of
|
Impress, you grant us a license to redistribute them with
|
||||||
the site's functionality, respecting your privacy settings when
|
attribution as part of the site's functionality, respecting your
|
||||||
applicable. But each user still owns their own creations, so only
|
privacy settings when applicable. But each user still owns their
|
||||||
they can grant you permission to use or share it yourself.
|
own creations, so only they can grant you permission to use or
|
||||||
|
share it yourself.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>Please reach out before using our APIs!</strong> If you'd
|
<strong>Please reach out before using our APIs!</strong> If you'd
|
||||||
like to use our data to build something new, please contact us! We'd
|
like to use our data to build something new, please contact us!
|
||||||
like to help if we can. But please don't use our APIs without
|
We'd like to help if we can. But please don't use our APIs without
|
||||||
talking to us first: it can cause performance issues for us, and
|
talking to us first: it can cause performance issues for us, and
|
||||||
reliability issues for you. But we have a few folks who use Dress to
|
reliability issues for you. But we have a few folks who use Dress
|
||||||
Impress for things like Discord bots, and we'd like to support you
|
to Impress for things like Discord bots, and we'd like to support
|
||||||
and your community too!
|
you and your community too!
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<Heading2>Warranty and liability</Heading2>
|
<Heading2>Warranty and liability</Heading2>
|
||||||
<p>
|
<p>
|
||||||
<strong>Our data won't always be correct.</strong> While we do our
|
<strong>Our data won't always be correct.</strong> While we do our
|
||||||
best to keep the customization on our site in sync with Neopets.com,
|
best to keep the customization on our site in sync with
|
||||||
sometimes our data is out-of-date, and sometimes an item looks
|
Neopets.com, sometimes our data is out-of-date, and sometimes an
|
||||||
different on our site than on Neopets.com. We're glad to be a
|
item looks different on our site than on Neopets.com. We're glad
|
||||||
resource for users buying Neocash items, but as an unofficial
|
to be a resource for users buying Neocash items, but as an
|
||||||
service we simply can't make guarantees, and we encourage you to
|
unofficial service we simply can't make guarantees, and we
|
||||||
check other sources before making a purchase.
|
encourage you to check other sources before making a purchase.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
</VStack>
|
</VStack>
|
||||||
|
</TextContent>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,14 +139,14 @@ function UserOutfitsPageContent() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function OutfitCard({ outfit }) {
|
export function OutfitCard({ outfit, caption = null, alt = null }) {
|
||||||
const image = (
|
const image = (
|
||||||
<ClassNames>
|
<ClassNames>
|
||||||
{({ css }) => (
|
{({ css }) => (
|
||||||
<OutfitThumbnail
|
<OutfitThumbnail
|
||||||
outfitId={outfit.id}
|
outfitId={outfit.id}
|
||||||
updatedAt={outfit.updatedAt}
|
updatedAt={outfit.updatedAt}
|
||||||
alt={buildOutfitAltText(outfit)}
|
alt={alt ?? buildOutfitAltText(outfit)}
|
||||||
// Firefox shows alt text as a fallback for images it can't show yet.
|
// Firefox shows alt text as a fallback for images it can't show yet.
|
||||||
// Show our alt text clearly if the image failed to load... but hide
|
// Show our alt text clearly if the image failed to load... but hide
|
||||||
// it if it's still loading. It's normal for these to take a second
|
// it if it's still loading. It's normal for these to take a second
|
||||||
|
@ -184,7 +184,7 @@ function OutfitCard({ outfit }) {
|
||||||
outline: "none",
|
outline: "none",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<OutfitCardLayout image={image} caption={outfit.name} />
|
<OutfitCardLayout image={image} caption={caption ?? outfit.name} />
|
||||||
</Box>
|
</Box>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|
31
src/app/components/TextContent.js
Normal file
31
src/app/components/TextContent.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Box } from "@chakra-ui/react";
|
||||||
|
import { css } from "@emotion/react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TextContent is a wrapper for just, like, normal chill text-based content!
|
||||||
|
* Its children will receive some simple CSS for tags like <p>, <a>, etc.
|
||||||
|
*/
|
||||||
|
function TextContent({ children, ...props }) {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
{...props}
|
||||||
|
css={css`
|
||||||
|
p {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
h2,
|
||||||
|
h3 {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TextContent;
|
21
src/app/images/fastly-logo.svg
Normal file
21
src/app/images/fastly-logo.svg
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) by Marsupilami -->
|
||||||
|
<svg
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
id="svg278"
|
||||||
|
version="1.1"
|
||||||
|
width="1024"
|
||||||
|
height="411"
|
||||||
|
viewBox="-18.0552558 -18.0552558 1590.2355116 637.9523716">
|
||||||
|
<defs
|
||||||
|
id="defs275" />
|
||||||
|
<path
|
||||||
|
style="fill:#ff282d;fill-opacity:1"
|
||||||
|
id="polygon204"
|
||||||
|
d="m 871.9707,0 v 363.35742 c 0,71.341 17.60489,103.9004 94.3379,103.9004 18.173,0 43.145,-4.67685 61.998,-8.71485 l -12.1269,-72.81836 c -12.759,2.692 -23.9153,2.36741 -31.9883,2.56641 -33.557,0.825 -30.6582,-10.20375 -30.6582,-41.84375 V 209.67188 h 63.8731 V 149.06055 H 953.5332 V 0 Z m 267.25,0 -81.5625,0.0117 0.012,61.75391 v 405.49414 h 121.8008 v -61.95703 h -40.25 z M 139.89062,0.002 c -84.128,0 -98.48632,28.62866 -98.48632,94.59765 v 54.46289 L 0,155.87114 v 53.80078 H 41.4043 V 405.30274 H 0.00195 v 61.96484 l 163.94141,-0.0176 V 405.30469 H 122.96875 V 209.67188 h 61.54102 v -60.61133 h -61.54102 v -47.13086 c 0,-28.959 7.49381,-31.91602 37.13281,-31.91602 8.072,0 14.40774,0.35982 27.17774,2.38282 L 198.45508,6.08203 c -18.856,-3.397 -40.39445,-6.08008 -58.56446,-6.08008 z m 171.1504,85.85546 v 20.71485 h 9.94921 v 28.16601 h 0.56641 c -78.009,14.361 -137.12695,82.67004 -137.12695,164.83204 0,92.595 75.0632,167.6582 167.6582,167.6582 31.602,0 61.15477,-8.75508 86.38477,-23.95508 l 14.69336,23.98633 h 86.15234 v -81.53516 h -19.49414 l -0.0547,-253.81055 h -81.56055 v 23.80274 c -16.799,-10.078 -35.51107,-17.28452 -55.45507,-20.97852 h 0.46093 v -28.16601 h 9.95118 V 85.85746 Z m 391.59179,45.52735 c -76.571,0 -122.55078,26.96884 -122.55078,95.71484 0,72.629 56.74414,97.03886 128.24414,109.25586 37.951,7.696 49.5625,15.4097 49.5625,31.4707 0,11.573 -6.55011,33.33985 -47.03711,33.33985 -14.549,0 -42.41709,0.51486 -68.99609,-4.61914 l 0.0352,-10.82227 H 580.127 v 60.91016 c 37.934,9.626 87.64872,20.625 138.88672,20.625 76.57,0 112.94727,-36.51722 112.94727,-107.19922 0,-74.531 -60.36566,-88.60952 -120.47266,-102.10352 -41.121,-8.991 -46.38867,-17.33743 -46.38867,-32.77343 0,-10.911 4.62894,-31.03907 42.58594,-31.03907 13.292,0 37.7957,0.0635 62.4707,4.56446 v 10.85156 h 61.74805 v -60.5 c -37.954,-9.631 -77.39649,-17.67578 -129.27149,-17.67578 z m 683.26949,17.67578 v 60.55469 h 40.1211 l -59.2246,145.70117 -59.2226,-145.70117 h 40.2051 v -60.55274 h -168.3106 v 60.55274 h 40.2109 l 104.5371,257.23242 c -12.101,37.004 -50.7927,58.25781 -85.0937,58.25781 -7.404,0 -21.5161,-1.3268 -32.2891,-3.3418 l -7.3769,74.02735 c 16.789,4.034 40.9995,6.05078 59.1855,6.05078 73.966,0 122.5326,-67.1082 152.1016,-138.4082 L 1513.916,209.61332 h 40.209 V 149.06055 Z M 356.97266,213.22852 c 42.935,2.295 77.52081,35.97276 81.25781,78.50976 v 2.80274 h -9.79102 v 9.77148 h 9.79297 v 2.67578 c -3.717,42.557 -38.31076,76.25578 -81.25976,78.55078 v -9.58789 h -9.77149 v 9.5625 c -43.775,-2.551 -78.79022,-37.72236 -81.07422,-81.56835 h 9.71289 v -9.77149 h -9.66211 c 2.596,-43.542 37.46644,-78.37701 81.02344,-80.91601 v 9.60937 h 9.77149 z m 31.71679,43.66211 -31.52343,27.46875 c -1.591,-0.57 -3.29232,-0.89649 -5.07032,-0.89649 -8.506,0 -15.39257,7.08922 -15.39258,15.82422 0,8.745 6.88658,15.83203 15.39258,15.83203 8.502,0 15.4043,-7.08703 15.4043,-15.83203 0,-1.659 -0.25189,-3.25676 -0.71289,-4.75976 l 28.10742,-31.42774 z M 1522.8594,405.63672 c -17.14,0 -30.9404,13.62458 -30.9414,30.76758 0,17.138 13.8004,30.75976 30.9414,30.75976 17.14,0 31.0234,-13.62176 31.0234,-30.75976 0,-17.143 -13.8824,-30.76758 -31.0234,-30.76758 z m 0,5.1875 c 14.239,0 25.8379,11.33508 25.8379,25.58008 0,14.237 -11.5989,25.92383 -25.8379,25.92383 -14.236,0 -25.7568,-11.68783 -25.7578,-25.92383 0,-14.244 11.5218,-25.58008 25.7578,-25.58008 z m -11.7754,10.45508 v 30.23828 h 6.9414 v -9.14062 h 4.3047 l 6.2422,9.14062 h 8.5254 l -7.5567,-10.37305 c 3.864,-1.14 6.3262,-4.48443 6.3262,-9.39843 0,-6.686 -4.6598,-10.4668 -12.1328,-10.4668 z m 6.9414,6.06641 h 5.709 c 2.995,0 5.1035,1.23734 5.1035,4.40234 0,3.334 -2.1074,4.5625 -5.2754,4.5625 h -5.5371 z"
|
||||||
|
clip-path="none"
|
||||||
|
mask="none" />
|
||||||
|
</svg>
|
||||||
|
<!-- version: 20171223, original size: 1554.125 601.84186, border: 3% -->
|
After Width: | Height: | Size: 4.2 KiB |
Loading…
Reference in a new issue