2020-04-25 04:33:05 -07:00
|
|
|
import React from "react";
|
|
|
|
import gql from "graphql-tag";
|
2020-05-03 01:52:39 -07:00
|
|
|
import useFetch from "use-http";
|
2020-04-25 04:33:05 -07:00
|
|
|
import { useQuery } from "@apollo/react-hooks";
|
|
|
|
import { Box, Flex, Select, Text, useToast } from "@chakra-ui/core";
|
|
|
|
|
|
|
|
import { Delay } from "./util";
|
|
|
|
|
2020-04-26 01:44:26 -07:00
|
|
|
/**
|
|
|
|
* SpeciesColorPicker lets the user pick the species/color of their pet.
|
|
|
|
*
|
|
|
|
* It preloads all species, colors, and valid species/color pairs; and then
|
|
|
|
* ensures that the outfit is always in a valid state.
|
|
|
|
*/
|
2020-05-02 13:40:37 -07:00
|
|
|
function SpeciesColorPicker({ outfitState, dispatchToOutfit }) {
|
2020-04-25 04:33:05 -07:00
|
|
|
const toast = useToast();
|
2020-05-03 01:52:39 -07:00
|
|
|
const { loading: loadingMeta, error: errorMeta, data: meta } = useQuery(gql`
|
2020-04-25 04:33:05 -07:00
|
|
|
query {
|
|
|
|
allSpecies {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
}
|
|
|
|
|
|
|
|
allColors {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`);
|
2020-05-03 01:52:39 -07:00
|
|
|
const {
|
|
|
|
loading: loadingValids,
|
|
|
|
error: errorValids,
|
|
|
|
data: validsBuffer,
|
|
|
|
} = useFetch("/api/validPetPoses", { responseType: "arrayBuffer" }, []);
|
|
|
|
const valids = React.useMemo(
|
|
|
|
() => validsBuffer && new DataView(validsBuffer),
|
|
|
|
[validsBuffer]
|
|
|
|
);
|
2020-04-25 04:33:05 -07:00
|
|
|
|
2020-05-03 01:52:39 -07:00
|
|
|
const allColors = (meta && [...meta.allColors]) || [];
|
2020-04-25 04:33:05 -07:00
|
|
|
allColors.sort((a, b) => a.name.localeCompare(b.name));
|
2020-05-03 01:52:39 -07:00
|
|
|
const allSpecies = (meta && [...meta.allSpecies]) || [];
|
2020-04-25 04:33:05 -07:00
|
|
|
allSpecies.sort((a, b) => a.name.localeCompare(b.name));
|
2020-04-26 01:44:26 -07:00
|
|
|
|
2020-05-03 01:52:39 -07:00
|
|
|
if (loadingMeta || loadingValids) {
|
2020-04-25 04:33:05 -07:00
|
|
|
return (
|
|
|
|
<Delay ms={5000}>
|
|
|
|
<Text color="gray.50" textShadow="md">
|
|
|
|
Loading species/color data…
|
|
|
|
</Text>
|
|
|
|
</Delay>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-05-03 01:52:39 -07:00
|
|
|
if (errorMeta || errorValids) {
|
2020-04-25 04:33:05 -07:00
|
|
|
return (
|
|
|
|
<Text color="gray.50" textShadow="md">
|
|
|
|
Error loading species/color data.
|
|
|
|
</Text>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-04-26 01:44:26 -07:00
|
|
|
// When the color changes, check if the new pair is valid, and update the
|
|
|
|
// outfit if so!
|
2020-04-25 04:33:05 -07:00
|
|
|
const onChangeColor = (e) => {
|
|
|
|
const speciesId = outfitState.speciesId;
|
|
|
|
const colorId = e.target.value;
|
2020-05-03 01:52:39 -07:00
|
|
|
if (pairIsValid(valids, meta, speciesId, colorId)) {
|
2020-04-25 04:33:05 -07:00
|
|
|
dispatchToOutfit({ type: "changeColor", colorId: e.target.value });
|
|
|
|
} else {
|
|
|
|
const species = allSpecies.find((s) => s.id === speciesId);
|
|
|
|
const color = allColors.find((c) => c.id === colorId);
|
|
|
|
toast({
|
|
|
|
title: `We haven't seen a ${color.name} ${species.name} before! 😓`,
|
|
|
|
status: "warning",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-04-26 01:44:26 -07:00
|
|
|
// When the species changes, check if the new pair is valid, and update the
|
|
|
|
// outfit if so!
|
2020-04-25 04:33:05 -07:00
|
|
|
const onChangeSpecies = (e) => {
|
|
|
|
const colorId = outfitState.colorId;
|
|
|
|
const speciesId = e.target.value;
|
2020-05-03 01:52:39 -07:00
|
|
|
if (pairIsValid(valids, meta, speciesId, colorId)) {
|
2020-04-25 04:33:05 -07:00
|
|
|
dispatchToOutfit({ type: "changeSpecies", speciesId: e.target.value });
|
|
|
|
} else {
|
|
|
|
const species = allSpecies.find((s) => s.id === speciesId);
|
|
|
|
const color = allColors.find((c) => c.id === colorId);
|
|
|
|
toast({
|
|
|
|
title: `We haven't seen a ${color.name} ${species.name} before! 😓`,
|
2020-05-03 01:52:39 -07:00
|
|
|
status: "warning",
|
2020-04-25 04:33:05 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Flex direction="row">
|
|
|
|
<Select
|
|
|
|
aria-label="Pet color"
|
|
|
|
value={outfitState.colorId}
|
|
|
|
onChange={onChangeColor}
|
|
|
|
backgroundColor="gray.600"
|
|
|
|
color="gray.50"
|
|
|
|
border="none"
|
|
|
|
boxShadow="md"
|
2020-04-28 01:14:07 -07:00
|
|
|
width="auto"
|
2020-04-25 04:33:05 -07:00
|
|
|
>
|
|
|
|
{allColors.map((color) => (
|
|
|
|
<option key={color.id} value={color.id}>
|
|
|
|
{color.name}
|
|
|
|
</option>
|
|
|
|
))}
|
|
|
|
</Select>
|
2020-04-28 01:14:07 -07:00
|
|
|
<Box width="4" />
|
2020-04-25 04:33:05 -07:00
|
|
|
<Select
|
|
|
|
aria-label="Pet species"
|
|
|
|
value={outfitState.speciesId}
|
|
|
|
onChange={onChangeSpecies}
|
|
|
|
backgroundColor="gray.600"
|
|
|
|
color="gray.50"
|
|
|
|
border="none"
|
|
|
|
boxShadow="md"
|
2020-04-28 01:14:07 -07:00
|
|
|
width="auto"
|
2020-04-25 04:33:05 -07:00
|
|
|
>
|
|
|
|
{allSpecies.map((species) => (
|
|
|
|
<option key={species.id} value={species.id}>
|
|
|
|
{species.name}
|
|
|
|
</option>
|
|
|
|
))}
|
|
|
|
</Select>
|
|
|
|
</Flex>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-05-03 01:52:39 -07:00
|
|
|
function pairIsValid(valids, meta, speciesId, colorId) {
|
|
|
|
// Reading a bit table, owo!
|
|
|
|
const speciesIndex = speciesId - 1;
|
|
|
|
const colorIndex = colorId - 1;
|
|
|
|
const numColors = meta.allColors.length;
|
|
|
|
const pairByteIndex = speciesIndex * numColors + colorIndex;
|
|
|
|
const pairByte = valids.getUint8(pairByteIndex);
|
|
|
|
return pairByte !== 0;
|
|
|
|
}
|
|
|
|
|
2020-04-25 04:33:05 -07:00
|
|
|
export default SpeciesColorPicker;
|