Files
Aislo/B05_Profile/B05_Profile_UI_Drainage_Run.ts
T

73 lines
3.0 KiB
TypeScript

/* =============================================================================
* 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;
updateButton: 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<DetailBasinResponse>, quiet?: boolean): Promise<boolean>;
}
export function createDetailRunner(deps: DetailRunnerDeps): DetailRunner {
const { updateButton, status, showProgress, label } = deps;
let requestSeq = 0; // 세부유역 요청 — 최신 응답만 채택
return {
projectId: deps.projectId,
async run(request, quiet = false) {
if (!deps.projectId()) return false;
if (!quiet) {
updateButton.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) {
updateButton.disabled = false;
showProgress(null, null);
}
}
},
};
}