Skip to content

Fix/tapping sliders#1855

Open
kissu wants to merge 5 commits into
music-assistant:mainfrom
kissu:fix/tapping-sliders
Open

Fix/tapping sliders#1855
kissu wants to merge 5 commits into
music-assistant:mainfrom
kissu:fix/tapping-sliders

Conversation

@kissu

@kissu kissu commented Jun 3, 2026

Copy link
Copy Markdown

Changelog

Makes the player sliders seek/jump to the tapped position on touch, and stops the transport controls flickering during a seek.
I made a few extra fews along the way that were related to the same part of the app to smoothen the UX a little bit. 🌟

This is how it looks with all of the bugs fixed from below, the videos are here to showcase the bugs in question so that we have full context on what was wrong

final.mp4
  • 🟢 Progress slider — tap-to-seek: tapping the progress bar jumps to the tapped position instead of nudging by a small step.
    Tapping on mobile was overall not working at all if not dragging the seek along.
original_bug.mp4
  • 🟢 Progress slider — no post-seek bounce: it no longer briefly snaps back to the old position before settling on the seeked one.
    I fixed the ugly behavior on desktop where it would take the stale value.
glitchy_skipping.mp4
  • 🟢 Volume slider — tap-to-position: tapping the volume bar on touch jumps to the tapped position instead of stepping volume up/down.
    I wasn't sure about this one initially but then decided to just match parity with the desktop behavior, kinda safe decision I think. 🤗
CleanShot 2026-06-03 at 19 56 39
  • 🟢 No control-bar flicker on seek: the play / next / previous / shuffle / repeat buttons no longer flash their spinner / disabled state during a seek.
    This one didn't look professional because of the flickering controls on both mobile + desktop.
controls_desktop
controls_mobile

Technical details

  1. Progress tap-to-seek: @end seeked to a lazily-synced tempTime mirror that only refreshed during a continuous drag, so a quick tap sent a stale value.
    Now it uses the value from Vuetify's @end event (the committed position).

  2. Post-seek bounce: between the seek command and the backend's queue_time_updated confirmation, the elapsed-time watcher wrote the stale pre-seek value back.
    The slider is now held optimistically at the target until the backend's elapsed catches up (±2s) or a 3s safety timeout.

  3. Volume tap: on touch, onTouchEnd stepped volume up/down based on which side of the thumb was tapped, instead of jumping. Desktop already jumps via the reka-ui slider's @update:model-value. Touch taps now jump to the tapped position too (reusing the existing getPercentageFromX helper). Drag and the group-popout tap are unchanged.

  4. Control-bar flicker: play_action_in_progress blips true for ~50ms during a seek (the stream restarts). Five buttons each read it directly to drive their spinner / disabled state. Extracted into a shared usePlayActionInProgress composable that debounces it (200ms), so the spinner only shows for genuinely slow actions.

Refactor in commit #3

Removing tempTime let computedElapsedTime drop a mutation it performed inside the getter (tempTime.value = curTimeValue.value, previously suppressed with an eslint-disable).
Mostly wanted to avoid doing the Vue anti-pattern there.

Commits details

I split my work across 3 commits so that any could be reverted if not considered atomic enough (or just outside of the scope of this PR).
We could argue that this PR is "doing too much", yet I wanted to leave it better than it initially was without making it too huge.

  1. Fix progress slider seek: PlayerTimeline.vue: tap-to-seek + optimistic post-seek hold, plus the side-effect-free computedElapsedTime cleanup above.
  2. Fix volume slider tap on touch: PlayerVolume.vue (jump to tapped position).
  3. Debounce play_action_in_progress across transport controls: new composable + the five control buttons. Related polish; can be reverted independently.
  4. Unit test the new composable: I somehow totally forgot to check the structure of the project more in depth and glanced over the UT. After double-checking this part, it is now added! 🤦🏻‍♂️

Verification

Linter + typecking + tests all pass (added coverage of my new composable). ✅

  • Manually checked on desktop and a real Android device (as shown in the GIFs/videos above), in both the
    mini player and the fullscreen player:
    • tapping the progress bar (touch + mouse) jumps to the tapped position
    • tapping the volume bar jumps to the tapped position
    • no post-seek bounce — progress no longer flicks back toward 0 before settling
    • no spinner / disabled flicker on the transport controls while seeking
    • drag and the group-volume popout behave as before
    • verified across several different tracks

Closes music-assistant/backlog#67


PS: disclaimer regarding the OHF AI Policy:

  • Claude Code was used to scaffold the writing of this PR, I filled in the gaps + added some visuals to illustrate all the bugs/work done 🤗
  • it was also used to speed up my dev workflow, I take full ownership and I'm 100% able to explain what was pushed

@MarvinSchenkel

Copy link
Copy Markdown
Contributor

Just a little heads up, we deliberately implemented a 'tap to volume step up/down' to make sure we do not accidentally blast music at 100%. On desktop, we do allow clicking to a certain volume level.

The changes for 'seek' do make sense here

@kissu

kissu commented Jun 3, 2026

Copy link
Copy Markdown
Author

Yeah I commented on that topic here, thought that it would be maybe intentional.

Yet the increment was only 2 by 2. Maybe we should bump it to 10 pts per step instead?
Otherwise people could indeed just grab and move the volume level. 👍🏻

Anyway, very happy to revert the tap on mobile if it makes more sense here. 🙏🏻

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tapping progress slider acts like volume, does small step. That's unexpected I think. Can't jump to middle of song, valid use case

2 participants