Skip to content

Commit bc127a3

Browse files
committed
refactor: debounce media controls
1 parent e89bb32 commit bc127a3

6 files changed

Lines changed: 48 additions & 30 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { onUnmounted, ref, watch, type Ref } from "vue";
2+
import type { PlayerQueue } from "@/plugins/api/interfaces";
3+
4+
// `play_action_in_progress` blips true for ~50ms during a seek (the stream
5+
// restarts at the new position). Debouncing it means the dependent UI — the
6+
// play-button spinner and the disabled state of the transport controls — only
7+
// reacts to genuinely slow actions instead of flickering on every seek.
8+
export function usePlayActionInProgress(
9+
playerQueue: Ref<PlayerQueue | undefined>,
10+
delay = 200,
11+
) {
12+
const isLoading = ref(false);
13+
let timer: ReturnType<typeof setTimeout> | null = null;
14+
15+
watch(
16+
() => playerQueue.value?.extra_attributes?.play_action_in_progress === true,
17+
(inProgress) => {
18+
if (timer) {
19+
clearTimeout(timer);
20+
timer = null;
21+
}
22+
if (inProgress) {
23+
timer = setTimeout(() => (isLoading.value = true), delay);
24+
} else {
25+
isLoading.value = false;
26+
}
27+
},
28+
{ immediate: true },
29+
);
30+
31+
onUnmounted(() => {
32+
if (timer) clearTimeout(timer);
33+
});
34+
35+
return { isLoading };
36+
}

src/layouts/default/PlayerOSD/PlayerControlBtn/NextBtn.vue

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import api from "@/plugins/api";
1818
import { Player, PlayerFeature, PlayerQueue } from "@/plugins/api/interfaces";
1919
import { useActiveAudioSource } from "@/composables/activeAudioSource";
2020
import { useActiveSource } from "@/composables/activeSource";
21+
import { usePlayActionInProgress } from "@/composables/playActionInProgress";
2122
import { computed, toRef } from "vue";
2223
import { SkipForward } from "lucide-vue-next";
2324
@@ -74,10 +75,5 @@ const canNext = computed(() => {
7475
return queueHasNext.value || playerHasNext.value;
7576
});
7677
77-
const isLoading = computed(() => {
78-
if (!compProps.player) return false;
79-
return (
80-
compProps.playerQueue?.extra_attributes?.play_action_in_progress === true
81-
);
82-
});
78+
const { isLoading } = usePlayActionInProgress(toRef(compProps, "playerQueue"));
8379
</script>

src/layouts/default/PlayerOSD/PlayerControlBtn/PlayBtn.vue

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ defineOptions({ inheritAttrs: false });
3636
import Icon, { IconProps } from "@/components/Icon.vue";
3737
import { useActiveAudioSource } from "@/composables/activeAudioSource";
3838
import { useActiveSource } from "@/composables/activeSource";
39+
import { usePlayActionInProgress } from "@/composables/playActionInProgress";
3940
import api from "@/plugins/api";
4041
import {
4142
MediaType,
@@ -109,12 +110,7 @@ const isPlaying = computed(() => {
109110
return compProps.player?.playback_state == PlaybackState.PLAYING;
110111
});
111112
112-
const isLoading = computed(() => {
113-
if (!compProps.player) return false;
114-
return (
115-
compProps.playerQueue?.extra_attributes?.play_action_in_progress === true
116-
);
117-
});
113+
const { isLoading } = usePlayActionInProgress(toRef(compProps, "playerQueue"));
118114
119115
const isDisabled = computed(() => {
120116
if (isLoading.value) return true;

src/layouts/default/PlayerOSD/PlayerControlBtn/PreviousBtn.vue

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import api from "@/plugins/api";
1818
import { Player, PlayerFeature, PlayerQueue } from "@/plugins/api/interfaces";
1919
import { useActiveAudioSource } from "@/composables/activeAudioSource";
2020
import { useActiveSource } from "@/composables/activeSource";
21+
import { usePlayActionInProgress } from "@/composables/playActionInProgress";
2122
import { computed, toRef } from "vue";
2223
import { SkipBack } from "lucide-vue-next";
2324
@@ -74,10 +75,5 @@ const canPrevious = computed(() => {
7475
return queueHasPrevious.value || playerHasPrevious.value;
7576
});
7677
77-
const isLoading = computed(() => {
78-
if (!compProps.player) return false;
79-
return (
80-
compProps.playerQueue?.extra_attributes?.play_action_in_progress === true
81-
);
82-
});
78+
const { isLoading } = usePlayActionInProgress(toRef(compProps, "playerQueue"));
8379
</script>

src/layouts/default/PlayerOSD/PlayerControlBtn/RepeatBtn.vue

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ import {
5151
isQueueDynamicPlaylist,
5252
isQueueInfiniteStream,
5353
} from "@/plugins/api/helpers";
54-
import { computed } from "vue";
54+
import { usePlayActionInProgress } from "@/composables/playActionInProgress";
55+
import { computed, toRef } from "vue";
5556
import { IconRepeat, IconRepeatOff, IconRepeatOnce } from "@tabler/icons-vue";
5657
5758
// properties
@@ -67,11 +68,7 @@ const compProps = withDefaults(defineProps<Props>(), {
6768
size: 20,
6869
});
6970
70-
const isLoading = computed(() => {
71-
return (
72-
compProps.playerQueue?.extra_attributes?.play_action_in_progress === true
73-
);
74-
});
71+
const { isLoading } = usePlayActionInProgress(toRef(compProps, "playerQueue"));
7572
7673
const isSingleDynamicPlaylist = computed(() =>
7774
isQueueDynamicPlaylist(compProps.playerQueue),

src/layouts/default/PlayerOSD/PlayerControlBtn/ShuffleBtn.vue

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ import {
3838
isQueueDynamicPlaylist,
3939
isQueueInfiniteStream,
4040
} from "@/plugins/api/helpers";
41+
import { usePlayActionInProgress } from "@/composables/playActionInProgress";
4142
import { IconArrowsRight } from "@tabler/icons-vue";
4243
import { Shuffle } from "lucide-vue-next";
43-
import { computed } from "vue";
44+
import { computed, toRef } from "vue";
4445
4546
// properties
4647
export interface Props {
@@ -55,11 +56,7 @@ const compProps = withDefaults(defineProps<Props>(), {
5556
size: 20,
5657
});
5758
58-
const isLoading = computed(() => {
59-
return (
60-
compProps.playerQueue?.extra_attributes?.play_action_in_progress === true
61-
);
62-
});
59+
const { isLoading } = usePlayActionInProgress(toRef(compProps, "playerQueue"));
6360
6461
const isSingleDynamicPlaylist = computed(() =>
6562
isQueueDynamicPlaylist(compProps.playerQueue),

0 commit comments

Comments
 (0)