feat : add metrial fetcher
This commit is contained in:
+20
-1
@@ -16,7 +16,11 @@ import {
|
|||||||
varchar,
|
varchar,
|
||||||
} from "drizzle-orm/pg-core";
|
} from "drizzle-orm/pg-core";
|
||||||
|
|
||||||
import type { CatalogConstellation, CatalogTalent } from "@/lib/catalog/lunaris";
|
import type {
|
||||||
|
CatalogAscensionGroup,
|
||||||
|
CatalogConstellation,
|
||||||
|
CatalogTalent,
|
||||||
|
} from "@/lib/catalog/lunaris";
|
||||||
import type { ExtraSheet } from "@/lib/guides/extra-sheet";
|
import type { ExtraSheet } from "@/lib/guides/extra-sheet";
|
||||||
|
|
||||||
const authSchema = pgSchema("auth");
|
const authSchema = pgSchema("auth");
|
||||||
@@ -451,6 +455,7 @@ export const catalogCharacters = catalogSchema.table(
|
|||||||
nameCardImageKey: text("name_card_image_key"),
|
nameCardImageKey: text("name_card_image_key"),
|
||||||
constellations: jsonb("constellations").$type<CatalogConstellation[]>().default([]).notNull(),
|
constellations: jsonb("constellations").$type<CatalogConstellation[]>().default([]).notNull(),
|
||||||
talents: jsonb("talents").$type<CatalogTalent[]>().default([]).notNull(),
|
talents: jsonb("talents").$type<CatalogTalent[]>().default([]).notNull(),
|
||||||
|
ascension: jsonb("ascension").$type<CatalogAscensionGroup[]>().default([]).notNull(),
|
||||||
},
|
},
|
||||||
(table) => [
|
(table) => [
|
||||||
primaryKey({ columns: [table.key] }),
|
primaryKey({ columns: [table.key] }),
|
||||||
@@ -458,6 +463,20 @@ export const catalogCharacters = catalogSchema.table(
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const catalogMaterials = catalogSchema.table(
|
||||||
|
"material",
|
||||||
|
{
|
||||||
|
key: varchar("key", { length: 64 }).primaryKey(),
|
||||||
|
name: text("name").notNull(),
|
||||||
|
description: text("description"),
|
||||||
|
rarity: integer("rarity"),
|
||||||
|
type: varchar("type", { length: 64 }),
|
||||||
|
imageKey: text("image_key").notNull(),
|
||||||
|
...timestamps,
|
||||||
|
},
|
||||||
|
(table) => [index("catalog_material_name_idx").on(table.name)],
|
||||||
|
);
|
||||||
|
|
||||||
export const catalogWeapons = catalogSchema.table(
|
export const catalogWeapons = catalogSchema.table(
|
||||||
"weapon",
|
"weapon",
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
CREATE TABLE "catalog"."material" (
|
||||||
|
"key" varchar(64) PRIMARY KEY NOT NULL,
|
||||||
|
"name" text NOT NULL,
|
||||||
|
"description" text,
|
||||||
|
"rarity" integer,
|
||||||
|
"type" varchar(64),
|
||||||
|
"image_key" text NOT NULL,
|
||||||
|
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||||
|
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "catalog"."character" ADD COLUMN "ascension" jsonb DEFAULT '[]'::jsonb NOT NULL;--> statement-breakpoint
|
||||||
|
CREATE INDEX "catalog_material_name_idx" ON "catalog"."material" USING btree ("name");
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -218,6 +218,13 @@
|
|||||||
"when": 1788605973095,
|
"when": 1788605973095,
|
||||||
"tag": "0030_third_maddog",
|
"tag": "0030_third_maddog",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 31,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1788673251760,
|
||||||
|
"tag": "0031_watery_sumo",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
avatarIconName,
|
avatarIconName,
|
||||||
|
ascensionSnapshot,
|
||||||
characterDetailSchema,
|
characterDetailSchema,
|
||||||
cleanLunarisText,
|
cleanLunarisText,
|
||||||
constellationSnapshot,
|
constellationSnapshot,
|
||||||
@@ -58,6 +59,26 @@ describe("Lunaris character data", () => {
|
|||||||
expect(constellationSnapshot(detail, (icon) => icon)).toEqual([]);
|
expect(constellationSnapshot(detail, (icon) => icon)).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("normalizes scalar and grouped ascension materials", () => {
|
||||||
|
const detail = characterDetailSchema.parse({
|
||||||
|
ascension: {
|
||||||
|
"202": 7049900,
|
||||||
|
exp: { "104001": 2, "104003": 418 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(ascensionSnapshot(detail)).toEqual([
|
||||||
|
{ type: "202", items: [{ key: "202", amount: 7049900 }] },
|
||||||
|
{
|
||||||
|
type: "exp",
|
||||||
|
items: [
|
||||||
|
{ key: "104001", amount: 2 },
|
||||||
|
{ key: "104003", amount: 418 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it("reads the supported weapon type from character details", () => {
|
it("reads the supported weapon type from character details", () => {
|
||||||
const detail = characterDetailSchema.parse({
|
const detail = characterDetailSchema.parse({
|
||||||
info: { element: "Cryo", weapon: "WEAPON_SWORD_ONE_HAND" },
|
info: { element: "Cryo", weapon: "WEAPON_SWORD_ONE_HAND" },
|
||||||
|
|||||||
@@ -14,6 +14,25 @@ export interface CatalogTalent {
|
|||||||
iconKey: string | null;
|
iconKey: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CatalogAscensionMaterial {
|
||||||
|
key: string;
|
||||||
|
amount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CatalogAscensionGroup {
|
||||||
|
type: string;
|
||||||
|
items: CatalogAscensionMaterial[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CatalogMaterial {
|
||||||
|
key: string;
|
||||||
|
name: string;
|
||||||
|
description: string | null;
|
||||||
|
rarity: number | null;
|
||||||
|
type: string | null;
|
||||||
|
imageKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
const constellationSchema = z.object({
|
const constellationSchema = z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
description: z.string(),
|
description: z.string(),
|
||||||
@@ -37,6 +56,10 @@ export const characterDetailSchema = z.object({
|
|||||||
elementalskill: skillSchema.optional(),
|
elementalskill: skillSchema.optional(),
|
||||||
elementalburst: skillSchema.optional(),
|
elementalburst: skillSchema.optional(),
|
||||||
}).nullish(),
|
}).nullish(),
|
||||||
|
ascension: z.record(
|
||||||
|
z.string(),
|
||||||
|
z.union([z.number().int(), z.record(z.string(), z.number().int())]),
|
||||||
|
).nullish(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function cleanLunarisText(value: string): string {
|
export function cleanLunarisText(value: string): string {
|
||||||
@@ -106,3 +129,15 @@ export function talentSnapshot(
|
|||||||
iconKey: iconKey(talent.icon),
|
iconKey: iconKey(talent.icon),
|
||||||
}] : []);
|
}] : []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ascensionSnapshot(
|
||||||
|
value: z.infer<typeof characterDetailSchema>,
|
||||||
|
): CatalogAscensionGroup[] {
|
||||||
|
return Object.entries(value.ascension ?? {}).map(([type, entries]) => ({
|
||||||
|
type,
|
||||||
|
items:
|
||||||
|
typeof entries === "number"
|
||||||
|
? [{ key: type, amount: entries }]
|
||||||
|
: Object.entries(entries).map(([key, amount]) => ({ key, amount })),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|||||||
+53
-2
@@ -6,9 +6,11 @@ import { getDb } from "@/db/client";
|
|||||||
import {
|
import {
|
||||||
catalogArtifacts,
|
catalogArtifacts,
|
||||||
catalogCharacters,
|
catalogCharacters,
|
||||||
|
catalogMaterials,
|
||||||
catalogWeapons,
|
catalogWeapons,
|
||||||
} from "@/db/schema";
|
} from "@/db/schema";
|
||||||
import {
|
import {
|
||||||
|
ascensionSnapshot,
|
||||||
avatarIconName,
|
avatarIconName,
|
||||||
characterDetailSchema,
|
characterDetailSchema,
|
||||||
constellationSnapshot,
|
constellationSnapshot,
|
||||||
@@ -17,7 +19,9 @@ import {
|
|||||||
talentSnapshot,
|
talentSnapshot,
|
||||||
} from "@/lib/catalog/lunaris";
|
} from "@/lib/catalog/lunaris";
|
||||||
import type {
|
import type {
|
||||||
|
CatalogAscensionGroup,
|
||||||
CatalogConstellation,
|
CatalogConstellation,
|
||||||
|
CatalogMaterial,
|
||||||
CatalogTalent,
|
CatalogTalent,
|
||||||
} from "@/lib/catalog/lunaris";
|
} from "@/lib/catalog/lunaris";
|
||||||
import { customTravelers, isUpstreamTraveler } from "@/lib/catalog/travelers";
|
import { customTravelers, isUpstreamTraveler } from "@/lib/catalog/travelers";
|
||||||
@@ -63,6 +67,16 @@ const artifactSchema = z.record(
|
|||||||
qualityType: z.string().optional(),
|
qualityType: z.string().optional(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
const materialSchema = z.record(
|
||||||
|
z.string(),
|
||||||
|
z.object({
|
||||||
|
enName: z.string(),
|
||||||
|
enDescription: z.string().optional(),
|
||||||
|
icon: z.string(),
|
||||||
|
qualityType: z.string().optional(),
|
||||||
|
type: z.string().optional(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
interface SyncedCharacter {
|
interface SyncedCharacter {
|
||||||
key: string;
|
key: string;
|
||||||
@@ -80,6 +94,7 @@ interface SyncedCharacter {
|
|||||||
localImage: string | null;
|
localImage: string | null;
|
||||||
constellations: CatalogConstellation[];
|
constellations: CatalogConstellation[];
|
||||||
talents: CatalogTalent[];
|
talents: CatalogTalent[];
|
||||||
|
ascension: CatalogAscensionGroup[];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getJson(url: string): Promise<unknown> {
|
async function getJson(url: string): Promise<unknown> {
|
||||||
@@ -107,6 +122,10 @@ function webpName(value: string): string {
|
|||||||
return value.endsWith(".webp") ? value : `${value}.webp`;
|
return value.endsWith(".webp") ? value : `${value}.webp`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pngName(value: string): string {
|
||||||
|
return value.endsWith(".png") ? value : `${value}.png`;
|
||||||
|
}
|
||||||
|
|
||||||
function rarity(value: string | undefined): number | null {
|
function rarity(value: string | undefined): number | null {
|
||||||
if (!value) return null;
|
if (!value) return null;
|
||||||
return (
|
return (
|
||||||
@@ -186,14 +205,26 @@ async function uploadAssets(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function syncCatalogVersion(version: string): Promise<void> {
|
async function syncCatalogVersion(version: string): Promise<void> {
|
||||||
const [charactersRaw, weaponsRaw, artifactsRaw] = await Promise.all([
|
const [charactersRaw, weaponsRaw, artifactsRaw, materialsRaw] = await Promise.all([
|
||||||
getJson(`${API_ROOT}/${version}/charlist.json`),
|
getJson(`${API_ROOT}/${version}/charlist.json`),
|
||||||
getJson(`${API_ROOT}/${version}/weaponlist.json`),
|
getJson(`${API_ROOT}/${version}/weaponlist.json`),
|
||||||
getJson(`${API_ROOT}/${version}/artifactlist.json`),
|
getJson(`${API_ROOT}/${version}/artifactlist.json`),
|
||||||
|
getJson(`${API_ROOT}/${version}/materiallist.json`),
|
||||||
]);
|
]);
|
||||||
const characterData = characterSchema.parse(charactersRaw);
|
const characterData = characterSchema.parse(charactersRaw);
|
||||||
const weaponData = weaponSchema.parse(weaponsRaw);
|
const weaponData = weaponSchema.parse(weaponsRaw);
|
||||||
const artifactData = artifactSchema.parse(artifactsRaw);
|
const artifactData = artifactSchema.parse(artifactsRaw);
|
||||||
|
const materialData = materialSchema.parse(materialsRaw);
|
||||||
|
const materials: CatalogMaterial[] = Object.entries(materialData).map(
|
||||||
|
([key, value]) => ({
|
||||||
|
key,
|
||||||
|
name: value.enName,
|
||||||
|
description: value.enDescription ?? null,
|
||||||
|
rarity: rarity(value.qualityType),
|
||||||
|
type: value.type ?? null,
|
||||||
|
imageKey: `catalog/materials/${pngName(value.icon)}`,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const remoteCharacters: SyncedCharacter[] = await mapConcurrent(
|
const remoteCharacters: SyncedCharacter[] = await mapConcurrent(
|
||||||
Object.entries(characterData).filter(
|
Object.entries(characterData).filter(
|
||||||
@@ -207,7 +238,7 @@ async function syncCatalogVersion(version: string): Promise<void> {
|
|||||||
const detailRaw = await getOptionalJson(
|
const detailRaw = await getOptionalJson(
|
||||||
`${API_ROOT}/${version}/en/char/${encodeURIComponent(key)}.json`,
|
`${API_ROOT}/${version}/en/char/${encodeURIComponent(key)}.json`,
|
||||||
);
|
);
|
||||||
const detail = detailRaw
|
const detail = detailRaw
|
||||||
? characterDetailSchema.parse(detailRaw)
|
? characterDetailSchema.parse(detailRaw)
|
||||||
: null;
|
: null;
|
||||||
const avatarIcon = webpName(avatarIconName(value.CardImg));
|
const avatarIcon = webpName(avatarIconName(value.CardImg));
|
||||||
@@ -242,6 +273,7 @@ async function syncCatalogVersion(version: string): Promise<void> {
|
|||||||
(icon) => `catalog/characters/talents/${webpName(icon)}`,
|
(icon) => `catalog/characters/talents/${webpName(icon)}`,
|
||||||
)
|
)
|
||||||
: [],
|
: [],
|
||||||
|
ascension: detail ? ascensionSnapshot(detail) : [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -275,6 +307,7 @@ async function syncCatalogVersion(version: string): Promise<void> {
|
|||||||
detail,
|
detail,
|
||||||
(icon) => `catalog/characters/talents/${webpName(icon)}`,
|
(icon) => `catalog/characters/talents/${webpName(icon)}`,
|
||||||
),
|
),
|
||||||
|
ascension: ascensionSnapshot(detail),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -339,6 +372,14 @@ async function syncCatalogVersion(version: string): Promise<void> {
|
|||||||
source: item.sourceImage,
|
source: item.sourceImage,
|
||||||
target: item.imageKey,
|
target: item.imageKey,
|
||||||
});
|
});
|
||||||
|
for (const material of materials) {
|
||||||
|
const source = materialData[material.key].icon;
|
||||||
|
uniqueAssets.set(material.imageKey, {
|
||||||
|
source: `${API_ROOT}/assets/items/${encodeURIComponent(pngName(source))}`,
|
||||||
|
target: material.imageKey,
|
||||||
|
type: "image/png",
|
||||||
|
});
|
||||||
|
}
|
||||||
for (const character of characters) {
|
for (const character of characters) {
|
||||||
if (character.elementIconKey && character.element)
|
if (character.elementIconKey && character.element)
|
||||||
uniqueAssets.set(character.elementIconKey, {
|
uniqueAssets.set(character.elementIconKey, {
|
||||||
@@ -414,6 +455,7 @@ async function syncCatalogVersion(version: string): Promise<void> {
|
|||||||
nameCardImageKey: item.nameCardImageKey,
|
nameCardImageKey: item.nameCardImageKey,
|
||||||
constellations: item.constellations,
|
constellations: item.constellations,
|
||||||
talents: item.talents,
|
talents: item.talents,
|
||||||
|
ascension: item.ascension,
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
{ type: "application/json", acl: "public-read" },
|
{ type: "application/json", acl: "public-read" },
|
||||||
@@ -443,10 +485,16 @@ async function syncCatalogVersion(version: string): Promise<void> {
|
|||||||
),
|
),
|
||||||
{ type: "application/json", acl: "public-read" },
|
{ type: "application/json", acl: "public-read" },
|
||||||
),
|
),
|
||||||
|
storage.write(
|
||||||
|
"catalog/materials.json",
|
||||||
|
JSON.stringify(materials),
|
||||||
|
{ type: "application/json", acl: "public-read" },
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await getDb().transaction(async (tx) => {
|
await getDb().transaction(async (tx) => {
|
||||||
await tx.delete(catalogCharacters);
|
await tx.delete(catalogCharacters);
|
||||||
|
await tx.delete(catalogMaterials);
|
||||||
await tx.delete(catalogWeapons);
|
await tx.delete(catalogWeapons);
|
||||||
await tx.delete(catalogArtifacts);
|
await tx.delete(catalogArtifacts);
|
||||||
if (characters.length)
|
if (characters.length)
|
||||||
@@ -465,8 +513,11 @@ async function syncCatalogVersion(version: string): Promise<void> {
|
|||||||
nameCardImageKey: item.nameCardImageKey,
|
nameCardImageKey: item.nameCardImageKey,
|
||||||
constellations: item.constellations,
|
constellations: item.constellations,
|
||||||
talents: item.talents,
|
talents: item.talents,
|
||||||
|
ascension: item.ascension,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
if (materials.length)
|
||||||
|
await tx.insert(catalogMaterials).values(materials);
|
||||||
if (weapons.length)
|
if (weapons.length)
|
||||||
await tx
|
await tx
|
||||||
.insert(catalogWeapons)
|
.insert(catalogWeapons)
|
||||||
|
|||||||
+35
-5
@@ -10,6 +10,7 @@ import {
|
|||||||
artifactRecommendations,
|
artifactRecommendations,
|
||||||
catalogArtifacts,
|
catalogArtifacts,
|
||||||
catalogCharacters,
|
catalogCharacters,
|
||||||
|
catalogMaterials,
|
||||||
catalogWeapons,
|
catalogWeapons,
|
||||||
constellationConditions,
|
constellationConditions,
|
||||||
constellationRecommendations,
|
constellationRecommendations,
|
||||||
@@ -28,7 +29,10 @@ import { aliasesByItem } from "@/lib/glossary";
|
|||||||
|
|
||||||
const catalogVersionSchema = z.object({ version: z.string() });
|
const catalogVersionSchema = z.object({ version: z.string() });
|
||||||
|
|
||||||
function catalogCharacterWithUrls(item: typeof catalogCharacters.$inferSelect) {
|
function catalogCharacterWithUrls(
|
||||||
|
item: typeof catalogCharacters.$inferSelect,
|
||||||
|
materialByKey = new Map<string, typeof catalogMaterials.$inferSelect>(),
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
imageUrl: publicMediaUrl(item.imageKey),
|
imageUrl: publicMediaUrl(item.imageKey),
|
||||||
@@ -43,28 +47,47 @@ function catalogCharacterWithUrls(item: typeof catalogCharacters.$inferSelect) {
|
|||||||
...talent,
|
...talent,
|
||||||
iconUrl: talent.iconKey ? publicMediaUrl(talent.iconKey) : null,
|
iconUrl: talent.iconKey ? publicMediaUrl(talent.iconKey) : null,
|
||||||
})),
|
})),
|
||||||
|
ascension: item.ascension.map((group) => ({
|
||||||
|
...group,
|
||||||
|
items: group.items.map((entry) => {
|
||||||
|
const material = materialByKey.get(entry.key);
|
||||||
|
return {
|
||||||
|
...entry,
|
||||||
|
name: material?.name ?? entry.key,
|
||||||
|
description: material?.description ?? null,
|
||||||
|
rarity: material?.rarity ?? null,
|
||||||
|
type: material?.type ?? null,
|
||||||
|
imageUrl: material ? publicMediaUrl(material.imageKey) : null,
|
||||||
|
lunarisUrl: `https://lunaris.moe/material/${encodeURIComponent(entry.key)}`,
|
||||||
|
available: Boolean(material),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getActiveCatalog() {
|
export async function getActiveCatalog() {
|
||||||
const db = getDb();
|
const db = getDb();
|
||||||
const [characters, weapons, artifacts, aliases] = await Promise.all([
|
const [characters, weapons, artifacts, materials, aliases] = await Promise.all([
|
||||||
db.select().from(catalogCharacters).orderBy(asc(catalogCharacters.name)),
|
db.select().from(catalogCharacters).orderBy(asc(catalogCharacters.name)),
|
||||||
db.select().from(catalogWeapons).orderBy(asc(catalogWeapons.name)),
|
db.select().from(catalogWeapons).orderBy(asc(catalogWeapons.name)),
|
||||||
db.select().from(catalogArtifacts).orderBy(asc(catalogArtifacts.name)),
|
db.select().from(catalogArtifacts).orderBy(asc(catalogArtifacts.name)),
|
||||||
|
db.select().from(catalogMaterials).orderBy(asc(catalogMaterials.name)),
|
||||||
db.select().from(glossaryAliases),
|
db.select().from(glossaryAliases),
|
||||||
]);
|
]);
|
||||||
if (!characters.length) return null;
|
if (!characters.length) return null;
|
||||||
const groupedAliases = aliasesByItem(aliases);
|
const groupedAliases = aliasesByItem(aliases);
|
||||||
|
const materialByKey = new Map(materials.map((item) => [item.key, item]));
|
||||||
const storage = await getMediaStorage();
|
const storage = await getMediaStorage();
|
||||||
const { version } = catalogVersionSchema.parse(
|
const { version } = catalogVersionSchema.parse(
|
||||||
await storage.file("catalog/current.json").json(),
|
await storage.file("catalog/current.json").json(),
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
version,
|
version,
|
||||||
characters: characters.map((item) => ({ ...catalogCharacterWithUrls(item), aliases: groupedAliases.character.get(item.key) ?? [] })),
|
characters: characters.map((item) => ({ ...catalogCharacterWithUrls(item, materialByKey), aliases: groupedAliases.character.get(item.key) ?? [] })),
|
||||||
weapons: weapons.map((item) => ({ ...item, aliases: groupedAliases.weapon.get(item.key) ?? [], imageUrl: publicMediaUrl(item.imageKey) })),
|
weapons: weapons.map((item) => ({ ...item, aliases: groupedAliases.weapon.get(item.key) ?? [], imageUrl: publicMediaUrl(item.imageKey) })),
|
||||||
artifacts: artifacts.map((item) => ({ ...item, aliases: groupedAliases.artifact.get(item.key) ?? [], imageUrl: publicMediaUrl(item.imageKey) })),
|
artifacts: artifacts.map((item) => ({ ...item, aliases: groupedAliases.artifact.get(item.key) ?? [], imageUrl: publicMediaUrl(item.imageKey) })),
|
||||||
|
materials: materials.map((item) => ({ ...item, imageUrl: publicMediaUrl(item.imageKey), lunarisUrl: `https://lunaris.moe/material/${encodeURIComponent(item.key)}` })),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +137,10 @@ async function getGuideContent(slug: string, publicOnly: boolean) {
|
|||||||
))
|
))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
if (!row) return null;
|
if (!row) return null;
|
||||||
const [sections, weaponConditionsRows, groups, profile, constellationGroups, teams, aliases] = await Promise.all([
|
const materialKeys = [...new Set(
|
||||||
|
row.character.ascension.flatMap((group) => group.items.map((item) => item.key)),
|
||||||
|
)];
|
||||||
|
const [sections, weaponConditionsRows, groups, profile, constellationGroups, teams, aliases, materials] = await Promise.all([
|
||||||
getDb().select().from(guideSections).where(and(eq(guideSections.guideId, row.guide.id), eq(guideSections.enabled, true))).orderBy(asc(guideSections.sortOrder)),
|
getDb().select().from(guideSections).where(and(eq(guideSections.guideId, row.guide.id), eq(guideSections.enabled, true))).orderBy(asc(guideSections.sortOrder)),
|
||||||
getDb().select().from(weaponConditions).where(eq(weaponConditions.guideId, row.guide.id)).orderBy(asc(weaponConditions.sortOrder)),
|
getDb().select().from(weaponConditions).where(eq(weaponConditions.guideId, row.guide.id)).orderBy(asc(weaponConditions.sortOrder)),
|
||||||
getDb().select().from(artifactGroups).where(eq(artifactGroups.guideId, row.guide.id)).orderBy(asc(artifactGroups.sortOrder)),
|
getDb().select().from(artifactGroups).where(eq(artifactGroups.guideId, row.guide.id)).orderBy(asc(artifactGroups.sortOrder)),
|
||||||
@@ -122,8 +148,12 @@ async function getGuideContent(slug: string, publicOnly: boolean) {
|
|||||||
getDb().select().from(constellationConditions).where(eq(constellationConditions.guideId, row.guide.id)).orderBy(asc(constellationConditions.sortOrder)),
|
getDb().select().from(constellationConditions).where(eq(constellationConditions.guideId, row.guide.id)).orderBy(asc(constellationConditions.sortOrder)),
|
||||||
getDb().select().from(teamCompositions).where(eq(teamCompositions.guideId, row.guide.id)).orderBy(asc(teamCompositions.sortOrder)),
|
getDb().select().from(teamCompositions).where(eq(teamCompositions.guideId, row.guide.id)).orderBy(asc(teamCompositions.sortOrder)),
|
||||||
getDb().select().from(glossaryAliases),
|
getDb().select().from(glossaryAliases),
|
||||||
|
materialKeys.length
|
||||||
|
? getDb().select().from(catalogMaterials).where(inArray(catalogMaterials.key, materialKeys))
|
||||||
|
: [],
|
||||||
]);
|
]);
|
||||||
const groupedAliases = aliasesByItem(aliases);
|
const groupedAliases = aliasesByItem(aliases);
|
||||||
|
const materialByKey = new Map(materials.map((item) => [item.key, item]));
|
||||||
|
|
||||||
const elementIconUrls = Object.fromEntries(
|
const elementIconUrls = Object.fromEntries(
|
||||||
(await getDb()
|
(await getDb()
|
||||||
@@ -168,7 +198,7 @@ async function getGuideContent(slug: string, publicOnly: boolean) {
|
|||||||
return {
|
return {
|
||||||
...row.guide,
|
...row.guide,
|
||||||
coverUrl: publicMediaUrl(row.cover.objectKey),
|
coverUrl: publicMediaUrl(row.cover.objectKey),
|
||||||
character: { ...catalogCharacterWithUrls(row.character), aliases: groupedAliases.character.get(row.character.key) ?? [] },
|
character: { ...catalogCharacterWithUrls(row.character, materialByKey), aliases: groupedAliases.character.get(row.character.key) ?? [] },
|
||||||
elementIconUrls,
|
elementIconUrls,
|
||||||
sections,
|
sections,
|
||||||
weapons: weaponRows.map(({ recommendation, weapon }) => ({ ...recommendation, weapon: { ...weapon, aliases: groupedAliases.weapon.get(weapon.key) ?? [], imageUrl: publicMediaUrl(weapon.imageKey) } })),
|
weapons: weaponRows.map(({ recommendation, weapon }) => ({ ...recommendation, weapon: { ...weapon, aliases: groupedAliases.weapon.get(weapon.key) ?? [], imageUrl: publicMediaUrl(weapon.imageKey) } })),
|
||||||
|
|||||||
Reference in New Issue
Block a user