load correct pet for species-specific items

uses the new canonical appearance GQL stuff :)

there's still an extra reload, we're not using the apollo cache correctly!
This commit is contained in:
Emi Matchu 2020-09-21 02:43:58 -07:00
parent 1b59b9631b
commit a6761a2403
4 changed files with 97 additions and 38 deletions

View file

@ -105,6 +105,8 @@ function StartOutfitForm({ onChange }) {
colorId={colorId}
idealPose={idealPose}
showPlaceholders
colorPlaceholderText="Blue"
speciesPlaceholderText="Acara"
onChange={(species, color, isValid, closestPose) => {
setSpeciesId(species.id);
setColorId(color.id);

View file

@ -443,41 +443,64 @@ function ItemPageOutfitPreview({ itemId }) {
[]
);
const [petState, setPetState] = React.useState({
// Start by looking up Acara appearance data.
speciesId: "1",
colorId: "8",
pose: idealPose,
// We'll fill this in once the canonical appearance data arrives.
speciesId: null,
colorId: null,
pose: null,
});
// Start by loading the "canonical" pet and item appearance for the outfit
// preview. We'll use this to initialize both the preview and the picker.
const { loading, error, data } = useQuery(gql`
query ItemPageOutfitPreview($itemId: ID!) {
item(id: $itemId) {
id
canonicalAppearance {
const { loading, error } = useQuery(
gql`
query ItemPageOutfitPreview($itemId: ID!) {
item(id: $itemId) {
id
...ItemAppearanceFragment
body {
canonicalAppearance {
id
canonicalAppearance {
...ItemAppearanceForOutfitPreview
body {
id
...PetAppearanceFragment
canonicalAppearance {
id
species {
id
}
color {
id
}
pose
...PetAppearanceForOutfitPreview
}
}
}
}
}
}
${itemAppearanceFragment}
${petAppearanceFragment}
`);
${itemAppearanceFragment}
${petAppearanceFragment}
`,
{
variables: { itemId },
onCompleted: (data) => {
const canonicalBody = data?.item?.canonicalAppearance?.body;
const canonicalPetAppearance = canonicalBody?.canonicalAppearance;
setPetState({
speciesId: canonicalPetAppearance?.species?.id,
colorId: canonicalPetAppearance?.color?.id,
pose: canonicalPetAppearance?.pose,
});
},
}
);
// To check whether the item is compatible with this pet, query for the
// appearance, but only against the cache. That way, we don't send a
// redundant network request just for this (the OutfitPreview component will
// handle it!), but we'll get an update once it arrives in the cache.
const { cachedData } = useQuery(
const { data: cachedData } = useQuery(
gql`
query ItemPageOutfitPreview_CacheOnly(
$itemId: ID!
@ -500,17 +523,29 @@ function ItemPageOutfitPreview({ itemId }) {
colorId: petState.colorId,
},
fetchPolicy: "cache-only",
onCompleted: (data) => console.log("data", data),
}
);
const borderColor = useColorModeValue("green.700", "green.400");
const errorColor = useColorModeValue("red.600", "red.400");
if (error) {
return <Box color="red.400">{error.message}</Box>;
}
// If the layers are null-y, then we're still loading. Otherwise, if the
// layers are an empty array, then we're incomaptible. Or, if they're a
// non-empty array, then we're compatible!
const layers = cachedData?.item?.appearanceOn?.layers;
const isIncompatible = Array.isArray(layers) && layers.length === 0;
const borderColor = useColorModeValue("green.700", "green.400");
const errorColor = useColorModeValue("red.600", "red.400");
console.log(
petState.speciesId,
petState.colorId,
itemId,
layers,
isIncompatible
);
return (
<VStack spacing="3" width="100%">
@ -531,6 +566,7 @@ function ItemPageOutfitPreview({ itemId }) {
colorId={petState.colorId}
pose={petState.pose}
wornItemIds={[itemId]}
isLoading={loading}
spinnerVariant="corner"
loadingDelayMs={2000}
/>

View file

@ -27,6 +27,7 @@ function OutfitPreview({
pose,
wornItemIds,
appearanceId = null,
isLoading = false,
placeholder,
loadingDelayMs,
spinnerVariant,
@ -57,7 +58,7 @@ function OutfitPreview({
return (
<OutfitLayers
loading={loading || loading2}
loading={isLoading || loading || loading2}
visibleLayers={loadedLayers}
placeholder={placeholder}
loadingDelayMs={loadingDelayMs}

View file

@ -21,6 +21,8 @@ function SpeciesColorPicker({
colorId,
idealPose,
showPlaceholders = false,
colorPlaceholderText = "",
speciesPlaceholderText = "",
stateMustAlwaysBeValid = false,
isDisabled = false,
size = "md",
@ -84,7 +86,12 @@ function SpeciesColorPicker({
_hover={{
borderColor: "green.400",
}}
isInvalid={valids && !pairIsValid(valids, speciesId, colorId)}
isInvalid={
valids &&
speciesId &&
colorId &&
!pairIsValid(valids, speciesId, colorId)
}
isDisabled={isDisabled || isLoading}
errorBorderColor="red.300"
{...props}
@ -165,7 +172,7 @@ function SpeciesColorPicker({
// supported colors for a species makes sense, but the other way around feels
// confusing and restrictive.)
let visibleColors = allColors;
if (stateMustAlwaysBeValid && valids) {
if (stateMustAlwaysBeValid && valids && speciesId) {
visibleColors = visibleColors.filter(
(c) => getValidPoses(valids, speciesId, c.id).size > 0
);
@ -180,13 +187,19 @@ function SpeciesColorPicker({
isDisabled={isDisabled}
onChange={onChangeColor}
>
{allColors.length === 0 && (
<>
{/* The default case, and a long name for sizing! */}
<option>Blue</option>
<option>Dimensional</option>
</>
)}
{
// If the selected color isn't in the set we have here, show the
// placeholder. (Can happen during loading, or if an invalid color ID
// like null is intentionally provided while the real value loads.)
!visibleColors.some((c) => c.id === colorId) && (
<option>{colorPlaceholderText}</option>
)
}
{
// A long name for sizing! Should appear below the placeholder, out
// of view.
visibleColors.length === 0 && <option>Dimensional</option>
}
{visibleColors.map((color) => (
<option key={color.id} value={color.id}>
{color.name}
@ -201,13 +214,20 @@ function SpeciesColorPicker({
isDisabled={isDisabled}
onChange={onChangeSpecies}
>
{allSpecies.length === 0 && (
<>
{/* The default case, and a long name for sizing! */}
<option>Acara</option>
<option>Tuskaninny</option>
</>
)}
{
// If the selected species isn't in the set we have here, show the
// placeholder. (Can happen during loading, or if an invalid species
// ID like null is intentionally provided while the real value
// loads.)
!allSpecies.some((s) => s.id === speciesId) && (
<option>{speciesPlaceholderText}</option>
)
}
{
// A long name for sizing! Should appear below the placeholder, out
// of view.
allSpecies.length === 0 && <option>Tuskaninny</option>
}
{allSpecies.map((species) => (
<option key={species.id} value={species.id}>
{species.name}