clone

a desktop tv and program guide for over-the-air broadcasts: what's on air, what your tuner gets, live tv, recordings

Taps on a phone's TV: the picture shows above the controls, and a tap off a button puts them away

FelineStateMachine committed f6cf447
On a phone held sideways the controls covered about 70% of the picture, so a
tap aimed at the picture landed on the banner: it put the controls away but
left the banner up with no timer. Now, on a short touch screen, the controls
draw tighter (what's next is left to the guide) and cover about half. A tap
anywhere off a button puts the banner away with them, as a phone's video
players do. The tap is listened for on the picture and the banner themselves,
since an iPhone sends a tap's click only to what listens for one.

A finger's stand-in mouse events no longer act a second time. Two quick taps
in a list were also a double click, playing again what the second tap had
already played. A tap's move was also a pointer brought to the screen.

The first tap unmutes a picture that started muted on the tap's click, not
when the finger lands, which browsers don't count as the user's yet.
commit
f6cf4475353c7a4ce39af19307372557698e221a
parent
114b5948464065e2b8c9804bfd3d2dd85de51dba
author
FelineStateMachine <226808055+FelineStateMachine@users.noreply.github.com>
kind
fix risk score 2.1 of 3, Jev's estimate
4 files changed, +44 -13
MREADME.md5+++--
Mfrontend/dist/js/player.js6+++--
Mfrontend/dist/js/touch.js35++++++++++++++++++++++--------
Mfrontend/dist/style.css11+++++++++
M README.md +3 -2
M frontend/dist/js/player.js +4 -2
M frontend/dist/js/touch.js +26 -9
@@ -12,14 +12,24 @@
1212 // shows its actions (record, favorite, hide). In Recordings and Podcasts a1313 // tap on what's picked plays it. A phone held upright is asked to turn1414 // sideways (style.css).15-const touch = { x: 0, y: 0, on: false, held: 0, longDone: false };15+const touch = { x: 0, y: 0, on: false, held: 0, longDone: false, at: -Infinity };1616 const TOUCH_SWIPE = 48; // CSS pixels a finger travels for a swipe1717 const TOUCH_HOLD_MS = 550; // a press this long is a hold18+const TOUCH_MOUSE_MS = 1000; // a finger's stand-in mouse events follow it within this1819 1920 const touching = (e) => platform === 'touch' && e.pointerType !== 'mouse';2021 22+// A finger also sends a mouse's events after it, for pages made for one.23+// Here they'd act twice: its move is a pointer brought to the screen, which24+// brings up the dock and the controls (wireMouse), and two quick taps are a25+// double click, which plays what the second tap already played. What a26+// finger does is handled here, from its pointer events.27+const fromFinger = () => performance.now() - touch.at < TOUCH_MOUSE_MS;28+2129 function wireTouch() {30+ const lastFinger = (e) => { if (e.pointerType !== 'mouse') touch.at = performance.now(); };2231 document.addEventListener('pointerdown', (e) => {32+ lastFinger(e);2333 if (!touching(e)) return;2434 Object.assign(touch, { x: e.clientX, y: e.clientY, on: true, longDone: false });2535 clearTimeout(touch.held);
@@ -31,6 +41,7 @@ function wireTouch() {
3141 }, { capture: true, passive: true });3242 document.addEventListener('pointercancel', () => { touch.on = false; clearTimeout(touch.held); }, true);3343 document.addEventListener('pointerup', (e) => {44+ lastFinger(e);3445 if (!touch.on || !touching(e)) return;3546 touch.on = false;3647 clearTimeout(touch.held);
@@ -39,6 +50,9 @@ function wireTouch() {
3950 if (Math.max(Math.abs(dx), Math.abs(dy)) < TOUCH_SWIPE) return;4051 swipe(e.target, Math.abs(dx) > Math.abs(dy) ? (dx > 0 ? 'right' : 'left') : (dy > 0 ? 'down' : 'up'));4152 }, { capture: true, passive: true });53+ for (const type of ['mousemove', 'dblclick']) {54+ document.addEventListener(type, (e) => { if (fromFinger()) e.stopImmediatePropagation(); }, true);55+ }4256 // A hold's lift isn't also a tap.4357 document.addEventListener('click', (e) => {4458 if (touch.longDone) {
@@ -47,15 +61,18 @@ function wireTouch() {
4761 e.preventDefault();4862 }4963 }, true);50- // On TV a tap that isn't on a control, on the picture or the banner over51- // it, brings up the controls or puts them away: seen first (capture), so52- // a tap on the picture elsewhere, which goes back to TV (wireDock), isn't53- // one.54- document.addEventListener('click', (e) => {64+ // On TV a tap on the picture, or on the banner over it, brings up the65+ // controls; with them up, a tap anywhere but on a control puts them66+ // away, the banner too, as a phone's video players do. Listened for on67+ // the picture and the banner themselves, as an iPhone sends a tap's68+ // click only to what listens for one, and first (capture), so a tap on69+ // the picture elsewhere, which goes back to TV (wireDock), isn't one.70+ const tapTV = (e) => {5571 if (platform !== 'touch' || state.view !== 'tv' || document.body.classList.contains('booting')) return;56- if (e.target.closest('button, input, a, .ob, .tl, #dock, [data-act]')) return;57- if (osd.open) closeOSD(); else openOSD(null);58- }, true);72+ if (e.target.closest('button, input, a, .tl, [data-act]')) return;73+ if (osd.open) hideBanner(); else openOSD(null);74+ };75+ for (const el of [stage, $('#banner')]) el.addEventListener('click', tapTV, true);5976 }6077 6178 // swipe acts on a swipe over el: on TV, the arrows; in the guide, the
M frontend/dist/style.css +11 -0