82 lines
3.3 KiB
TypeScript
82 lines
3.3 KiB
TypeScript
import { create } from "zustand";
|
|
import type { PlayerSession } from "../app/types";
|
|
import { requestOnlineJson } from "../app/onlineAccountClient";
|
|
import { DEFAULT_PARTY_BUFF_POLICY, normalizePartyBuffPolicy, type PartyBuffPolicy } from "./partyBuffPolicy";
|
|
|
|
interface PublishedPolicy {
|
|
readonly revision: number;
|
|
readonly updatedAt: number | null;
|
|
readonly updatedBy?: string | null;
|
|
readonly config: PartyBuffPolicy;
|
|
}
|
|
interface PolicyState extends PublishedPolicy {
|
|
readonly draft: PartyBuffPolicy;
|
|
readonly loading: boolean;
|
|
readonly saving: boolean;
|
|
readonly dirty: boolean;
|
|
readonly error: string | null;
|
|
refresh: (session?: PlayerSession | null) => Promise<void>;
|
|
replaceDraft: (draft: PartyBuffPolicy) => void;
|
|
resetDraft: () => void;
|
|
publish: (session: PlayerSession | null) => Promise<boolean>;
|
|
}
|
|
|
|
const CACHE_KEY = "healer-man.party-buff-policy.v1";
|
|
function cached(): PublishedPolicy {
|
|
try {
|
|
const value = JSON.parse(window.localStorage.getItem(CACHE_KEY) ?? "null") as PublishedPolicy;
|
|
if (!Number.isInteger(value.revision)) throw new Error("missing");
|
|
return { ...value, config: normalizePartyBuffPolicy(value.config) };
|
|
} catch {
|
|
return { revision: 0, updatedAt: null, config: DEFAULT_PARTY_BUFF_POLICY };
|
|
}
|
|
}
|
|
function cache(payload: PublishedPolicy): void {
|
|
try { window.localStorage.setItem(CACHE_KEY, JSON.stringify(payload)); } catch { /* optional cache */ }
|
|
}
|
|
|
|
const initial = cached();
|
|
export const usePartyBuffPolicyStore = create<PolicyState>((set, get) => ({
|
|
...initial,
|
|
draft: initial.config,
|
|
loading: false,
|
|
saving: false,
|
|
dirty: false,
|
|
error: null,
|
|
refresh: async (session = null) => {
|
|
set({ loading: true, error: null });
|
|
try {
|
|
const gm = Boolean(session?.accessToken && session.roles?.includes("gm"));
|
|
const payload = await requestOnlineJson<PublishedPolicy>(
|
|
gm ? "/api/gm/party-buff-policy" : "/api/party-buff-policy", {}, gm ? session?.accessToken : undefined,
|
|
);
|
|
const normalized = { ...payload, config: normalizePartyBuffPolicy(payload.config) };
|
|
cache(normalized);
|
|
set({ ...normalized, draft: normalized.config, loading: false, dirty: false });
|
|
} catch (error) {
|
|
set({ loading: false, error: error instanceof Error ? error.message : "Could not load party buff policy." });
|
|
}
|
|
},
|
|
replaceDraft: (draft) => set({ draft: normalizePartyBuffPolicy(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;
|
|
}
|
|
set({ saving: true, error: null });
|
|
try {
|
|
const payload = await requestOnlineJson<PublishedPolicy>("/api/gm/party-buff-policy", {
|
|
method: "PUT", body: JSON.stringify({ expectedRevision: get().revision, config: get().draft }),
|
|
}, session.accessToken);
|
|
const normalized = { ...payload, config: normalizePartyBuffPolicy(payload.config) };
|
|
cache(normalized);
|
|
set({ ...normalized, draft: normalized.config, saving: false, dirty: false });
|
|
return true;
|
|
} catch (error) {
|
|
set({ saving: false, error: error instanceof Error ? error.message : "Could not publish party buff policy." });
|
|
return false;
|
|
}
|
|
},
|
|
}));
|