From 0002c393173cd1c9a710d8b737be0f04621e8b8e Mon Sep 17 00:00:00 2001 From: Ian Foster Date: Sat, 17 Oct 2026 16:02:17 -0700 Subject: [PATCH] Close detach() properly; stop re-running startup code on every disconnect detach() was missing its closing brace, so it swallowed a duplicate copy of the top-level startup code. Every disconnect re-registered the erase, full-screen, fullscreenchange and resize listeners, re-ran the service-worker cleanup and finalizeInterrupted(), and re-read settings. The duplicate first appeared in 82be485 and grew with each later commit. Remove the nested copy (identical to the real top-level code) and keep detach()'s own body, including the updateWakeLock() call it had before. --- web/js/main.js | 81 -------------------------------------------------- 1 file changed, 81 deletions(-) diff --git a/web/js/main.js b/web/js/main.js index aae9a4d..5c88925 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -326,87 +326,6 @@ function detach() { $('connect').hidden = view !== 'live'; $('live').hidden = true; renderConnection('disconnected'); -showKnownRower(); -setupGauge($('spm-gauge')); -$('app-version').textContent = APP_VERSION; - -// ---------- full screen (on Android it also rotates to landscape) ---------- - -$('btn-fullscreen').hidden = !document.fullscreenEnabled; - -async function enterFullscreen() { - try { - await document.documentElement.requestFullscreen({ navigationUI: 'hide' }); - } catch (e) { - showNotice(`Full screen isn't available here: ${e.message}`); - return; - } - try { - // Android Chrome honours this even when the phone's rotation is locked to portrait. - // Desktop browsers refuse it, and full screen alone is fine there. - await screen.orientation?.lock?.('landscape'); - } catch { - // orientation lock unsupported - } -} - -function exitFullscreen() { - if (document.fullscreenElement) document.exitFullscreen().catch(() => {}); -} - -document.addEventListener('fullscreenchange', () => { - const on = !!document.fullscreenElement; - document.body.classList.toggle('immersive', on); - $('fs-exit').hidden = !on; - requestAnimationFrame(() => fitValues(true)); - if (!on) { - try { - screen.orientation?.unlock?.(); - } catch { - // nothing locked - } - } -}); -$('btn-fullscreen').addEventListener('click', enterFullscreen); -window.addEventListener('resize', () => fitValues(true)); -$('fs-exit').addEventListener('click', exitFullscreen); - -// PWA (install + offline) is postponed until development is done; see PLAN.md "Later: PWA". -// A development build briefly registered a service worker; remove it so it can't serve stale files. -navigator.serviceWorker?.getRegistrations().then((regs) => regs.forEach((r) => r.unregister())).catch(() => {}); -globalThis.caches?.keys().then((keys) => keys.filter((k) => k.startsWith('rower-')).forEach((k) => caches.delete(k))).catch(() => {}); - -navigator.bluetooth?.addEventListener?.('availabilitychanged', (e) => { - if (!e.value) showNotice('Bluetooth is turned off. Turn it on to connect to the rower.'); -}); - -// two-step erase, no confirm() dialog -let eraseArmedUntil = 0; -$('btn-erase').addEventListener('click', async (e) => { - const btn = e.currentTarget; - if (performance.now() > eraseArmedUntil) { - eraseArmedUntil = performance.now() + 4000; - btn.textContent = 'Tap again to erase everything'; - setTimeout(() => { if (performance.now() > eraseArmedUntil) btn.textContent = 'Erase all data'; }, 4100); - return; - } - await eraseAllData(); - location.reload(); -}); - -finalizeInterrupted().catch((e) => console.warn('Could not tidy interrupted sessions:', e.message)); -try { - $('save-non-ble').checked = localStorage.getItem('rower.saveNonBle') === '1'; -} catch { - // storage unavailable: default off -} -$('save-non-ble').addEventListener('change', (e) => { - try { - localStorage.setItem('rower.saveNonBle', e.target.checked ? '1' : '0'); - } catch { - // not fatal - } -}); updateWakeLock(); }