diff --git a/.vscode/settings.json b/.vscode/settings.json
index 761cd6f..a1d5d55 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,5 @@
{
"editor.formatOnSave": true,
- "editor.tabSize": 2
+ "editor.tabSize": 2,
+ "jest.pathToJest": "yarn test"
}
\ No newline at end of file
diff --git a/src/ItemList.js b/src/ItemList.js
index d324d8a..659d2a2 100644
--- a/src/ItemList.js
+++ b/src/ItemList.js
@@ -26,7 +26,7 @@ function Item({ item, isWorn, onWear }) {
cursor="pointer"
onClick={onWear}
>
-
+
{item.name}
diff --git a/src/WardrobePage.js b/src/WardrobePage.js
index 7af1483..4fafb59 100644
--- a/src/WardrobePage.js
+++ b/src/WardrobePage.js
@@ -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() {
) : (
)}
diff --git a/src/data.js b/src/data.js
index ef7435d..52d9313 100644
--- a/src/data.js
+++ b/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",
},
];
diff --git a/src/server/apollo-server-vercel.js b/src/server/apollo-server-vercel.js
index 9e9bb42..082c2bc 100644
--- a/src/server/apollo-server-vercel.js
+++ b/src/server/apollo-server-vercel.js
@@ -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));
}
}
diff --git a/src/server/index.js b/src/server/index.js
index 2d909df..8f95acd 100644
--- a/src/server/index.js
+++ b/src/server/index.js
@@ -7,6 +7,7 @@ const typeDefs = gql`
type Item {
id: ID!
name: String!
+ thumbnailUrl: String!
}
type Query {
diff --git a/src/server/index.test.js b/src/server/index.test.js
index c52c728..269235c 100644
--- a/src/server/index.test.js
+++ b/src/server/index.test.js
@@ -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",
},
],
}
diff --git a/src/server/loaders.js b/src/server/loaders.js
index bf1c026..c4c4275 100644
--- a/src/server/loaders.js
+++ b/src/server/loaders.js
@@ -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 };
diff --git a/src/useItemData.js b/src/useItemData.js
new file mode 100644
index 0000000..307fa0d
--- /dev/null
+++ b/src/useItemData.js
@@ -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;
diff --git a/src/useOutfitState.js b/src/useOutfitState.js
index b52d623..77e0e4a 100644
--- a/src/useOutfitState.js
+++ b/src/useOutfitState.js
@@ -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;