Compare commits
No commits in common. "d10a35e541a1501bd2886ff847a17a2e34455831" and "2772cc70d6ea3f739462e9e279c00e6e9133a7b5" have entirely different histories.
d10a35e541
...
2772cc70d6
5 changed files with 3 additions and 129 deletions
|
|
@ -358,9 +358,6 @@ Then put it behind your existing reverse proxy like everything else
|
||||||
movie-poster tiles — circular artist tiles, square album tiles,
|
movie-poster tiles — circular artist tiles, square album tiles,
|
||||||
a real hero header, and a mobile "now playing" full-screen player
|
a real hero header, and a mobile "now playing" full-screen player
|
||||||
— see docs/architecture.md
|
— see docs/architecture.md
|
||||||
- [x] Mobile menu's bottom sheet is now swipe-to-close (a real drag
|
|
||||||
handle, not just a decorative grabber) instead of fighting the
|
|
||||||
browser's own pull-to-refresh gesture — see docs/architecture.md
|
|
||||||
- [ ] Try a full party end-to-end with two real accounts (still the main gap)
|
- [ ] Try a full party end-to-end with two real accounts (still the main gap)
|
||||||
- [ ] Try casting to a real Chromecast (only the SDK's own bootstrap is
|
- [ ] Try casting to a real Chromecast (only the SDK's own bootstrap is
|
||||||
confirmed here — no real device/session)
|
confirmed here — no real device/session)
|
||||||
|
|
|
||||||
|
|
@ -121,12 +121,6 @@
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transition: opacity .2s;
|
transition: opacity .2s;
|
||||||
/* A touch starting here has no scrollable content of its own to
|
|
||||||
"contain" (overscroll-behavior wouldn't apply) - this is what
|
|
||||||
stops the browser reading a drag over the dimmed backdrop as a
|
|
||||||
page-level gesture (pull-to-refresh) instead of just dismissing
|
|
||||||
the sheet via the existing tap-to-close below. */
|
|
||||||
touch-action: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobileNavScrim.open { opacity: 1; pointer-events: auto; }
|
.mobileNavScrim.open { opacity: 1; pointer-events: auto; }
|
||||||
|
|
@ -139,12 +133,6 @@
|
||||||
z-index: 21;
|
z-index: 21;
|
||||||
max-height: 75vh;
|
max-height: 75vh;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
/* Stops a drag that hits the top of the sheet's OWN scroll (its
|
|
||||||
library grid can be taller than 75vh) from chaining past this
|
|
||||||
element to the page underneath and surfacing as pull-to-refresh -
|
|
||||||
complements touch-action:none on the dedicated grabber handle
|
|
||||||
below, which instead covers a drag that never scrolls anything. */
|
|
||||||
overscroll-behavior: contain;
|
|
||||||
background: var(--bg-header);
|
background: var(--bg-header);
|
||||||
border-radius: 16px 16px 0 0;
|
border-radius: 16px 16px 0 0;
|
||||||
padding: 8px 16px calc(20px + env(safe-area-inset-bottom, 0px));
|
padding: 8px 16px calc(20px + env(safe-area-inset-bottom, 0px));
|
||||||
|
|
@ -155,27 +143,12 @@
|
||||||
|
|
||||||
.mobileNavSheet.open { transform: translateY(0); }
|
.mobileNavSheet.open { transform: translateY(0); }
|
||||||
|
|
||||||
/* Dragging (see Shell.ts) suspends the transition above for 1:1
|
|
||||||
tracking under the finger, then this class restores it for the
|
|
||||||
snap-back/close animation - same "instant while dragging, animated
|
|
||||||
on release" split views/player/Player.css's own seek bar uses. */
|
|
||||||
.mobileNavSheet.dragging { transition: none; }
|
|
||||||
|
|
||||||
/* Bigger drag/touch target than the pill it contains (36x4px is far
|
|
||||||
below a real touch-target minimum) - see Shell.html/Shell.ts. */
|
|
||||||
.sheetGrabberHandle {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 14px 0 10px;
|
|
||||||
touch-action: none;
|
|
||||||
cursor: grab;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sheetGrabber {
|
.sheetGrabber {
|
||||||
width: 36px;
|
width: 36px;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
border-radius: 99px;
|
border-radius: 99px;
|
||||||
background: var(--muted);
|
background: var(--muted);
|
||||||
|
margin: 10px auto 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Accueil and the (conditional) Listes de lecture row both use this -
|
/* Accueil and the (conditional) Listes de lecture row both use this -
|
||||||
|
|
|
||||||
|
|
@ -32,14 +32,7 @@
|
||||||
|
|
||||||
<div class="mobileNavScrim" id="mobileNavScrim"></div>
|
<div class="mobileNavScrim" id="mobileNavScrim"></div>
|
||||||
<div class="mobileNavSheet" id="mobileNavSheet">
|
<div class="mobileNavSheet" id="mobileNavSheet">
|
||||||
<!-- Bigger invisible drag target than the visible pill itself (see
|
<div class="sheetGrabber"></div>
|
||||||
.sheetGrabberHandle) - touch-action:none so this app's own
|
|
||||||
pointer handlers (Shell.ts) drive the drag instead of the
|
|
||||||
browser's default touch handling, which otherwise reads a
|
|
||||||
downward drag here as pull-to-refresh (the sheet sits right at
|
|
||||||
the top of the page's own scroll position, same conflict a
|
|
||||||
plain scrim tap-only close used to leave unsolved). -->
|
|
||||||
<div class="sheetGrabberHandle" id="sheetGrabberHandle"><div class="sheetGrabber"></div></div>
|
|
||||||
<a href="#/home" class="navItem sheetPrimary"><span class="navIcon" data-icon="home"></span>Accueil</a>
|
<a href="#/home" class="navItem sheetPrimary"><span class="navIcon" data-icon="home"></span>Accueil</a>
|
||||||
<div class="navSectionLabel">Bibliothèques</div>
|
<div class="navSectionLabel">Bibliothèques</div>
|
||||||
<div class="sheetGrid" id="mobileLibGrid"></div>
|
<div class="sheetGrid" id="mobileLibGrid"></div>
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ export async function renderShell(appEl: HTMLElement): Promise<HTMLElement> {
|
||||||
|
|
||||||
// ---- Mobile bottom sheet - see Shell.css's "Mobile navigation". ----
|
// ---- Mobile bottom sheet - see Shell.css's "Mobile navigation". ----
|
||||||
const navTrigger = appEl.querySelector('#mobileNavTrigger') as HTMLButtonElement;
|
const navTrigger = appEl.querySelector('#mobileNavTrigger') as HTMLButtonElement;
|
||||||
const navSheet = appEl.querySelector('#mobileNavSheet') as HTMLElement;
|
const navSheet = appEl.querySelector('#mobileNavSheet')!;
|
||||||
const navScrim = appEl.querySelector('#mobileNavScrim')!;
|
const navScrim = appEl.querySelector('#mobileNavScrim')!;
|
||||||
const closeMobileNav = () => {
|
const closeMobileNav = () => {
|
||||||
navSheet.classList.remove('open');
|
navSheet.classList.remove('open');
|
||||||
|
|
@ -127,39 +127,5 @@ export async function renderShell(appEl: HTMLElement): Promise<HTMLElement> {
|
||||||
});
|
});
|
||||||
window.addEventListener('hashchange', closeMobileNav);
|
window.addEventListener('hashchange', closeMobileNav);
|
||||||
|
|
||||||
// Drag-to-dismiss on the grabber handle only - not the whole sheet,
|
|
||||||
// which would fight the library grid's own scroll/tap targets (see
|
|
||||||
// docs/architecture.md's "Mobile navigation, take two"). Reported
|
|
||||||
// directly: without this, a scrim tap was the ONLY way to close the
|
|
||||||
// sheet, and the instinctive swipe-it-down gesture instead fell
|
|
||||||
// through to the browser's own pull-to-refresh (see Shell.css's
|
|
||||||
// touch-action/overscroll-behavior on the scrim/sheet themselves -
|
|
||||||
// this is the other half, giving that swipe something to actually do).
|
|
||||||
const CLOSE_DRAG_THRESHOLD_PX = 90;
|
|
||||||
const grabberHandle = appEl.querySelector('#sheetGrabberHandle') as HTMLElement;
|
|
||||||
let dragStartY: number | null = null;
|
|
||||||
let dragDeltaY = 0;
|
|
||||||
|
|
||||||
grabberHandle.addEventListener('pointerdown', (e) => {
|
|
||||||
dragStartY = e.clientY;
|
|
||||||
dragDeltaY = 0;
|
|
||||||
grabberHandle.setPointerCapture(e.pointerId);
|
|
||||||
navSheet.classList.add('dragging');
|
|
||||||
});
|
|
||||||
grabberHandle.addEventListener('pointermove', (e) => {
|
|
||||||
if (dragStartY === null) return;
|
|
||||||
dragDeltaY = Math.max(0, e.clientY - dragStartY); // only follows a downward drag - closing is the only thing this gesture does
|
|
||||||
navSheet.style.transform = `translateY(${dragDeltaY}px)`;
|
|
||||||
});
|
|
||||||
function endDrag() {
|
|
||||||
if (dragStartY === null) return;
|
|
||||||
dragStartY = null;
|
|
||||||
navSheet.classList.remove('dragging');
|
|
||||||
navSheet.style.transform = ''; // hands back to the .open class's own rule - either translateY(0) (snap back) or the closeMobileNav() call below immediately overrides it anyway
|
|
||||||
if (dragDeltaY > CLOSE_DRAG_THRESHOLD_PX) closeMobileNav();
|
|
||||||
}
|
|
||||||
grabberHandle.addEventListener('pointerup', endDrag);
|
|
||||||
grabberHandle.addEventListener('pointercancel', endDrag);
|
|
||||||
|
|
||||||
return appEl.querySelector('#mainContent') as HTMLElement;
|
return appEl.querySelector('#mainContent') as HTMLElement;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3775,61 +3775,6 @@ genuinely narrow viewport. **Not verified**: any of this against a
|
||||||
real Emby server's actual artist/album images and names, or on a real
|
real Emby server's actual artist/album images and names, or on a real
|
||||||
phone.
|
phone.
|
||||||
|
|
||||||
## Mobile navigation, take two: swipe-to-close was fighting pull-to-refresh
|
|
||||||
|
|
||||||
Reported directly, against a real phone: the bottom sheet (see "Mobile
|
|
||||||
navigation" above) had exactly one way to close it - tap the dimmed
|
|
||||||
scrim - since the grabber was purely decorative. Swiping the sheet
|
|
||||||
down, the instinctive gesture every real bottom sheet supports, fell
|
|
||||||
through to the browser's own default touch handling instead: with
|
|
||||||
nothing telling it otherwise, a downward drag starting on the scrim or
|
|
||||||
the sheet reads to the browser as "the user wants to pull the page
|
|
||||||
down," which on a phone is the built-in pull-to-refresh gesture - not
|
|
||||||
a bug in this app's own logic, but a real gap in it (nothing was
|
|
||||||
`preventDefault`-ing or claiming that gesture for the sheet instead).
|
|
||||||
|
|
||||||
**Two independent parts, not one fix**: containing the browser's own
|
|
||||||
gesture, and giving the swipe something to actually do.
|
|
||||||
|
|
||||||
- `touch-action: none` on `.mobileNavScrim` and the new
|
|
||||||
`.sheetGrabberHandle` - tells the browser not to apply *any* of its
|
|
||||||
own default touch behavior (panning, pull-to-refresh) to a touch
|
|
||||||
starting on either, since this app's own pointer handlers own that
|
|
||||||
gesture now. `overscroll-behavior: contain` on `.mobileNavSheet`
|
|
||||||
itself covers the other half of the same problem: its own library
|
|
||||||
grid can be taller than the sheet's `75vh` cap, so a drag that hits
|
|
||||||
*that* scroll boundary needs to stop there too, not chain past the
|
|
||||||
sheet to the page underneath.
|
|
||||||
- **The grabber is now a real drag handle**, not just a visual hint.
|
|
||||||
`#sheetGrabberHandle` wraps the small visible pill in a much bigger
|
|
||||||
invisible touch target (a real touch target, not a 36x4px sliver)
|
|
||||||
and drives the sheet with the same pointerdown/move/up(/cancel)
|
|
||||||
pattern `views/player/Player.css`'s own seek bar already uses:
|
|
||||||
1:1 tracking under the finger while dragging (`.dragging` suspends
|
|
||||||
the CSS transition for this), then either snapping back open (a
|
|
||||||
drag under 90px) or calling the exact same `closeMobileNav()` the
|
|
||||||
scrim tap already used (past it) - one close path, two ways to
|
|
||||||
reach it, not a second parallel implementation. Deliberately scoped
|
|
||||||
to the grabber alone, not the whole sheet: the sheet's own content
|
|
||||||
(the library grid, the logout row) needs normal taps and its own
|
|
||||||
scroll to keep working, which a whole-sheet drag-to-close would
|
|
||||||
fight.
|
|
||||||
|
|
||||||
Verified: full `typecheck`/`build` (client only). The CSS properties
|
|
||||||
(`touch-action`/`overscroll-behavior`) confirmed applied via
|
|
||||||
`getComputedStyle` against the real compiled `app.css`. The drag logic
|
|
||||||
itself confirmed with dispatched `PointerEvent`s carrying real
|
|
||||||
`clientY`/`pointerId` values against the real handlers copied from
|
|
||||||
`Shell.ts`: a 40px drag snaps back (stays open, `transform` cleared),
|
|
||||||
a 120px drag closes, and the live `transform` mid-drag tracks the
|
|
||||||
pointer 1:1 (confirmed at an intermediate point, not just the start/end
|
|
||||||
state) - not just asserted from reading the threshold constant.
|
|
||||||
Existing behavior re-confirmed unchanged in the same pass: scrim-tap-
|
|
||||||
to-close still closes. **Not verified**: actual touch input (not a
|
|
||||||
mouse or dispatched `PointerEvent`) on a real phone - no device
|
|
||||||
reachable from this sandbox, same standing gap as this project's other
|
|
||||||
touch-specific work (the video/music seek bars' own drag handling).
|
|
||||||
|
|
||||||
## 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