Everything written this session that has not been through the device: rounds 43 to 48 (each patch adversarially verified, 43's transient-notice expectation fixed), a partial round 49, the pipeline scripts, the Android Auto design and the icon comparison mock. Also the two harness changes that were never A/B'd — adaptive waits, which may only shorten a declared wait and never lengthen it, and a ship that runs the mock walk and the deploy at the same time. The A/B and its 255-step baseline are in handoff/tools; main stays at what the phone has actually walked.
42 lines
2.9 KiB
Plaintext
42 lines
2.9 KiB
Plaintext
- **Drag to reorder** (`src/mobile/reorder.js` — one Svelte action, on the
|
|
container, for the playlist page's rows and the player sheet's queue
|
|
alike). Press and hold a row for 320ms and it lifts (with a short tap in
|
|
the phone's motor), the rows below make room, and letting go lands it.
|
|
Release without having moved and the row's sheet opens exactly as a long
|
|
press always opened it — which matters most on the queue, where a row has
|
|
no ⋯ and `oncontextmenu` is the only door to Play now, Move to next and
|
|
Remove from queue. A press that moves more than 8px before the hold lands
|
|
is a scroll, and the gesture lets go of it.
|
|
Nothing carries `touch-action: none`: a flick that starts on a row still
|
|
scrolls the list. `touch-action` is latched at touchstart and cannot be
|
|
flipped mid-gesture, so the page's own scroll is refused instead by a
|
|
non-passive `touchmove` listener added at the lift — which works because
|
|
the finger has been still for 320ms and the browser has not started a
|
|
scroll yet. A click and a contextmenu guard sit on the container in the
|
|
CAPTURE phase while a lift is live (a released drag would otherwise fire
|
|
the row's own click and play the track, and Android's own 500ms long
|
|
press would open the sheet in the middle of the drag); both are off when
|
|
no lift is live, so a tap and a long press behave as they always have.
|
|
Every row is measured once at the lift, in the scroller's CONTENT space,
|
|
so the edge pull — within 64px of either edge, ramped, up to 14px a
|
|
frame — leaves the snapshot true and the row keeps up with the finger.
|
|
Each displaced row moves by the exact difference between its own top and
|
|
its neighbour's rather than by one assumed row height (the desktop's
|
|
`rowH`), because the queue face puts an album separator between rows.
|
|
The drop reuses the mutations the Move rows already use: `moveItem` and
|
|
`moveInQueue` each take a numeric target now as well as `"up"` /
|
|
`"down"` / `"top"`, so there is still exactly one call to
|
|
`playlist_set_order` with the whole id order. It is optimistic — the rows
|
|
go where you put them at once, and come back with the error toast if the
|
|
backend says no. Two refusals guard the middle of a gesture: the playlist
|
|
drops nothing if a refetch has replaced its rows (`pls.dragging` also
|
|
defers the page's own re-read while a drag is live, the phone's version
|
|
of the desktop's `skippedWhileDrag`), and the queue drops nothing if
|
|
`player.qi` has moved, because a track boundary shifts the whole rendered
|
|
window and the absolute indices would then address the wrong rows.
|
|
Dragging is off while the playlist's filter has anything in it — a
|
|
filtered list's visible order is not the stored order — and the note
|
|
under the field says so; the sheet's Move rows address rows by id and
|
|
stay correct under a filter, which is why they are the way to reorder
|
|
without clearing it.
|