add zones to user items page
idk the labels section was feeling empty, and I didn't see a way to streamline it more, so I figured, add info that might be useful! lol
This commit is contained in:
parent
5111ebcbfe
commit
274a4f716f
7 changed files with 177 additions and 79 deletions
|
@ -13,6 +13,7 @@ import ItemCard, {
|
||||||
NpBadge,
|
NpBadge,
|
||||||
YouOwnThisBadge,
|
YouOwnThisBadge,
|
||||||
YouWantThisBadge,
|
YouWantThisBadge,
|
||||||
|
ZoneBadgeList,
|
||||||
} from "./components/ItemCard";
|
} from "./components/ItemCard";
|
||||||
import useCurrentUser from "./components/useCurrentUser";
|
import useCurrentUser from "./components/useCurrentUser";
|
||||||
import WIPCallout from "./components/WIPCallout";
|
import WIPCallout from "./components/WIPCallout";
|
||||||
|
@ -35,6 +36,10 @@ function UserItemsPage() {
|
||||||
isNc
|
isNc
|
||||||
name
|
name
|
||||||
thumbnailUrl
|
thumbnailUrl
|
||||||
|
allOccupiedZones {
|
||||||
|
id
|
||||||
|
label @client
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
itemsTheyWant {
|
itemsTheyWant {
|
||||||
|
@ -42,6 +47,10 @@ function UserItemsPage() {
|
||||||
isNc
|
isNc
|
||||||
name
|
name
|
||||||
thumbnailUrl
|
thumbnailUrl
|
||||||
|
allOccupiedZones {
|
||||||
|
id
|
||||||
|
label @client
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,18 +123,24 @@ function UserItemsPage() {
|
||||||
{isCurrentUser ? "Items you own" : `Items ${data.user.username} owns`}
|
{isCurrentUser ? "Items you own" : `Items ${data.user.username} owns`}
|
||||||
</Heading2>
|
</Heading2>
|
||||||
<ItemCardList>
|
<ItemCardList>
|
||||||
{sortedItemsTheyOwn.map((item) => (
|
{sortedItemsTheyOwn.map((item) => {
|
||||||
<ItemCard
|
return (
|
||||||
key={item.id}
|
<ItemCard
|
||||||
item={item}
|
key={item.id}
|
||||||
badges={
|
item={item}
|
||||||
<ItemBadgeList>
|
badges={
|
||||||
{item.isNc ? <NcBadge /> : <NpBadge />}
|
<ItemBadgeList>
|
||||||
{showYouWantThisBadge(item) && <YouWantThisBadge />}
|
{item.isNc ? <NcBadge /> : <NpBadge />}
|
||||||
</ItemBadgeList>
|
{showYouWantThisBadge(item) && <YouWantThisBadge />}
|
||||||
}
|
<ZoneBadgeList
|
||||||
/>
|
zones={item.allOccupiedZones}
|
||||||
))}
|
variant="occupies"
|
||||||
|
/>
|
||||||
|
</ItemBadgeList>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</ItemCardList>
|
</ItemCardList>
|
||||||
|
|
||||||
<Heading2 marginBottom="6" marginTop="8">
|
<Heading2 marginBottom="6" marginTop="8">
|
||||||
|
@ -140,6 +155,10 @@ function UserItemsPage() {
|
||||||
<ItemBadgeList>
|
<ItemBadgeList>
|
||||||
{item.isNc ? <NcBadge /> : <NpBadge />}
|
{item.isNc ? <NcBadge /> : <NpBadge />}
|
||||||
{showYouOwnThisBadge(item) && <YouOwnThisBadge />}
|
{showYouOwnThisBadge(item) && <YouOwnThisBadge />}
|
||||||
|
<ZoneBadgeList
|
||||||
|
zones={item.allOccupiedZones}
|
||||||
|
variant="occupies"
|
||||||
|
/>
|
||||||
</ItemBadgeList>
|
</ItemBadgeList>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { css, cx } from "emotion";
|
import { css, cx } from "emotion";
|
||||||
import {
|
import {
|
||||||
Badge,
|
|
||||||
Box,
|
Box,
|
||||||
Flex,
|
Flex,
|
||||||
IconButton,
|
IconButton,
|
||||||
|
@ -10,24 +9,19 @@ import {
|
||||||
useColorModeValue,
|
useColorModeValue,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@chakra-ui/core";
|
} from "@chakra-ui/core";
|
||||||
import {
|
import { EditIcon, DeleteIcon, InfoIcon } from "@chakra-ui/icons";
|
||||||
EditIcon,
|
|
||||||
DeleteIcon,
|
|
||||||
InfoIcon,
|
|
||||||
NotAllowedIcon,
|
|
||||||
} from "@chakra-ui/icons";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import loadable from "@loadable/component";
|
import loadable from "@loadable/component";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ItemCardContent,
|
ItemCardContent,
|
||||||
ItemBadgeList,
|
ItemBadgeList,
|
||||||
ItemBadgeTooltip,
|
|
||||||
MaybeAnimatedBadge,
|
MaybeAnimatedBadge,
|
||||||
NcBadge,
|
NcBadge,
|
||||||
NpBadge,
|
NpBadge,
|
||||||
YouOwnThisBadge,
|
YouOwnThisBadge,
|
||||||
YouWantThisBadge,
|
YouWantThisBadge,
|
||||||
|
ZoneBadgeList,
|
||||||
} from "../components/ItemCard";
|
} from "../components/ItemCard";
|
||||||
import SupportOnly from "./support/SupportOnly";
|
import SupportOnly from "./support/SupportOnly";
|
||||||
import useSupport from "./support/useSupport";
|
import useSupport from "./support/useSupport";
|
||||||
|
@ -207,11 +201,9 @@ function ItemContainer({ children, isDisabled = false }) {
|
||||||
|
|
||||||
function ItemBadges({ item }) {
|
function ItemBadges({ item }) {
|
||||||
const { isSupportUser } = useSupport();
|
const { isSupportUser } = useSupport();
|
||||||
const occupiedZoneLabels = getZoneLabels(
|
const occupiedZones = item.appearanceOn.layers.map((l) => l.zone);
|
||||||
item.appearanceOn.layers.map((l) => l.zone)
|
const restrictedZones = item.appearanceOn.restrictedZones.filter(
|
||||||
);
|
(z) => z.isCommonlyUsedByItems
|
||||||
const restrictedZoneLabels = getZoneLabels(
|
|
||||||
item.appearanceOn.restrictedZones.filter((z) => z.isCommonlyUsedByItems)
|
|
||||||
);
|
);
|
||||||
const isMaybeAnimated = item.appearanceOn.layers.some(
|
const isMaybeAnimated = item.appearanceOn.layers.some(
|
||||||
(l) => l.canvasMovieLibraryUrl
|
(l) => l.canvasMovieLibraryUrl
|
||||||
|
@ -239,12 +231,8 @@ function ItemBadges({ item }) {
|
||||||
}
|
}
|
||||||
{item.currentUserOwnsThis && <YouOwnThisBadge variant="short" />}
|
{item.currentUserOwnsThis && <YouOwnThisBadge variant="short" />}
|
||||||
{item.currentUserWantsThis && <YouWantThisBadge variant="short" />}
|
{item.currentUserWantsThis && <YouWantThisBadge variant="short" />}
|
||||||
{occupiedZoneLabels.map((zoneLabel) => (
|
<ZoneBadgeList zones={occupiedZones} variant="occupied" />
|
||||||
<ZoneBadge key={zoneLabel} variant="occupies" zoneLabel={zoneLabel} />
|
<ZoneBadgeList zones={restrictedZones} variant="restricts" />
|
||||||
))}
|
|
||||||
{restrictedZoneLabels.map((zoneLabel) => (
|
|
||||||
<ZoneBadge key={zoneLabel} variant="restricts" zoneLabel={zoneLabel} />
|
|
||||||
))}
|
|
||||||
</ItemBadgeList>
|
</ItemBadgeList>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -323,53 +311,6 @@ export function ItemListSkeleton({ count }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* getZoneLabels returns the set of labels for the given zones. Sometimes an
|
|
||||||
* item occupies multiple zones of the same name, so it's especially important
|
|
||||||
* to de-duplicate them here!
|
|
||||||
*/
|
|
||||||
function getZoneLabels(zones) {
|
|
||||||
let labels = zones.map((z) => z.label);
|
|
||||||
labels = new Set(labels);
|
|
||||||
labels = [...labels].sort();
|
|
||||||
return labels;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ZoneBadge({ variant, zoneLabel }) {
|
|
||||||
// Shorten the label when necessary, to make the badges less bulky
|
|
||||||
const shorthand = zoneLabel
|
|
||||||
.replace("Background Item", "BG Item")
|
|
||||||
.replace("Foreground Item", "FG Item")
|
|
||||||
.replace("Lower-body", "Lower")
|
|
||||||
.replace("Upper-body", "Upper")
|
|
||||||
.replace("Transient", "Trans")
|
|
||||||
.replace("Biology", "Bio");
|
|
||||||
|
|
||||||
if (variant === "restricts") {
|
|
||||||
return (
|
|
||||||
<ItemBadgeTooltip
|
|
||||||
label={`Restricted: This item can't be worn with ${zoneLabel} items`}
|
|
||||||
>
|
|
||||||
<Badge>
|
|
||||||
<Box display="flex" alignItems="center">
|
|
||||||
{shorthand} <NotAllowedIcon marginLeft="1" />
|
|
||||||
</Box>
|
|
||||||
</Badge>
|
|
||||||
</ItemBadgeTooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shorthand !== zoneLabel) {
|
|
||||||
return (
|
|
||||||
<ItemBadgeTooltip label={zoneLabel}>
|
|
||||||
<Badge>{shorthand}</Badge>
|
|
||||||
</ItemBadgeTooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Badge>{shorthand}</Badge>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* containerHasFocus is a common CSS selector, for the case where our parent
|
* containerHasFocus is a common CSS selector, for the case where our parent
|
||||||
* .item-container is hovered or the adjacent hidden radio/checkbox is
|
* .item-container is hovered or the adjacent hidden radio/checkbox is
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
useColorModeValue,
|
useColorModeValue,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@chakra-ui/core";
|
} from "@chakra-ui/core";
|
||||||
import { CheckIcon, StarIcon } from "@chakra-ui/icons";
|
import { CheckIcon, NotAllowedIcon, StarIcon } from "@chakra-ui/icons";
|
||||||
import { HiSparkles } from "react-icons/hi";
|
import { HiSparkles } from "react-icons/hi";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
@ -279,6 +279,53 @@ export function YouWantThisBadge({ variant = "long" }) {
|
||||||
return badge;
|
return badge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ZoneBadge({ variant, zoneLabel }) {
|
||||||
|
// Shorten the label when necessary, to make the badges less bulky
|
||||||
|
const shorthand = zoneLabel
|
||||||
|
.replace("Background Item", "BG Item")
|
||||||
|
.replace("Foreground Item", "FG Item")
|
||||||
|
.replace("Lower-body", "Lower")
|
||||||
|
.replace("Upper-body", "Upper")
|
||||||
|
.replace("Transient", "Trans")
|
||||||
|
.replace("Biology", "Bio");
|
||||||
|
|
||||||
|
if (variant === "restricts") {
|
||||||
|
return (
|
||||||
|
<ItemBadgeTooltip
|
||||||
|
label={`Restricted: This item can't be worn with ${zoneLabel} items`}
|
||||||
|
>
|
||||||
|
<Badge>
|
||||||
|
<Box display="flex" alignItems="center">
|
||||||
|
{shorthand} <NotAllowedIcon marginLeft="1" />
|
||||||
|
</Box>
|
||||||
|
</Badge>
|
||||||
|
</ItemBadgeTooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shorthand !== zoneLabel) {
|
||||||
|
return (
|
||||||
|
<ItemBadgeTooltip label={zoneLabel}>
|
||||||
|
<Badge>{shorthand}</Badge>
|
||||||
|
</ItemBadgeTooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Badge>{shorthand}</Badge>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ZoneBadgeList({ zones, variant }) {
|
||||||
|
// Get the sorted zone labels. Sometimes an item occupies multiple zones of
|
||||||
|
// the same name, so it's important to de-duplicate them!
|
||||||
|
let labels = zones.map((z) => z.label);
|
||||||
|
labels = new Set(labels);
|
||||||
|
labels = [...labels].sort();
|
||||||
|
|
||||||
|
return labels.map((label) => (
|
||||||
|
<ZoneBadge key={label} zoneLabel={label} variant={variant} />
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
export function MaybeAnimatedBadge() {
|
export function MaybeAnimatedBadge() {
|
||||||
return (
|
return (
|
||||||
<ItemBadgeTooltip label="Maybe animated? (Support only)">
|
<ItemBadgeTooltip label="Maybe animated? (Support only)">
|
||||||
|
|
|
@ -324,6 +324,26 @@ const buildItemBodiesWithAppearanceDataLoader = (db) =>
|
||||||
return itemIds.map((itemId) => entities.filter((e) => e.itemId === itemId));
|
return itemIds.map((itemId) => entities.filter((e) => e.itemId === itemId));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const buildItemAllOccupiedZonesLoader = (db) =>
|
||||||
|
new DataLoader(async (itemIds) => {
|
||||||
|
const qs = itemIds.map((_) => "?").join(", ");
|
||||||
|
const [rows, _] = await db.execute(
|
||||||
|
`SELECT items.id, GROUP_CONCAT(DISTINCT sa.zone_id) AS zone_ids FROM items
|
||||||
|
INNER JOIN parents_swf_assets psa
|
||||||
|
ON psa.parent_type = "Item" AND psa.parent_id = items.id
|
||||||
|
INNER JOIN swf_assets sa ON sa.id = psa.swf_asset_id
|
||||||
|
WHERE items.id IN (${qs})
|
||||||
|
GROUP BY items.id;`,
|
||||||
|
itemIds
|
||||||
|
);
|
||||||
|
|
||||||
|
const entities = rows.map(normalizeRow);
|
||||||
|
|
||||||
|
return itemIds.map((itemId) =>
|
||||||
|
entities.find((e) => e.id === itemId).zoneIds.split(",")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const buildPetTypeLoader = (db, loaders) =>
|
const buildPetTypeLoader = (db, loaders) =>
|
||||||
new DataLoader(async (petTypeIds) => {
|
new DataLoader(async (petTypeIds) => {
|
||||||
const qs = petTypeIds.map((_) => "?").join(",");
|
const qs = petTypeIds.map((_) => "?").join(",");
|
||||||
|
@ -745,6 +765,7 @@ function buildLoaders(db) {
|
||||||
loaders.itemBodiesWithAppearanceDataLoader = buildItemBodiesWithAppearanceDataLoader(
|
loaders.itemBodiesWithAppearanceDataLoader = buildItemBodiesWithAppearanceDataLoader(
|
||||||
db
|
db
|
||||||
);
|
);
|
||||||
|
loaders.itemAllOccupiedZonesLoader = buildItemAllOccupiedZonesLoader(db);
|
||||||
loaders.petTypeLoader = buildPetTypeLoader(db, loaders);
|
loaders.petTypeLoader = buildPetTypeLoader(db, loaders);
|
||||||
loaders.petTypeBySpeciesAndColorLoader = buildPetTypeBySpeciesAndColorLoader(
|
loaders.petTypeBySpeciesAndColorLoader = buildPetTypeBySpeciesAndColorLoader(
|
||||||
db,
|
db,
|
||||||
|
|
|
@ -26,6 +26,9 @@ describe("Item", () => {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
explicitlyBodySpecific
|
explicitlyBodySpecific
|
||||||
|
allOccupiedZones {
|
||||||
|
label
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
@ -57,6 +60,22 @@ describe("Item", () => {
|
||||||
"78104",
|
"78104",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
Array [
|
||||||
|
"SELECT items.id, GROUP_CONCAT(DISTINCT sa.zone_id) AS zone_ids FROM items
|
||||||
|
INNER JOIN parents_swf_assets psa
|
||||||
|
ON psa.parent_type = \\"Item\\" AND psa.parent_id = items.id
|
||||||
|
INNER JOIN swf_assets sa ON sa.id = psa.swf_asset_id
|
||||||
|
WHERE items.id IN (?, ?, ?, ?, ?, ?)
|
||||||
|
GROUP BY items.id;",
|
||||||
|
Array [
|
||||||
|
"38913",
|
||||||
|
"38911",
|
||||||
|
"38912",
|
||||||
|
"55788",
|
||||||
|
"77530",
|
||||||
|
"78104",
|
||||||
|
],
|
||||||
|
],
|
||||||
Array [
|
Array [
|
||||||
"SELECT * FROM color_translations
|
"SELECT * FROM color_translations
|
||||||
WHERE color_id IN (?) AND locale = \\"en\\"",
|
WHERE color_id IN (?) AND locale = \\"en\\"",
|
||||||
|
@ -64,6 +83,17 @@ describe("Item", () => {
|
||||||
"44",
|
"44",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
Array [
|
||||||
|
"SELECT * FROM zone_translations WHERE zone_id IN (?,?,?,?,?,?) AND locale = \\"en\\"",
|
||||||
|
Array [
|
||||||
|
"25",
|
||||||
|
"40",
|
||||||
|
"26",
|
||||||
|
"46",
|
||||||
|
"23",
|
||||||
|
"3",
|
||||||
|
],
|
||||||
|
],
|
||||||
]
|
]
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
|
@ -7983,6 +7983,11 @@ exports[`Item loads metadata 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"items": Array [
|
"items": Array [
|
||||||
Object {
|
Object {
|
||||||
|
"allOccupiedZones": Array [
|
||||||
|
Object {
|
||||||
|
"label": "Gloves",
|
||||||
|
},
|
||||||
|
],
|
||||||
"createdAt": null,
|
"createdAt": null,
|
||||||
"description": "Dont leave any trace that you were there with these gloves.",
|
"description": "Dont leave any trace that you were there with these gloves.",
|
||||||
"explicitlyBodySpecific": false,
|
"explicitlyBodySpecific": false,
|
||||||
|
@ -7994,6 +7999,11 @@ Object {
|
||||||
"thumbnailUrl": "http://images.neopets.com/items/clo_zafara_agent_gloves.gif",
|
"thumbnailUrl": "http://images.neopets.com/items/clo_zafara_agent_gloves.gif",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
"allOccupiedZones": Array [
|
||||||
|
Object {
|
||||||
|
"label": "Hat",
|
||||||
|
},
|
||||||
|
],
|
||||||
"createdAt": null,
|
"createdAt": null,
|
||||||
"description": "Hide your face and hair so no one can recognise you.",
|
"description": "Hide your face and hair so no one can recognise you.",
|
||||||
"explicitlyBodySpecific": false,
|
"explicitlyBodySpecific": false,
|
||||||
|
@ -8005,6 +8015,11 @@ Object {
|
||||||
"thumbnailUrl": "http://images.neopets.com/items/clo_zafara_agent_hood.gif",
|
"thumbnailUrl": "http://images.neopets.com/items/clo_zafara_agent_hood.gif",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
"allOccupiedZones": Array [
|
||||||
|
Object {
|
||||||
|
"label": "Jacket",
|
||||||
|
},
|
||||||
|
],
|
||||||
"createdAt": null,
|
"createdAt": null,
|
||||||
"description": "This robe is great for being stealthy.",
|
"description": "This robe is great for being stealthy.",
|
||||||
"explicitlyBodySpecific": false,
|
"explicitlyBodySpecific": false,
|
||||||
|
@ -8016,6 +8031,11 @@ Object {
|
||||||
"thumbnailUrl": "http://images.neopets.com/items/clo_zafara_agent_robe.gif",
|
"thumbnailUrl": "http://images.neopets.com/items/clo_zafara_agent_robe.gif",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
"allOccupiedZones": Array [
|
||||||
|
Object {
|
||||||
|
"label": "Static",
|
||||||
|
},
|
||||||
|
],
|
||||||
"createdAt": "2020-01-01T00:00:00.000Z",
|
"createdAt": "2020-01-01T00:00:00.000Z",
|
||||||
"description": "Maybe youll be discovered by some Neopets from the future and thawed out!",
|
"description": "Maybe youll be discovered by some Neopets from the future and thawed out!",
|
||||||
"explicitlyBodySpecific": true,
|
"explicitlyBodySpecific": true,
|
||||||
|
@ -8027,6 +8047,11 @@ Object {
|
||||||
"thumbnailUrl": "http://images.neopets.com/items/mall_petinice.gif",
|
"thumbnailUrl": "http://images.neopets.com/items/mall_petinice.gif",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
"allOccupiedZones": Array [
|
||||||
|
Object {
|
||||||
|
"label": "Shirt/Dress",
|
||||||
|
},
|
||||||
|
],
|
||||||
"createdAt": "2020-01-01T00:00:00.000Z",
|
"createdAt": "2020-01-01T00:00:00.000Z",
|
||||||
"description": "Made with the finest jewels of the sea!",
|
"description": "Made with the finest jewels of the sea!",
|
||||||
"explicitlyBodySpecific": false,
|
"explicitlyBodySpecific": false,
|
||||||
|
@ -8041,6 +8066,11 @@ Object {
|
||||||
"thumbnailUrl": "http://images.neopets.com/items/mall_clo_marabluegown.gif",
|
"thumbnailUrl": "http://images.neopets.com/items/mall_clo_marabluegown.gif",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
"allOccupiedZones": Array [
|
||||||
|
Object {
|
||||||
|
"label": "Background",
|
||||||
|
},
|
||||||
|
],
|
||||||
"createdAt": "2020-01-01T00:00:00.000Z",
|
"createdAt": "2020-01-01T00:00:00.000Z",
|
||||||
"description": "You truly are the number one fan of Altador Cup, and your room reflects this!",
|
"description": "You truly are the number one fan of Altador Cup, and your room reflects this!",
|
||||||
"explicitlyBodySpecific": false,
|
"explicitlyBodySpecific": false,
|
||||||
|
|
|
@ -48,6 +48,11 @@ const typeDefs = gql`
|
||||||
# which species this is for by going through the body field on
|
# which species this is for by going through the body field on
|
||||||
# ItemAppearance!)
|
# ItemAppearance!)
|
||||||
canonicalAppearance: ItemAppearance
|
canonicalAppearance: ItemAppearance
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# info about the item.
|
||||||
|
allOccupiedZones: [Zone!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type ItemAppearance {
|
type ItemAppearance {
|
||||||
|
@ -210,6 +215,11 @@ const resolvers = {
|
||||||
body: { id: canonicalBodyId, species: { id: rows[0].speciesId } },
|
body: { id: canonicalBodyId, species: { id: rows[0].speciesId } },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
allOccupiedZones: async ({ id }, _, { itemAllOccupiedZonesLoader }) => {
|
||||||
|
const zoneIds = await itemAllOccupiedZonesLoader.load(id);
|
||||||
|
const zones = zoneIds.map((id) => ({ id }));
|
||||||
|
return zones;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
ItemAppearance: {
|
ItemAppearance: {
|
||||||
|
|
Loading…
Reference in a new issue