/* ============================================================================= * B05_Profile_UI_Drainage_Run.ts * 세부유역 요청 한 번 감싸기 — 버튼 잠금 · 진행 문구 · 오류 표기 · 최신 응답만 채택(차례 번호). * * 패널 본체(B05_Profile_UI_Drainage_Panel)가 700줄이라 나눔(347-2). 「지금 노선으로 셈」 답도 * 같은 차례 번호를 타서, 늦게 온 세부유역 답이 새 답을 덮지 않음 — `quiet` 이면 버튼 · 가운데 진행 * 원 · 상태 글을 안 건드림(칸 구석 「계산 중」은 부르는 쪽 · 조작을 막지 않음). * ========================================================================== */ import type { DetailBasinResponse } from "../B04_PreProcess/B04_PreProcess_Api_Fetch"; import { analyzeFailed } from "./B05_Profile_UI_Drainage_Parts"; type StatusKey = | "B05_Drainage_Status_Analyzing" | "B05_Drainage_Status_NoBasin" | "B05_Drainage_Status_AnalyzeFailed"; export interface DetailRunnerDeps { projectId: () => string | null; analyzeButton: HTMLButtonElement; status: HTMLElement; showProgress: (ratio: number | null, label: string | null) => void; apply: (response: DetailBasinResponse) => void; basinCount: () => number; scheduleDraw: () => void; label: (key: StatusKey) => string; } export interface DetailRunner { projectId: () => string | null; /** 요청 하나 — 답을 화면에 올렸으면 true(뒤에 보낸 요청이 있으면 false). */ run(request: () => Promise, quiet?: boolean): Promise; } export function createDetailRunner(deps: DetailRunnerDeps): DetailRunner { const { analyzeButton, status, showProgress, label } = deps; let requestSeq = 0; // 세부유역 요청 — 최신 응답만 채택 return { projectId: deps.projectId, async run(request, quiet = false) { if (!deps.projectId()) return false; if (!quiet) { analyzeButton.disabled = true; status.hidden = false; status.textContent = label("B05_Drainage_Status_Analyzing"); showProgress(null, label("B05_Drainage_Status_Analyzing")); } const seq = ++requestSeq; try { const response = await request(); if (seq !== requestSeq) return false; // 뒤에 보낸 요청이 있음 — 그 응답을 기다림 deps.apply(response); status.hidden = deps.basinCount() > 0; if (!deps.basinCount()) status.textContent = response.stale ?? label("B05_Drainage_Status_NoBasin"); deps.scheduleDraw(); return true; } catch (error) { if (seq !== requestSeq) return false; if (error instanceof DOMException && error.name === "AbortError") return false; status.hidden = false; status.textContent = analyzeFailed(error, label("B05_Drainage_Status_AnalyzeFailed")); return false; } finally { if (seq === requestSeq) { analyzeButton.disabled = false; showProgress(null, null); } } }, }; }