diff --git a/src/app/ItemPage.js b/src/app/ItemPage.js index 9289148..1a37ca7 100644 --- a/src/app/ItemPage.js +++ b/src/app/ItemPage.js @@ -513,11 +513,15 @@ function ItemPageOutfitPreview({ itemId }) { // query after this loads, because our Apollo cache can't detect the // shared item appearance. (For standard colors though, our logic to // cover standard-color switches works for this preloading too.) - const { loading, error } = useQuery( + const { loading, error, data } = useQuery( gql` query ItemPageOutfitPreview($itemId: ID!) { item(id: $itemId) { id + compatibleBodies { + id + representsAllBodies + } canonicalAppearance { id ...ItemAppearanceForOutfitPreview @@ -559,6 +563,8 @@ function ItemPageOutfitPreview({ itemId }) { } ); + const compatibleBodies = data?.item?.compatibleBodies || []; + // 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 @@ -688,7 +694,7 @@ function ItemPageOutfitPreview({ itemId }) { setPetState({ speciesId, @@ -754,10 +760,15 @@ function PlayPauseButton({ isPaused, onClick }) { function SpeciesFacesPicker({ selectedSpeciesId, - compatibleBodyIds, + compatibleBodies, onChange, isLoading, }) { + const allBodiesAreCompatible = compatibleBodies.some( + (body) => body.representsAllBodies + ); + const compatibleBodyIds = compatibleBodies.map((body) => body.id); + const allSpeciesFaces = speciesFaces.sort((a, b) => a.speciesName.localeCompare(b.speciesName) ); @@ -791,7 +802,10 @@ function SpeciesFacesPicker({ speciesName={speciesFace.speciesName} colorId={speciesFace.colorId} neopetsImageHash={speciesFace.neopetsImageHash} - isCompatible={compatibleBodyIds.includes(speciesFace.bodyId)} + isCompatible={ + allBodiesAreCompatible || + compatibleBodyIds.includes(speciesFace.bodyId) + } isSelected={speciesFace.speciesId === selectedSpeciesId} onChange={onChange} isLoading={isLoading} @@ -833,23 +847,32 @@ function SpeciesFaceOption({ const isHappy = isLoading || isCompatible; const emotionId = isHappy ? "1" : "2"; - const tooltipLabel = isCompatible ? ( - speciesName - ) : ( -
- {speciesName} -
- (Not compatible yet) + const tooltipLabel = + isCompatible || isLoading ? ( + speciesName + ) : ( +
+ {speciesName} +
+ (Not compatible yet) +
-
- ); + ); const cursor = isLoading ? "wait" : !isCompatible ? "not-allowed" : "pointer"; return ( {({ css }) => ( - + ({ id })); return zones; }, + compatibleBodies: async ({ id }, _, { db }) => { + const [rows, __] = await db.query( + ` + SELECT DISTINCT swf_assets.body_id + FROM items + INNER JOIN parents_swf_assets ON + items.id = parents_swf_assets.parent_id AND + parents_swf_assets.parent_type = "Item" + INNER JOIN swf_assets ON + parents_swf_assets.swf_asset_id = swf_assets.id + WHERE items.id = ? + `, + [id] + ); + const bodyIds = rows.map((row) => row.body_id); + const bodies = bodyIds.map((id) => ({ id })); + return bodies; + }, }, ItemAppearance: { diff --git a/src/server/types/PetAppearance.js b/src/server/types/PetAppearance.js index 9f2fa9f..1ac3588 100644 --- a/src/server/types/PetAppearance.js +++ b/src/server/types/PetAppearance.js @@ -44,6 +44,9 @@ const typeDefs = gql` # A PetAppearance that has this body. Prefers Blue and happy poses. canonicalAppearance: PetAppearance + + # Whether this is the special body type that represents fitting _all_ pets. + representsAllBodies: Boolean! } # Cache for 1 week (unlikely to change) @@ -131,6 +134,9 @@ const resolvers = { "don't have a direct query for it yet, oops!" ); }, + representsAllBodies: ({ id }) => { + return id == "0"; + }, canonicalAppearance: async ( { id }, _,