enable HTTP caching for pet appearances
This commit is contained in:
parent
9f11c83b20
commit
ffde7172de
2 changed files with 69 additions and 33 deletions
|
@ -9,31 +9,31 @@ import { useQuery } from "@apollo/react-hooks";
|
||||||
export default function useOutfitAppearance(outfitState) {
|
export default function useOutfitAppearance(outfitState) {
|
||||||
const { wornItemIds, speciesId, colorId, pose } = outfitState;
|
const { wornItemIds, speciesId, colorId, pose } = outfitState;
|
||||||
|
|
||||||
const { loading, error, data } = useQuery(
|
// We split this query out from the other one, so that we can HTTP cache it.
|
||||||
|
//
|
||||||
|
// While Apollo gives us fine-grained caching during the page session, we can
|
||||||
|
// only HTTP a full query at a time.
|
||||||
|
//
|
||||||
|
// This is a minor optimization with respect to keeping the user's cache
|
||||||
|
// populated with their favorite species/color combinations. Once we start
|
||||||
|
// caching the items by body instead of species/color, this could make color
|
||||||
|
// changes really snappy!
|
||||||
|
//
|
||||||
|
// The larger optimization is that this enables the CDN to edge-cache the
|
||||||
|
// most popular species/color combinations, for very fast previews on the
|
||||||
|
// HomePage. At time of writing, Vercel isn't actually edge-caching these, I
|
||||||
|
// assume because our traffic isn't enough - so let's keep an eye on this!
|
||||||
|
const { loading: loading1, error: error1, data: data1 } = useQuery(
|
||||||
gql`
|
gql`
|
||||||
query OutfitAppearance(
|
query OutfitPetAppearance($speciesId: ID!, $colorId: ID!, $pose: Pose!) {
|
||||||
$wornItemIds: [ID!]!
|
|
||||||
$speciesId: ID!
|
|
||||||
$colorId: ID!
|
|
||||||
$pose: Pose!
|
|
||||||
) {
|
|
||||||
petAppearance(speciesId: $speciesId, colorId: $colorId, pose: $pose) {
|
petAppearance(speciesId: $speciesId, colorId: $colorId, pose: $pose) {
|
||||||
...PetAppearanceForOutfitPreview
|
...PetAppearanceForOutfitPreview
|
||||||
}
|
}
|
||||||
|
|
||||||
items(ids: $wornItemIds) {
|
|
||||||
id
|
|
||||||
appearanceOn(speciesId: $speciesId, colorId: $colorId) {
|
|
||||||
...ItemAppearanceForOutfitPreview
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
${itemAppearanceFragment}
|
|
||||||
${petAppearanceFragment}
|
${petAppearanceFragment}
|
||||||
`,
|
`,
|
||||||
{
|
{
|
||||||
variables: {
|
variables: {
|
||||||
wornItemIds,
|
|
||||||
speciesId,
|
speciesId,
|
||||||
colorId,
|
colorId,
|
||||||
pose,
|
pose,
|
||||||
|
@ -42,16 +42,46 @@ export default function useOutfitAppearance(outfitState) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const itemAppearances = React.useMemo(
|
const { loading: loading2, error: error2, data: data2 } = useQuery(
|
||||||
() => (data?.items || []).map((i) => i.appearanceOn),
|
gql`
|
||||||
[data]
|
query OutfitItemsAppearance(
|
||||||
);
|
$speciesId: ID!
|
||||||
const visibleLayers = React.useMemo(
|
$colorId: ID!
|
||||||
() => getVisibleLayers(data?.petAppearance, itemAppearances),
|
$wornItemIds: [ID!]!
|
||||||
[data, itemAppearances]
|
) {
|
||||||
|
items(ids: $wornItemIds) {
|
||||||
|
id
|
||||||
|
appearanceOn(speciesId: $speciesId, colorId: $colorId) {
|
||||||
|
...ItemAppearanceForOutfitPreview
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${itemAppearanceFragment}
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
variables: {
|
||||||
|
speciesId,
|
||||||
|
colorId,
|
||||||
|
wornItemIds,
|
||||||
|
},
|
||||||
|
skip: speciesId == null || colorId == null || wornItemIds.length === 0,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return { loading, error, visibleLayers };
|
const itemAppearances = React.useMemo(
|
||||||
|
() => (data2?.items || []).map((i) => i.appearanceOn),
|
||||||
|
[data2]
|
||||||
|
);
|
||||||
|
const visibleLayers = React.useMemo(
|
||||||
|
() => getVisibleLayers(data1?.petAppearance, itemAppearances),
|
||||||
|
[data1, itemAppearances]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
loading: loading1 || loading2,
|
||||||
|
error: error1 || error2,
|
||||||
|
visibleLayers,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getVisibleLayers(petAppearance, itemAppearances) {
|
export function getVisibleLayers(petAppearance, itemAppearances) {
|
||||||
|
|
|
@ -68,7 +68,8 @@ const typeDefs = gql`
|
||||||
appearanceOn(speciesId: ID!, colorId: ID!): ItemAppearance
|
appearanceOn(speciesId: ID!, colorId: ID!): ItemAppearance
|
||||||
}
|
}
|
||||||
|
|
||||||
type PetAppearance {
|
# Cache for 1 week (unlikely to change)
|
||||||
|
type PetAppearance @cacheControl(maxAge: 604800) {
|
||||||
id: ID!
|
id: ID!
|
||||||
species: Species!
|
species: Species!
|
||||||
color: Color!
|
color: Color!
|
||||||
|
@ -84,7 +85,8 @@ const typeDefs = gql`
|
||||||
restrictedZones: [Zone!]!
|
restrictedZones: [Zone!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppearanceLayer {
|
# Cache for 1 week (unlikely to change)
|
||||||
|
type AppearanceLayer @cacheControl(maxAge: 604800) {
|
||||||
id: ID!
|
id: ID!
|
||||||
zone: Zone!
|
zone: Zone!
|
||||||
imageUrl(size: LayerImageSize): String
|
imageUrl(size: LayerImageSize): String
|
||||||
|
@ -98,7 +100,8 @@ const typeDefs = gql`
|
||||||
svgUrl: String
|
svgUrl: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type Zone {
|
# Cache for 1 week (unlikely to change)
|
||||||
|
type Zone @cacheControl(maxAge: 604800) {
|
||||||
id: ID!
|
id: ID!
|
||||||
depth: Int!
|
depth: Int!
|
||||||
label: String!
|
label: String!
|
||||||
|
@ -109,12 +112,14 @@ const typeDefs = gql`
|
||||||
items: [Item!]!
|
items: [Item!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Color {
|
# Cache for 1 week (unlikely to change)
|
||||||
|
type Color @cacheControl(maxAge: 604800) {
|
||||||
id: ID!
|
id: ID!
|
||||||
name: String!
|
name: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Species {
|
# Cache for 1 week (unlikely to change)
|
||||||
|
type Species @cacheControl(maxAge: 604800) {
|
||||||
id: ID!
|
id: ID!
|
||||||
name: String!
|
name: String!
|
||||||
}
|
}
|
||||||
|
@ -138,8 +143,8 @@ const typeDefs = gql`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
allColors: [Color!]! @cacheControl(maxAge: 10800) # Cache for 3 hours
|
allColors: [Color!]! @cacheControl(maxAge: 10800) # Cache for 3 hours (we might add more!)
|
||||||
allSpecies: [Species!]! @cacheControl(maxAge: 10800) # Cache for 3 hours
|
allSpecies: [Species!]! @cacheControl(maxAge: 10800) # Cache for 3 hours (we might add more!)
|
||||||
allValidSpeciesColorPairs: [SpeciesColorPair!]! # deprecated
|
allValidSpeciesColorPairs: [SpeciesColorPair!]! # deprecated
|
||||||
items(ids: [ID!]!): [Item!]!
|
items(ids: [ID!]!): [Item!]!
|
||||||
itemSearch(query: String!): ItemSearchResult!
|
itemSearch(query: String!): ItemSearchResult!
|
||||||
|
@ -151,8 +156,9 @@ const typeDefs = gql`
|
||||||
limit: Int
|
limit: Int
|
||||||
): ItemSearchResult!
|
): ItemSearchResult!
|
||||||
petAppearance(speciesId: ID!, colorId: ID!, pose: Pose!): PetAppearance
|
petAppearance(speciesId: ID!, colorId: ID!, pose: Pose!): PetAppearance
|
||||||
|
@cacheControl(maxAge: 604800) # Cache for 1 week (unlikely to change)
|
||||||
petAppearances(speciesId: ID!, colorId: ID!): [PetAppearance!]!
|
petAppearances(speciesId: ID!, colorId: ID!): [PetAppearance!]!
|
||||||
|
@cacheControl(maxAge: 10800) # Cache for 3 hours (we might add more!)
|
||||||
outfit(id: ID!): Outfit
|
outfit(id: ID!): Outfit
|
||||||
|
|
||||||
petOnNeopetsDotCom(petName: String!): Outfit
|
petOnNeopetsDotCom(petName: String!): Outfit
|
||||||
|
@ -371,7 +377,7 @@ const resolvers = {
|
||||||
return allPairs;
|
return allPairs;
|
||||||
},
|
},
|
||||||
items: (_, { ids }) => {
|
items: (_, { ids }) => {
|
||||||
return ids.map(id => ({ id }));
|
return ids.map((id) => ({ id }));
|
||||||
},
|
},
|
||||||
itemSearch: async (_, { query }, { itemSearchLoader }) => {
|
itemSearch: async (_, { query }, { itemSearchLoader }) => {
|
||||||
const items = await itemSearchLoader.load(query.trim());
|
const items = await itemSearchLoader.load(query.trim());
|
||||||
|
|
Loading…
Reference in a new issue