diff --git a/mobile/main.js b/mobile/main.js index 08b9dc39c..543e1d81d 100644 --- a/mobile/main.js +++ b/mobile/main.js @@ -159,13 +159,16 @@ function applyTab(tab) { else el.hidden = id !== tab && !(two && tab === 'capture' && id === prev); } tabbar.select(tab); - if (tab === 'capture') { - globe?.pause(); - capture?.enter(); - } else { - if (prev === 'capture') capture?.leave(); - updateGlobeActivity(); + // A camera or sensor failure must never trap someone in Capture. + try { + if (tab === 'capture') { + globe?.pause(); + capture?.enter(); + } else if (prev === 'capture') capture?.leave(); + } catch (err) { + console.warn('capture', err); } + if (tab !== 'capture') updateGlobeActivity(); if (tab === 'you') you?.render?.(); if (tab === 'rooms') threads?.refresh?.(); if (tab === 'live' && two) chrome?.renderLeadView(); @@ -180,16 +183,14 @@ function selectTab(tab) { if (player?.current) player.close(); if (world?.current && !isTwoPane()) world.close(); if (narrative?.active) narrative.stop(); - const vt = document.startViewTransition && !reducedMotion(); - if (vt) document.startViewTransition(() => applyTab(tab)); - else { - applyTab(tab); - const v = views[tab]; - if (v && !v.hidden) { - v.classList.remove('view-enter'); - void v.offsetWidth; - v.classList.add('view-enter'); - } + // Switch synchronously. A View Transition waits on frames the globe and + // the camera can hold up, which left taps on the tab bar unanswered. + applyTab(tab); + const v = views[tab]; + if (v && !v.hidden && !reducedMotion()) { + v.classList.remove('view-enter'); + void v.offsetWidth; + v.classList.add('view-enter'); } } @@ -1298,7 +1299,7 @@ function buildDirector(g) { // Keyboard: Escape closes the topmost thing. document.addEventListener('keydown', (e) => { - if (e.key !== 'Escape') return; + if (e.key !== 'Escape' || e.defaultPrevented) return; if (document.querySelector('.sheet-layer.is-modal:not([hidden])')) return; if (dossier?.isOpen) return dossier.close(); if (player?.current) return; diff --git a/mobile/ui/globeChrome.js b/mobile/ui/globeChrome.js index 38acfaa97..dd0019401 100644 --- a/mobile/ui/globeChrome.js +++ b/mobile/ui/globeChrome.js @@ -120,7 +120,7 @@ export function createGlobeChrome({ const top = h(`
Counts are live from each feed; dotted means stale.
`; + } + function renderGrid(fam) { + if (fam === 'all') return renderAllGrid(); const st = familyState()[fam]; const f = FAMILIES.find((x) => x.id === fam); grid.sheet.dataset.family = fam; @@ -427,6 +450,15 @@ export function createGlobeChrome({ } grid.root.addEventListener('click', (e) => { + const look = e.target.closest('[data-lens3]'); + if (look) { + haptic('tick'); + setLens(look.dataset.lens3); + $$('[data-lens3]', grid.root).forEach((b) => + b.classList.toggle('on', b === look), + ); + return; + } const soon = e.target.closest('[data-soon]'); if (soon) { toast( @@ -506,8 +538,8 @@ export function createGlobeChrome({ top.addEventListener('click', (e) => { const act = e.target.closest('[data-act]')?.dataset.act; if (act === 'lens') { - lensPop.hidden = !lensPop.hidden; haptic('tick'); + openGrid('all'); } else if (act === 'home') { haptic('tick'); hidePick(); diff --git a/mobile/ui/sheet.js b/mobile/ui/sheet.js index 9da845cc6..eee686756 100644 --- a/mobile/ui/sheet.js +++ b/mobile/ui/sheet.js @@ -19,6 +19,16 @@ const DEFAULT_DETENTS = { * heights?: Record