115 lines
4.5 KiB
TypeScript
115 lines
4.5 KiB
TypeScript
import { create } from "zustand";
|
|
import type { PlayerSession } from "../app/types";
|
|
import { requestOnlineJson } from "../app/onlineAccountClient";
|
|
import {
|
|
DEFAULT_MANASTORM_ADMIN_CONFIG,
|
|
LEGACY_MANASTORM_ADMIN_CACHE_KEY,
|
|
MANASTORM_ADMIN_CACHE_KEY,
|
|
normalizeManastormAdminConfig,
|
|
publicManastormAdminConfig,
|
|
resolvedBossAdminCatalog,
|
|
type ManastormAdminConfig,
|
|
type PublishedManastormAdminConfig,
|
|
} from "./manastormAdminConfig";
|
|
|
|
interface CachedPayload extends PublishedManastormAdminConfig {}
|
|
|
|
function readCache(): CachedPayload {
|
|
try {
|
|
const raw = typeof window !== "undefined"
|
|
? window.localStorage.getItem(MANASTORM_ADMIN_CACHE_KEY)
|
|
?? window.localStorage.getItem(LEGACY_MANASTORM_ADMIN_CACHE_KEY)
|
|
: null;
|
|
const parsed = raw ? JSON.parse(raw) as Partial<CachedPayload> : null;
|
|
if (!parsed || !Number.isInteger(parsed.revision) || (parsed.revision ?? -1) < 0) throw new Error("missing cache");
|
|
return {
|
|
revision: parsed.revision!,
|
|
updatedAt: typeof parsed.updatedAt === "number" ? parsed.updatedAt : null,
|
|
config: publicManastormAdminConfig(normalizeManastormAdminConfig(parsed.config)),
|
|
};
|
|
} catch {
|
|
return { revision: 0, updatedAt: null, config: DEFAULT_MANASTORM_ADMIN_CONFIG };
|
|
}
|
|
}
|
|
|
|
function writeCache(payload: PublishedManastormAdminConfig): void {
|
|
try {
|
|
window.localStorage.setItem(MANASTORM_ADMIN_CACHE_KEY, JSON.stringify({
|
|
...payload,
|
|
config: publicManastormAdminConfig(payload.config),
|
|
}));
|
|
} catch {
|
|
// Private browsing and storage quotas must not prevent play.
|
|
}
|
|
}
|
|
|
|
export interface ManastormAdminState extends PublishedManastormAdminConfig {
|
|
readonly draft: ManastormAdminConfig;
|
|
readonly loading: boolean;
|
|
readonly saving: boolean;
|
|
readonly error: string | null;
|
|
readonly editingBossKey: string | null;
|
|
readonly dirty: boolean;
|
|
refresh: (session?: PlayerSession | null) => Promise<void>;
|
|
replaceDraft: (config: ManastormAdminConfig) => void;
|
|
resetDraft: () => void;
|
|
publish: (session: PlayerSession | null) => Promise<boolean>;
|
|
setEditingBossKey: (key: string | null) => void;
|
|
}
|
|
|
|
const cached = readCache();
|
|
|
|
export const useManastormAdminStore = create<ManastormAdminState>((set, get) => ({
|
|
...cached,
|
|
draft: cached.config,
|
|
loading: false,
|
|
saving: false,
|
|
error: null,
|
|
editingBossKey: null,
|
|
dirty: false,
|
|
refresh: async (session = null) => {
|
|
set({ loading: true, error: null });
|
|
try {
|
|
const gm = Boolean(session?.accessToken && session.roles?.includes("gm"));
|
|
const payload = await requestOnlineJson<PublishedManastormAdminConfig>(
|
|
gm ? "/api/gm/manastorm-config" : "/api/manastorm-config",
|
|
{},
|
|
gm ? session?.accessToken : undefined,
|
|
);
|
|
const normalized = { ...payload, config: normalizeManastormAdminConfig(payload.config) };
|
|
writeCache(normalized);
|
|
set({ ...normalized, draft: normalized.config, dirty: false, loading: false });
|
|
} catch (error) {
|
|
set({ loading: false, error: error instanceof Error ? error.message : "Could not refresh Manastorm configuration." });
|
|
}
|
|
},
|
|
replaceDraft: (draft) => set({ draft: normalizeManastormAdminConfig(draft), dirty: true, error: null }),
|
|
resetDraft: () => set((state) => ({ draft: state.config, dirty: false, error: null })),
|
|
publish: async (session) => {
|
|
if (!session?.accessToken || !session.roles?.includes("gm")) {
|
|
set({ error: "Game Master access is required." });
|
|
return false;
|
|
}
|
|
const invalidReadyBoss = resolvedBossAdminCatalog(get().draft).find((entry) => entry.status === "ready" && !entry.runnable);
|
|
if (invalidReadyBoss) {
|
|
set({ error: `${invalidReadyBoss.name} cannot be published as Ready: ${invalidReadyBoss.validationIssues.join(" ")}` });
|
|
return false;
|
|
}
|
|
set({ saving: true, error: null });
|
|
try {
|
|
const payload = await requestOnlineJson<PublishedManastormAdminConfig>("/api/gm/manastorm-config", {
|
|
method: "PUT",
|
|
body: JSON.stringify({ expectedRevision: get().revision, config: get().draft }),
|
|
}, session.accessToken);
|
|
const normalized = { ...payload, config: normalizeManastormAdminConfig(payload.config) };
|
|
writeCache(normalized);
|
|
set({ ...normalized, draft: normalized.config, dirty: false, saving: false });
|
|
return true;
|
|
} catch (error) {
|
|
set({ saving: false, error: error instanceof Error ? error.message : "Could not publish Manastorm configuration." });
|
|
return false;
|
|
}
|
|
},
|
|
setEditingBossKey: (editingBossKey) => set({ editingBossKey }),
|
|
}));
|