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.tabSize": 2
"editor.tabSize": 2,
"jest.pathToJest": "yarn test"
}

View file

@ -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>

View file

@ -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>

View file

@ -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",
},
];

View file

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

View file

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

View file

@ -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",
},
],
}

View file

@ -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
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 { 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;