Show previews for bulk-add layer tool
This commit is contained in:
parent
9eb0906a69
commit
e4c8031c3b
4 changed files with 241 additions and 63 deletions
|
@ -11,13 +11,13 @@ import {
|
||||||
ModalContent,
|
ModalContent,
|
||||||
ModalHeader,
|
ModalHeader,
|
||||||
ModalOverlay,
|
ModalOverlay,
|
||||||
Select,
|
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Wrap,
|
Wrap,
|
||||||
WrapItem,
|
WrapItem,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { gql, useQuery } from "@apollo/client";
|
import { gql, useQuery } from "@apollo/client";
|
||||||
import {
|
import {
|
||||||
|
appearanceLayerFragment,
|
||||||
itemAppearanceFragment,
|
itemAppearanceFragment,
|
||||||
petAppearanceFragment,
|
petAppearanceFragment,
|
||||||
} from "../../components/useOutfitAppearance";
|
} from "../../components/useOutfitAppearance";
|
||||||
|
@ -25,7 +25,6 @@ import HangerSpinner from "../../components/HangerSpinner";
|
||||||
import { ErrorMessage, useCommonStyles } from "../../util";
|
import { ErrorMessage, useCommonStyles } from "../../util";
|
||||||
import ItemSupportAppearanceLayer from "./ItemSupportAppearanceLayer";
|
import ItemSupportAppearanceLayer from "./ItemSupportAppearanceLayer";
|
||||||
import { EditIcon } from "@chakra-ui/icons";
|
import { EditIcon } from "@chakra-ui/icons";
|
||||||
import cachedZones from "../../cached-data/zones.json";
|
|
||||||
|
|
||||||
function AllItemLayersSupportModal({ item, isOpen, onClose }) {
|
function AllItemLayersSupportModal({ item, isOpen, onClose }) {
|
||||||
const [bulkAddProposal, setBulkAddProposal] = React.useState(null);
|
const [bulkAddProposal, setBulkAddProposal] = React.useState(null);
|
||||||
|
@ -46,19 +45,15 @@ function AllItemLayersSupportModal({ item, isOpen, onClose }) {
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<ModalCloseButton />
|
<ModalCloseButton />
|
||||||
<ModalBody paddingBottom="12">
|
<ModalBody paddingBottom="12">
|
||||||
<BulkAddBodySpecificAssetsForm onSubmit={setBulkAddProposal} />
|
<BulkAddBodySpecificAssetsForm
|
||||||
|
bulkAddProposal={bulkAddProposal}
|
||||||
|
onSubmit={setBulkAddProposal}
|
||||||
|
/>
|
||||||
<Box height="8" />
|
<Box height="8" />
|
||||||
{bulkAddProposal ? (
|
<AllItemLayersSupportModalContent
|
||||||
<>
|
item={item}
|
||||||
TODO: Show assets {bulkAddProposal.minAssetId}–
|
bulkAddProposal={bulkAddProposal}
|
||||||
{Number(bulkAddProposal.minAssetId) + 53}, tenatively applied to
|
/>
|
||||||
zone {bulkAddProposal.zoneId}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
""
|
|
||||||
)}
|
|
||||||
<Box height="8" />
|
|
||||||
<AllItemLayersSupportModalContent item={item} />
|
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</ModalOverlay>
|
</ModalOverlay>
|
||||||
|
@ -66,14 +61,11 @@ function AllItemLayersSupportModal({ item, isOpen, onClose }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function BulkAddBodySpecificAssetsForm({ onSubmit }) {
|
function BulkAddBodySpecificAssetsForm({ bulkAddProposal, onSubmit }) {
|
||||||
const zones = [...cachedZones].sort((a, b) =>
|
const [minAssetId, setMinAssetId] = React.useState(
|
||||||
`${a.label}-${a.id}`.localeCompare(`${b.label}-${b.id}`)
|
bulkAddProposal?.minAssetId
|
||||||
);
|
);
|
||||||
|
|
||||||
const [minAssetId, setMinAssetId] = React.useState(null);
|
|
||||||
const [zoneId, setZoneId] = React.useState(zones[0].id);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
align="center"
|
align="center"
|
||||||
|
@ -83,7 +75,7 @@ function BulkAddBodySpecificAssetsForm({ onSubmit }) {
|
||||||
transition="0.2s all"
|
transition="0.2s all"
|
||||||
onSubmit={(e) => {
|
onSubmit={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
onSubmit({ minAssetId, zoneId });
|
onSubmit({ minAssetId });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
@ -121,33 +113,18 @@ function BulkAddBodySpecificAssetsForm({ onSubmit }) {
|
||||||
<Box width="1" />
|
<Box width="1" />
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
min="54"
|
min="55"
|
||||||
step="1"
|
step="1"
|
||||||
size="xs"
|
size="xs"
|
||||||
width="9ch"
|
width="9ch"
|
||||||
placeholder="Max ID"
|
placeholder="Max ID"
|
||||||
// There are 54 species at time of writing, so offsetting the max ID
|
// There are 55 species at time of writing, so offsetting the max ID
|
||||||
// by 53 gives us ranges like 1–54, one for each species.
|
// by 54 gives us ranges like 1–55, one for each species.
|
||||||
value={minAssetId != null ? Number(minAssetId) + 53 : ""}
|
value={minAssetId != null ? Number(minAssetId) + 54 : ""}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setMinAssetId(e.target.value ? Number(e.target.value) - 53 : null)
|
setMinAssetId(e.target.value ? Number(e.target.value) - 54 : null)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Box width="1" />
|
|
||||||
<Box>, assigned to </Box>
|
|
||||||
<Box width="2" />
|
|
||||||
<Select
|
|
||||||
size="xs"
|
|
||||||
width="20ch"
|
|
||||||
value={zoneId}
|
|
||||||
onChange={(e) => setZoneId(e.target.value)}
|
|
||||||
>
|
|
||||||
{zones.map((zone) => (
|
|
||||||
<option key={zone.id} value={zone.id}>
|
|
||||||
{zone.label} (Zone {zone.id})
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
<Box width="2" />
|
<Box width="2" />
|
||||||
<Button type="submit" size="xs" isDisabled={minAssetId == null}>
|
<Button type="submit" size="xs" isDisabled={minAssetId == null}>
|
||||||
Preview
|
Preview
|
||||||
|
@ -156,7 +133,7 @@ function BulkAddBodySpecificAssetsForm({ onSubmit }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function AllItemLayersSupportModalContent({ item }) {
|
function AllItemLayersSupportModalContent({ item, bulkAddProposal }) {
|
||||||
const { loading, error, data } = useQuery(
|
const { loading, error, data } = useQuery(
|
||||||
gql`
|
gql`
|
||||||
query AllItemLayersSupportModal($itemId: ID!) {
|
query AllItemLayersSupportModal($itemId: ID!) {
|
||||||
|
@ -193,7 +170,57 @@ function AllItemLayersSupportModalContent({ item }) {
|
||||||
{ variables: { itemId: item.id } }
|
{ variables: { itemId: item.id } }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (loading) {
|
const {
|
||||||
|
loading: loading2,
|
||||||
|
error: error2,
|
||||||
|
data: bulkAddProposalData,
|
||||||
|
} = useQuery(
|
||||||
|
gql`
|
||||||
|
query AllItemLayersSupportModal_BulkAddProposal($layerRemoteIds: [ID!]!) {
|
||||||
|
layersToAdd: itemAppearanceLayersByRemoteId(
|
||||||
|
remoteIds: $layerRemoteIds
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
...AppearanceLayerForOutfitPreview
|
||||||
|
}
|
||||||
|
|
||||||
|
allSpecies {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
standardBodyId
|
||||||
|
canonicalAppearance {
|
||||||
|
id
|
||||||
|
species {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
color {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
isStandard
|
||||||
|
}
|
||||||
|
pose
|
||||||
|
...PetAppearanceForOutfitPreview
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
${appearanceLayerFragment}
|
||||||
|
${petAppearanceFragment}
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
variables: {
|
||||||
|
layerRemoteIds: bulkAddProposal
|
||||||
|
? Array.from({ length: 54 }, (_, i) =>
|
||||||
|
String(Number(bulkAddProposal.minAssetId) + i)
|
||||||
|
)
|
||||||
|
: [],
|
||||||
|
},
|
||||||
|
skip: bulkAddProposal == null,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (loading || loading2) {
|
||||||
return (
|
return (
|
||||||
<Flex align="center" justify="center" minHeight="64">
|
<Flex align="center" justify="center" minHeight="64">
|
||||||
<HangerSpinner />
|
<HangerSpinner />
|
||||||
|
@ -201,17 +228,21 @@ function AllItemLayersSupportModalContent({ item }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
if (error || error2) {
|
||||||
return <ErrorMessage>{error.message}</ErrorMessage>;
|
return <ErrorMessage>{(error || error2).message}</ErrorMessage>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemAppearances = [...(data.item?.allAppearances || [])].sort(
|
let itemAppearances = data.item?.allAppearances || [];
|
||||||
(a, b) => {
|
itemAppearances = mergeBulkAddProposalIntoItemAppearances(
|
||||||
const aKey = getSortKeyForPetAppearance(a.body.canonicalAppearance);
|
itemAppearances,
|
||||||
const bKey = getSortKeyForPetAppearance(b.body.canonicalAppearance);
|
bulkAddProposal,
|
||||||
return aKey.localeCompare(bKey);
|
bulkAddProposalData
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
itemAppearances = [...itemAppearances].sort((a, b) => {
|
||||||
|
const aKey = getSortKeyForBody(a.body);
|
||||||
|
const bKey = getSortKeyForBody(b.body);
|
||||||
|
return aKey.localeCompare(bKey);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrap justify="center" spacing="4">
|
<Wrap justify="center" spacing="4">
|
||||||
|
@ -246,6 +277,18 @@ function ItemAppearanceCard({ item, itemAppearance }) {
|
||||||
</Heading>
|
</Heading>
|
||||||
<Box height="3" />
|
<Box height="3" />
|
||||||
<Wrap paddingX="3" spacing="5">
|
<Wrap paddingX="3" spacing="5">
|
||||||
|
{itemLayers.length === 0 && (
|
||||||
|
<Flex
|
||||||
|
minWidth="150px"
|
||||||
|
minHeight="150px"
|
||||||
|
align="center"
|
||||||
|
justify="center"
|
||||||
|
>
|
||||||
|
<Box fontSize="sm" fontStyle="italic">
|
||||||
|
(No data)
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
{itemLayers.map((itemLayer) => (
|
{itemLayers.map((itemLayer) => (
|
||||||
<WrapItem key={itemLayer.id}>
|
<WrapItem key={itemLayer.id}>
|
||||||
<ItemSupportAppearanceLayer
|
<ItemSupportAppearanceLayer
|
||||||
|
@ -265,7 +308,13 @@ function ItemAppearanceCard({ item, itemAppearance }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSortKeyForPetAppearance({ color, species }) {
|
function getSortKeyForBody(body) {
|
||||||
|
// "All bodies" sorts first!
|
||||||
|
if (body.representsAllBodies) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const { color, species } = body.canonicalAppearance;
|
||||||
// Sort standard colors first, then special colors by name, then by species
|
// Sort standard colors first, then special colors by name, then by species
|
||||||
// within each color.
|
// within each color.
|
||||||
return `${color.isStandard ? "A" : "Z"}-${color.name}-${species.name}`;
|
return `${color.isStandard ? "A" : "Z"}-${color.name}-${species.name}`;
|
||||||
|
@ -286,4 +335,71 @@ function capitalize(str) {
|
||||||
return str[0].toUpperCase() + str.slice(1);
|
return str[0].toUpperCase() + str.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mergeBulkAddProposalIntoItemAppearances(
|
||||||
|
itemAppearances,
|
||||||
|
bulkAddProposal,
|
||||||
|
bulkAddProposalData
|
||||||
|
) {
|
||||||
|
if (!bulkAddProposalData) {
|
||||||
|
return itemAppearances;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do a deep copy of the existing item appearances, so we can mutate them as
|
||||||
|
// we loop through them in this function!
|
||||||
|
const mergedItemAppearances = JSON.parse(JSON.stringify(itemAppearances));
|
||||||
|
|
||||||
|
// Set up the data in convenient formats.
|
||||||
|
const { allSpecies, layersToAdd } = bulkAddProposalData;
|
||||||
|
const sortedSpecies = [...allSpecies].sort((a, b) =>
|
||||||
|
a.name.localeCompare(b.name)
|
||||||
|
);
|
||||||
|
const layersToAddByRemoteId = {};
|
||||||
|
for (const layer of layersToAdd) {
|
||||||
|
layersToAddByRemoteId[layer.remoteId] = layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [index, species] of sortedSpecies.entries()) {
|
||||||
|
// Find the existing item appearance to add to, or create a new one if it
|
||||||
|
// doesn't exist yet.
|
||||||
|
let itemAppearance = mergedItemAppearances.find(
|
||||||
|
(a) =>
|
||||||
|
a.body.canonicalAppearance.species.id === species.id &&
|
||||||
|
!a.body.representsAllBodies
|
||||||
|
);
|
||||||
|
if (!itemAppearance) {
|
||||||
|
itemAppearance = {
|
||||||
|
id: `bulk-add-proposal-new-item-appearance-for-body-${species.standardBodyId}`,
|
||||||
|
layers: [],
|
||||||
|
body: {
|
||||||
|
id: species.standardBodyId,
|
||||||
|
canonicalAppearance: species.canonicalAppearance,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
mergedItemAppearances.push(itemAppearance);
|
||||||
|
}
|
||||||
|
|
||||||
|
const layerToAddRemoteId = String(
|
||||||
|
Number(bulkAddProposal.minAssetId) + index
|
||||||
|
);
|
||||||
|
const layerToAdd = layersToAddByRemoteId[layerToAddRemoteId];
|
||||||
|
if (!layerToAdd) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete this layer from other appearances (because we're going to
|
||||||
|
// override its body ID), then add it to this new one.
|
||||||
|
for (const otherItemAppearance of mergedItemAppearances) {
|
||||||
|
const indexToDelete = otherItemAppearance.layers.findIndex(
|
||||||
|
(l) => l.remoteId === layerToAddRemoteId
|
||||||
|
);
|
||||||
|
if (indexToDelete >= 0) {
|
||||||
|
otherItemAppearance.layers.splice(indexToDelete, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
itemAppearance.layers.push(layerToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergedItemAppearances;
|
||||||
|
}
|
||||||
|
|
||||||
export default AllItemLayersSupportModal;
|
export default AllItemLayersSupportModal;
|
||||||
|
|
|
@ -204,25 +204,34 @@ export const itemAppearanceFragmentForGetVisibleLayers = gql`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const appearanceLayerFragment = gql`
|
||||||
|
fragment AppearanceLayerForOutfitPreview on AppearanceLayer {
|
||||||
|
id
|
||||||
|
remoteId # HACK: This is for Support tools, but other views don't need it
|
||||||
|
svgUrl
|
||||||
|
canvasMovieLibraryUrl
|
||||||
|
imageUrl(size: SIZE_600)
|
||||||
|
swfUrl # HACK: This is for Support tools, but other views don't need it
|
||||||
|
knownGlitches # HACK: This is for Support tools, but other views don't need it
|
||||||
|
bodyId
|
||||||
|
zone {
|
||||||
|
id
|
||||||
|
depth @client
|
||||||
|
label @client # HACK: This is for Support tools, but other views don't need it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const itemAppearanceFragment = gql`
|
export const itemAppearanceFragment = gql`
|
||||||
fragment ItemAppearanceForOutfitPreview on ItemAppearance {
|
fragment ItemAppearanceForOutfitPreview on ItemAppearance {
|
||||||
id
|
id
|
||||||
layers {
|
layers {
|
||||||
id
|
...AppearanceLayerForOutfitPreview
|
||||||
remoteId # HACK: This is for Support tools, but other views don't need it
|
|
||||||
svgUrl
|
|
||||||
canvasMovieLibraryUrl
|
|
||||||
imageUrl(size: SIZE_600)
|
|
||||||
swfUrl # HACK: This is for Support tools, but other views don't need it
|
|
||||||
knownGlitches # HACK: This is for Support tools, but other views don't need it
|
|
||||||
bodyId
|
|
||||||
zone {
|
|
||||||
label @client # HACK: This is for Support tools, but other views don't need it
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
...ItemAppearanceForGetVisibleLayers
|
...ItemAppearanceForGetVisibleLayers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
${appearanceLayerFragment}
|
||||||
${itemAppearanceFragmentForGetVisibleLayers}
|
${itemAppearanceFragmentForGetVisibleLayers}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,11 @@ const typeDefs = gql`
|
||||||
}
|
}
|
||||||
|
|
||||||
extend type Query {
|
extend type Query {
|
||||||
|
# Return the item appearance layers with the given remoteIds. We use this
|
||||||
|
# in Support tool to bulk-add a range of layers to an item. When we can't
|
||||||
|
# find a layer with the given ID, we omit its entry from the returned list.
|
||||||
|
itemAppearanceLayersByRemoteId(remoteIds: [ID!]!): [AppearanceLayer]!
|
||||||
|
|
||||||
# Return the number of layers that have been converted to HTML5, optionally
|
# Return the number of layers that have been converted to HTML5, optionally
|
||||||
# filtered by type. Cache for 30 minutes (we re-sync with Neopets every
|
# filtered by type. Cache for 30 minutes (we re-sync with Neopets every
|
||||||
# hour).
|
# hour).
|
||||||
|
@ -253,6 +258,16 @@ const resolvers = {
|
||||||
},
|
},
|
||||||
|
|
||||||
Query: {
|
Query: {
|
||||||
|
itemAppearanceLayersByRemoteId: async (
|
||||||
|
_,
|
||||||
|
{ remoteIds },
|
||||||
|
{ swfAssetByRemoteIdLoader }
|
||||||
|
) => {
|
||||||
|
const layers = await swfAssetByRemoteIdLoader.loadMany(
|
||||||
|
remoteIds.map((remoteId) => ({ type: "object", remoteId }))
|
||||||
|
);
|
||||||
|
return layers.filter((l) => l).map(({ id }) => ({ id }));
|
||||||
|
},
|
||||||
numAppearanceLayersConverted: async (
|
numAppearanceLayersConverted: async (
|
||||||
_,
|
_,
|
||||||
{ type },
|
{ type },
|
||||||
|
|
|
@ -23,6 +23,10 @@ const typeDefs = gql`
|
||||||
id: ID!
|
id: ID!
|
||||||
name: String!
|
name: String!
|
||||||
|
|
||||||
|
# A PetAppearance that has this species. Prefers Blue (or the optional
|
||||||
|
# preferredColorId), and happy poses.
|
||||||
|
canonicalAppearance(preferredColorId: ID): PetAppearance
|
||||||
|
|
||||||
# The bodyId for PetAppearances that use this species and a standard color.
|
# The bodyId for PetAppearances that use this species and a standard color.
|
||||||
# We use this to preload the standard body IDs, so that items stay when
|
# We use this to preload the standard body IDs, so that items stay when
|
||||||
# switching between standard colors.
|
# switching between standard colors.
|
||||||
|
@ -60,6 +64,7 @@ const typeDefs = gql`
|
||||||
species: Species!
|
species: Species!
|
||||||
color: Color!
|
color: Color!
|
||||||
pose: Pose!
|
pose: Pose!
|
||||||
|
body: Body!
|
||||||
bodyId: ID!
|
bodyId: ID!
|
||||||
|
|
||||||
layers: [AppearanceLayer!]!
|
layers: [AppearanceLayer!]!
|
||||||
|
@ -134,6 +139,34 @@ const resolvers = {
|
||||||
const speciesTranslation = await speciesTranslationLoader.load(id);
|
const speciesTranslation = await speciesTranslationLoader.load(id);
|
||||||
return capitalize(speciesTranslation.name);
|
return capitalize(speciesTranslation.name);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
canonicalAppearance: async (
|
||||||
|
{ id, species },
|
||||||
|
{ preferredColorId },
|
||||||
|
{ petTypeBySpeciesAndColorLoader, canonicalPetStateForBodyLoader }
|
||||||
|
) => {
|
||||||
|
const petType = await petTypeBySpeciesAndColorLoader.load({
|
||||||
|
speciesId: id,
|
||||||
|
colorId: preferredColorId || "8", // defaults to Blue
|
||||||
|
});
|
||||||
|
if (!petType) {
|
||||||
|
// HACK: For a new species, we shouldn't necessarily crash if Blue
|
||||||
|
// isn't modeled… but like, whatever :p
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const petState = await canonicalPetStateForBodyLoader.load({
|
||||||
|
bodyId: petType.bodyId,
|
||||||
|
preferredColorId,
|
||||||
|
fallbackColorId: FALLBACK_COLOR_IDS[species?.id] || "8",
|
||||||
|
});
|
||||||
|
if (!petState) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { id: petState.id };
|
||||||
|
},
|
||||||
|
|
||||||
standardBodyId: async ({ id }, _, { petTypeBySpeciesAndColorLoader }) => {
|
standardBodyId: async ({ id }, _, { petTypeBySpeciesAndColorLoader }) => {
|
||||||
const petType = await petTypeBySpeciesAndColorLoader.load({
|
const petType = await petTypeBySpeciesAndColorLoader.load({
|
||||||
speciesId: id,
|
speciesId: id,
|
||||||
|
@ -195,6 +228,11 @@ const resolvers = {
|
||||||
const petType = await petTypeLoader.load(petState.petTypeId);
|
const petType = await petTypeLoader.load(petState.petTypeId);
|
||||||
return { id: petType.speciesId };
|
return { id: petType.speciesId };
|
||||||
},
|
},
|
||||||
|
body: async ({ id }, _, { petStateLoader, petTypeLoader }) => {
|
||||||
|
const petState = await petStateLoader.load(id);
|
||||||
|
const petType = await petTypeLoader.load(petState.petTypeId);
|
||||||
|
return { id: petType.bodyId };
|
||||||
|
},
|
||||||
bodyId: async ({ id }, _, { petStateLoader, petTypeLoader }) => {
|
bodyId: async ({ id }, _, { petStateLoader, petTypeLoader }) => {
|
||||||
const petState = await petStateLoader.load(id);
|
const petState = await petStateLoader.load(id);
|
||||||
const petType = await petTypeLoader.load(petState.petTypeId);
|
const petType = await petTypeLoader.load(petState.petTypeId);
|
||||||
|
|
Loading…
Reference in a new issue