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} colorId={colorId}
idealPose={idealPose} idealPose={idealPose}
showPlaceholders showPlaceholders
colorPlaceholderText="Blue"
speciesPlaceholderText="Acara"
onChange={(species, color, isValid, closestPose) => { onChange={(species, color, isValid, closestPose) => {
setSpeciesId(species.id); setSpeciesId(species.id);
setColorId(color.id); setColorId(color.id);

View file

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

View file

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

View file

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