From 6e1e0a5c0b1096826aa68c8e9c9c35ca5ea6aa15 Mon Sep 17 00:00:00 2001 From: Matt Dunn-Rankin Date: Sat, 2 May 2020 22:32:08 -0700 Subject: [PATCH] show updated pet appearance in outfit preview! --- src/app/useOutfitAppearance.js | 37 ++++++++++--- src/app/useOutfitState.js | 37 ++++++++----- src/server/index.js | 55 ++++++++------------ src/server/query-tests/PetAppearance.test.js | 7 ++- src/server/util.js | 26 ++++++++- 5 files changed, 110 insertions(+), 52 deletions(-) diff --git a/src/app/useOutfitAppearance.js b/src/app/useOutfitAppearance.js index 9293f08..ea56bf6 100644 --- a/src/app/useOutfitAppearance.js +++ b/src/app/useOutfitAppearance.js @@ -6,12 +6,29 @@ import { useQuery } from "@apollo/react-hooks"; * visibleLayers for rendering. */ export default function useOutfitAppearance(outfitState) { - const { wornItemIds, speciesId, colorId } = outfitState; + const { + wornItemIds, + speciesId, + colorId, + emotion, + genderPresentation, + } = outfitState; const { loading, error, data } = useQuery( gql` - query($wornItemIds: [ID!]!, $speciesId: ID!, $colorId: ID!) { - petAppearance(speciesId: $speciesId, colorId: $colorId) { + query( + $wornItemIds: [ID!]! + $speciesId: ID! + $colorId: ID! + $emotion: Emotion! + $genderPresentation: GenderPresentation! + ) { + petAppearance( + speciesId: $speciesId + colorId: $colorId + emotion: $emotion + genderPresentation: $genderPresentation + ) { ...PetAppearanceForOutfitPreview } @@ -26,7 +43,13 @@ export default function useOutfitAppearance(outfitState) { ${petAppearanceFragment} `, { - variables: { wornItemIds, speciesId, colorId }, + variables: { + wornItemIds, + speciesId, + colorId, + emotion, + genderPresentation, + }, } ); @@ -41,7 +64,9 @@ export function getVisibleLayers(petAppearance, itemAppearances) { return []; } - const allAppearances = [petAppearance, ...itemAppearances].filter((a) => a); + const validItemAppearances = itemAppearances.filter((a) => a); + + const allAppearances = [petAppearance, ...validItemAppearances]; let allLayers = allAppearances.map((a) => a.layers).flat(); // Clean up our data a bit, by ensuring only one layer per zone. This @@ -51,7 +76,7 @@ export function getVisibleLayers(petAppearance, itemAppearances) { return allLayers.findIndex((l2) => l2.zone.id === l.zone.id) === i; }); - const allRestrictedZoneIds = itemAppearances + const allRestrictedZoneIds = validItemAppearances .map((l) => l.restrictedZones) .flat() .map((z) => z.id); diff --git a/src/app/useOutfitState.js b/src/app/useOutfitState.js index 61dffc4..f48b147 100644 --- a/src/app/useOutfitState.js +++ b/src/app/useOutfitState.js @@ -183,21 +183,34 @@ const outfitStateReducer = (apolloClient) => (baseState, action) => { closetedItemIds.delete(itemId); }); case "setPose": - const { emotion, genderPresentation } = action; - return { ...baseState, emotion, genderPresentation }; + return produce(baseState, (state) => { + const { emotion, genderPresentation } = action; + state.emotion = emotion; + state.genderPresentation = genderPresentation; + }); case "reset": - const { name, speciesId, colorId, wornItemIds, closetedItemIds } = action; - return { - name, - speciesId: speciesId ? String(speciesId) : baseState.speciesId, - colorId: colorId ? String(colorId) : baseState.colorId, - wornItemIds: wornItemIds + return produce(baseState, (state) => { + const { + name, + speciesId, + colorId, + emotion, + genderPresentation, + wornItemIds, + closetedItemIds, + } = action; + state.name = name; + state.speciesId = speciesId ? String(speciesId) : baseState.speciesId; + state.colorId = colorId ? String(colorId) : baseState.colorId; + state.emotion = emotion; + state.genderPresentation = genderPresentation; + state.wornItemIds = wornItemIds ? new Set(wornItemIds.map(String)) - : baseState.wornItemIds, - closetedItemIds: closetedItemIds + : baseState.wornItemIds; + state.closetedItemIds = closetedItemIds ? new Set(closetedItemIds.map(String)) - : baseState.closetedItemIds, - }; + : baseState.closetedItemIds; + }); default: throw new Error(`unexpected action ${JSON.stringify(action)}`); } diff --git a/src/server/index.js b/src/server/index.js index 95d19d4..4268944 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -3,7 +3,7 @@ const { gql } = require("apollo-server"); const connectToDb = require("./db"); const buildLoaders = require("./loaders"); const neopets = require("./neopets"); -const { capitalize } = require("./util"); +const { capitalize, getEmotion, getGenderPresentation } = require("./util"); const typeDefs = gql` enum LayerImageSize { @@ -111,7 +111,12 @@ const typeDefs = gql` offset: Int limit: Int ): ItemSearchResult! - petAppearance(speciesId: ID!, colorId: ID!): PetAppearance + petAppearance( + speciesId: ID! + colorId: ID! + emotion: Emotion! + genderPresentation: GenderPresentation! + ): PetAppearance petAppearances(speciesId: ID!, colorId: ID!): [PetAppearance!]! petOnNeopetsDotCom(petName: String!): Outfit @@ -162,34 +167,9 @@ const resolvers = { }, PetAppearance: { id: ({ petState }) => petState.id, - genderPresentation: ({ petState }) => { - if (petState.female === 1) { - return "FEMININE"; - } else if (petState.female === 0) { - return "MASCULINE"; - } else if (petState.female === null) { - return null; - } else { - throw new Error( - `unrecognized gender value ${JSON.stringify(petState.female)}` - ); - } - }, - emotion: ({ petState }) => { - if (petState.moodId === "1") { - return "HAPPY"; - } else if (petState.moodId === "2") { - return "SAD"; - } else if (petState.moodId === "4") { - return "SICK"; - } else if (petState.moodId === null) { - return null; - } else { - throw new Error( - `unrecognized moodId ${JSON.stringify(petState.moodId)}` - ); - } - }, + genderPresentation: ({ petState }) => + getGenderPresentation(petState.female), + emotion: ({ petState }) => getEmotion(petState.moodId), approximateThumbnailUrl: ({ petType, petState }) => { return `http://pets.neopets.com/cp/${petType.basicImageHash}/${petState.moodId}/1.png`; }, @@ -285,15 +265,26 @@ const resolvers = { }, petAppearance: async ( _, - { speciesId, colorId }, + { speciesId, colorId, emotion, genderPresentation }, { petTypeLoader, petStateLoader } ) => { const petType = await petTypeLoader.load({ speciesId, colorId, }); + const petStates = await petStateLoader.load(petType.id); - return { petType, petState: petStates[0] }; + // TODO: This could be optimized into the query condition 🤔 + const petState = petStates.find( + (ps) => + getEmotion(ps.moodId) === emotion && + getGenderPresentation(ps.female) === genderPresentation + ); + if (!petState) { + return null; + } + + return { petType, petState }; }, petAppearances: async ( _, diff --git a/src/server/query-tests/PetAppearance.test.js b/src/server/query-tests/PetAppearance.test.js index 75df0c3..a892787 100644 --- a/src/server/query-tests/PetAppearance.test.js +++ b/src/server/query-tests/PetAppearance.test.js @@ -6,7 +6,12 @@ describe("PetAppearance", () => { const res = await query({ query: gql` query { - petAppearance(speciesId: "54", colorId: "75") { + petAppearance( + speciesId: "54" + colorId: "75" + emotion: HAPPY + genderPresentation: FEMININE + ) { layers { id imageUrl(size: SIZE_600) diff --git a/src/server/util.js b/src/server/util.js index 8751052..ef692ea 100644 --- a/src/server/util.js +++ b/src/server/util.js @@ -2,4 +2,28 @@ function capitalize(str) { return str[0].toUpperCase() + str.slice(1); } -module.exports = { capitalize }; +function getEmotion(moodId) { + if (moodId === "1") { + return "HAPPY"; + } else if (moodId === "2") { + return "SAD"; + } else if (moodId === "4") { + return "SICK"; + } else if (moodId === null) { + return null; + } else { + throw new Error(`unrecognized moodId ${JSON.stringify(moodId)}`); + } +} + +function getGenderPresentation(modelPetWasFemale) { + if (modelPetWasFemale === 1) { + return "FEMININE"; + } else if (modelPetWasFemale === 0) { + return "MASCULINE"; + } else { + return null; + } +} + +module.exports = { capitalize, getEmotion, getGenderPresentation };