Fix the bottom bar's auto-hide never firing on mobile
Reported right after shipping: the bar never actually collapsed on mobile. Classic mobile-web trap: mouseenter/mouseleave on the bar tracked "is the pointer resting on the controls" so hovering could hold it open, but a touch pointer has no real hover - tapping the bar fires a synthetic pointerenter with no pointerleave ever coming after it (no cursor left to lift away). The very first tap anywhere on the bar latched isPointerOverBottomBar permanently true, and the bar could never auto-hide again for the rest of that session. Fixed by checking event.pointerType === 'mouse' in both handlers - touch/pen never even attempt the "hovering holds it open" path, and fall back to the same plain activity-based wake-up (pointerdown/ pointermove on the whole player) every device already has. Verified: full typecheck/build. Reproduced the exact failure directly - dispatched a synthetic touch pointerenter with no matching pointerleave (the real-world case) against the fixed logic and confirmed it still transitions to .minimal on schedule, where the mouseenter-based version would have latched open. A real touchscreen's own event behavior isn't independently reproducible here, so this is the strongest confirmation available without one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
d3e2811bd0
commit
e61b302f6e
2 changed files with 37 additions and 6 deletions
|
|
@ -525,20 +525,30 @@ function renderVideoPlayer(root: HTMLElement, partyId: string, party: PartyPubli
|
||||||
scheduleControlsHide();
|
scheduleControlsHide();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onBottomBarPointerEnter() {
|
// Mouse only (pointerType check) - not just an optimization. A touch
|
||||||
|
// pointer has no real "hovering": tapping the bar fires a pointerenter
|
||||||
|
// with no matching pointerleave ever coming (there's no cursor left to
|
||||||
|
// lift away), which used to latch isPointerOverBottomBar permanently
|
||||||
|
// true after the very first tap on the bar - the whole thing could
|
||||||
|
// never auto-hide again from that point on, on any touch device.
|
||||||
|
// Touch's own "wake the bar up" path is already covered below by the
|
||||||
|
// plain pointerdown/pointermove listeners on the whole player.
|
||||||
|
function onBottomBarPointerEnter(e: PointerEvent) {
|
||||||
|
if (e.pointerType !== 'mouse') return;
|
||||||
isPointerOverBottomBar = true;
|
isPointerOverBottomBar = true;
|
||||||
showControls();
|
showControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onBottomBarPointerLeave() {
|
function onBottomBarPointerLeave(e: PointerEvent) {
|
||||||
|
if (e.pointerType !== 'mouse') return;
|
||||||
isPointerOverBottomBar = false;
|
isPointerOverBottomBar = false;
|
||||||
scheduleControlsHide();
|
scheduleControlsHide();
|
||||||
}
|
}
|
||||||
|
|
||||||
playerWrap.addEventListener('pointermove', showControls);
|
playerWrap.addEventListener('pointermove', showControls);
|
||||||
playerWrap.addEventListener('pointerdown', showControls);
|
playerWrap.addEventListener('pointerdown', showControls);
|
||||||
bottomBar.addEventListener('mouseenter', onBottomBarPointerEnter);
|
bottomBar.addEventListener('pointerenter', onBottomBarPointerEnter);
|
||||||
bottomBar.addEventListener('mouseleave', onBottomBarPointerLeave);
|
bottomBar.addEventListener('pointerleave', onBottomBarPointerLeave);
|
||||||
scheduleControlsHide();
|
scheduleControlsHide();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1108,8 +1118,8 @@ function renderVideoPlayer(root: HTMLElement, partyId: string, party: PartyPubli
|
||||||
document.removeEventListener('fullscreenchange', onFullscreenChange);
|
document.removeEventListener('fullscreenchange', onFullscreenChange);
|
||||||
playerWrap.removeEventListener('pointermove', showControls);
|
playerWrap.removeEventListener('pointermove', showControls);
|
||||||
playerWrap.removeEventListener('pointerdown', showControls);
|
playerWrap.removeEventListener('pointerdown', showControls);
|
||||||
bottomBar.removeEventListener('mouseenter', onBottomBarPointerEnter);
|
bottomBar.removeEventListener('pointerenter', onBottomBarPointerEnter);
|
||||||
bottomBar.removeEventListener('mouseleave', onBottomBarPointerLeave);
|
bottomBar.removeEventListener('pointerleave', onBottomBarPointerLeave);
|
||||||
unsubscribeCastAvailability();
|
unsubscribeCastAvailability();
|
||||||
unsubscribeCastSessionEnded();
|
unsubscribeCastSessionEnded();
|
||||||
// Deliberately NOT stopping an active cast here - leaving this page
|
// Deliberately NOT stopping an active cast here - leaving this page
|
||||||
|
|
|
||||||
|
|
@ -2769,6 +2769,27 @@ inactivity, does hovering genuinely prevent it) wasn't driven through
|
||||||
real wall-clock waits here - the CSS transition and class-toggling
|
real wall-clock waits here - the CSS transition and class-toggling
|
||||||
logic were checked directly instead.
|
logic were checked directly instead.
|
||||||
|
|
||||||
|
**Reported broken on mobile, right after shipping**: it never actually
|
||||||
|
auto-hid there. Root cause - a classic mobile-web trap, not a typo:
|
||||||
|
`mouseenter`/`mouseleave` on the bar tracked "is the pointer resting on
|
||||||
|
the controls" so hovering could hold them open, but a touch pointer has
|
||||||
|
no real hover - tapping the bar fires a synthetic `pointerenter` with
|
||||||
|
no `pointerleave` ever coming after it (there's no cursor left to lift
|
||||||
|
away), so the very first tap anywhere on the bar latched
|
||||||
|
`isPointerOverBottomBar` permanently `true` and the bar could never
|
||||||
|
auto-hide again for the rest of that session. Fixed by checking
|
||||||
|
`event.pointerType === 'mouse'` in both handlers - touch/pen never even
|
||||||
|
attempt the "hovering holds it open" path at all, and correctly fall
|
||||||
|
back to the same plain activity-based wake-up (`pointerdown`/
|
||||||
|
`pointermove` on the whole player) every device already gets. Confirmed
|
||||||
|
by reproducing the exact failure directly - dispatched a synthetic
|
||||||
|
touch `pointerenter` with no matching `pointerleave` (the real-world
|
||||||
|
case) against the fixed logic and watched it still transition to
|
||||||
|
`.minimal` on schedule, where the original `mouseenter`-based version
|
||||||
|
would have latched open. A real touchscreen's own event behavior still
|
||||||
|
isn't independently reproducible here, so this is the strongest
|
||||||
|
confirmation available without one.
|
||||||
|
|
||||||
## Known limitations
|
## Known limitations
|
||||||
|
|
||||||
- Playback always goes through `hls.js` against whatever Emby's
|
- Playback always goes through `hls.js` against whatever Emby's
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue