Files
Aislo/A00_Common/design/design_page_data.ts
T
eomsangdonandClaude Opus 5.5 99a224c6c9 fix(505-2 B05 · B06 저장 · 고름): 확정 중 고침 알림 · 저장 중 초기화 막음 + 서버 잠금 · 계산 중 누름 폄 · B06 목록 밂 · 추가 측점 data-plus · 옛 초안 간격 · 풀개 — 요구 309-3 · 310
- 1 가림막 저장(확정)도 도중 고침 검사 · 「저장 중 고친 값」 알림(키는 안 막음 · B07~B09 안 번짐)
- 2b snapOptions 간격 = 10 만 · 옛 초안 0.1 · 1 m = null(load_station_snap_step 거울)
- 3 추가 측점 = 측점 묶음 data-plus · B05 누름 · 가까이 표시가 그 속성으로
- 4d 숨은 동안 고른 카드 = 다음 다시 그림 끝에 밂 · 휠 · 누름이면 버림
- 7 pipe_calc set 다섯 = 풀개 · 횡단 · 종단 보기 onDispose
- 8 저장 중 [초기화] = [저장]과 같은 알림 + 막음 · /route/reset = 프로젝트 잠금 안
- 9 isUserPick 거름 = 묶음 띄운 그 누름만 · 계산 중 새 누름 = 폄
- 시험 test_505_2_save_pick 새 · 398_1 busyPick 1 · 390_5 글 맞춤

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 13:44:25 +09:00

99 lines
5.2 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));
};
const launchedBy = new Map<string, Event>();
/** 이 누름이 묶음 차례를 띄웠나 — 그 누름 안 폼 다시 실음은 좌측 패널 안 폄(`design_user_pick` · 398-1 · 505-2). */
export const pageLaunchedBy = (projectId: string, event: Event): boolean =>
launchedBy.get(projectId) === event;
/** 묶음 차례 하나가 뜸 — 띄운 누름을 적음 · 끝나면(밀림 · 실패 포함) 내림. */
export function trackPageBusy(projectId: string, run: Promise<unknown> | null): void {
if (!run) return;
const event = (globalThis as { event?: Event }).event;
if (event) launchedBy.set(projectId, event);
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);
}