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-03 23:31:02 -08:00
|
|
|
import gql from "graphql-tag";
|
|
|
|
import { useQuery } from "@apollo/client";
|
|
|
|
|
2021-01-04 01:17:30 -08:00
|
|
|
import { ErrorMessage, Heading1, useCommonStyles } from "./util";
|
2021-01-04 00:10:35 -08:00
|
|
|
import {
|
|
|
|
getVisibleLayers,
|
|
|
|
petAppearanceFragmentForGetVisibleLayers,
|
2021-01-04 00:26:05 -08:00
|
|
|
itemAppearanceFragmentForGetVisibleLayers,
|
2021-01-04 00:10:35 -08:00
|
|
|
} from "./components/useOutfitAppearance";
|
2021-01-03 23:31:02 -08:00
|
|
|
import HangerSpinner from "./components/HangerSpinner";
|
|
|
|
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>
|
|
|
|
<WIPCallout />
|
|
|
|
</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 {
|
|
|
|
outfits {
|
|
|
|
id
|
2021-01-03 23:36:00 -08:00
|
|
|
name
|
|
|
|
petAppearance {
|
|
|
|
id
|
2021-01-04 00:10:35 -08:00
|
|
|
layers {
|
|
|
|
id
|
|
|
|
svgUrl
|
|
|
|
imageUrl(size: $size)
|
|
|
|
}
|
|
|
|
...PetAppearanceForGetVisibleLayers
|
2021-01-03 23:36:00 -08:00
|
|
|
}
|
2021-01-04 00:26:05 -08:00
|
|
|
itemAppearances {
|
|
|
|
id
|
|
|
|
layers {
|
|
|
|
id
|
|
|
|
svgUrl
|
|
|
|
imageUrl(size: $size)
|
|
|
|
}
|
|
|
|
...ItemAppearanceForGetVisibleLayers
|
|
|
|
}
|
2021-01-03 23:31:02 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-04 00:10:35 -08:00
|
|
|
${petAppearanceFragmentForGetVisibleLayers}
|
2021-01-04 00:26:05 -08:00
|
|
|
${itemAppearanceFragmentForGetVisibleLayers}
|
2021-01-03 23:31:02 -08:00
|
|
|
`,
|
2021-01-04 00:10:35 -08:00
|
|
|
{ variables: { size: "SIZE_" + getBestImageSize() }, 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-04 00:26:05 -08:00
|
|
|
const thumbnailUrl = buildOutfitThumbnailUrl(
|
|
|
|
outfit.petAppearance,
|
|
|
|
outfit.itemAppearances
|
|
|
|
);
|
2021-01-04 00:10:35 -08:00
|
|
|
|
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 21:20:07 -08:00
|
|
|
as="a"
|
|
|
|
href={`https://impress.openneo.net/outfits/${outfit.id}`}
|
|
|
|
_hover={{ transform: `scale(1.05)` }}
|
|
|
|
_focus={{
|
|
|
|
transform: `scale(1.05)`,
|
|
|
|
boxShadow: "outline",
|
|
|
|
outline: "none",
|
|
|
|
}}
|
2021-01-04 00:10:35 -08:00
|
|
|
>
|
2021-01-04 00:39:15 -08:00
|
|
|
<Box
|
|
|
|
as="img"
|
|
|
|
src={thumbnailUrl}
|
|
|
|
width={150}
|
|
|
|
height={150}
|
|
|
|
marginBottom="2"
|
|
|
|
borderRadius="md"
|
2021-01-04 01:17:30 -08:00
|
|
|
background="gray.600"
|
2021-01-04 00:45:52 -08:00
|
|
|
transition="all 0.2s"
|
2021-01-04 00:39:15 -08:00
|
|
|
/>
|
2021-01-04 00:10:35 -08:00
|
|
|
<Box>{outfit.name}</Box>
|
|
|
|
</Flex>
|
2021-01-03 23:31:02 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-01-04 00:10:35 -08:00
|
|
|
function buildOutfitThumbnailUrl(petAppearance, itemAppearances) {
|
|
|
|
const size = getBestImageSize();
|
|
|
|
const visibleLayers = getVisibleLayers(petAppearance, itemAppearances);
|
|
|
|
const layerUrls = visibleLayers.map(
|
|
|
|
(layer) => layer.svgUrl || layer.imageUrl
|
|
|
|
);
|
|
|
|
|
|
|
|
return `/api/outfitImage?size=${size}&layerUrls=${layerUrls.join(",")}`;
|
|
|
|
}
|
|
|
|
|
2021-01-04 00:26:05 -08:00
|
|
|
/**
|
|
|
|
* getBestImageSize returns the right image size to render at 150x150, for the
|
|
|
|
* current device.
|
|
|
|
*
|
|
|
|
* On high-DPI devices, we'll download a 300x300 image to render at 150x150
|
|
|
|
* scale. On standard-DPI devices, we'll download a 150x150 image, to save
|
|
|
|
* bandwidth.
|
|
|
|
*/
|
2021-01-04 00:10:35 -08:00
|
|
|
function getBestImageSize() {
|
|
|
|
if (window.devicePixelRatio > 1) {
|
|
|
|
return 300;
|
|
|
|
} else {
|
|
|
|
return 150;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-03 23:31:02 -08:00
|
|
|
export default UserOutfitsPage;
|