Save user's preferred species for item previews
This commit is contained in:
parent
13a5a0a7aa
commit
787dc7da87
2 changed files with 40 additions and 10 deletions
|
@ -511,17 +511,40 @@ function ItemPageOutfitPreview({ itemId }) {
|
|||
// switch back to it though... we could maybe do something clever there!)
|
||||
appearanceId: null,
|
||||
});
|
||||
const [preferredSpeciesId, setPreferredSpeciesId] = useLocalStorage(
|
||||
"DTIItemPreviewPreferredSpeciesId",
|
||||
null
|
||||
);
|
||||
|
||||
const setPetStateFromUserAction = (petState) => {
|
||||
setPetState(petState);
|
||||
|
||||
// When the user _intentionally_ chooses a species, save it in local
|
||||
// storage for next time. (This won't update when e.g. their preferred
|
||||
// species isn't available for this item, so we update to the canonical
|
||||
// species automatically.)
|
||||
if (petState.speciesId) {
|
||||
// I have no reason to expect null to come in here, but, since this is
|
||||
// touching client-persisted data, I want it to be even more guaranteed
|
||||
// reliable than usual!
|
||||
setPreferredSpeciesId(petState.speciesId);
|
||||
}
|
||||
};
|
||||
|
||||
// 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.
|
||||
//
|
||||
// If the user has a preferred species saved from using the ItemPage in the
|
||||
// past, we'll send that instead. This will return the appearance on that
|
||||
// species if possible, or the default canonical species if not.
|
||||
//
|
||||
// TODO: If this is a non-standard pet color, like Mutant, we'll do an extra
|
||||
// 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: loadingGQL, error: errorGQL, data } = useQuery(
|
||||
gql`
|
||||
query ItemPageOutfitPreview($itemId: ID!) {
|
||||
query ItemPageOutfitPreview($itemId: ID!, $preferredSpeciesId: ID) {
|
||||
item(id: $itemId) {
|
||||
id
|
||||
name
|
||||
|
@ -529,7 +552,7 @@ function ItemPageOutfitPreview({ itemId }) {
|
|||
id
|
||||
representsAllBodies
|
||||
}
|
||||
canonicalAppearance {
|
||||
canonicalAppearance(preferredSpeciesId: $preferredSpeciesId) {
|
||||
id
|
||||
...ItemAppearanceForOutfitPreview
|
||||
body {
|
||||
|
@ -556,7 +579,7 @@ function ItemPageOutfitPreview({ itemId }) {
|
|||
${petAppearanceFragment}
|
||||
`,
|
||||
{
|
||||
variables: { itemId },
|
||||
variables: { itemId, preferredSpeciesId },
|
||||
onCompleted: (data) => {
|
||||
const canonicalBody = data?.item?.canonicalAppearance?.body;
|
||||
const canonicalPetAppearance = canonicalBody?.canonicalAppearance;
|
||||
|
@ -692,7 +715,7 @@ function ItemPageOutfitPreview({ itemId }) {
|
|||
pose={petState.pose}
|
||||
idealPose={idealPose}
|
||||
onChange={(species, color, _, closestPose) => {
|
||||
setPetState({
|
||||
setPetStateFromUserAction({
|
||||
speciesId: species.id,
|
||||
colorId: color.id,
|
||||
pose: closestPose,
|
||||
|
@ -732,7 +755,7 @@ function ItemPageOutfitPreview({ itemId }) {
|
|||
onChange={({ speciesId, colorId }) => {
|
||||
const validPoses = getValidPoses(valids, speciesId, colorId);
|
||||
const pose = getClosestPose(validPoses, idealPose);
|
||||
setPetState({
|
||||
setPetStateFromUserAction({
|
||||
speciesId,
|
||||
colorId,
|
||||
pose,
|
||||
|
|
|
@ -60,7 +60,11 @@ const typeDefs = gql`
|
|||
# on the item page, to initialize the preview section. (You can find out
|
||||
# which species this is for by going through the body field on
|
||||
# ItemAppearance!)
|
||||
canonicalAppearance: ItemAppearance @cacheControl(maxAge: 1, staleWhileRevalidate: ${oneWeek})
|
||||
#
|
||||
# There's also an optional preferredSpeciesId field, which you can use to
|
||||
# request a certain species if compatible. If not, we'll fall back to the
|
||||
# default species, as described above.
|
||||
canonicalAppearance(preferredSpeciesId: ID): ItemAppearance @cacheControl(maxAge: 1, staleWhileRevalidate: ${oneWeek})
|
||||
|
||||
# All zones that this item occupies, for at least one body. That is, it's
|
||||
# a union of zones for all of its appearances! We use this for overview
|
||||
|
@ -313,20 +317,23 @@ const resolvers = {
|
|||
},
|
||||
canonicalAppearance: async (
|
||||
{ id },
|
||||
_,
|
||||
{ preferredSpeciesId },
|
||||
{ itemBodiesWithAppearanceDataLoader }
|
||||
) => {
|
||||
const rows = await itemBodiesWithAppearanceDataLoader.load(id);
|
||||
const canonicalBodyId = rows[0].bodyId;
|
||||
const preferredRow = preferredSpeciesId
|
||||
? rows.find((row) => row.speciesId === preferredSpeciesId)
|
||||
: null;
|
||||
const bestRow = preferredRow || rows[0];
|
||||
return {
|
||||
item: { id },
|
||||
bodyId: canonicalBodyId,
|
||||
bodyId: bestRow.bodyId,
|
||||
// An optimization: we know the species already, so fill it in here
|
||||
// without requiring an extra query if we want it.
|
||||
// TODO: Maybe this would be cleaner if we make the body -> species
|
||||
// loader, and prime it in the item bodies loader, rather than
|
||||
// setting it here?
|
||||
body: { id: canonicalBodyId, species: { id: rows[0].speciesId } },
|
||||
body: { id: bestRow.bodyId, species: { id: bestRow.speciesId } },
|
||||
};
|
||||
},
|
||||
allOccupiedZones: async ({ id }, _, { itemAllOccupiedZonesLoader }) => {
|
||||
|
|
Loading…
Reference in a new issue