stop using hardcoded items entirely!

This commit is contained in:
Matt Dunn-Rankin 2020-04-24 19:16:24 -07:00
parent abfe854756
commit a9d50bd0c3
5 changed files with 80 additions and 114 deletions

View file

@ -8,10 +8,12 @@ import { Delay } from "./util";
import "./OutfitPreview.css";
function OutfitPreview({ itemIds, speciesId, colorId }) {
function OutfitPreview({ outfitState }) {
const { wornItemIds, speciesId, colorId } = outfitState;
const { loading, error, data } = useQuery(
gql`
query($itemIds: [ID!]!, $speciesId: ID!, $colorId: ID!) {
query($wornItemIds: [ID!]!, $speciesId: ID!, $colorId: ID!) {
petAppearance(speciesId: $speciesId, colorId: $colorId) {
layers {
id
@ -27,7 +29,7 @@ function OutfitPreview({ itemIds, speciesId, colorId }) {
}
}
items(ids: $itemIds) {
items(ids: $wornItemIds) {
id
appearanceOn(speciesId: $speciesId, colorId: $colorId) {
layers {
@ -47,7 +49,7 @@ function OutfitPreview({ itemIds, speciesId, colorId }) {
}
`,
{
variables: { itemIds, speciesId, colorId },
variables: { wornItemIds, speciesId, colorId },
}
);

View file

@ -19,7 +19,6 @@ import {
useToast,
} from "@chakra-ui/core";
import { ITEMS } from "./data";
import ItemList, { ItemListSkeleton } from "./ItemList";
import useItemData from "./useItemData";
import useOutfitState from "./useOutfitState.js";
@ -27,7 +26,7 @@ import OutfitPreview from "./OutfitPreview";
import { Delay } from "./util";
function WardrobePage() {
const { loading, error, data, dispatch: dispatchToOutfit } = useOutfitState();
const { loading, error, outfitState, dispatchToOutfit } = useOutfitState();
const [searchQuery, setSearchQuery] = React.useState("");
const toast = useToast();
@ -67,11 +66,7 @@ function WardrobePage() {
width="100%"
>
<Box gridArea="outfit" backgroundColor="gray.900">
<OutfitPreview
itemIds={data.wornItemIds}
speciesId={data.speciesId}
colorId={data.colorId}
/>
<OutfitPreview outfitState={outfitState} />
</Box>
<Box gridArea="search" boxShadow="sm">
<Box px="5" py="3">
@ -83,13 +78,13 @@ function WardrobePage() {
{searchQuery ? (
<SearchPanel
query={searchQuery}
wornItemIds={data.wornItemIds}
outfitState={outfitState}
dispatchToOutfit={dispatchToOutfit}
/>
) : (
<ItemsPanel
zonesAndItems={data.zonesAndItems}
loading={loading}
outfitState={outfitState}
dispatchToOutfit={dispatchToOutfit}
/>
)}
@ -135,8 +130,13 @@ function SearchToolbar({ query, onChange }) {
);
}
function SearchPanel({ query, wornItemIds, dispatchToOutfit }) {
const { loading, error, itemsById } = useItemData(ITEMS.map((i) => i.id));
function SearchPanel({ query, outfitState, dispatchToOutfit }) {
const { allItemIds, wornItemIds, speciesId, colorId } = outfitState;
const { loading, error, itemsById } = useItemData(
allItemIds,
speciesId,
colorId
);
const normalize = (s) => s.toLowerCase();
const results = Object.values(itemsById).filter((item) =>
@ -172,7 +172,7 @@ function SearchResults({
if (error) {
return (
<Text color="green.500">
We hit an error trying to load your search results
We hit an error trying to load your search results{" "}
<span role="img" aria-label="(sweat emoji)">
😓
</span>{" "}
@ -202,7 +202,9 @@ function SearchResults({
);
}
function ItemsPanel({ zonesAndItems, loading, dispatchToOutfit }) {
function ItemsPanel({ outfitState, loading, dispatchToOutfit }) {
const { zonesAndItems, wornItemIds } = outfitState;
return (
<Box color="green.800">
<OutfitHeading />
@ -217,12 +219,14 @@ function ItemsPanel({ zonesAndItems, loading, dispatchToOutfit }) {
</Box>
))}
{!loading &&
zonesAndItems.map(({ zoneName, items, wornItemId }) => (
<Box key={zoneName}>
<Heading2 mb="3">{zoneName}</Heading2>
zonesAndItems.map(({ zone, items }) => (
<Box key={zone.id}>
<Heading2 mb="3">{zone.label}</Heading2>
<ItemList
items={items}
wornItemIds={[wornItemId]}
wornItemIds={items
.map((i) => i.id)
.filter((id) => wornItemIds.includes(id))}
dispatchToOutfit={dispatchToOutfit}
/>
</Box>

View file

@ -1,56 +0,0 @@
export const ITEMS = [
{
id: "38913",
// name: "Zafara Agent Gloves",
// thumbnailSrc: "http://images.neopets.com/items/clo_zafara_agent_gloves.gif",
zoneName: "Gloves",
},
{
id: "38911",
// name: "Zafara Agent Hood",
// thumbnailSrc: "http://images.neopets.com/items/clo_zafara_agent_hood.gif",
zoneName: "Hat",
},
{
id: "38912",
// name: "Zafara Agent Robe",
// thumbnailSrc: "http://images.neopets.com/items/clo_zafara_agent_robe.gif",
zoneName: "Jacket",
},
{
id: "37375",
// name: "Moon and Stars Background",
// thumbnailSrc: "http://images.neopets.com/items/bg_moonstars.gif",
zoneName: "Background",
},
{
id: "74166",
// name: "Altador Forest Background",
// thumbnailSrc: "http://images.neopets.com/items/bg_ddy18_altadorforest.gif",
zoneName: "Background",
},
{
id: "48313",
// name: "Altador Cup Brooch",
// thumbnailSrc: "http://images.neopets.com/items/clo_altcuplogo_brooch.gif",
zoneName: "Collar",
},
{
id: "37229",
// name: "Magic Ball Table",
// thumbnailSrc: "http://images.neopets.com/items/gif_magicball_table.gif",
zoneName: "Lower Foreground Item",
},
{
id: "43014",
// name: "Green Leaf String Lights",
// thumbnailSrc: "http://images.neopets.com/items/toy_stringlight_illleaf.gif",
zoneName: "Background Item",
},
{
id: "43397",
// name: "Jewelled Staff",
// thumbnailSrc: "http://images.neopets.com/items/mall_staff_jewelled.gif",
zoneName: "Left-hand item",
},
];

View file

@ -1,8 +1,6 @@
import gql from "graphql-tag";
import { useQuery } from "@apollo/react-hooks";
import { ITEMS } from "./data";
function useItemData(itemIds, speciesId, colorId) {
const { loading, error, data } = useQuery(
gql`
@ -12,12 +10,13 @@ function useItemData(itemIds, speciesId, colorId) {
name
thumbnailUrl
# This is used for wearItem actions, to resolve conflicts. We don't
# use it directly; we just expect it to be in the cache!
# This is used to group items by zone, and to detect conflicts when
# wearing a new item.
appearanceOn(speciesId: $speciesId, colorId: $colorId) {
layers {
zone {
id
label
}
}
}
@ -30,11 +29,7 @@ function useItemData(itemIds, speciesId, colorId) {
const items = (data && data.items) || [];
const itemsById = {};
for (const item of items) {
const hardcodedItem = ITEMS.find((i) => i.id === item.id);
itemsById[item.id] = {
...hardcodedItem,
...item,
};
itemsById[item.id] = item;
}
return { loading, error, itemsById };

View file

@ -9,21 +9,24 @@ enableMapSet();
function useOutfitState() {
const apolloClient = useApolloClient();
const [state, dispatch] = React.useReducer(outfitStateReducer(apolloClient), {
wornItemIds: new Set([
"38913",
"38911",
"38912",
"37375",
"48313",
"37229",
"43014",
"43397",
]),
closetedItemIds: new Set(["74166"]),
speciesId: "54", // Starry
colorId: "75", // Zafara
});
const [state, dispatchToOutfit] = React.useReducer(
outfitStateReducer(apolloClient),
{
wornItemIds: new Set([
"38913",
"38911",
"38912",
"37375",
"48313",
"37229",
"43014",
"43397",
]),
closetedItemIds: new Set(["74166", "68626", "40319"]),
speciesId: "54", // Starry
colorId: "75", // Zafara
}
);
const { speciesId, colorId } = state;
@ -45,16 +48,22 @@ function useOutfitState() {
closetedItemIds
);
const data = { zonesAndItems, wornItemIds, speciesId, colorId };
const outfitState = {
zonesAndItems,
wornItemIds,
allItemIds,
speciesId,
colorId,
};
return { loading, error, data, dispatch };
return { loading, error, outfitState, dispatchToOutfit };
}
const outfitStateReducer = (apolloClient) => (baseState, action) => {
switch (action.type) {
case "wearItem":
return produce(baseState, (state) => {
const { wornItemIds, closetedItemIds, speciesId, colorId } = state;
const { wornItemIds, closetedItemIds } = state;
const { itemId } = action;
// Move the item out of the closet.
@ -131,6 +140,7 @@ function findItemConflicts(itemIdToAdd, state, apolloClient) {
return conflictingIds;
}
// TODO: Get this out of here, tbh...
function getZonesAndItems(itemsById, wornItemIds, closetedItemIds) {
const wornItems = wornItemIds.map((id) => itemsById[id]).filter((i) => i);
const closetedItems = closetedItemIds
@ -138,17 +148,28 @@ function getZonesAndItems(itemsById, wornItemIds, closetedItemIds) {
.filter((i) => i);
const allItems = [...wornItems, ...closetedItems];
const allZoneNames = [...new Set(allItems.map((item) => item.zoneName))];
allZoneNames.sort();
const zonesById = new Map();
const itemsByZoneId = new Map();
for (const item of allItems) {
for (const layer of item.appearanceOn.layers) {
const zoneId = layer.zone.id;
zonesById.set(zoneId, layer.zone);
const zonesAndItems = allZoneNames.map((zoneName) => {
const items = allItems.filter((item) => item.zoneName === zoneName);
items.sort((a, b) => a.name.localeCompare(b.name));
const wornItemId =
items.map((item) => item.id).find((id) => wornItemIds.includes(id)) ||
null;
return { zoneName, items, wornItemId };
});
if (!itemsByZoneId.has(zoneId)) {
itemsByZoneId.set(zoneId, []);
}
itemsByZoneId.get(zoneId).push(item);
}
}
const zonesAndItems = Array.from(itemsByZoneId.entries()).map(
([zoneId, items]) => ({
zone: zonesById.get(zoneId),
items: [...items].sort((a, b) => a.name.localeCompare(b.name)),
})
);
zonesAndItems.sort((a, b) => a.zone.label.localeCompare(b.zone.label));
return zonesAndItems;
}