load name and thumbnail from the db :o
This commit is contained in:
parent
496d69dc95
commit
7397243c7f
10 changed files with 130 additions and 76 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.tabSize": 2
|
||||
"editor.tabSize": 2,
|
||||
"jest.pathToJest": "yarn test"
|
||||
}
|
|
@ -26,7 +26,7 @@ function Item({ item, isWorn, onWear }) {
|
|||
cursor="pointer"
|
||||
onClick={onWear}
|
||||
>
|
||||
<ItemThumbnail src={item.thumbnailSrc} isWorn={isWorn} />
|
||||
<ItemThumbnail src={item.thumbnailUrl} isWorn={isWorn} />
|
||||
<Box width="3" />
|
||||
<ItemName isWorn={isWorn}>{item.name}</ItemName>
|
||||
</PseudoBox>
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import React from "react";
|
||||
import gql from "graphql-tag";
|
||||
import { useQuery } from "@apollo/react-hooks";
|
||||
import {
|
||||
Box,
|
||||
Editable,
|
||||
|
@ -27,24 +25,14 @@ import useOutfitState from "./useOutfitState.js";
|
|||
import { ITEMS } from "./data";
|
||||
|
||||
function WardrobePage() {
|
||||
const { loading, error, data: datax } = useQuery(gql`
|
||||
query {
|
||||
items(ids: [38913, 38911]) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`);
|
||||
console.log(loading, error, datax);
|
||||
|
||||
const [data, wearItemRaw] = useOutfitState();
|
||||
const { loading, error, data, wearItem } = useOutfitState();
|
||||
const [searchQuery, setSearchQuery] = React.useState("");
|
||||
|
||||
const toast = useToast();
|
||||
const [hasSentToast, setHasSentToast] = React.useState(false);
|
||||
const wearItem = React.useCallback(
|
||||
const wearItemAndToast = React.useCallback(
|
||||
(itemIdToAdd) => {
|
||||
wearItemRaw(itemIdToAdd);
|
||||
wearItem(itemIdToAdd);
|
||||
|
||||
if (!hasSentToast) {
|
||||
setTimeout(() => {
|
||||
|
@ -62,7 +50,7 @@ function WardrobePage() {
|
|||
setHasSentToast(true);
|
||||
}
|
||||
},
|
||||
[toast, wearItemRaw, hasSentToast, setHasSentToast]
|
||||
[toast, wearItem, hasSentToast, setHasSentToast]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -104,12 +92,12 @@ function WardrobePage() {
|
|||
<SearchPanel
|
||||
query={searchQuery}
|
||||
wornItemIds={data.wornItemIds}
|
||||
onWearItem={wearItem}
|
||||
onWearItem={wearItemAndToast}
|
||||
/>
|
||||
) : (
|
||||
<ItemsPanel
|
||||
zonesAndItems={data.zonesAndItems}
|
||||
onWearItem={wearItem}
|
||||
onWearItem={wearItemAndToast}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
|
|
54
src/data.js
54
src/data.js
|
@ -1,56 +1,56 @@
|
|||
export const ITEMS = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Zafara Agent Gloves",
|
||||
thumbnailSrc: "http://images.neopets.com/items/clo_zafara_agent_gloves.gif",
|
||||
id: "38913",
|
||||
// name: "Zafara Agent Gloves",
|
||||
// thumbnailSrc: "http://images.neopets.com/items/clo_zafara_agent_gloves.gif",
|
||||
zoneName: "Gloves",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Zafara Agent Hood",
|
||||
thumbnailSrc: "http://images.neopets.com/items/clo_zafara_agent_hood.gif",
|
||||
id: "38911",
|
||||
// name: "Zafara Agent Hood",
|
||||
// thumbnailSrc: "http://images.neopets.com/items/clo_zafara_agent_hood.gif",
|
||||
zoneName: "Hat",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Zafara Agent Robe",
|
||||
thumbnailSrc: "http://images.neopets.com/items/clo_zafara_agent_robe.gif",
|
||||
id: "38912",
|
||||
// name: "Zafara Agent Robe",
|
||||
// thumbnailSrc: "http://images.neopets.com/items/clo_zafara_agent_robe.gif",
|
||||
zoneName: "Jacket",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Moon and Stars Background",
|
||||
thumbnailSrc: "http://images.neopets.com/items/bg_moonstars.gif",
|
||||
id: "37375",
|
||||
// name: "Moon and Stars Background",
|
||||
// thumbnailSrc: "http://images.neopets.com/items/bg_moonstars.gif",
|
||||
zoneName: "Background",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Altador Forest Background",
|
||||
thumbnailSrc: "http://images.neopets.com/items/bg_ddy18_altadorforest.gif",
|
||||
id: "74166",
|
||||
// name: "Altador Forest Background",
|
||||
// thumbnailSrc: "http://images.neopets.com/items/bg_ddy18_altadorforest.gif",
|
||||
zoneName: "Background",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Altador Cup Brooch",
|
||||
thumbnailSrc: "http://images.neopets.com/items/clo_altcuplogo_brooch.gif",
|
||||
id: "48313",
|
||||
// name: "Altador Cup Brooch",
|
||||
// thumbnailSrc: "http://images.neopets.com/items/clo_altcuplogo_brooch.gif",
|
||||
zoneName: "Collar",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "Magic Ball Table",
|
||||
thumbnailSrc: "http://images.neopets.com/items/gif_magicball_table.gif",
|
||||
id: "37229",
|
||||
// name: "Magic Ball Table",
|
||||
// thumbnailSrc: "http://images.neopets.com/items/gif_magicball_table.gif",
|
||||
zoneName: "Lower Foreground Item",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "Green Leaf String Lights",
|
||||
thumbnailSrc: "http://images.neopets.com/items/toy_stringlight_illleaf.gif",
|
||||
id: "43014",
|
||||
// name: "Green Leaf String Lights",
|
||||
// thumbnailSrc: "http://images.neopets.com/items/toy_stringlight_illleaf.gif",
|
||||
zoneName: "Background Item",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: "Jewelled Staff",
|
||||
thumbnailSrc: "http://images.neopets.com/items/mall_staff_jewelled.gif",
|
||||
id: "43397",
|
||||
// name: "Jewelled Staff",
|
||||
// thumbnailSrc: "http://images.neopets.com/items/mall_staff_jewelled.gif",
|
||||
zoneName: "Left-hand item",
|
||||
},
|
||||
];
|
||||
|
|
|
@ -177,10 +177,15 @@ class ApolloServer extends ApolloServerBase {
|
|||
...this.playgroundOptions,
|
||||
};
|
||||
|
||||
return setHeaders(res, {
|
||||
return setHeaders(
|
||||
res,
|
||||
new Headers({
|
||||
"Content-Type": "text/html",
|
||||
...requestCorsHeadersObject,
|
||||
}).send(renderPlaygroundPage(playgroundRenderPageOptions));
|
||||
})
|
||||
)
|
||||
.status(200)
|
||||
.send(renderPlaygroundPage(playgroundRenderPageOptions));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ const typeDefs = gql`
|
|||
type Item {
|
||||
id: ID!
|
||||
name: String!
|
||||
thumbnailUrl: String!
|
||||
}
|
||||
|
||||
type Query {
|
||||
|
|
|
@ -30,6 +30,7 @@ it("can load items", async () => {
|
|||
items(ids: $ids) {
|
||||
id
|
||||
name
|
||||
thumbnailUrl
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
@ -49,14 +50,17 @@ it("can load items", async () => {
|
|||
Object {
|
||||
"id": "38911",
|
||||
"name": "Zafara Agent Hood",
|
||||
"thumbnailUrl": "http://images.neopets.com/items/clo_zafara_agent_hood.gif",
|
||||
},
|
||||
Object {
|
||||
"id": "38912",
|
||||
"name": "Zafara Agent Robe",
|
||||
"thumbnailUrl": "http://images.neopets.com/items/clo_zafara_agent_robe.gif",
|
||||
},
|
||||
Object {
|
||||
"id": "38913",
|
||||
"name": "Zafara Agent Gloves",
|
||||
"thumbnailUrl": "http://images.neopets.com/items/clo_zafara_agent_gloves.gif",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ async function loadItems(db, ids) {
|
|||
`SELECT * FROM items WHERE id IN (${qs})`,
|
||||
ids
|
||||
);
|
||||
|
||||
return rows;
|
||||
const entities = rows.map(normalizeProperties);
|
||||
return entities;
|
||||
}
|
||||
|
||||
const buildItemTranslationLoader = (db) =>
|
||||
|
@ -17,14 +17,24 @@ const buildItemTranslationLoader = (db) =>
|
|||
`SELECT * FROM item_translations WHERE item_id IN (${qs}) AND locale = "en"`,
|
||||
itemIds
|
||||
);
|
||||
const entities = rows.map(normalizeProperties);
|
||||
|
||||
const rowsByItemId = new Map(rows.map((row) => [row.item_id, row]));
|
||||
const entitiesByItemId = new Map(entities.map((e) => [e.itemId, e]));
|
||||
|
||||
return itemIds.map(
|
||||
(itemId) =>
|
||||
rowsByItemId.get(itemId) ||
|
||||
entitiesByItemId.get(itemId) ||
|
||||
new Error(`could not find translation for item ${itemId}`)
|
||||
);
|
||||
});
|
||||
|
||||
function normalizeProperties(row) {
|
||||
const normalizedRow = {};
|
||||
for (const [key, value] of Object.entries(row)) {
|
||||
const normalizedKey = key.replace(/_([a-z])/gi, (m) => m[1].toUpperCase());
|
||||
normalizedRow[normalizedKey] = value;
|
||||
}
|
||||
return normalizedRow;
|
||||
}
|
||||
|
||||
module.exports = { loadItems, buildItemTranslationLoader };
|
||||
|
|
33
src/useItemData.js
Normal file
33
src/useItemData.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import gql from "graphql-tag";
|
||||
import { useQuery } from "@apollo/react-hooks";
|
||||
|
||||
import { ITEMS } from "./data";
|
||||
|
||||
function useItemData(itemIds) {
|
||||
const { loading, error, data } = useQuery(
|
||||
gql`
|
||||
query($itemIds: [ID!]!) {
|
||||
items(ids: $itemIds) {
|
||||
id
|
||||
name
|
||||
thumbnailUrl
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ variables: { itemIds } }
|
||||
);
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
return { loading, error, itemsById };
|
||||
}
|
||||
|
||||
export default useItemData;
|
|
@ -1,19 +1,23 @@
|
|||
import React from "react";
|
||||
|
||||
import { ITEMS } from "./data.js";
|
||||
import useItemData from "./useItemData";
|
||||
|
||||
function useOutfitState() {
|
||||
const [wornItemIds, setWornItemIds] = React.useState([
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
"38913",
|
||||
"38911",
|
||||
"38912",
|
||||
"37375",
|
||||
"48313",
|
||||
"37229",
|
||||
"43014",
|
||||
"43397",
|
||||
]);
|
||||
const [closetedItemIds, setClosetedItemIds] = React.useState([5]);
|
||||
const [closetedItemIds, setClosetedItemIds] = React.useState(["74166"]);
|
||||
|
||||
const allItemIds = [...wornItemIds, ...closetedItemIds];
|
||||
|
||||
const { loading, error, itemsById } = useItemData(allItemIds);
|
||||
|
||||
const wearItem = React.useCallback(
|
||||
(itemIdToAdd) => {
|
||||
|
@ -24,7 +28,7 @@ function useOutfitState() {
|
|||
let newWornItemIds = wornItemIds;
|
||||
let newClosetedItemIds = closetedItemIds;
|
||||
|
||||
const itemToAdd = ITEMS.find((item) => item.id === itemIdToAdd);
|
||||
const itemToAdd = itemsById[itemIdToAdd];
|
||||
|
||||
// Move the item out of the closet.
|
||||
newClosetedItemIds = newClosetedItemIds.filter(
|
||||
|
@ -33,7 +37,7 @@ function useOutfitState() {
|
|||
|
||||
// Move conflicting items to the closet.
|
||||
const conflictingItemIds = newWornItemIds.filter((wornItemId) => {
|
||||
const wornItem = ITEMS.find((item) => item.id === wornItemId);
|
||||
const wornItem = itemsById[wornItemId];
|
||||
return wornItem.zoneName === itemToAdd.zoneName;
|
||||
});
|
||||
newWornItemIds = newWornItemIds.filter(
|
||||
|
@ -47,16 +51,26 @@ function useOutfitState() {
|
|||
setWornItemIds(newWornItemIds);
|
||||
setClosetedItemIds(newClosetedItemIds);
|
||||
},
|
||||
[wornItemIds, setWornItemIds, closetedItemIds, setClosetedItemIds]
|
||||
[wornItemIds, closetedItemIds, itemsById]
|
||||
);
|
||||
|
||||
const wornItems = wornItemIds.map((id) =>
|
||||
ITEMS.find((item) => item.id === id)
|
||||
);
|
||||
const closetedItems = closetedItemIds.map((id) =>
|
||||
ITEMS.find((item) => item.id === id)
|
||||
const zonesAndItems = getZonesAndItems(
|
||||
itemsById,
|
||||
wornItemIds,
|
||||
closetedItemIds
|
||||
);
|
||||
|
||||
const data = { zonesAndItems, wornItemIds };
|
||||
|
||||
return { loading, error, data, wearItem };
|
||||
}
|
||||
|
||||
function getZonesAndItems(itemsById, wornItemIds, closetedItemIds) {
|
||||
const wornItems = wornItemIds.map((id) => itemsById[id]).filter((i) => i);
|
||||
const closetedItems = closetedItemIds
|
||||
.map((id) => itemsById[id])
|
||||
.filter((i) => i);
|
||||
|
||||
const allItems = [...wornItems, ...closetedItems];
|
||||
const allZoneNames = [...new Set(allItems.map((item) => item.zoneName))];
|
||||
allZoneNames.sort();
|
||||
|
@ -70,9 +84,7 @@ function useOutfitState() {
|
|||
return { zoneName, items, wornItemId };
|
||||
});
|
||||
|
||||
const data = { zonesAndItems, wornItemIds };
|
||||
|
||||
return [data, wearItem];
|
||||
return zonesAndItems;
|
||||
}
|
||||
|
||||
export default useOutfitState;
|
||||
|
|
Loading…
Reference in a new issue