Rewrite fullscreen as history entries: each open=push, each back=pop

Every fullscreen (map/chart) inside a modal now pushes its own history
entry via overlay._pushFullscreen(). Back pops it, popHandler collapses.
No more re-pushing, no flag juggling between handlers.

History stack trace:
  click track → push {modal}
  expand map  → push {modalFs}
  back        → pop {modalFs}, collapse map, stay in modal
  back        → pop {modal}, close modal, return to track list
  back        → pop {screen:gps}, return to previous screen

Map toggle: expand pushes via _pushFullscreen, collapse calls history.back()
Chart toggle: same pattern — push on expand, back on X button
closeModal: pops all entries this modal owns (_historyDepth counter)

Fixes: back from fullscreen map/chart no longer closes app,
expand button works correctly after back-collapse
This commit is contained in:
4aiman 2026-05-27 20:41:33 +03:00
commit cb5a916100

View file

@ -808,53 +808,60 @@ function showModal(html, onSubmit) {
</div>`;
document.body.appendChild(overlay);
// Simple history stack: each open = pushState, each back = popState pops it.
// Modal = 1 entry. Fullscreen map/chart inside modal = 1 more entry each.
// Back always pops the top entry. No re-pushing, no flags.
let _historyDepth = 1; // how many entries this modal owns
history.pushState({ modal: true }, "");
const popHandler = () => {
// Fullscreen map inside modal? Collapse it, stay in modal.
_historyDepth--;
// Check what's on top and collapse it
const fsMap = overlay.querySelector(".sd-map-full");
if (fsMap) {
fsMap.classList.remove("sd-map-full");
// Update expand button icon
const expandBtn = fsMap.querySelector(".sd-map-expand-btn");
if (expandBtn) expandBtn.innerHTML = "&#x26F6;";
// Reset map controls and size
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);
setTimeout(() => { mapObj.invalidateSize(); if (fsMap._bounds) mapObj.fitBounds(fsMap._bounds); }, 100);
}
} catch {}
// Stay in modal — re-push entry that back consumed
history.pushState({ modal: true }, "");
return;
return; // stay in modal, depth already decremented
}
// 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");
if (btn) btn.remove();
history.pushState({ modal: true }, "");
return;
return; // stay in modal
}
// Nothing fullscreen — close modal. Set flag so global handler skips this event.
// Nothing fullscreen — close modal
_modalPopHandled = true;
window.removeEventListener("popstate", popHandler);
overlay.remove();
};
window.addEventListener("popstate", popHandler);
// Called when fullscreen opens inside this modal — push 1 entry
overlay._pushFullscreen = function() {
_historyDepth++;
history.pushState({ modalFs: true }, "");
};
function closeModal() {
_modalPopHandled = true; // prevent global handler from also processing the back
window.removeEventListener("popstate", popHandler);
overlay.remove();
history.back(); // pops {modal: true}, returns to previous screen
// Pop all entries this modal owns
if (_historyDepth > 0) {
for (let i = 0; i < _historyDepth; i++) history.back();
}
}
overlay.querySelector("#modal-back").onclick = () => closeModal();
@ -2606,19 +2613,18 @@ async function showSessionDetail(sessionId) {
// Fullscreen map toggle
function toggleMapFs() {
if (mapEl.classList.contains("sd-map-full")) {
// Collapse
mapEl.classList.remove("sd-map-full");
fsDiv.innerHTML = "&#x26F6;";
map.dragging.disable();
map.scrollWheelZoom.disable();
setTimeout(() => { map.invalidateSize(); map.fitBounds(L.latLngBounds(allCoords).pad(0.05)); }, 100);
// Collapse via history.back() — popHandler will handle the CSS
history.back();
} else {
// Expand
// Expand — push to history stack
mapEl.classList.add("sd-map-full");
fsDiv.innerHTML = "&#x2715;";
map.dragging.enable();
map.scrollWheelZoom.enable();
setTimeout(() => { map.invalidateSize(); map.fitBounds(L.latLngBounds(allCoords).pad(0.05)); }, 100);
// Push fullscreen entry — back will pop it
const modal = mapEl.closest(".modal-overlay");
if (modal && modal._pushFullscreen) modal._pushFullscreen();
}
}
@ -2786,18 +2792,19 @@ 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.
// Chart fullscreen in modal — pushState so back collapses it
document.querySelectorAll(".chart-wrap").forEach(wrap => {
wrap.onclick = () => {
if (wrap.classList.contains("chart-full")) return;
wrap.classList.add("chart-full");
const modal = wrap.closest(".modal-overlay");
if (modal && modal._pushFullscreen) modal._pushFullscreen();
const closeBtn = document.createElement("div");
closeBtn.className = "chart-close-btn";
closeBtn.innerHTML = "&#x2715;";
closeBtn.onclick = (e) => {
e.stopPropagation();
wrap.classList.remove("chart-full");
closeBtn.remove();
history.back(); // popHandler will collapse
};
wrap.appendChild(closeBtn);
};