Files
Aislo/A00_Common/design/design_page_data.ts
T
eomsangdonandClaude Opus 5.5 c77e44ab07 refactor(B05·B06 한 페이지): 노선 id 한 칸 — 출처 다섯을 페이지 자료 줄 하나로 (386-2b)
- design_page_route 새로(칸 · 끄는 모듈 없음) · 채움 = design_page_data pageSectionContext(횡단 context · 세션 캐시) 하나
- 읽는 곳 = 종단 보기 [저장] 몫 · 횡단 보기 · 종단도 render · 평면 횡단 판(노선 번호 훑기 지움) · 3D · 측점 그림
- 페이지 떠남에 칸 비움 · 저장 정본 바뀜엔 그대로
- design_profile 700줄 앞 쪼갬(design_profile_context — 만들 때 넘기는 것 · 미리보기 대기)
- 시험 test_384_2b · 평면 횡단 판 시험 = 페이지 노선 값만

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TteK8mfYZetk5P3bEZkyVZ
2026-10-17 19:08:37 +09:00

91 lines
4.8 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_Fetch");
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));
};
/** 묶음 차례 하나가 뜸 — 끝나면(밀림 · 실패 포함) 내림. */
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);
}