feat(ui) : improve guide navigation, signup, and commissions
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
import * as z from "zod";
|
||||
import type { CommissionRequest } from "./request";
|
||||
|
||||
const draftKey = z.string().max(64);
|
||||
const draftRequestSchema = z.object({
|
||||
teams: z.array(z.object({ members: z.array(z.object({
|
||||
characterKey: draftKey, weaponKey: draftKey, artifactKey: draftKey,
|
||||
constellation: z.enum(["0", "1", "2", "3", "4", "5", "6", "0-6"]),
|
||||
refinement: z.enum(["1", "2", "3", "4", "5", "1-5"]),
|
||||
})).length(4) })).max(20),
|
||||
weapons: z.array(z.object({
|
||||
characterKey: draftKey, weaponKeys: z.array(draftKey).min(1).max(100),
|
||||
refinements: z.record(draftKey, z.enum(["1", "2", "3", "4", "5"])).optional(),
|
||||
})).max(20),
|
||||
constellations: z.array(z.object({
|
||||
characterKey: draftKey, levels: z.array(z.number().int().min(0).max(6)).max(7),
|
||||
})).max(20),
|
||||
});
|
||||
const draftSchema = z.object({ version: z.literal(1), savedAt: z.number(), request: draftRequestSchema });
|
||||
const DRAFT_LIFETIME_MS = 7 * 24 * 60 * 60 * 1000;
|
||||
|
||||
type DraftCatalog = {
|
||||
characters: { key: string; weaponType?: string | null }[];
|
||||
weapons: { key: string; weaponType?: string | null }[];
|
||||
artifacts: { key: string }[];
|
||||
};
|
||||
|
||||
export function commissionDraftKey(userId: string) {
|
||||
return `commission-request-draft:${userId}`;
|
||||
}
|
||||
|
||||
export function serializeCommissionDraft(request: CommissionRequest, now = Date.now()) {
|
||||
return JSON.stringify({ version: 1, savedAt: now, request });
|
||||
}
|
||||
|
||||
export function restoreCommissionDraft(serialized: string | null, catalog: DraftCatalog, now = Date.now()): CommissionRequest | null {
|
||||
if (!serialized) return null;
|
||||
let parsed;
|
||||
try { parsed = draftSchema.safeParse(JSON.parse(serialized)); } catch { return null; }
|
||||
if (!parsed.success || now - parsed.data.savedAt > DRAFT_LIFETIME_MS || parsed.data.savedAt > now) return null;
|
||||
const request = parsed.data.request;
|
||||
const characterKeys = new Set(catalog.characters.map((item) => item.key));
|
||||
const artifactKeys = new Set(catalog.artifacts.map((item) => item.key));
|
||||
const compatibleWeapon = (characterKey: string, weaponKey: string) => {
|
||||
const character = catalog.characters.find((item) => item.key === characterKey);
|
||||
return character?.weaponType && catalog.weapons.some((item) => item.key === weaponKey && item.weaponType === character.weaponType)
|
||||
? weaponKey : "";
|
||||
};
|
||||
for (const team of request.teams) for (const member of team.members) {
|
||||
if (!characterKeys.has(member.characterKey)) member.characterKey = "";
|
||||
member.weaponKey = compatibleWeapon(member.characterKey, member.weaponKey);
|
||||
if (!artifactKeys.has(member.artifactKey)) member.artifactKey = "";
|
||||
}
|
||||
for (const set of request.weapons) {
|
||||
if (!characterKeys.has(set.characterKey)) set.characterKey = "";
|
||||
set.weaponKeys = set.weaponKeys.map((key) => compatibleWeapon(set.characterKey, key));
|
||||
set.refinements = Object.fromEntries(Object.entries(set.refinements ?? {}).filter(([key]) => key && set.weaponKeys.includes(key)));
|
||||
}
|
||||
for (const set of request.constellations) if (!characterKeys.has(set.characterKey)) set.characterKey = "";
|
||||
return request;
|
||||
}
|
||||
Reference in New Issue
Block a user