stop using hardcoded items entirely!
This commit is contained in:
parent
abfe854756
commit
a9d50bd0c3
5 changed files with 80 additions and 114 deletions
|
@ -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 },
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
56
src/data.js
56
src/data.js
|
@ -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",
|
||||
},
|
||||
];
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue