/* ============================================================================= * A00_Common/design/design_pipeline_facilities.ts * 계산 묶음 관 · 구조물 · 추가 측점 먹이기(PLAN 415 · 429 ⑨) — 가게 초안이 바뀌면 묶음에 넣음(비면 저장분). * · [저장] 도중 = 안 넣음(H2) — 초안이 비는 사이 열 때 읽은 옛 저장분으로 돌지 않게 · 끝나면 `resync` * (저장 목록 다시 읽기 · 입력 맞춤) 한 번. * · 되돌리기 · 다시하기 = 아래 되살림 훅 한 길 — 묶음 입력과 견줘 다르면 넣음(415-2 · 노선 초안은 노선 길이 * 맡음) · 그 사이 가게 알림(`announceDesignStore` · 472-5)은 구독이 받지 않음(두 번 안 넣음). * 저장 창구는 늦게 불러옴(묶음 시험이 화면 모듈을 안 끌게). * ========================================================================== */ import { onPageHistory, pageReplaying } from "../../B05_Profile/B05_Profile_UI_Profile_History"; import type { useDesignStore } from "./design_store"; import type { ChangeSource } from "./logic/design_pipeline_types"; type Key = "pipes" | "structures" | "plusStations"; const FACILITIES: ReadonlyArray<{ source: ChangeSource; key: Key }> = [ { source: "pipe", key: "pipes" }, { source: "structure", key: "structures" }, { source: "plus", key: "plusStations" }, ]; export interface FacilityFeed { saving(): boolean; /** 저장이 끝나면 `resync` 한 번 — 기다리는 동안 다시 불러도 한 번. */ afterSave(): void; stop(): void; } export function feedFacilities(o: { projectId: string; store: ReturnType; /** 묶음이 받을 때인가(열었음 · 묶음이 가게에 쓰는 메아리 아님). */ ready: () => boolean; gone: () => boolean; saved: (key: Key) => unknown; input: (key: Key) => unknown; /** 노선 초안이 걸렸나 — 되돌리기는 노선 길이 맡음. */ routed: () => boolean; feed: (source: ChangeSource, value: unknown) => void; resync: () => Promise; }): FacilityFeed { let savingOf: ((id: string) => boolean) | null = null; // 한 번 불러 봄 — 덜 선 모듈(시험 싣개의 고리)은 안 받음 void import("./design_save") .then((m) => (m.savingDesign(o.projectId), (savingOf = m.savingDesign))) .catch(() => undefined); const saving = (): boolean => savingOf?.(o.projectId) ?? false; let waiting: Promise | null = null; const afterSave = (): void => { waiting ??= (async () => { // ponytail: 100 ms 마다 물음 — 저장 끝 알림이 생기면 그것으로 while (saving() && !o.gone()) await new Promise((done) => setTimeout(done, 100)); if (!o.gone()) await o.resync(); })().finally(() => (waiting = null)); }; const put = (source: ChangeSource, value: unknown): void => saving() ? afterSave() : o.feed(source, value); const stops = [ ...FACILITIES.map(({ source, key }) => o.store.subscribe(key, (value) => { const now = value ?? o.saved(key); // 묶음 입력과 같은 값 = 이미 받음(되살림 뒤 추가 측점 다시 알림 등) — 한 번만 넣음(472-5) if (!o.ready() || pageReplaying() || JSON.stringify(now) === JSON.stringify(o.input(key))) return; put(source, now); }), ), onPageHistory({ restore: () => { if (!o.ready() || o.routed()) return; for (const { source, key } of FACILITIES) { const now = o.store.get(key) ?? o.saved(key); if (JSON.stringify(now) !== JSON.stringify(o.input(key))) put(source, now); } }, }), ]; return { saving, afterSave, stop: () => stops.forEach((stop) => stop()) }; }