Hook up compatibility data for SpeciesFacesPicker
It looks really nice!! :3
This commit is contained in:
parent
93c00c5e79
commit
cdff51dfef
3 changed files with 65 additions and 14 deletions
|
@ -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 }) {
|
|||
</VStack>
|
||||
<SpeciesFacesPicker
|
||||
selectedSpeciesId={petState.speciesId}
|
||||
compatibleBodyIds={["180"]}
|
||||
compatibleBodies={compatibleBodies}
|
||||
onChange={({ speciesId, colorId }) =>
|
||||
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
|
||||
) : (
|
||||
<div style={{ textAlign: "center" }}>
|
||||
{speciesName}
|
||||
<div style={{ fontStyle: "italic", fontSize: "0.75em" }}>
|
||||
(Not compatible yet)
|
||||
const tooltipLabel =
|
||||
isCompatible || isLoading ? (
|
||||
speciesName
|
||||
) : (
|
||||
<div style={{ textAlign: "center" }}>
|
||||
{speciesName}
|
||||
<div style={{ fontStyle: "italic", fontSize: "0.75em" }}>
|
||||
(Not compatible yet)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
|
||||
const cursor = isLoading ? "wait" : !isCompatible ? "not-allowed" : "pointer";
|
||||
|
||||
return (
|
||||
<ClassNames>
|
||||
{({ css }) => (
|
||||
<Tooltip label={tooltipLabel} placement="top" gutter={-12}>
|
||||
<Tooltip
|
||||
label={tooltipLabel}
|
||||
placement="top"
|
||||
// TODO: This looks great visually, but disrupts the hover state and
|
||||
// causes flicker. I couldn't figure out how to apply
|
||||
// `pointer-events: none` to the portal container, which I
|
||||
// think is intercepting the hover even if the label doesn't.
|
||||
gutter={-12}
|
||||
>
|
||||
<Box as="label" cursor={cursor}>
|
||||
<VisuallyHidden
|
||||
as="input"
|
||||
|
|
|
@ -66,6 +66,10 @@ const typeDefs = gql`
|
|||
# a union of zones for all of its appearances! We use this for overview
|
||||
# info about the item.
|
||||
allOccupiedZones: [Zone!]!
|
||||
|
||||
# All bodies that this item is compatible with. Note that this might return
|
||||
# the special representsAllPets body, e.g. if this is just a Background!
|
||||
compatibleBodies: [Body!]!
|
||||
}
|
||||
|
||||
type ItemAppearance {
|
||||
|
@ -330,6 +334,24 @@ const resolvers = {
|
|||
const zones = zoneIds.map((id) => ({ 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: {
|
||||
|
|
|
@ -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 },
|
||||
_,
|
||||
|
|
Loading…
Reference in a new issue