impress/app/javascript/wardrobe-2020/components/useOutfitAppearance.js

211 lines
4.9 KiB
JavaScript
Raw Normal View History

import React from "react";
import gql from "graphql-tag";
import { useQuery } from "@apollo/client";
import getVisibleLayers, {
itemAppearanceFragmentForGetVisibleLayers,
petAppearanceFragmentForGetVisibleLayers,
} from "./getVisibleLayers";
import { useAltStyle } from "../loaders/alt-styles";
/**
* useOutfitAppearance downloads the outfit's appearance data, and returns
* visibleLayers for rendering.
*/
export default function useOutfitAppearance(outfitState) {
const { wornItemIds, speciesId, colorId, pose, altStyleId, appearanceId } =
outfitState;
// 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(
appearanceId == null
? gql`
query OutfitPetAppearance(
$speciesId: ID!
$colorId: ID!
$pose: Pose!
) {
petAppearance(
speciesId: $speciesId
colorId: $colorId
pose: $pose
) {
...PetAppearanceForOutfitPreview
}
}
${petAppearanceFragment}
`
: gql`
query OutfitPetAppearanceById($appearanceId: ID!) {
petAppearance: petAppearanceById(id: $appearanceId) {
...PetAppearanceForOutfitPreview
}
}
${petAppearanceFragment}
`,
{
variables: {
speciesId,
colorId,
pose,
appearanceId,
},
skip:
speciesId == null ||
colorId == null ||
(pose == null && appearanceId == null),
},
);
const {
loading: loading2,
error: error2,
data: data2,
} = useQuery(
gql`
query OutfitItemsAppearance(
$speciesId: ID!
$colorId: ID!
$altStyleId: ID
$wornItemIds: [ID!]!
) {
items(ids: $wornItemIds) {
id
name # HACK: This is for HTML5 detection UI in OutfitControls!
appearance: appearanceOn(
speciesId: $speciesId
colorId: $colorId
altStyleId: $altStyleId
) {
...ItemAppearanceForOutfitPreview
}
}
}
${itemAppearanceFragment}
`,
{
variables: {
speciesId,
colorId,
altStyleId,
wornItemIds,
},
skip: speciesId == null || colorId == null || wornItemIds.length === 0,
},
);
const {
isLoading: loading3,
error: error3,
data: altStyle,
} = useAltStyle(altStyleId, speciesId);
const petAppearance = altStyle?.appearance ?? data1?.petAppearance;
const items = data2?.items;
const itemAppearances = React.useMemo(
() => (items || []).map((i) => i.appearance),
[items],
);
const visibleLayers = React.useMemo(
() => getVisibleLayers(petAppearance, itemAppearances),
[petAppearance, itemAppearances],
);
const bodyId = petAppearance?.bodyId;
return {
loading: loading1 || loading2 || loading3,
error: error1 || error2 || error3,
petAppearance,
items: items || [],
itemAppearances,
visibleLayers,
bodyId,
};
}
export const appearanceLayerFragment = gql`
fragment AppearanceLayerForOutfitPreview on AppearanceLayer {
id
svgUrl
canvasMovieLibraryUrl
imageUrl: imageUrlV2(idealSize: SIZE_600)
bodyId
knownGlitches # For HTML5 & Known Glitches UI
zone {
id
depth
label
}
swfUrl # For the layer info modal
}
`;
export const appearanceLayerFragmentForSupport = gql`
fragment AppearanceLayerForSupport on AppearanceLayer {
id
remoteId # HACK: This is for Support tools, but other views don't need it
swfUrl # HACK: This is for Support tools, but other views don't need it
zone {
id
label # HACK: This is for Support tools, but other views don't need it
}
}
`;
export const itemAppearanceFragment = gql`
fragment ItemAppearanceForOutfitPreview on ItemAppearance {
id
layers {
id
...AppearanceLayerForOutfitPreview
...AppearanceLayerForSupport # HACK: Most users don't need this!
}
...ItemAppearanceForGetVisibleLayers
}
${appearanceLayerFragment}
${appearanceLayerFragmentForSupport}
${itemAppearanceFragmentForGetVisibleLayers}
`;
export const petAppearanceFragment = gql`
fragment PetAppearanceForOutfitPreview on PetAppearance {
id
bodyId
pose # For Known Glitches UI
isGlitched # For Known Glitches UI
species {
id # For Known Glitches UI
}
color {
id # For Known Glitches UI
}
layers {
id
...AppearanceLayerForOutfitPreview
}
...PetAppearanceForGetVisibleLayers
}
${appearanceLayerFragment}
${petAppearanceFragmentForGetVisibleLayers}
`;