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;
|
||||
|
||||
-- Public data tables: read
|
||||
GRANT SELECT ON campaigns TO impress2020;
|
||||
GRANT SELECT ON colors 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 item_translations 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>
|
||||
<ChakraLink>Privacy Policy (Sep 2022)</ChakraLink>
|
||||
</Link>
|
||||
<Link href="/donate" passHref>
|
||||
<ChakraLink>Donors</ChakraLink>
|
||||
</Link>
|
||||
<ChakraLink href={classicDTIUrl}>Classic DTI</ChakraLink>
|
||||
</HStack>
|
||||
<Box as="p" opacity="0.75">
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React from "react";
|
||||
import { css } from "@emotion/react";
|
||||
import { VStack } from "@chakra-ui/react";
|
||||
|
||||
import { Heading1, Heading2, Heading3 } from "./util";
|
||||
import { useAuthModeFeatureFlag } from "./components/useCurrentUser";
|
||||
import Head from "next/head";
|
||||
import TextContent from "./components/TextContent";
|
||||
|
||||
function PrivacyPolicyPage() {
|
||||
const [authMode] = useAuthModeFeatureFlag();
|
||||
|
@ -15,34 +15,18 @@ function PrivacyPolicyPage() {
|
|||
<title>Privacy Policy | Dress to Impress</title>
|
||||
</Head>
|
||||
<Heading1 marginBottom="4">Our privacy policy</Heading1>
|
||||
<VStack
|
||||
spacing="4"
|
||||
alignItems="flex-start"
|
||||
css={css`
|
||||
max-width: 800px;
|
||||
|
||||
p {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
h2,
|
||||
h3 {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<TextContent maxWidth="800px">
|
||||
<VStack spacing="4" alignItems="flex-start">
|
||||
<section>
|
||||
<p>
|
||||
Hi, friends! Dress to Impress collects certain personal data. Here's
|
||||
how we use it!
|
||||
Hi, friends! Dress to Impress collects certain personal data.
|
||||
Here's how we use it!
|
||||
</p>
|
||||
<p>
|
||||
First off, we'll <em>never</em> sell your private data, ever. It'll
|
||||
only be available to you and our small trusted staff—and we'll only
|
||||
use it to serve you directly, debug site issues, and help you share
|
||||
your creations with others.
|
||||
First off, we'll <em>never</em> sell your private data, ever.
|
||||
It'll only be available to you and our small trusted staff—and
|
||||
we'll only use it to serve you directly, debug site issues, and
|
||||
help you share your creations with others.
|
||||
</p>
|
||||
</section>
|
||||
{authMode === "auth0" && (
|
||||
|
@ -55,10 +39,10 @@ function PrivacyPolicyPage() {
|
|||
account creation and login.
|
||||
</p>
|
||||
<p>
|
||||
We made this decision because authentication is difficult to write
|
||||
and maintain securely. We felt that Auth0 was the smoothest and
|
||||
most secure experience we could offer, especially as a small team
|
||||
of volunteers{" "}
|
||||
We made this decision because authentication is difficult to
|
||||
write and maintain securely. We felt that Auth0 was the
|
||||
smoothest and most secure experience we could offer, especially
|
||||
as a small team of volunteers{" "}
|
||||
<span role="img" aria-label="Sweat smile emoji">
|
||||
😅
|
||||
</span>
|
||||
|
@ -67,15 +51,15 @@ function PrivacyPolicyPage() {
|
|||
<a href="https://auth0.com/legal/ss-tos">
|
||||
Auth0's terms of service
|
||||
</a>{" "}
|
||||
commit to treating your user data as confidential information, not
|
||||
to be shared with anyone else, and only to be used as part of
|
||||
Dress to Impress. (The details are in Sections 6 and 7!)
|
||||
commit to treating your user data as confidential information,
|
||||
not to be shared with anyone else, and only to be used as part
|
||||
of Dress to Impress. (The details are in Sections 6 and 7!)
|
||||
</p>
|
||||
<p>
|
||||
When signing up, Auth0 will ask for a username, password, and
|
||||
email address. They store your password as a <em>hash</em> (which,
|
||||
colloquially, is like a one-way encryption), rather than as the
|
||||
plain password itself.
|
||||
email address. They store your password as a <em>hash</em>{" "}
|
||||
(which, colloquially, is like a one-way encryption), rather than
|
||||
as the plain password itself.
|
||||
</p>
|
||||
<p>
|
||||
Some user accounts were created before we moved to Auth0. For
|
||||
|
@ -89,8 +73,8 @@ function PrivacyPolicyPage() {
|
|||
<Heading2>Analytics and logging</Heading2>
|
||||
<p>
|
||||
To understand how people use our site, we use a service called{" "}
|
||||
<a href="https://plausible.io/">Plausible</a>. Every time you visit
|
||||
a page, we send them a{" "}
|
||||
<a href="https://plausible.io/">Plausible</a>. Every time you
|
||||
visit a page, we send them a{" "}
|
||||
<a href="https://plausible.io/data-policy">
|
||||
small packet of information
|
||||
</a>
|
||||
|
@ -105,29 +89,29 @@ function PrivacyPolicyPage() {
|
|||
</a>
|
||||
</p>
|
||||
<p>
|
||||
We also use a service called <a href="https://sentry.io/">Sentry</a>{" "}
|
||||
to track errors. When you encounter an error on our site, we send a
|
||||
copy of it to our Sentry account, to help us debug it later. This
|
||||
might sometimes include personal data, but Sentry will only share it
|
||||
with us.{" "}
|
||||
We also use a service called{" "}
|
||||
<a href="https://sentry.io/">Sentry</a> to track errors. When you
|
||||
encounter an error on our site, we send a copy of it to our Sentry
|
||||
account, to help us debug it later. This might sometimes include
|
||||
personal data, but Sentry will only share it with us.{" "}
|
||||
<a href="https://sentry.io/legal/dpa/2.0.0/">
|
||||
Here's their data policy.
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
We also use <a href="https://www.linode.com/">Linode</a> and{" "}
|
||||
<a href="https://www.fastly.com/">Fastly</a> for web hosting. Linode
|
||||
stores our database, and handles most web traffic dealing with
|
||||
personal data. Personal data also travels through Fastly's servers
|
||||
temporarily, but they only store aggregate usage logs for us, not
|
||||
any personally-identifying data.
|
||||
<a href="https://www.fastly.com/">Fastly</a> for web hosting.
|
||||
Linode stores our database, and handles most web traffic dealing
|
||||
with personal data. Personal data also travels through Fastly's
|
||||
servers temporarily, but they only store aggregate usage logs for
|
||||
us, not any personally-identifying data.
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<Heading2>Creations and contributions</Heading2>
|
||||
<p>
|
||||
People use Dress to Impress to create, share, and communicate! Some
|
||||
of these things are public, some are private, and some are
|
||||
People use Dress to Impress to create, share, and communicate!
|
||||
Some of these things are public, some are private, and some are
|
||||
configurable.
|
||||
</p>
|
||||
<Heading3>Outfits</Heading3>
|
||||
|
@ -140,8 +124,8 @@ function PrivacyPolicyPage() {
|
|||
share outfits by URL without logging in.
|
||||
</p>
|
||||
<p>
|
||||
When you save an outfit to your account, it's somewhat private, but
|
||||
somewhat public.
|
||||
When you save an outfit to your account, it's somewhat private,
|
||||
but somewhat public.
|
||||
</p>
|
||||
<p>
|
||||
It's private in the sense that there is no central place where
|
||||
|
@ -155,7 +139,8 @@ function PrivacyPolicyPage() {
|
|||
<p>
|
||||
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
|
||||
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>
|
||||
<Heading3>Item lists</Heading3>
|
||||
<p>
|
||||
|
@ -163,8 +148,8 @@ function PrivacyPolicyPage() {
|
|||
and want, by saving item lists to their account.
|
||||
</p>
|
||||
<p>
|
||||
These lists are private by default, but can be configured to either
|
||||
be "public" or "trading" as well.
|
||||
These lists are private by default, but can be configured to
|
||||
either be "public" or "trading" as well.
|
||||
</p>
|
||||
<p>
|
||||
The "public" status means that anyone who knows your Dress to
|
||||
|
@ -182,9 +167,9 @@ function PrivacyPolicyPage() {
|
|||
</p>
|
||||
<p>
|
||||
Sometimes, this will download new public outfit data that we've
|
||||
never seen before. For example, you might show us a Draik (a species
|
||||
of Neopet) wearing a new item, and we don't have data for a Draik
|
||||
wearing that item yet.
|
||||
never seen before. For example, you might show us a Draik (a
|
||||
species of Neopet) wearing a new item, and we don't have data for
|
||||
a Draik wearing that item yet.
|
||||
</p>
|
||||
<p>
|
||||
When that happens, we'll extract that specific piece of data from
|
||||
|
@ -193,26 +178,27 @@ function PrivacyPolicyPage() {
|
|||
"modeling".
|
||||
</p>
|
||||
<p>
|
||||
When you model new data for us, it's separated from your pet. Users
|
||||
can't discover what pet modeled a certain piece of data, or what
|
||||
else that pet was wearing.
|
||||
When you model new data for us, it's separated from your pet.
|
||||
Users can't discover what pet modeled a certain piece of data, or
|
||||
what else that pet was wearing.
|
||||
</p>
|
||||
<p>
|
||||
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
|
||||
places, including a list of the most recent contributions, and it
|
||||
will add points to your account that contribute to a public high
|
||||
score list. This will publicly display your username.
|
||||
account for the new "contribution". This will appear in a number
|
||||
of places, including a list of the most recent contributions, and
|
||||
it will add points to your account that contribute to a public
|
||||
high score list. This will publicly display your username.
|
||||
</p>
|
||||
<p>
|
||||
Right now, modeling contributions from logged-in users are always
|
||||
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
|
||||
contributions removed from the site, please use the contact link at
|
||||
the bottom of the page.
|
||||
contributions removed from the site, please use the contact link
|
||||
at the bottom of the page.
|
||||
</p>
|
||||
</section>
|
||||
</VStack>
|
||||
</TextContent>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { css } from "@emotion/react";
|
||||
import { VStack } from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
|
||||
import { Heading1, Heading2 } from "./util";
|
||||
import TextContent from "./components/TextContent";
|
||||
|
||||
function TermsOfUsePage() {
|
||||
return (
|
||||
|
@ -11,30 +11,14 @@ function TermsOfUsePage() {
|
|||
<title>Terms of Use | Dress to Impres</title>
|
||||
</Head>
|
||||
<Heading1 marginBottom="4">Our terms of use</Heading1>
|
||||
<VStack
|
||||
spacing="4"
|
||||
alignItems="flex-start"
|
||||
css={css`
|
||||
max-width: 800px;
|
||||
|
||||
p {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
h2,
|
||||
h3 {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<TextContent maxWidth="800px">
|
||||
<VStack spacing="4" alignItems="flex-start">
|
||||
<section>
|
||||
<p>
|
||||
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
|
||||
they're clear, and we take them very seriously. Thank you for taking
|
||||
the time to read!
|
||||
they're clear, and we take them very seriously. Thank you for
|
||||
taking the time to read!
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
|
@ -58,67 +42,69 @@ function TermsOfUsePage() {
|
|||
<section>
|
||||
<Heading2>What you can post on this service</Heading2>
|
||||
<p>
|
||||
<strong>Keep it Neoboard-safe.</strong> Neopets.com allows links to
|
||||
Dress to Impress, so everything needs to be safe for Neopians of all
|
||||
ages! Please keep all content "PG" and appropriate for young
|
||||
community members, just like you do on Neopets.com. (That said, the
|
||||
rules on the Neoboards haven't always been morally right, such as
|
||||
when LGBTQIA+ discussion was banned. We'll always diverge from those
|
||||
rules when it's ethically appropriate!)
|
||||
<strong>Keep it Neoboard-safe.</strong> Neopets.com allows links
|
||||
to Dress to Impress, so everything needs to be safe for Neopians
|
||||
of all ages! Please keep all content "PG" and appropriate for
|
||||
young community members, just like you do on Neopets.com. (That
|
||||
said, the rules on the Neoboards haven't always been morally
|
||||
right, such as when LGBTQIA+ discussion was banned. We'll always
|
||||
diverge from those rules when it's ethically appropriate!)
|
||||
</p>
|
||||
<p>
|
||||
<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,
|
||||
so we err on the side of safety and ban <em>all</em> sales. If
|
||||
you're selling something, please do it in a community where trust
|
||||
and reputation can be managed more appropriately, and please make
|
||||
sure it's in line with Neopets's terms.
|
||||
have the capacity to validate who is and isn't a legitimate
|
||||
seller, so we err on the side of safety and ban <em>all</em>{" "}
|
||||
sales. If you're selling something, please do it in a community
|
||||
where trust and reputation can be managed more appropriately, and
|
||||
please make sure it's in line with Neopets's terms.
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<Heading2>How you can use our data</Heading2>
|
||||
<p>
|
||||
<strong>Be thoughtful using Neopets's data.</strong> While Dress to
|
||||
Impress has a license to distribute Neopets data and images, we
|
||||
<strong>Be thoughtful using Neopets's data.</strong> While Dress
|
||||
to Impress has a license to distribute Neopets data and images, we
|
||||
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
|
||||
find on this site, and make sure you're complying with their
|
||||
licensing agreements and fair use laws, especially for derived works
|
||||
like outfits. But personal use, and usage that stays on our site,
|
||||
are always okay!
|
||||
Please think carefully about how you use Neopets's art and data
|
||||
you find on this site, and make sure you're complying with their
|
||||
licensing agreements and fair use laws, especially for derived
|
||||
works like outfits. But personal use, and usage that stays on our
|
||||
site, are always okay!
|
||||
</p>
|
||||
<p>
|
||||
<strong>Be thoughtful using user-generated data.</strong> Some data
|
||||
posted to Dress to Impress is generated by our users, like their
|
||||
outfits and item lists. When you post those to Dress to Impress, you
|
||||
grant us a license to redistribute them with attribution as part of
|
||||
the site's functionality, respecting your privacy settings when
|
||||
applicable. But each user still owns their own creations, so only
|
||||
they can grant you permission to use or share it yourself.
|
||||
<strong>Be thoughtful using user-generated data.</strong> Some
|
||||
data posted to Dress to Impress is generated by our users, like
|
||||
their outfits and item lists. When you post those to Dress to
|
||||
Impress, you grant us a license to redistribute them with
|
||||
attribution as part of the site's functionality, respecting your
|
||||
privacy settings when applicable. But each user still owns their
|
||||
own creations, so only they can grant you permission to use or
|
||||
share it yourself.
|
||||
</p>
|
||||
<p>
|
||||
<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 help if we can. But please don't use our APIs without
|
||||
like to use our data to build something new, please contact us!
|
||||
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
|
||||
reliability issues for you. But we have a few folks who use Dress to
|
||||
Impress for things like Discord bots, and we'd like to support you
|
||||
and your community too!
|
||||
reliability issues for you. But we have a few folks who use Dress
|
||||
to Impress for things like Discord bots, and we'd like to support
|
||||
you and your community too!
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<Heading2>Warranty and liability</Heading2>
|
||||
<p>
|
||||
<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,
|
||||
sometimes our data is out-of-date, and sometimes an item looks
|
||||
different on our site than on Neopets.com. We're glad to be a
|
||||
resource for users buying Neocash items, but as an unofficial
|
||||
service we simply can't make guarantees, and we encourage you to
|
||||
check other sources before making a purchase.
|
||||
best to keep the customization on our site in sync with
|
||||
Neopets.com, sometimes our data is out-of-date, and sometimes an
|
||||
item looks different on our site than on Neopets.com. We're glad
|
||||
to be a resource for users buying Neocash items, but as an
|
||||
unofficial service we simply can't make guarantees, and we
|
||||
encourage you to check other sources before making a purchase.
|
||||
</p>
|
||||
</section>
|
||||
</VStack>
|
||||
</TextContent>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -139,14 +139,14 @@ function UserOutfitsPageContent() {
|
|||
);
|
||||
}
|
||||
|
||||
function OutfitCard({ outfit }) {
|
||||
export function OutfitCard({ outfit, caption = null, alt = null }) {
|
||||
const image = (
|
||||
<ClassNames>
|
||||
{({ css }) => (
|
||||
<OutfitThumbnail
|
||||
outfitId={outfit.id}
|
||||
updatedAt={outfit.updatedAt}
|
||||
alt={buildOutfitAltText(outfit)}
|
||||
alt={alt ?? buildOutfitAltText(outfit)}
|
||||
// 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
|
||||
// it if it's still loading. It's normal for these to take a second
|
||||
|
@ -184,7 +184,7 @@ function OutfitCard({ outfit }) {
|
|||
outline: "none",
|
||||
}}
|
||||
>
|
||||
<OutfitCardLayout image={image} caption={outfit.name} />
|
||||
<OutfitCardLayout image={image} caption={caption ?? outfit.name} />
|
||||
</Box>
|
||||
</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