feat: add Deduplicator class for managing unique promises and implement icon library generator

This commit is contained in:
2026-03-17 16:07:24 +07:00 Unverified
parent b28ac7b9db
commit 54c8f48a4f
7 changed files with 3247 additions and 34 deletions
+31 -32
View File
@@ -15,16 +15,20 @@ import type { App } from "@";
import { generateBackground } from "@/assets/background";
import { ConstsSvg } from "@/assets/consts";
import { featherImage } from "@/assets/feather";
import { EnkaIcon } from "@/assets/icons";
import { library } from "@/assets/icons-library";
import shadeSvg from "@/assets/shade.svg" with { type: "file" };
import fontFile from "@/assets/shinshin.woff2" with { type: "file" };
import { star } from "@/assets/stars";
import { TalentSvg } from "@/assets/talent";
import { WeaponGlazeSvg } from "@/assets/weapon-glaze";
import { AssetManager, getEnkaStores } from "@/lib/assets";
import { Deduplicator } from "@/lib/dedupe";
import { mockData } from "@/lib/mock";
const assets = new AssetManager();
const cache = new TTLCache();
const deduper = new Deduplicator();
const font = file(fontFile);
const shade = file(shadeSvg);
@@ -44,13 +48,17 @@ export function card(app: App) {
// const data = mockData;
const [[avatarStore, weaponStore, relicStore, locs], data] =
await Promise.all([
getEnkaStores().finally(() =>
console.log(`\n${Date.now() - start}ms - Stores loaded`),
deduper.acquire("stores", () =>
getEnkaStores().finally(() =>
console.log(`\n${Date.now() - start}ms - Stores loaded`),
),
),
feature("API")
? fetchEnka(uid, false).finally(() =>
console.log(
`${Date.now() - start}ms - Enka data loaded`,
? deduper.acquire(`enka-${uid}`, () =>
fetchEnka(uid, false).finally(() =>
console.log(
`${Date.now() - start}ms - Enka data loaded`,
),
),
)
: mockData,
@@ -71,12 +79,11 @@ export function card(app: App) {
if (!char.equipList)
return status(501, "INTERNAL: No equipList provided");
const weaponRaw = char.equipList.find((e) => e.weapon)!;
const weapon = {
data: char.equipList.find((e) => e.weapon)!.weapon!,
store:
weaponStore[
char.equipList.find((e) => e.weapon)!.itemId ?? "Unknown"
],
data: weaponRaw.weapon!,
flat: weaponRaw.flat,
store: weaponStore[weaponRaw.itemId ?? "Unknown"],
};
const cacheKey = hash(JSON.stringify(data)).toString(36);
@@ -264,7 +271,7 @@ export function card(app: App) {
</div>
</div>
{/* middle section */}
<div tw="absolute top-0 bottom-0 right-[23.1em] w-[21.5em] p-[1.4em_.7em_.7em] flex flex-col">
<div tw="absolute top-0 bottom-0 right-[23.1em] w-[25.5em] p-[1.4em_.7em_.7em] flex flex-col">
{/* weapon */}
<div tw="flex flex-start" className="Weapon">
{/* display */}
@@ -289,7 +296,7 @@ export function card(app: App) {
</div>
</div>
{/* caption */}
<div tw="px-[5%] grow text-white self-center">
<div tw="px-[5%] grow text-white self-center flex flex-col">
<div
tw="text-[108%] leading-[1.3]"
style={{
@@ -300,7 +307,17 @@ export function card(app: App) {
<span>{locs.en[weapon.store.NameTextMapHash]}</span>
</div>
{/* stats */}
<div tw="pt-[3%] pb-[2%]"></div>
<div tw="pt-[3%] pb-[2%] flex gap-[.5em]">
{weapon.flat.weaponStats?.map((s) => (
<div tw="rounded-[.2em] leading-none bg-white/15 px-[.34em] py-[0.25em] flex items-center gap-[.7em] text-[1.03em]">
<EnkaIcon
icon={s.appendPropId as keyof typeof library}
tw="h-[1.1em]"
/>
<div tw="pt-[.1em]">{s.statValue}</div>
</div>
))}
</div>
</div>
</div>
</div>
@@ -317,7 +334,7 @@ export function card(app: App) {
format: "webp",
// drawDebugBorder: true,
stylesheets: [
`
/* css */ `
.Rock { color: #ffc64a; }
.Talents.Rock { color: #4a3a18!important }
.Wind { color: #71fdec; }
@@ -332,24 +349,6 @@ export function card(app: App) {
.Talents.Fire { color: #541900!important }
.Grass { color: #a5ffab; }
.Talents.Grass { color: #134517!important }
.Substat {
display: inline-block;
background: rgba(255,255,255,.15);
border-radius: .2em;
padding: 2% 3%;
box-sizing: content-box;
margin-right: .5em;
line-height: 1;
}
.Weapon .Substat:before {
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: .2em
}
`,
],
},