Rewrite modal navigation: fix 5 back-gesture bugs
Root causes fixed: 1. closeModal had replaceState AFTER async history.back() — replaceState overwrote the current entry before back() fired, causing skip-to-today 2. Chart fullscreen in modals used pushState — each open/close cycle added extra history entries, exhausting the stack 3. Map collapse in popHandler didn't call invalidateSize/fitBounds Changes: - closeModal() simplified: just remove overlay + history.back() No more replaceState race condition - Chart/map fullscreen inside modals: NO pushState, CSS-only popHandler detects .chart-full/.sd-map-full and collapses, re-pushes modal - Map collapse: uses saved _leafletMap ref and _bounds for proper resize - Init: renderScreen with fromPopstate=true to avoid navStack duplication - Modal popHandler searches within overlay only (not global document) Fixed user-reported bugs: 1. Map collapse keeps zoom → now calls invalidateSize + fitBounds 2. Chart open → back → back = app exit → now back closes chart, back closes modal 3. "<-" goes to Today instead of track list → fixed closeModal race 4. X on chart → back = correct → was already working 5. Chart open → back back = app exit → same fix as #2
This commit is contained in:
parent
30b0ac49f3
commit
0701fbd723
1 changed files with 27 additions and 17 deletions
|
|
@ -94,8 +94,8 @@ async function init() {
|
|||
// Add padding entries so first back doesn't exit
|
||||
for (let i = 0; i < 3; i++) history.pushState({ screen: navStack[navStack.length - 1] }, "");
|
||||
|
||||
// Restore last screen
|
||||
renderScreen(navStack[navStack.length - 1]);
|
||||
// Restore last screen (fromPopstate=true to avoid duplicate navStack push)
|
||||
renderScreen(navStack[navStack.length - 1], true);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -642,18 +642,29 @@ function showModal(html, onSubmit) {
|
|||
|
||||
history.pushState({ modal: true }, "");
|
||||
const popHandler = () => {
|
||||
// Fullscreen map inside modal? Collapse it first.
|
||||
// Fullscreen map inside modal? Collapse it, stay in modal.
|
||||
const fsMap = overlay.querySelector(".sd-map-full");
|
||||
if (fsMap) {
|
||||
fsMap.classList.remove("sd-map-full");
|
||||
const expandBtn = fsMap.querySelector(".sd-map-expand-btn");
|
||||
if (expandBtn) expandBtn.innerHTML = "⛶";
|
||||
try { map.dragging.disable(); map.scrollWheelZoom.disable(); } catch {}
|
||||
try {
|
||||
const mapObj = fsMap._leafletMap;
|
||||
if (mapObj) {
|
||||
mapObj.dragging.disable();
|
||||
mapObj.scrollWheelZoom.disable();
|
||||
setTimeout(() => {
|
||||
mapObj.invalidateSize();
|
||||
const bounds = fsMap._bounds;
|
||||
if (bounds) mapObj.fitBounds(bounds);
|
||||
}, 100);
|
||||
}
|
||||
} catch {}
|
||||
history.pushState({ modal: true }, "");
|
||||
return;
|
||||
}
|
||||
// Fullscreen chart inside modal? Collapse it first.
|
||||
const fsChart = document.querySelector(".chart-full");
|
||||
// Fullscreen chart inside modal? Collapse it, stay in modal.
|
||||
const fsChart = overlay.querySelector(".chart-full");
|
||||
if (fsChart) {
|
||||
fsChart.classList.remove("chart-full");
|
||||
const btn = fsChart.querySelector(".chart-close-btn");
|
||||
|
|
@ -663,23 +674,21 @@ function showModal(html, onSubmit) {
|
|||
}
|
||||
// Nothing fullscreen — close modal
|
||||
window.removeEventListener("popstate", popHandler);
|
||||
closeModal(false);
|
||||
overlay.remove();
|
||||
};
|
||||
window.addEventListener("popstate", popHandler);
|
||||
|
||||
function closeModal(goBack) {
|
||||
overlay.remove();
|
||||
function closeModal() {
|
||||
window.removeEventListener("popstate", popHandler);
|
||||
if (goBack) history.back();
|
||||
// Ensure history isn't empty after modal close (prevents app exit on next back)
|
||||
history.replaceState({ screen: state.screen }, "");
|
||||
overlay.remove();
|
||||
history.back(); // pops {modal: true}, returns to previous screen
|
||||
}
|
||||
|
||||
overlay.querySelector("#modal-back").onclick = () => closeModal(true);
|
||||
overlay.querySelector("#modal-cancel").onclick = () => closeModal(true);
|
||||
overlay.querySelector("#modal-back").onclick = () => closeModal();
|
||||
overlay.querySelector("#modal-cancel").onclick = () => closeModal();
|
||||
overlay.querySelector("#modal-ok").onclick = async () => {
|
||||
const ok = await onSubmit(overlay);
|
||||
if (ok !== false) { closeModal(true); renderScreen(state.screen); }
|
||||
if (ok !== false) { closeModal(); renderScreen(state.screen); }
|
||||
};
|
||||
setTimeout(() => overlay.querySelector("input")?.focus(), 50);
|
||||
}
|
||||
|
|
@ -2182,6 +2191,7 @@ async function showSessionDetail(sessionId) {
|
|||
|
||||
L.Icon.Default.imagePath = "lib/";
|
||||
const map = L.map(mapEl, { attributionControl: false, zoomControl: false, scrollWheelZoom: false, dragging: false, tap: false, touchZoom: true });
|
||||
mapEl._leafletMap = map; // save for popHandler map collapse
|
||||
// Ctrl+wheel zoom on desktop
|
||||
mapEl.addEventListener("wheel", (e) => {
|
||||
if (e.ctrlKey) { e.preventDefault(); map.setZoom(map.getZoom() + (e.deltaY < 0 ? 1 : -1)); }
|
||||
|
|
@ -2225,6 +2235,7 @@ async function showSessionDetail(sessionId) {
|
|||
}
|
||||
|
||||
const allCoords = sortedCoords;
|
||||
mapEl._bounds = L.latLngBounds(sortedCoords).pad(0.1); // save for collapse fitBounds
|
||||
|
||||
// Fullscreen toggle button on map
|
||||
// Fullscreen map toggle
|
||||
|
|
@ -2417,12 +2428,11 @@ async function showSessionDetail(sessionId) {
|
|||
if (exp) exp.onclick = () => GPS.exportGPX(sessionId);
|
||||
|
||||
// Chart fullscreen toggle — tap to expand, tap X or back to collapse
|
||||
// Chart fullscreen in modal — NO pushState. Modal popHandler handles back.
|
||||
document.querySelectorAll(".chart-wrap").forEach(wrap => {
|
||||
wrap.onclick = () => {
|
||||
if (wrap.classList.contains("chart-full")) return;
|
||||
wrap.classList.add("chart-full");
|
||||
// Push history so back gesture collapses chart, not modal
|
||||
history.pushState({ chartFull: true }, "");
|
||||
const closeBtn = document.createElement("div");
|
||||
closeBtn.className = "chart-close-btn";
|
||||
closeBtn.innerHTML = "✕";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue