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";
|
import "./OutfitPreview.css";
|
||||||
|
|
||||||
function OutfitPreview({ itemIds, speciesId, colorId }) {
|
function OutfitPreview({ outfitState }) {
|
||||||
|
const { wornItemIds, speciesId, colorId } = outfitState;
|
||||||
|
|
||||||
const { loading, error, data } = useQuery(
|
const { loading, error, data } = useQuery(
|
||||||
gql`
|
gql`
|
||||||
query($itemIds: [ID!]!, $speciesId: ID!, $colorId: ID!) {
|
query($wornItemIds: [ID!]!, $speciesId: ID!, $colorId: ID!) {
|
||||||
petAppearance(speciesId: $speciesId, colorId: $colorId) {
|
petAppearance(speciesId: $speciesId, colorId: $colorId) {
|
||||||
layers {
|
layers {
|
||||||
id
|
id
|
||||||
|
@ -27,7 +29,7 @@ function OutfitPreview({ itemIds, speciesId, colorId }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items(ids: $itemIds) {
|
items(ids: $wornItemIds) {
|
||||||
id
|
id
|
||||||
appearanceOn(speciesId: $speciesId, colorId: $colorId) {
|
appearanceOn(speciesId: $speciesId, colorId: $colorId) {
|
||||||
layers {
|
layers {
|
||||||
|
@ -47,7 +49,7 @@ function OutfitPreview({ itemIds, speciesId, colorId }) {
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
{
|
{
|
||||||
variables: { itemIds, speciesId, colorId },
|
variables: { wornItemIds, speciesId, colorId },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ import {
|
||||||
useToast,
|
useToast,
|
||||||
} from "@chakra-ui/core";
|
} from "@chakra-ui/core";
|
||||||
|
|
||||||
import { ITEMS } from "./data";
|
|
||||||
import ItemList, { ItemListSkeleton } from "./ItemList";
|
import ItemList, { ItemListSkeleton } from "./ItemList";
|
||||||
import useItemData from "./useItemData";
|
import useItemData from "./useItemData";
|
||||||
import useOutfitState from "./useOutfitState.js";
|
import useOutfitState from "./useOutfitState.js";
|
||||||
|
@ -27,7 +26,7 @@ import OutfitPreview from "./OutfitPreview";
|
||||||
import { Delay } from "./util";
|
import { Delay } from "./util";
|
||||||
|
|
||||||
function WardrobePage() {
|
function WardrobePage() {
|
||||||
const { loading, error, data, dispatch: dispatchToOutfit } = useOutfitState();
|
const { loading, error, outfitState, dispatchToOutfit } = useOutfitState();
|
||||||
const [searchQuery, setSearchQuery] = React.useState("");
|
const [searchQuery, setSearchQuery] = React.useState("");
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
|
@ -67,11 +66,7 @@ function WardrobePage() {
|
||||||
width="100%"
|
width="100%"
|
||||||
>
|
>
|
||||||
<Box gridArea="outfit" backgroundColor="gray.900">
|
<Box gridArea="outfit" backgroundColor="gray.900">
|
||||||
<OutfitPreview
|
<OutfitPreview outfitState={outfitState} />
|
||||||
itemIds={data.wornItemIds}
|
|
||||||
speciesId={data.speciesId}
|
|
||||||
colorId={data.colorId}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box gridArea="search" boxShadow="sm">
|
<Box gridArea="search" boxShadow="sm">
|
||||||
<Box px="5" py="3">
|
<Box px="5" py="3">
|
||||||
|
@ -83,13 +78,13 @@ function WardrobePage() {
|
||||||
{searchQuery ? (
|
{searchQuery ? (
|
||||||
<SearchPanel
|
<SearchPanel
|
||||||
query={searchQuery}
|
query={searchQuery}
|
||||||
wornItemIds={data.wornItemIds}
|
outfitState={outfitState}
|
||||||
dispatchToOutfit={dispatchToOutfit}
|
dispatchToOutfit={dispatchToOutfit}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ItemsPanel
|
<ItemsPanel
|
||||||
zonesAndItems={data.zonesAndItems}
|
|
||||||
loading={loading}
|
loading={loading}
|
||||||
|
outfitState={outfitState}
|
||||||
dispatchToOutfit={dispatchToOutfit}
|
dispatchToOutfit={dispatchToOutfit}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -135,8 +130,13 @@ function SearchToolbar({ query, onChange }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SearchPanel({ query, wornItemIds, dispatchToOutfit }) {
|
function SearchPanel({ query, outfitState, dispatchToOutfit }) {
|
||||||
const { loading, error, itemsById } = useItemData(ITEMS.map((i) => i.id));
|
const { allItemIds, wornItemIds, speciesId, colorId } = outfitState;
|
||||||
|
const { loading, error, itemsById } = useItemData(
|
||||||
|
allItemIds,
|
||||||
|
speciesId,
|
||||||
|
colorId
|
||||||
|
);
|
||||||
|
|
||||||
const normalize = (s) => s.toLowerCase();
|
const normalize = (s) => s.toLowerCase();
|
||||||
const results = Object.values(itemsById).filter((item) =>
|
const results = Object.values(itemsById).filter((item) =>
|
||||||
|
@ -172,7 +172,7 @@ function SearchResults({
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<Text color="green.500">
|
<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 role="img" aria-label="(sweat emoji)">
|
||||||
😓
|
😓
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
|
@ -202,7 +202,9 @@ function SearchResults({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ItemsPanel({ zonesAndItems, loading, dispatchToOutfit }) {
|
function ItemsPanel({ outfitState, loading, dispatchToOutfit }) {
|
||||||
|
const { zonesAndItems, wornItemIds } = outfitState;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box color="green.800">
|
<Box color="green.800">
|
||||||
<OutfitHeading />
|
<OutfitHeading />
|
||||||
|
@ -217,12 +219,14 @@ function ItemsPanel({ zonesAndItems, loading, dispatchToOutfit }) {
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
{!loading &&
|
{!loading &&
|
||||||
zonesAndItems.map(({ zoneName, items, wornItemId }) => (
|
zonesAndItems.map(({ zone, items }) => (
|
||||||
<Box key={zoneName}>
|
<Box key={zone.id}>
|
||||||
<Heading2 mb="3">{zoneName}</Heading2>
|
<Heading2 mb="3">{zone.label}</Heading2>
|
||||||
<ItemList
|
<ItemList
|
||||||
items={items}
|
items={items}
|
||||||
wornItemIds={[wornItemId]}
|
wornItemIds={items
|
||||||
|
.map((i) => i.id)
|
||||||
|
.filter((id) => wornItemIds.includes(id))}
|
||||||
dispatchToOutfit={dispatchToOutfit}
|
dispatchToOutfit={dispatchToOutfit}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</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 gql from "graphql-tag";
|
||||||
import { useQuery } from "@apollo/react-hooks";
|
import { useQuery } from "@apollo/react-hooks";
|
||||||
|
|
||||||
import { ITEMS } from "./data";
|
|
||||||
|
|
||||||
function useItemData(itemIds, speciesId, colorId) {
|
function useItemData(itemIds, speciesId, colorId) {
|
||||||
const { loading, error, data } = useQuery(
|
const { loading, error, data } = useQuery(
|
||||||
gql`
|
gql`
|
||||||
|
@ -12,12 +10,13 @@ function useItemData(itemIds, speciesId, colorId) {
|
||||||
name
|
name
|
||||||
thumbnailUrl
|
thumbnailUrl
|
||||||
|
|
||||||
# This is used for wearItem actions, to resolve conflicts. We don't
|
# This is used to group items by zone, and to detect conflicts when
|
||||||
# use it directly; we just expect it to be in the cache!
|
# wearing a new item.
|
||||||
appearanceOn(speciesId: $speciesId, colorId: $colorId) {
|
appearanceOn(speciesId: $speciesId, colorId: $colorId) {
|
||||||
layers {
|
layers {
|
||||||
zone {
|
zone {
|
||||||
id
|
id
|
||||||
|
label
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,11 +29,7 @@ function useItemData(itemIds, speciesId, colorId) {
|
||||||
const items = (data && data.items) || [];
|
const items = (data && data.items) || [];
|
||||||
const itemsById = {};
|
const itemsById = {};
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
const hardcodedItem = ITEMS.find((i) => i.id === item.id);
|
itemsById[item.id] = item;
|
||||||
itemsById[item.id] = {
|
|
||||||
...hardcodedItem,
|
|
||||||
...item,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { loading, error, itemsById };
|
return { loading, error, itemsById };
|
||||||
|
|
|
@ -9,21 +9,24 @@ enableMapSet();
|
||||||
|
|
||||||
function useOutfitState() {
|
function useOutfitState() {
|
||||||
const apolloClient = useApolloClient();
|
const apolloClient = useApolloClient();
|
||||||
const [state, dispatch] = React.useReducer(outfitStateReducer(apolloClient), {
|
const [state, dispatchToOutfit] = React.useReducer(
|
||||||
wornItemIds: new Set([
|
outfitStateReducer(apolloClient),
|
||||||
"38913",
|
{
|
||||||
"38911",
|
wornItemIds: new Set([
|
||||||
"38912",
|
"38913",
|
||||||
"37375",
|
"38911",
|
||||||
"48313",
|
"38912",
|
||||||
"37229",
|
"37375",
|
||||||
"43014",
|
"48313",
|
||||||
"43397",
|
"37229",
|
||||||
]),
|
"43014",
|
||||||
closetedItemIds: new Set(["74166"]),
|
"43397",
|
||||||
speciesId: "54", // Starry
|
]),
|
||||||
colorId: "75", // Zafara
|
closetedItemIds: new Set(["74166", "68626", "40319"]),
|
||||||
});
|
speciesId: "54", // Starry
|
||||||
|
colorId: "75", // Zafara
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const { speciesId, colorId } = state;
|
const { speciesId, colorId } = state;
|
||||||
|
|
||||||
|
@ -45,16 +48,22 @@ function useOutfitState() {
|
||||||
closetedItemIds
|
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) => {
|
const outfitStateReducer = (apolloClient) => (baseState, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case "wearItem":
|
case "wearItem":
|
||||||
return produce(baseState, (state) => {
|
return produce(baseState, (state) => {
|
||||||
const { wornItemIds, closetedItemIds, speciesId, colorId } = state;
|
const { wornItemIds, closetedItemIds } = state;
|
||||||
const { itemId } = action;
|
const { itemId } = action;
|
||||||
|
|
||||||
// Move the item out of the closet.
|
// Move the item out of the closet.
|
||||||
|
@ -131,6 +140,7 @@ function findItemConflicts(itemIdToAdd, state, apolloClient) {
|
||||||
return conflictingIds;
|
return conflictingIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Get this out of here, tbh...
|
||||||
function getZonesAndItems(itemsById, wornItemIds, closetedItemIds) {
|
function getZonesAndItems(itemsById, wornItemIds, closetedItemIds) {
|
||||||
const wornItems = wornItemIds.map((id) => itemsById[id]).filter((i) => i);
|
const wornItems = wornItemIds.map((id) => itemsById[id]).filter((i) => i);
|
||||||
const closetedItems = closetedItemIds
|
const closetedItems = closetedItemIds
|
||||||
|
@ -138,17 +148,28 @@ function getZonesAndItems(itemsById, wornItemIds, closetedItemIds) {
|
||||||
.filter((i) => i);
|
.filter((i) => i);
|
||||||
|
|
||||||
const allItems = [...wornItems, ...closetedItems];
|
const allItems = [...wornItems, ...closetedItems];
|
||||||
const allZoneNames = [...new Set(allItems.map((item) => item.zoneName))];
|
const zonesById = new Map();
|
||||||
allZoneNames.sort();
|
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) => {
|
if (!itemsByZoneId.has(zoneId)) {
|
||||||
const items = allItems.filter((item) => item.zoneName === zoneName);
|
itemsByZoneId.set(zoneId, []);
|
||||||
items.sort((a, b) => a.name.localeCompare(b.name));
|
}
|
||||||
const wornItemId =
|
itemsByZoneId.get(zoneId).push(item);
|
||||||
items.map((item) => item.id).find((id) => wornItemIds.includes(id)) ||
|
}
|
||||||
null;
|
}
|
||||||
return { zoneName, items, wornItemId };
|
|
||||||
});
|
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;
|
return zonesAndItems;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue