Files
Aislo/A00_Common/design/design_page_data.ts
T
eomsangdonandClaude Opus 5.5 7f5019c7ff fix(493-2 손 고침 저장 그대로): 492-2 띠 · [저장] 확인 창 · 저장 뒤 초기화 알림 걷음 · 추천 [적용] 창 수 = 서버 지울 수
- 손 고침 [저장] = 492 이전처럼 바로 저장(확인 창 없음 · 세션 초안 통째 비움 없음) · 평면 · B06 평면 패널 띠 걷음
- 추천 [적용] 확인 창 = 서버 저장분(구조물 · 관 저장 파일 · 추가 측점) 수 — 응답 route_reset.dropped 와 같은 셈 · 세션 초안 안 셈 · 종단 편집 수 뺌
- 안 쓰게 된 것 걷음: routeShapeChanged(TS) · draftShapeChanged · askRouteReset · ROUTE_RESET_BAND · pageRoutePlan · 띠 CSS
- 추가 측점 저장분 받기 fetchSavedPlusStations 한 줄
- 시험: test_283_route_reset_confirm 고쳐 씀(걷음 소스 · 창 수 = 알림 수 · 관 저장 파일 없음 0) · 저장 헬퍼 셋 확인 창 가짜 걷음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 09:34:05 +09:00

94 lines
5.0 KiB
TypeScript

/* =============================================================================
* A00_Common/design/design_page_data.ts
* B05 · B06 한 페이지 자료 줄(PLAN 386-2) — 저장분(구조물 · 관 · 정의 판 확인)을 페이지에서 한 번만 받음.
*
* 받는 쪽 = 묶음(`design_pipeline_page`) · 종단 보기 구조물 칸 · 횡단 보기 구조물 칸 · 초안 측점
* (`B06_Section_Section_Drafts`) · [저장] 관 얹기(`design_save`) — 보기 전환 · 다시 보임 = 요청 0.
* 다시 받기 = 저장 정본이 바뀐 뒤 한 번 — `invalidateSectionDetail` → `forgetDraftStations` → 여기 비움
* ([저장] · [확정] · [초기화] · 노선 [적용] · 파일 다시 올림) · 페이지를 떠나면 비움.
* 종류 목록은 `fetchStructureTypes` 가 탭 수명 동안 쥠 — 정의 판 확인만 페이지마다 한 번.
* 돌려줄 때마다 사본 — 받는 쪽이 고쳐도 다른 쪽 저장분이 안 바뀜.
* 「계산 중」 — 묶음 차례가 떠 있는 동안 좌측 아래 점(`onPageBusy`).
* 노선 id 한 칸(386-2b) — 횡단 context(서버 지금 노선 · 세션 캐시)에서 채움 · 두 보기 · 종단도 · [저장] ·
* 평면 횡단 판이 여기서만 읽음 · 노선이 바뀌면 페이지를 통째 다시 세움(remountRoute) → 페이지 동안 고정.
* ========================================================================== */
import { fetchDetailPipePoints } from "../../B04_PreProcess/B04_PreProcess_Api_Fetch";
import type { SectionContextResponse } from "../../B06_Section/B06_Section_Api_Fetch";
import { setPageRouteId } from "./design_page_route";
export { pageRouteId } from "./design_page_route";
import {
dropStaleStructureTypes,
fetchStructures,
} from "../../B05_Profile/B05_Profile_Api_Structures";
const memo = new Map<string, Promise<unknown>>();
function once<T>(key: string, load: () => Promise<T>): Promise<T> {
let hit = memo.get(key) as Promise<T> | undefined;
if (!hit) {
hit = load();
memo.set(key, hit);
hit.catch(() => memo.get(key) === hit && memo.delete(key)); // 실패는 다음에 다시 물음
}
return hit.then((value) => structuredClone(value));
}
/** 저장 구조물(structures.json · 판번호 · 정의 판). */
export const pageStructures = (projectId: string): ReturnType<typeof fetchStructures> =>
once(`structures:${projectId}`, () => fetchStructures(projectId));
/** 저장 관(pipe_points.json · 유역). */
export const pagePipes = (projectId: string): ReturnType<typeof fetchDetailPipePoints> =>
once(`pipes:${projectId}`, () => fetchDetailPipePoints(projectId));
/** 정의 판이 바뀌었으면 종류 캐시 버림 — 페이지마다 한 번(탭이 다시 보일 때는 `onStaleStructureTypes`). */
export const pageTypesChecked = (projectId: string): Promise<boolean> =>
once(`types:${projectId}`, () => dropStaleStructureTypes());
/** 저장 정본이 바뀜 · 페이지를 떠남(`leaving` = 노선 id 칸도) — 다음에 부르면 한 번 다시 받음. */
export function forgetPageData(projectId: string, leaving = false): void {
for (const key of [...memo.keys()]) if (key.endsWith(`:${projectId}`)) memo.delete(key);
if (leaving) setPageRouteId(projectId, null);
}
/** 횡단 context(세션 캐시 · 새 요청 없음) — 노선 id 칸을 채움 · 두 보기가 이것으로만 받음. 서버 창구는 부를 때
* 불러옴(묶음 · 초안 측점이 끄는 이 파일이 화면 모듈을 안 끌게). */
export async function pageSectionContext(projectId: string): Promise<SectionContextResponse> {
const { fetchSectionContext } = await import("../../B06_Section/B06_Section_Api_Context");
const context = await fetchSectionContext(projectId);
setPageRouteId(projectId, context.route_id); // 노선 id 한 칸(design_page_route)
return context;
}
const busy = new Map<string, number>();
const busyListeners = new Map<string, Set<(busy: boolean) => void>>();
const notify = (projectId: string): void => {
const now = (busy.get(projectId) ?? 0) > 0;
busyListeners.get(projectId)?.forEach((fn) => fn(now));
};
/** 묶음 차례가 떠 있나 — 그 사이 폼이 다시 실려도 좌측 패널은 안 폄(`design_user_pick` · 398-1). */
export const pageBusy = (projectId: string): boolean => (busy.get(projectId) ?? 0) > 0;
/** 묶음 차례 하나가 뜸 — 끝나면(밀림 · 실패 포함) 내림. */
export function trackPageBusy(projectId: string, run: Promise<unknown> | null): void {
if (!run) return;
busy.set(projectId, (busy.get(projectId) ?? 0) + 1);
notify(projectId);
const done = (): void => {
busy.set(projectId, Math.max(0, (busy.get(projectId) ?? 1) - 1));
notify(projectId);
};
run.then(done, done);
}
/** 「계산 중」 켬 · 끔 듣기 — 떼는 함수를 돌려줌. */
export function onPageBusy(projectId: string, fn: (busy: boolean) => void): () => void {
const set = busyListeners.get(projectId) ?? new Set();
busyListeners.set(projectId, set.add(fn));
fn((busy.get(projectId) ?? 0) > 0);
return () => set.delete(fn);
}