added pet appearance queries, using in frontend
This commit is contained in:
parent
f5cb1d263c
commit
dd5a7f9242
5 changed files with 283 additions and 88 deletions
|
@ -2,6 +2,7 @@ GRANT SELECT ON openneo_impress.items TO impress2020;
|
|||
GRANT SELECT ON openneo_impress.item_translations TO impress2020;
|
||||
GRANT SELECT ON openneo_impress.parents_swf_assets TO impress2020;
|
||||
GRANT SELECT ON openneo_impress.pet_types TO impress2020;
|
||||
GRANT SELECT ON openneo_impress.pet_states TO impress2020;
|
||||
GRANT SELECT ON openneo_impress.swf_assets TO impress2020;
|
||||
GRANT SELECT ON openneo_impress.zones TO impress2020;
|
||||
GRANT SELECT ON openneo_impress.zone_translations TO impress2020;
|
||||
|
|
|
@ -9,12 +9,25 @@ function OutfitPreview({ itemIds, speciesId, colorId }) {
|
|||
const { loading, error, data } = useQuery(
|
||||
gql`
|
||||
query($itemIds: [ID!]!, $speciesId: ID!, $colorId: ID!) {
|
||||
petAppearance(speciesId: $speciesId, colorId: $colorId) {
|
||||
layers {
|
||||
id
|
||||
imageUrl(size: SIZE_600)
|
||||
zone {
|
||||
depth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
items(ids: $itemIds) {
|
||||
id
|
||||
appearanceOn(speciesId: $speciesId, colorId: $colorId) {
|
||||
layers {
|
||||
id
|
||||
imageUrl(size: SIZE_600)
|
||||
zone {
|
||||
depth
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,14 +58,22 @@ function OutfitPreview({ itemIds, speciesId, colorId }) {
|
|||
);
|
||||
}
|
||||
|
||||
const allLayers = [
|
||||
...data.petAppearance.layers,
|
||||
...data.items.map((i) => i.appearanceOn.layers).flat(),
|
||||
];
|
||||
allLayers.sort((a, b) => a.zone.depth - b.zone.depth);
|
||||
|
||||
return (
|
||||
<Box pos="relative" height="100%" width="100%">
|
||||
{allLayers.map((layer) => (
|
||||
<Box pos="absolute" top="0" right="0" bottom="0" left="0">
|
||||
<FullScreenCenter>
|
||||
<Image
|
||||
src="http://pets.neopets.com/cp/wgmdtdwz/1/7.png"
|
||||
maxHeight="100%"
|
||||
maxWidth="100%"
|
||||
/>
|
||||
<Image src={layer.imageUrl} maxWidth="100%" maxHeight="100%" />
|
||||
</FullScreenCenter>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const { gql } = require("apollo-server");
|
||||
|
||||
const connectToDb = require("./db");
|
||||
const loaders = require("./loaders");
|
||||
const buildLoaders = require("./loaders");
|
||||
|
||||
const typeDefs = gql`
|
||||
enum LayerImageSize {
|
||||
|
@ -14,14 +14,14 @@ const typeDefs = gql`
|
|||
id: ID!
|
||||
name: String!
|
||||
thumbnailUrl: String!
|
||||
appearanceOn(speciesId: ID!, colorId: ID!): ItemAppearance
|
||||
appearanceOn(speciesId: ID!, colorId: ID!): Appearance
|
||||
}
|
||||
|
||||
type ItemAppearance {
|
||||
layers: [ItemAppearanceLayer!]!
|
||||
type Appearance {
|
||||
layers: [AppearanceLayer!]!
|
||||
}
|
||||
|
||||
type ItemAppearanceLayer {
|
||||
type AppearanceLayer {
|
||||
id: ID!
|
||||
zone: Zone!
|
||||
imageUrl(size: LayerImageSize): String
|
||||
|
@ -35,6 +35,7 @@ const typeDefs = gql`
|
|||
|
||||
type Query {
|
||||
items(ids: [ID!]!): [Item!]!
|
||||
petAppearance(speciesId: ID!, colorId: ID!): Appearance
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -44,26 +45,23 @@ const resolvers = {
|
|||
const translation = await itemTranslationLoader.load(item.id);
|
||||
return translation.name;
|
||||
},
|
||||
appearanceOn: (item, { speciesId, colorId }) => ({
|
||||
itemId: item.id,
|
||||
speciesId,
|
||||
colorId,
|
||||
}),
|
||||
},
|
||||
ItemAppearance: {
|
||||
layers: async (ia, _, { petTypeLoader, swfAssetLoader }) => {
|
||||
appearanceOn: async (
|
||||
item,
|
||||
{ speciesId, colorId },
|
||||
{ petTypeLoader, itemSwfAssetLoader }
|
||||
) => {
|
||||
const petType = await petTypeLoader.load({
|
||||
speciesId: ia.speciesId,
|
||||
colorId: ia.colorId,
|
||||
speciesId: speciesId,
|
||||
colorId: colorId,
|
||||
});
|
||||
const swfAssets = await swfAssetLoader.load({
|
||||
itemId: ia.itemId,
|
||||
const swfAssets = await itemSwfAssetLoader.load({
|
||||
itemId: item.id,
|
||||
bodyId: petType.bodyId,
|
||||
});
|
||||
return swfAssets;
|
||||
return { layers: swfAssets };
|
||||
},
|
||||
},
|
||||
ItemAppearanceLayer: {
|
||||
AppearanceLayer: {
|
||||
zone: async (layer, _, { zoneLoader }) => {
|
||||
const zone = await zoneLoader.load(layer.zoneId);
|
||||
return zone;
|
||||
|
@ -82,7 +80,10 @@ const resolvers = {
|
|||
const rid3 = paddedId.slice(6, 9);
|
||||
const time = Number(new Date(layer.convertedAt));
|
||||
|
||||
return `https://impress-asset-images.s3.amazonaws.com/object/${rid1}/${rid2}/${rid3}/${rid}/${sizeNum}x${sizeNum}.png?${time}`;
|
||||
return (
|
||||
`https://impress-asset-images.s3.amazonaws.com/${layer.type}` +
|
||||
`/${rid1}/${rid2}/${rid3}/${rid}/${sizeNum}x${sizeNum}.png?${time}`
|
||||
);
|
||||
},
|
||||
},
|
||||
Zone: {
|
||||
|
@ -92,10 +93,24 @@ const resolvers = {
|
|||
},
|
||||
},
|
||||
Query: {
|
||||
items: async (_, { ids }, { db }) => {
|
||||
const items = await loaders.loadItems(db, ids);
|
||||
items: async (_, { ids }, { itemLoader }) => {
|
||||
const items = await itemLoader.loadMany(ids);
|
||||
return items;
|
||||
},
|
||||
petAppearance: async (
|
||||
_,
|
||||
{ speciesId, colorId },
|
||||
{ petTypeLoader, petStateLoader, petSwfAssetLoader }
|
||||
) => {
|
||||
const petType = await petTypeLoader.load({
|
||||
speciesId,
|
||||
colorId,
|
||||
});
|
||||
const petStates = await petStateLoader.load(petType.id);
|
||||
const petState = petStates[0]; // TODO
|
||||
const swfAssets = await petSwfAssetLoader.load(petState.id);
|
||||
return { layers: swfAssets };
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -105,12 +120,7 @@ const config = {
|
|||
context: async () => {
|
||||
const db = await connectToDb();
|
||||
return {
|
||||
db,
|
||||
itemTranslationLoader: loaders.buildItemTranslationLoader(db),
|
||||
petTypeLoader: loaders.buildPetTypeLoader(db),
|
||||
swfAssetLoader: loaders.buildSwfAssetLoader(db),
|
||||
zoneLoader: loaders.buildZoneLoader(db),
|
||||
zoneTranslationLoader: loaders.buildZoneTranslationLoader(db),
|
||||
...buildLoaders(db),
|
||||
};
|
||||
},
|
||||
|
||||
|
|
|
@ -45,6 +45,11 @@ describe("Item", () => {
|
|||
expect(res.data).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"items": Array [
|
||||
Object {
|
||||
"id": "38913",
|
||||
"name": "Zafara Agent Gloves",
|
||||
"thumbnailUrl": "http://images.neopets.com/items/clo_zafara_agent_gloves.gif",
|
||||
},
|
||||
Object {
|
||||
"id": "38911",
|
||||
"name": "Zafara Agent Hood",
|
||||
|
@ -55,11 +60,6 @@ describe("Item", () => {
|
|||
"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",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
|
@ -76,9 +76,9 @@ describe("Item", () => {
|
|||
Array [
|
||||
"SELECT * FROM item_translations WHERE item_id IN (?,?,?) AND locale = \\"en\\"",
|
||||
Array [
|
||||
"38913",
|
||||
"38911",
|
||||
"38912",
|
||||
"38913",
|
||||
],
|
||||
],
|
||||
]
|
||||
|
@ -117,18 +117,18 @@ describe("Item", () => {
|
|||
"appearanceOn": Object {
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "30203",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/object/000/000/006/6829/600x600.png?0",
|
||||
"id": "37128",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/object/000/000/014/14856/600x600.png?1587653266000",
|
||||
"zone": Object {
|
||||
"depth": 3,
|
||||
"id": "3",
|
||||
"label": "Background",
|
||||
"depth": 30,
|
||||
"id": "26",
|
||||
"label": "Jacket",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
"id": "37375",
|
||||
"name": "Moon and Stars Background",
|
||||
"id": "38912",
|
||||
"name": "Zafara Agent Robe",
|
||||
},
|
||||
Object {
|
||||
"appearanceOn": Object {
|
||||
|
@ -151,18 +151,18 @@ describe("Item", () => {
|
|||
"appearanceOn": Object {
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "37128",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/object/000/000/014/14856/600x600.png?1587653266000",
|
||||
"id": "30203",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/object/000/000/006/6829/600x600.png?0",
|
||||
"zone": Object {
|
||||
"depth": 30,
|
||||
"id": "26",
|
||||
"label": "Jacket",
|
||||
"depth": 3,
|
||||
"id": "3",
|
||||
"label": "Background",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
"id": "38912",
|
||||
"name": "Zafara Agent Robe",
|
||||
"id": "37375",
|
||||
"name": "Moon and Stars Background",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
@ -180,9 +180,9 @@ describe("Item", () => {
|
|||
Array [
|
||||
"SELECT * FROM item_translations WHERE item_id IN (?,?,?) AND locale = \\"en\\"",
|
||||
Array [
|
||||
"37375",
|
||||
"38911",
|
||||
"38912",
|
||||
"38911",
|
||||
"37375",
|
||||
],
|
||||
],
|
||||
Array [
|
||||
|
@ -203,28 +203,145 @@ describe("Item", () => {
|
|||
rel.swf_asset_id = sa.id
|
||||
WHERE (rel.parent_id = ? AND (sa.body_id = ? OR sa.body_id = 0)) OR (rel.parent_id = ? AND (sa.body_id = ? OR sa.body_id = 0)) OR (rel.parent_id = ? AND (sa.body_id = ? OR sa.body_id = 0))",
|
||||
Array [
|
||||
"37375",
|
||||
"38912",
|
||||
"180",
|
||||
"38911",
|
||||
"180",
|
||||
"38912",
|
||||
"37375",
|
||||
"180",
|
||||
],
|
||||
],
|
||||
Array [
|
||||
"SELECT * FROM zones WHERE id IN (?,?,?)",
|
||||
Array [
|
||||
"3",
|
||||
"40",
|
||||
"26",
|
||||
"40",
|
||||
"3",
|
||||
],
|
||||
],
|
||||
Array [
|
||||
"SELECT * FROM zone_translations WHERE zone_id IN (?,?,?) AND locale = \\"en\\"",
|
||||
Array [
|
||||
"3",
|
||||
"40",
|
||||
"26",
|
||||
"40",
|
||||
"3",
|
||||
],
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("PetAppearance", () => {
|
||||
it("loads for species and color", async () => {
|
||||
const res = await query({
|
||||
query: gql`
|
||||
query {
|
||||
petAppearance(speciesId: "54", colorId: "75") {
|
||||
layers {
|
||||
id
|
||||
imageUrl(size: SIZE_600)
|
||||
zone {
|
||||
depth
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
expect(res).toHaveNoErrors();
|
||||
expect(res.data).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"petAppearance": Object {
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7941/600x600.png?0",
|
||||
"zone": Object {
|
||||
"depth": 18,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "5996",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7942/600x600.png?0",
|
||||
"zone": Object {
|
||||
"depth": 7,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "6000",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7946/600x600.png?0",
|
||||
"zone": Object {
|
||||
"depth": 40,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "16467",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/024/24008/600x600.png?0",
|
||||
"zone": Object {
|
||||
"depth": 34,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "19549",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/028/28548/600x600.png?1345719457000",
|
||||
"zone": Object {
|
||||
"depth": 37,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "19550",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/028/28549/600x600.png?0",
|
||||
"zone": Object {
|
||||
"depth": 38,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "163528",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/028/28549/600x600.png?1326455337000",
|
||||
"zone": Object {
|
||||
"depth": 38,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
`);
|
||||
expect(queryFn.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"SELECT * FROM pet_types WHERE (species_id = ? AND color_id = ?)",
|
||||
Array [
|
||||
"54",
|
||||
"75",
|
||||
],
|
||||
],
|
||||
Array [
|
||||
"SELECT * FROM pet_states WHERE pet_type_id IN (?)",
|
||||
Array [
|
||||
"2",
|
||||
],
|
||||
],
|
||||
Array [
|
||||
"SELECT sa.*, rel.parent_id FROM swf_assets sa
|
||||
INNER JOIN parents_swf_assets rel ON
|
||||
rel.parent_type = \\"PetState\\" AND
|
||||
rel.swf_asset_id = sa.id
|
||||
WHERE rel.parent_id IN (?)",
|
||||
Array [
|
||||
"2",
|
||||
],
|
||||
],
|
||||
Array [
|
||||
"SELECT * FROM zones WHERE id IN (?,?,?,?,?,?)",
|
||||
Array [
|
||||
"15",
|
||||
"5",
|
||||
"37",
|
||||
"30",
|
||||
"33",
|
||||
"34",
|
||||
],
|
||||
],
|
||||
]
|
||||
|
|
|
@ -1,14 +1,21 @@
|
|||
const DataLoader = require("dataloader");
|
||||
|
||||
async function loadItems(db, ids) {
|
||||
const buildItemsLoader = (db) =>
|
||||
new DataLoader(async (ids) => {
|
||||
const qs = ids.map((_) => "?").join(",");
|
||||
const [rows, _] = await db.execute(
|
||||
`SELECT * FROM items WHERE id IN (${qs})`,
|
||||
ids
|
||||
);
|
||||
|
||||
const entities = rows.map(normalizeRow);
|
||||
return entities;
|
||||
}
|
||||
const entitiesById = new Map(entities.map((e) => [e.id, e]));
|
||||
|
||||
return ids.map(
|
||||
(id) =>
|
||||
entitiesById.get(id) || new Error(`could not find item with ID: ${id}`)
|
||||
);
|
||||
});
|
||||
|
||||
const buildItemTranslationLoader = (db) =>
|
||||
new DataLoader(async (itemIds) => {
|
||||
|
@ -52,7 +59,7 @@ const buildPetTypeLoader = (db) =>
|
|||
);
|
||||
});
|
||||
|
||||
const buildSwfAssetLoader = (db) =>
|
||||
const buildItemSwfAssetLoader = (db) =>
|
||||
new DataLoader(async (itemAndBodyPairs) => {
|
||||
const conditions = [];
|
||||
const values = [];
|
||||
|
@ -82,21 +89,54 @@ const buildSwfAssetLoader = (db) =>
|
|||
);
|
||||
});
|
||||
|
||||
const buildPetSwfAssetLoader = (db) =>
|
||||
new DataLoader(async (petStateIds) => {
|
||||
const qs = petStateIds.map((_) => "?").join(",");
|
||||
const [rows, _] = await db.execute(
|
||||
`SELECT sa.*, rel.parent_id FROM swf_assets sa
|
||||
INNER JOIN parents_swf_assets rel ON
|
||||
rel.parent_type = "PetState" AND
|
||||
rel.swf_asset_id = sa.id
|
||||
WHERE rel.parent_id IN (${qs})`,
|
||||
petStateIds
|
||||
);
|
||||
|
||||
const entities = rows.map(normalizeRow);
|
||||
|
||||
return petStateIds.map((petStateId) =>
|
||||
entities.filter((e) => e.parentId === petStateId)
|
||||
);
|
||||
});
|
||||
|
||||
const buildPetStateLoader = (db) =>
|
||||
new DataLoader(async (petTypeIds) => {
|
||||
const qs = petTypeIds.map((_) => "?").join(",");
|
||||
const [rows, _] = await db.execute(
|
||||
`SELECT * FROM pet_states WHERE pet_type_id IN (${qs})`,
|
||||
petTypeIds
|
||||
);
|
||||
|
||||
const entities = rows.map(normalizeRow);
|
||||
|
||||
return petTypeIds.map((petTypeId) =>
|
||||
entities.filter((e) => e.petTypeId === petTypeId)
|
||||
);
|
||||
});
|
||||
|
||||
const buildZoneLoader = (db) =>
|
||||
new DataLoader(async (zoneIds) => {
|
||||
const qs = zoneIds.map((_) => "?").join(",");
|
||||
new DataLoader(async (ids) => {
|
||||
const qs = ids.map((_) => "?").join(",");
|
||||
const [rows, _] = await db.execute(
|
||||
`SELECT * FROM zones WHERE id IN (${qs})`,
|
||||
zoneIds
|
||||
ids
|
||||
);
|
||||
|
||||
const entities = rows.map(normalizeRow);
|
||||
const entitiesById = new Map(entities.map((e) => [e.id, e]));
|
||||
|
||||
return zoneIds.map(
|
||||
(zoneId) =>
|
||||
entitiesById.get(zoneId) ||
|
||||
new Error(`could not find zone with ID: ${zoneId}`)
|
||||
return ids.map(
|
||||
(id) =>
|
||||
entitiesById.get(id) || new Error(`could not find zone with ID: ${id}`)
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -130,11 +170,17 @@ function normalizeRow(row) {
|
|||
return normalizedRow;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
loadItems,
|
||||
buildItemTranslationLoader,
|
||||
buildPetTypeLoader,
|
||||
buildSwfAssetLoader,
|
||||
buildZoneLoader,
|
||||
buildZoneTranslationLoader,
|
||||
};
|
||||
function buildLoaders(db) {
|
||||
return {
|
||||
itemLoader: buildItemsLoader(db),
|
||||
itemTranslationLoader: buildItemTranslationLoader(db),
|
||||
petTypeLoader: buildPetTypeLoader(db),
|
||||
itemSwfAssetLoader: buildItemSwfAssetLoader(db),
|
||||
petSwfAssetLoader: buildPetSwfAssetLoader(db),
|
||||
petStateLoader: buildPetStateLoader(db),
|
||||
zoneLoader: buildZoneLoader(db),
|
||||
zoneTranslationLoader: buildZoneTranslationLoader(db),
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = buildLoaders;
|
||||
|
|
Loading…
Reference in a new issue