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} `;