Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,35 @@
}, 3000);
}

// Render-freeze self-heal. iOS WebKit (iOS 26 / WKWebView — e.g. Edge & Chrome
// on iOS) intermittently loads the page into a frozen render process: the DOM is
// complete and the bundle runs, but the whole content area stays white and ONLY
// a reload (not scroll/touch/rotate) clears it. The Paint Timing API records a
// 'first-contentful-paint' entry only when WebKit actually paints content; if
// it's still absent ~3s in, the page froze — reload once (guarded) to recover.
// This complements the boot-watchdog above (which covers a bundle that never
// runs); here the bundle runs fine but the render is dead until a fresh load.
var RKEY = 'webmap-render-reload';
setTimeout(function () {
var painted;
try {
painted = performance.getEntriesByType('paint').some(function (p) {
return p.name === 'first-contentful-paint';
});
} catch (e) { painted = true; } // no Paint Timing API → never risk a reload loop
if (painted) { try { sessionStorage.removeItem(RKEY); } catch (e) {} return; }
var rAlready;
try { rAlready = sessionStorage.getItem(RKEY) === '1'; } catch (e) { rAlready = false; }
if (!rAlready) {
// Only reload if the guard actually persisted — if sessionStorage is
// unavailable (private mode, storage disabled), reloading would loop
// forever. Mirrors the boot-watchdog above. (Key: 'webmap-render-reload'.)
var rPersisted = false;
try { sessionStorage.setItem(RKEY, '1'); rPersisted = true; } catch (e) { rPersisted = false; }
if (rPersisted) { location.reload(); }
}
}, 3000);

// Blank-state probe: if the map still hasn't rendered after 5s and no error
// was captured, dump enough state to see WHY it's blank.
setTimeout(function () {
Expand Down
22 changes: 5 additions & 17 deletions src/consent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,16 @@ export function showConsentModal(): Promise<boolean> {
`;

overlay.appendChild(panel);
document.body.appendChild(overlay);

function cleanup(accepted: boolean): void {
overlay.remove();
if (accepted) recordConsent();
resolve(accepted);
}

// Wire listeners on the still-detached panel so they're ready before mount
// (querySelector works on a detached element; getElementById would not).
panel.querySelector('#consent-accept')!.addEventListener('click', () => cleanup(true));
panel.querySelector('#consent-decline')!.addEventListener('click', () => cleanup(false));
document.getElementById('consent-accept')!.addEventListener('click', () => cleanup(true));
document.getElementById('consent-decline')!.addEventListener('click', () => cleanup(false));
overlay.addEventListener('click', (e) => {
if (e.target === overlay) cleanup(false);
});
Expand All @@ -88,18 +87,7 @@ export function showConsentModal(): Promise<boolean> {
};
document.addEventListener('keydown', onKey);

// Mount on the next frame, NOT synchronously during the page's first paint.
// iOS WebKit (including Edge/Chrome on iOS, which run on WKWebView) intermittently
// fails to composite a fixed-position layer injected during that first paint,
// leaving a white screen with the modal present in the DOM but unpainted — the
// blank-on-cold-start that "prevents usage" until a manual refresh (confirmed via
// on-screen diagnostic: bundleRan=true, hasConsent=false, nothing visible).
// Mounting after first paint and flushing layout makes the overlay paint reliably.
requestAnimationFrame(() => {
document.body.appendChild(overlay);
void overlay.getBoundingClientRect(); // flush layout → ensure a paint
// Focus the accept button for keyboard accessibility (post-mount).
(panel.querySelector('#consent-accept') as HTMLElement | null)?.focus();
});
// Focus the accept button for keyboard accessibility
document.getElementById('consent-accept')!.focus();
});
}
Loading