From fd178ad97d522b8af8154853a3a5d6fbe89491ce Mon Sep 17 00:00:00 2001 From: 4aiman Date: Tue, 29 Sep 2026 16:03:52 +0300 Subject: [PATCH] Track detail: await history.go popstates before re-render after gap-fill revert/save reopenDetail fired history.go(-count) then re-rendered synchronously, so the async map/chart build raced the popstate unwind: the pending popstates desynced the back stack and bumped the detail render token, and the render bailed at the token check - map and charts came up blank until the next event repainted them. Make reopenDetail async and await the popstate(s) before showSessionDetail, the same resolve-on-popstate pattern as closeThen (no setTimeout/setInterval). --- .../session-logs/2026-09-29_startup-slowness.md | 17 +++++++++++++++++ frontend/js/main.js | 17 +++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/docs/session-logs/2026-09-29_startup-slowness.md b/docs/session-logs/2026-09-29_startup-slowness.md index 1d631a1..5ba89b4 100644 --- a/docs/session-logs/2026-09-29_startup-slowness.md +++ b/docs/session-logs/2026-09-29_startup-slowness.md @@ -96,3 +96,20 @@ reopen -> убрал достройку -> зум/таскание; буфер Проверка: воспроизвести тот же сценарий, снять logcat - ожидаем, что `tile memory limits exceeded` резко упадёт/исчезнет, джанк уйдёт; POI на малом зуме - мало маркеров. +Результат (build 480): пользователь - "всё ок". + +## Баг: после "убрать достройку" пусто (карта+графики) до следующего действия +Симптом: revert достройки -> графики и карта пропадают, из экрана трека не выкидывает, +появляются только после следующего действия ("Отмена"). Стек окон + гонка. + +Корень: `reopenDetail` (main.js:1176) стрелял `history.go(-count)` (асинхронные popstate) и +СРАЗУ синхронно `showSessionDetail`. showSessionDetail пушит свою history-запись, потом +отложенные popstate от go(-count) раскручивают уже ЕЁ и десинхронят стек; при fallthrough в +renderScreen второй showSessionDetail бампает `_sessionDetailRender`, и первый async-рендер +карты/графиков бросается на проверке токена (5411) -> пусто до следующего события. Ровно тот +же класс, что чинил `closeThen()` (комментарий там про это). + +Фикс: `reopenDetail` сделан async и ДОЖИДАЕТСЯ popstate от history.go перед showSessionDetail - +через Promise + слушатель popstate (как `closeThen`), без setTimeout/setInterval. +Проверка: revert на устройстве, по [SD]-логам один прогон showSessionDetail, карта+графики +сразу на месте. diff --git a/frontend/js/main.js b/frontend/js/main.js index 6e87eb9..d682463 100644 --- a/frontend/js/main.js +++ b/frontend/js/main.js @@ -1173,7 +1173,7 @@ function appAlert(message, okText = "OK") { // balance the history stack, then reopen it fresh. Mirrors the recalc/refine pattern // but pops down to the detail modal so it also works while the map is fullscreen — // re-rendering in place instead of stacking a second overlay (which broke the map/charts). -function reopenDetail(sessionId) { +async function reopenDetail(sessionId) { let count = 0; while (winStack.length > 0) { let top = winStack.pop(); @@ -1181,7 +1181,20 @@ function reopenDetail(sessionId) { try { top.close(); } catch {} if (top.type === "modal") break; } - if (count > 0) { _skipPops = count; history.go(-count); } + // AWAIT the history.go(-count) popstate(s) before re-rendering. Rendering synchronously + // after history.go races it: showSessionDetail pushes its own history entry, then the + // pending popstates unwind THAT entry and desync the stack, and the async map/chart build + // comes up blank until the next event repaints it (the "graphs+map gone until Cancel" bug). + // Same fix as closeThen() — resolve on the popstates, never a timeout. + if (count > 0) { + _skipPops = count; + await new Promise(res => { + let remaining = count; + let h = () => { if (--remaining <= 0) { window.removeEventListener("popstate", h); res(); } }; + window.addEventListener("popstate", h); + history.go(-count); + }); + } showSessionDetail(sessionId); }