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.formatOnSave": true,
|
||||||
"editor.tabSize": 2
|
"editor.tabSize": 2,
|
||||||
|
"jest.pathToJest": "yarn test"
|
||||||
}
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
54
src/data.js
54
src/data.js
|
@ -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",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
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 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;
|
||||||
|
|
Loading…
Reference in a new issue