Merge remote-tracking branch 'origin/sub_laptop_3' into main_laptop_1
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
* 기다리지 않고 null 을 돌려줌(배수유역은 뒤 단계 · 출력 없음).
|
||||
* - 노선 초안이 있으면 「낡음」 — 자동 = 3 s 조용한 뒤 셈 · 수동 = 칸만 흐림 + 가운데 [업데이트].
|
||||
* - 다시 조작하면 처음으로 — 떠 있는 요청을 끊고(AbortController) 다시 흐림 또는 다시 3 s.
|
||||
* 묶음이 같은 노선 · 관으로 다시 돌면(입력 지문 같음) 그대로 둠 — 떠 있는 답을 받음.
|
||||
* - 서버가 「낡음」으로 답하면(노선 [저장] 뒤 등) 같은 흐림 + [업데이트] · 자동이면 3 s 뒤.
|
||||
* - 답은 패널 요청 감싸기(`DetailRunner`, quiet)로 올림 — 옛 차례 · 서버가 그만둔 답은 버림.
|
||||
* - 덮개 · 잠금 없음 — 다른 칸 조작은 그대로.
|
||||
* ========================================================================== */
|
||||
@@ -24,6 +26,7 @@ import {
|
||||
import {
|
||||
acceptLive,
|
||||
draftMakesStale,
|
||||
inputKey,
|
||||
liveRequestBody,
|
||||
parseDrainageMode,
|
||||
QUIET_MS,
|
||||
@@ -50,6 +53,8 @@ export interface DrainageLive {
|
||||
stage: StageFn<"drainage">;
|
||||
/** 낡음을 지금 노선으로 다시 셈(자동 · [업데이트]와 같은 길). */
|
||||
refresh(): void;
|
||||
/** 서버 답이 「낡음」 — 흐림 + [업데이트](자동이면 3 s 뒤 셈). 이미 낡음이면 그대로. */
|
||||
markStale(): void;
|
||||
/** [확정]이 서버에서 다시 셈 — 낡음 지움(떠 있는 요청 · 기다림도 끊음). */
|
||||
markFresh(): void;
|
||||
readonly stale: boolean;
|
||||
@@ -93,6 +98,8 @@ export function createDrainageLive(deps: DrainageLiveDeps): DrainageLive {
|
||||
let route: unknown = null;
|
||||
let stale = false;
|
||||
let generation = 0;
|
||||
/** 마지막 단계 입력 지문 — 같으면 묶음이 다시 돈 것뿐. */
|
||||
let key = "";
|
||||
let timer = 0;
|
||||
let inflight: AbortController | null = null;
|
||||
|
||||
@@ -134,16 +141,16 @@ export function createDrainageLive(deps: DrainageLiveDeps): DrainageLive {
|
||||
const projectId = deps.runner.projectId();
|
||||
if (!projectId) return;
|
||||
cancel();
|
||||
const sent = generation;
|
||||
const sent = { generation: ++generation, key };
|
||||
const controller = new AbortController();
|
||||
inflight = controller;
|
||||
paint();
|
||||
const body = liveRequestBody(sent, route, deps.pipeRequest());
|
||||
const body = liveRequestBody(sent.generation, route, deps.pipeRequest());
|
||||
void deps.runner
|
||||
.run(async () => {
|
||||
const response = await fetchLive(projectId, body, controller.signal);
|
||||
// 옛 차례 · 서버가 그만둔 답 — 감싸기가 조용히 버리게 끊김으로 돌려줌.
|
||||
if (!acceptLive(response, sent, generation))
|
||||
if (!acceptLive(response, sent, { generation, key }))
|
||||
throw new DOMException("superseded", "AbortError");
|
||||
return response;
|
||||
}, true)
|
||||
@@ -157,7 +164,6 @@ export function createDrainageLive(deps: DrainageLiveDeps): DrainageLive {
|
||||
|
||||
/** 평면 · 관 자리 조작 — 처음으로(요청 끊기 · 다시 흐림 또는 다시 3 s). */
|
||||
function invalidate(): void {
|
||||
generation += 1;
|
||||
cancel();
|
||||
stale = true;
|
||||
if (mode === "auto") timer = window.setTimeout(refresh, QUIET_MS);
|
||||
@@ -208,11 +214,20 @@ export function createDrainageLive(deps: DrainageLiveDeps): DrainageLive {
|
||||
return {
|
||||
stage(ctx) {
|
||||
route = ctx.inputs.route;
|
||||
const next = inputKey(route, ctx.inputs.pipes);
|
||||
if (next === key) return null;
|
||||
key = next;
|
||||
if (draftMakesStale(route) || stale) invalidate();
|
||||
return null;
|
||||
},
|
||||
refresh,
|
||||
suggest,
|
||||
markStale() {
|
||||
if (stale) return;
|
||||
stale = true;
|
||||
if (mode === "auto" && !inflight) timer = window.setTimeout(refresh, QUIET_MS);
|
||||
paint();
|
||||
},
|
||||
markFresh() {
|
||||
cancel();
|
||||
stale = false;
|
||||
|
||||
@@ -340,6 +340,7 @@ export function createDrainagePanel(callbacks: DrainagePanelCallbacks = {}): Dra
|
||||
function apply(response: DetailBasinResponse): void {
|
||||
basins = response.basins;
|
||||
live.suggest.update(response);
|
||||
if (response.stale) live.markStale(); // 노선 [저장] 뒤 등 — 흐림 + [업데이트](자동이면 3 s 뒤)
|
||||
mainBoundary = response.main_polygon_lonlat ?? [];
|
||||
upstreamLines = (response.upstream_lonlat ?? []) as Array<Array<[number, number]>>;
|
||||
flowArrows = (response.flow_arrows ?? []) as FlowArrow[];
|
||||
|
||||
@@ -291,6 +291,7 @@
|
||||
}
|
||||
|
||||
.b05-drainage__veil .b05-drainage__update {
|
||||
margin-left: 0; /* 머리 줄 단추의 오른쪽 붙임(auto) 풂 — 흐린 칸 가운데 */
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
* - 대상 = 평면(노선 초안) · 관 자리만 — 종단 · 횡단 조작은 배수유역 결과를 안 바꿈(묶음 deps 와 같음).
|
||||
* - 노선 초안이 없으면(저장 노선 그대로) 낡음 아님 — 관 조작은 세부 유역 나눔(0.2 s)이 바로 맞춤.
|
||||
* - 자동 = 조용한 뒤 `QUIET_MS` · 수동 = 흐림 + [업데이트] · 기본 수동.
|
||||
* - 답 채택 = 보낸 차례가 지금 차례와 같고 서버가 그만두지 않았을 때만(늦은 답 버림).
|
||||
* - 묶음이 같은 입력으로 다시 돌면(입력 지문 같음) 낡음 · 떠 있는 요청을 그대로 둠.
|
||||
* - 답 채택 = 보낸 차례 · 입력 지문이 지금과 같고 서버가 그만두지 않았을 때만(늦은 답 버림).
|
||||
* ========================================================================== */
|
||||
|
||||
export type DrainageMode = "manual" | "auto";
|
||||
@@ -20,6 +21,15 @@ export const parseDrainageMode = (raw: string | null | undefined): DrainageMode
|
||||
/** 묶음 drainage 단계 입력 → 낡았는가 — 노선 초안이 있을 때만. */
|
||||
export const draftMakesStale = (route: unknown): boolean => route !== null && route !== undefined;
|
||||
|
||||
/** 묶음 drainage 입력 지문 — 노선 초안(판 번호 rev 뺌) · 관 id · 자리(cm). */
|
||||
export function inputKey(route: unknown, pipes: unknown): string {
|
||||
const draft = route && typeof route === "object" ? { ...route, rev: undefined } : null;
|
||||
const spots = Array.isArray(pipes)
|
||||
? pipes.map((pipe) => `${pipe?.id ?? ""}@${Math.round(Number(pipe?.chainage_m) * 100)}`).sort()
|
||||
: [];
|
||||
return JSON.stringify([draft, spots]);
|
||||
}
|
||||
|
||||
/** 「지금 노선으로 셈」 몸통 — 서버 `post_drainage_live`. */
|
||||
export function liveRequestBody<P>(
|
||||
generation: number,
|
||||
@@ -29,15 +39,16 @@ export function liveRequestBody<P>(
|
||||
return { generation, route: route ?? null, points };
|
||||
}
|
||||
|
||||
/** 답을 화면에 올리는가 — 서버가 그만둔 답 · 옛 차례 답은 버림. */
|
||||
/** 답을 화면에 올리는가 — 서버가 그만둔 답 · 옛 차례 · 입력이 바뀐 뒤 답은 버림. */
|
||||
export function acceptLive(
|
||||
response: { status?: string; generation?: number },
|
||||
sentGeneration: number,
|
||||
currentGeneration: number,
|
||||
sent: { generation: number; key: string },
|
||||
now: { generation: number; key: string },
|
||||
): boolean {
|
||||
return (
|
||||
response.status !== "superseded" &&
|
||||
sentGeneration === currentGeneration &&
|
||||
(response.generation === undefined || response.generation === sentGeneration)
|
||||
sent.generation === now.generation &&
|
||||
sent.key === now.key &&
|
||||
(response.generation === undefined || response.generation === sent.generation)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user