54 lines
2.4 KiB
TypeScript
54 lines
2.4 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* 브라우저 로컬 환경설정 — 게이트 자동 승인.
|
|
* 키 `playreel.autoApprove` = { fetch_confirm?: true, narration_confirm?: true }
|
|
* 대상은 크레딧이 들지 않고 되돌릴 수 있는 게이트(①·③)뿐. ②·④·⑤는 GATE_META.credits/비가역이라 제외.
|
|
* 서버에는 저장하지 않는다(사용자별 계정 개념이 아직 없음). 서버 `approve` 자동 호출은 /playreel/[id] 에서.
|
|
*/
|
|
|
|
import { useSyncExternalStore } from "react";
|
|
import type { GateKey } from "@/lib/playreel";
|
|
|
|
export const AUTO_APPROVABLE = ["fetch_confirm", "narration_confirm"] as const satisfies readonly GateKey[];
|
|
export type AutoApprovable = (typeof AUTO_APPROVABLE)[number];
|
|
export type AutoApprovePrefs = Partial<Record<AutoApprovable, boolean>>;
|
|
|
|
const KEY = "playreel.autoApprove";
|
|
const EVT = "playreel:prefs";
|
|
const EMPTY: AutoApprovePrefs = {};
|
|
let cache: { raw: string | null; value: AutoApprovePrefs } = { raw: null, value: EMPTY };
|
|
|
|
export function readAutoApprove(): AutoApprovePrefs {
|
|
if (typeof window === "undefined") return EMPTY;
|
|
let raw: string | null = null;
|
|
try { raw = window.localStorage.getItem(KEY); } catch { return EMPTY; }
|
|
if (raw === cache.raw) return cache.value; // useSyncExternalStore 는 참조 안정성이 필요
|
|
let value: AutoApprovePrefs = EMPTY;
|
|
try {
|
|
const parsed = raw ? JSON.parse(raw) : {};
|
|
value = Object.fromEntries(AUTO_APPROVABLE.filter((g) => parsed?.[g] === true).map((g) => [g, true]));
|
|
} catch { /* 깨진 값은 비활성으로 */ }
|
|
cache = { raw, value };
|
|
return value;
|
|
}
|
|
|
|
export function setAutoApprove(gate: AutoApprovable, on: boolean) {
|
|
if (typeof window === "undefined") return;
|
|
const next = { ...readAutoApprove() };
|
|
if (on) next[gate] = true; else delete next[gate];
|
|
try { window.localStorage.setItem(KEY, JSON.stringify(next)); } catch { /* 프라이빗 모드 등 — 이 세션만 유지 안 됨 */ }
|
|
window.dispatchEvent(new Event(EVT));
|
|
}
|
|
|
|
function subscribe(cb: () => void) {
|
|
window.addEventListener(EVT, cb);
|
|
window.addEventListener("storage", cb); // 다른 탭에서 바꾼 경우
|
|
return () => { window.removeEventListener(EVT, cb); window.removeEventListener("storage", cb); };
|
|
}
|
|
|
|
/** 렌더 중 읽는 훅. SSR·하이드레이션 첫 렌더는 항상 비활성(EMPTY)으로 맞춘다. */
|
|
export function useAutoApprove(): AutoApprovePrefs {
|
|
return useSyncExternalStore(subscribe, readAutoApprove, () => EMPTY);
|
|
}
|