2021-01-03 23:31:02 -08:00
|
|
|
import React from "react";
|
2021-01-04 01:17:30 -08:00
|
|
|
import { Box, Center, Flex, Wrap, WrapItem } from "@chakra-ui/react";
|
2021-01-08 00:35:56 -08:00
|
|
|
import { ClassNames } from "@emotion/react";
|
2021-01-03 23:31:02 -08:00
|
|
|
import gql from "graphql-tag";
|
|
|
|
import { useQuery } from "@apollo/client";
|
2021-01-04 22:29:39 -08:00
|
|
|
import { Link } from "react-router-dom";
|
2021-01-03 23:31:02 -08:00
|
|
|
|
2021-01-04 01:17:30 -08:00
|
|
|
import { ErrorMessage, Heading1, useCommonStyles } from "./util";
|
2021-01-03 23:31:02 -08:00
|
|
|
import HangerSpinner from "./components/HangerSpinner";
|
2021-01-08 01:23:24 -08:00
|
|
|
import OutfitThumbnail, {
|
|
|
|
outfitThumbnailFragment,
|
|
|
|
getOutfitThumbnailRenderSize,
|
|
|
|
} from "./components/OutfitThumbnail";
|
2021-01-03 23:31:02 -08:00
|
|
|
import useRequireLogin from "./components/useRequireLogin";
|
2021-01-04 00:12:25 -08:00
|
|
|
import WIPCallout from "./components/WIPCallout";
|
2021-01-03 23:31:02 -08:00
|
|
|
|
|
|
|
function UserOutfitsPage() {
|
|
|
|
return (
|
|
|
|
<Box>
|
2021-01-04 00:12:25 -08:00
|
|
|
<Flex justifyContent="space-between" marginBottom="4">
|
|
|
|
<Heading1>Your outfits</Heading1>
|
2021-01-17 08:05:05 -08:00
|
|
|
<WIPCallout details="This list doesn't work well with a lot of outfits yet. We'll paginate it soon! And starred outfits are coming, too!" />
|
2021-01-04 00:12:25 -08:00
|
|
|
</Flex>
|
2021-01-03 23:31:02 -08:00
|
|
|
<UserOutfitsPageContent />
|
|
|
|
</Box>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function UserOutfitsPageContent() {
|
|
|
|
const { isLoading: userLoading } = useRequireLogin();
|
|
|
|
|
|
|
|
const { loading: queryLoading, error, data } = useQuery(
|
|
|
|
gql`
|
2021-01-04 00:10:35 -08:00
|
|
|
query UserOutfitsPageContent($size: LayerImageSize) {
|
2021-01-03 23:31:02 -08:00
|
|
|
currentUser {
|
2021-01-04 22:45:27 -08:00
|
|
|
id
|
2021-01-03 23:31:02 -08:00
|
|
|
outfits {
|
|
|
|
id
|
2021-01-03 23:36:00 -08:00
|
|
|
name
|
2021-01-08 01:23:24 -08:00
|
|
|
|
|
|
|
...OutfitThumbnailFragment
|
|
|
|
|
|
|
|
# For alt text
|
2021-01-03 23:36:00 -08:00
|
|
|
petAppearance {
|
2021-01-08 00:35:09 -08:00
|
|
|
species {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
}
|
|
|
|
color {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
}
|
2021-01-04 00:26:05 -08:00
|
|
|
}
|
2021-01-08 00:35:09 -08:00
|
|
|
wornItems {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
}
|
2021-01-03 23:31:02 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-08 01:23:24 -08:00
|
|
|
${outfitThumbnailFragment}
|
2021-01-03 23:31:02 -08:00
|
|
|
`,
|
2021-01-08 01:23:24 -08:00
|
|
|
{
|
|
|
|
variables: {
|
|
|
|
// NOTE: This parameter is used inside `OutfitThumbnailFragment`!
|
|
|
|
size: "SIZE_" + getOutfitThumbnailRenderSize(),
|
|
|
|
},
|
2021-01-21 14:57:21 -08:00
|
|
|
context: { sendAuth: true },
|
2021-01-08 01:23:24 -08:00
|
|
|
skip: userLoading,
|
|
|
|
}
|
2021-01-03 23:31:02 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
if (userLoading || queryLoading) {
|
|
|
|
return (
|
|
|
|
<Center>
|
|
|
|
<HangerSpinner />
|
|
|
|
</Center>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
return <ErrorMessage>Error loading outfits: {error.message}</ErrorMessage>;
|
|
|
|
}
|
|
|
|
|
2021-01-04 00:10:35 -08:00
|
|
|
const outfits = data.currentUser.outfits;
|
|
|
|
|
2021-01-04 00:47:39 -08:00
|
|
|
if (outfits.length === 0) {
|
|
|
|
return (
|
|
|
|
<Box>You don't have any outfits yet. Maybe you can create some!</Box>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-01-04 00:10:35 -08:00
|
|
|
return (
|
2021-01-04 01:36:01 -08:00
|
|
|
<Wrap spacing="4" justify="space-around">
|
2021-01-04 00:10:35 -08:00
|
|
|
{outfits.map((outfit) => (
|
|
|
|
<WrapItem key={outfit.id}>
|
|
|
|
<OutfitCard outfit={outfit} />
|
|
|
|
</WrapItem>
|
|
|
|
))}
|
|
|
|
</Wrap>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function OutfitCard({ outfit }) {
|
2021-01-08 00:35:09 -08:00
|
|
|
const image = (
|
2021-01-08 00:35:56 -08:00
|
|
|
<ClassNames>
|
|
|
|
{({ css }) => (
|
2021-01-08 01:23:24 -08:00
|
|
|
<OutfitThumbnail
|
|
|
|
petAppearance={outfit.petAppearance}
|
|
|
|
itemAppearances={outfit.itemAppearances}
|
2021-01-08 00:35:56 -08:00
|
|
|
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
|
|
|
|
// to load on a new device, and the flash of text is unhelpful.
|
|
|
|
color="white"
|
|
|
|
fontSize="xs"
|
2021-01-08 01:26:05 -08:00
|
|
|
width={150}
|
|
|
|
height={150}
|
2021-01-08 00:35:56 -08:00
|
|
|
overflow="auto"
|
|
|
|
className={css`
|
|
|
|
&:-moz-loading {
|
|
|
|
visibility: hidden;
|
|
|
|
}
|
2021-01-08 01:26:05 -08:00
|
|
|
|
|
|
|
&:-moz-broken {
|
|
|
|
padding: 0.5rem;
|
|
|
|
}
|
2021-01-08 00:35:56 -08:00
|
|
|
`}
|
|
|
|
/>
|
2021-01-08 00:35:09 -08:00
|
|
|
)}
|
2021-01-08 00:35:56 -08:00
|
|
|
</ClassNames>
|
2021-01-04 00:26:05 -08:00
|
|
|
);
|
2021-01-04 00:10:35 -08:00
|
|
|
|
2021-01-08 00:35:09 -08:00
|
|
|
return (
|
|
|
|
<Box
|
|
|
|
as={Link}
|
|
|
|
to={`/outfits/${outfit.id}`}
|
|
|
|
display="block"
|
|
|
|
transition="all 0.2s"
|
|
|
|
_hover={{ transform: `scale(1.05)` }}
|
|
|
|
_focus={{
|
|
|
|
transform: `scale(1.05)`,
|
|
|
|
boxShadow: "outline",
|
|
|
|
outline: "none",
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<OutfitCardLayout image={image} caption={outfit.name} />
|
|
|
|
</Box>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function OutfitCardLayout({ image, caption }) {
|
2021-01-04 01:17:30 -08:00
|
|
|
const { brightBackground } = useCommonStyles();
|
2021-01-04 00:45:52 -08:00
|
|
|
|
2021-01-03 23:31:02 -08:00
|
|
|
return (
|
2021-01-04 00:10:35 -08:00
|
|
|
<Flex
|
|
|
|
direction="column"
|
|
|
|
alignItems="center"
|
|
|
|
textAlign="center"
|
|
|
|
boxShadow="md"
|
|
|
|
borderRadius="md"
|
|
|
|
padding="3"
|
|
|
|
width="calc(150px + 2em)"
|
2021-01-04 01:17:30 -08:00
|
|
|
backgroundColor={brightBackground}
|
2021-01-04 00:45:52 -08:00
|
|
|
transition="all 0.2s"
|
2021-01-04 00:10:35 -08:00
|
|
|
>
|
2021-01-04 00:39:15 -08:00
|
|
|
<Box
|
|
|
|
width={150}
|
|
|
|
height={150}
|
|
|
|
marginBottom="2"
|
|
|
|
borderRadius="md"
|
2021-01-04 01:17:30 -08:00
|
|
|
background="gray.600"
|
2021-01-08 01:26:05 -08:00
|
|
|
overflow="hidden"
|
2021-01-08 00:35:09 -08:00
|
|
|
>
|
|
|
|
{image}
|
|
|
|
</Box>
|
|
|
|
<Box>{caption}</Box>
|
2021-01-04 00:10:35 -08:00
|
|
|
</Flex>
|
2021-01-03 23:31:02 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-01-08 00:35:56 -08:00
|
|
|
function buildOutfitAltText(outfit) {
|
|
|
|
const { petAppearance, wornItems } = outfit;
|
|
|
|
const { species, color } = petAppearance;
|
|
|
|
|
|
|
|
let altText = "";
|
|
|
|
|
|
|
|
const petDescription = `${color.name} ${species.name}`;
|
|
|
|
altText += petDescription;
|
|
|
|
|
|
|
|
if (wornItems.length > 0) {
|
|
|
|
const itemNames = wornItems
|
|
|
|
.map((item) => item.name)
|
|
|
|
.sort()
|
|
|
|
.join(", ");
|
|
|
|
altText += ` wearing ${itemNames}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return altText;
|
|
|
|
}
|
|
|
|
|
2021-01-03 23:31:02 -08:00
|
|
|
export default UserOutfitsPage;
|