load name and thumbnail from the db :o

This commit is contained in:
Matt Dunn-Rankin 2020-04-22 14:55:12 -07:00
parent 496d69dc95
commit 7397243c7f
10 changed files with 130 additions and 76 deletions

View file

@ -1,4 +1,5 @@
{ {
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.tabSize": 2 "editor.tabSize": 2,
"jest.pathToJest": "yarn test"
} }

View file

@ -26,7 +26,7 @@ function Item({ item, isWorn, onWear }) {
cursor="pointer" cursor="pointer"
onClick={onWear} onClick={onWear}
> >
<ItemThumbnail src={item.thumbnailSrc} isWorn={isWorn} /> <ItemThumbnail src={item.thumbnailUrl} isWorn={isWorn} />
<Box width="3" /> <Box width="3" />
<ItemName isWorn={isWorn}>{item.name}</ItemName> <ItemName isWorn={isWorn}>{item.name}</ItemName>
</PseudoBox> </PseudoBox>

View file

@ -1,6 +1,4 @@
import React from "react"; import React from "react";
import gql from "graphql-tag";
import { useQuery } from "@apollo/react-hooks";
import { import {
Box, Box,
Editable, Editable,
@ -27,24 +25,14 @@ import useOutfitState from "./useOutfitState.js";
import { ITEMS } from "./data"; import { ITEMS } from "./data";
function WardrobePage() { function WardrobePage() {
const { loading, error, data: datax } = useQuery(gql` const { loading, error, data, wearItem } = useOutfitState();
query {
items(ids: [38913, 38911]) {
id
name
}
}
`);
console.log(loading, error, datax);
const [data, wearItemRaw] = useOutfitState();
const [searchQuery, setSearchQuery] = React.useState(""); const [searchQuery, setSearchQuery] = React.useState("");
const toast = useToast(); const toast = useToast();
const [hasSentToast, setHasSentToast] = React.useState(false); const [hasSentToast, setHasSentToast] = React.useState(false);
const wearItem = React.useCallback( const wearItemAndToast = React.useCallback(
(itemIdToAdd) => { (itemIdToAdd) => {
wearItemRaw(itemIdToAdd); wearItem(itemIdToAdd);
if (!hasSentToast) { if (!hasSentToast) {
setTimeout(() => { setTimeout(() => {
@ -62,7 +50,7 @@ function WardrobePage() {
setHasSentToast(true); setHasSentToast(true);
} }
}, },
[toast, wearItemRaw, hasSentToast, setHasSentToast] [toast, wearItem, hasSentToast, setHasSentToast]
); );
return ( return (
@ -104,12 +92,12 @@ function WardrobePage() {
<SearchPanel <SearchPanel
query={searchQuery} query={searchQuery}
wornItemIds={data.wornItemIds} wornItemIds={data.wornItemIds}
onWearItem={wearItem} onWearItem={wearItemAndToast}
/> />
) : ( ) : (
<ItemsPanel <ItemsPanel
zonesAndItems={data.zonesAndItems} zonesAndItems={data.zonesAndItems}
onWearItem={wearItem} onWearItem={wearItemAndToast}
/> />
)} )}
</Box> </Box>

View file

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

View file

@ -177,10 +177,15 @@ class ApolloServer extends ApolloServerBase {
...this.playgroundOptions, ...this.playgroundOptions,
}; };
return setHeaders(res, { return setHeaders(
res,
new Headers({
"Content-Type": "text/html", "Content-Type": "text/html",
...requestCorsHeadersObject, ...requestCorsHeadersObject,
}).send(renderPlaygroundPage(playgroundRenderPageOptions)); })
)
.status(200)
.send(renderPlaygroundPage(playgroundRenderPageOptions));
} }
} }

View file

@ -7,6 +7,7 @@ const typeDefs = gql`
type Item { type Item {
id: ID! id: ID!
name: String! name: String!
thumbnailUrl: String!
} }
type Query { type Query {

View file

@ -30,6 +30,7 @@ it("can load items", async () => {
items(ids: $ids) { items(ids: $ids) {
id id
name name
thumbnailUrl
} }
} }
`, `,
@ -49,14 +50,17 @@ it("can load items", async () => {
Object { Object {
"id": "38911", "id": "38911",
"name": "Zafara Agent Hood", "name": "Zafara Agent Hood",
"thumbnailUrl": "http://images.neopets.com/items/clo_zafara_agent_hood.gif",
}, },
Object { Object {
"id": "38912", "id": "38912",
"name": "Zafara Agent Robe", "name": "Zafara Agent Robe",
"thumbnailUrl": "http://images.neopets.com/items/clo_zafara_agent_robe.gif",
}, },
Object { Object {
"id": "38913", "id": "38913",
"name": "Zafara Agent Gloves", "name": "Zafara Agent Gloves",
"thumbnailUrl": "http://images.neopets.com/items/clo_zafara_agent_gloves.gif",
}, },
], ],
} }

View file

@ -6,8 +6,8 @@ async function loadItems(db, ids) {
`SELECT * FROM items WHERE id IN (${qs})`, `SELECT * FROM items WHERE id IN (${qs})`,
ids ids
); );
const entities = rows.map(normalizeProperties);
return rows; return entities;
} }
const buildItemTranslationLoader = (db) => const buildItemTranslationLoader = (db) =>
@ -17,14 +17,24 @@ const buildItemTranslationLoader = (db) =>
`SELECT * FROM item_translations WHERE item_id IN (${qs}) AND locale = "en"`, `SELECT * FROM item_translations WHERE item_id IN (${qs}) AND locale = "en"`,
itemIds 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( return itemIds.map(
(itemId) => (itemId) =>
rowsByItemId.get(itemId) || entitiesByItemId.get(itemId) ||
new Error(`could not find translation for item ${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 }; module.exports = { loadItems, buildItemTranslationLoader };

33
src/useItemData.js Normal file
View 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;

View file

@ -1,19 +1,23 @@
import React from "react"; import React from "react";
import { ITEMS } from "./data.js"; import useItemData from "./useItemData";
function useOutfitState() { function useOutfitState() {
const [wornItemIds, setWornItemIds] = React.useState([ const [wornItemIds, setWornItemIds] = React.useState([
1, "38913",
2, "38911",
3, "38912",
4, "37375",
6, "48313",
7, "37229",
8, "43014",
9, "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( const wearItem = React.useCallback(
(itemIdToAdd) => { (itemIdToAdd) => {
@ -24,7 +28,7 @@ function useOutfitState() {
let newWornItemIds = wornItemIds; let newWornItemIds = wornItemIds;
let newClosetedItemIds = closetedItemIds; let newClosetedItemIds = closetedItemIds;
const itemToAdd = ITEMS.find((item) => item.id === itemIdToAdd); const itemToAdd = itemsById[itemIdToAdd];
// Move the item out of the closet. // Move the item out of the closet.
newClosetedItemIds = newClosetedItemIds.filter( newClosetedItemIds = newClosetedItemIds.filter(
@ -33,7 +37,7 @@ function useOutfitState() {
// Move conflicting items to the closet. // Move conflicting items to the closet.
const conflictingItemIds = newWornItemIds.filter((wornItemId) => { const conflictingItemIds = newWornItemIds.filter((wornItemId) => {
const wornItem = ITEMS.find((item) => item.id === wornItemId); const wornItem = itemsById[wornItemId];
return wornItem.zoneName === itemToAdd.zoneName; return wornItem.zoneName === itemToAdd.zoneName;
}); });
newWornItemIds = newWornItemIds.filter( newWornItemIds = newWornItemIds.filter(
@ -47,16 +51,26 @@ function useOutfitState() {
setWornItemIds(newWornItemIds); setWornItemIds(newWornItemIds);
setClosetedItemIds(newClosetedItemIds); setClosetedItemIds(newClosetedItemIds);
}, },
[wornItemIds, setWornItemIds, closetedItemIds, setClosetedItemIds] [wornItemIds, closetedItemIds, itemsById]
); );
const wornItems = wornItemIds.map((id) => const zonesAndItems = getZonesAndItems(
ITEMS.find((item) => item.id === id) itemsById,
); wornItemIds,
const closetedItems = closetedItemIds.map((id) => closetedItemIds
ITEMS.find((item) => item.id === id)
); );
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 allItems = [...wornItems, ...closetedItems];
const allZoneNames = [...new Set(allItems.map((item) => item.zoneName))]; const allZoneNames = [...new Set(allItems.map((item) => item.zoneName))];
allZoneNames.sort(); allZoneNames.sort();
@ -70,9 +84,7 @@ function useOutfitState() {
return { zoneName, items, wornItemId }; return { zoneName, items, wornItemId };
}); });
const data = { zonesAndItems, wornItemIds }; return zonesAndItems;
return [data, wearItem];
} }
export default useOutfitState; export default useOutfitState;