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:
parent
1b59b9631b
commit
a6761a2403
4 changed files with 97 additions and 38 deletions
|
@ -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);
|
||||||
|
|
|
@ -443,41 +443,64 @@ 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(
|
||||||
query ItemPageOutfitPreview($itemId: ID!) {
|
gql`
|
||||||
item(id: $itemId) {
|
query ItemPageOutfitPreview($itemId: ID!) {
|
||||||
id
|
item(id: $itemId) {
|
||||||
canonicalAppearance {
|
|
||||||
id
|
id
|
||||||
...ItemAppearanceFragment
|
canonicalAppearance {
|
||||||
body {
|
|
||||||
id
|
id
|
||||||
canonicalAppearance {
|
...ItemAppearanceForOutfitPreview
|
||||||
|
body {
|
||||||
id
|
id
|
||||||
...PetAppearanceFragment
|
canonicalAppearance {
|
||||||
|
id
|
||||||
|
species {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
color {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
pose
|
||||||
|
|
||||||
|
...PetAppearanceForOutfitPreview
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
${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}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
Loading…
Reference in a new issue