fix(queue): suspend idle pull after local queue edits (#1132)

This commit is contained in:
cucadmuh
2026-06-19 20:37:18 +03:00
committed by GitHub
parent 955a9fcbd6
commit c037ab459a
12 changed files with 301 additions and 10 deletions
+4 -3
View File
@@ -5,15 +5,15 @@ import { useOrbitStore } from '../store/orbitStore';
import { usePlayerStore } from '../store/playerStore';
import {
getPlaybackIdleSinceMs,
hasRecentQueueMutation,
isIdleQueuePullSuspended,
isPlaybackIdleLongEnough,
markPlaybackIdle,
} from '../store/queuePlaybackIdle';
import { hasPendingQueueSync } from '../store/queueSync';
import type { ConnectionStatus } from './useConnectionStatus';
import { canAutoIdlePlayQueuePull } from './usePlayQueueSyncLedState';
const IDLE_THRESHOLD_MS = 30_000;
const MUTATION_QUIET_MS = 15_000;
const POLL_INTERVAL_MS = 10_000;
/** Background pull when paused/stopped long enough on a single-server, in-sync browse context. */
@@ -37,7 +37,8 @@ export function useIdlePlayQueuePull(status: ConnectionStatus) {
if (!canAutoIdlePlayQueuePull(status, orbitRole)) return;
if (isPlaying) return;
if (!isPlaybackIdleLongEnough(IDLE_THRESHOLD_MS)) return;
if (hasRecentQueueMutation(MUTATION_QUIET_MS)) return;
if (isIdleQueuePullSuspended()) return;
if (hasPendingQueueSync()) return;
if (!activeServerId) return;
inFlightRef.current = true;
+20 -2
View File
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useState, useSyncExternalStore } from 'react';
import { useTranslation } from 'react-i18next';
import type { ConnectionStatus } from '../hooks/useConnectionStatus';
import { pullPlayQueueFromActiveServer } from '../store/applyServerPlayQueue';
@@ -6,6 +6,10 @@ import { useAuthStore } from '../store/authStore';
import { useOrbitStore } from '../store/orbitStore';
import { usePlayerStore } from '../store/playerStore';
import { getPlaybackServerId, queueIsMultiServer } from '../utils/playback/playbackServer';
import {
getIdleQueuePullSuspendedSnapshot,
subscribeIdleQueuePullSuspended,
} from '../store/queuePlaybackIdle';
import { clearQueueHandoffPending, isQueueHandoffPending } from '../store/queueSyncUiState';
import { showToast } from '../utils/ui/toast';
@@ -13,9 +17,12 @@ export function usePlayQueueSyncLedState(status: ConnectionStatus) {
const { t } = useTranslation();
const activeServerId = useAuthStore(s => s.activeServerId);
const orbitRole = useOrbitStore(s => s.role);
const isPlaying = usePlayerStore(s => s.isPlaying);
const currentRadio = usePlayerStore(s => s.currentRadio);
const [pullInFlight, setPullInFlight] = useState(false);
const idlePullSuspended = useSyncExternalStore(
subscribeIdleQueuePullSuspended,
getIdleQueuePullSuspendedSnapshot,
);
const queueItems = usePlayerStore(s => s.queueItems);
const queueIndex = usePlayerStore(s => s.queueIndex);
@@ -32,13 +39,22 @@ export function usePlayQueueSyncLedState(status: ConnectionStatus) {
}
}, [activeServerId, playbackServerId]);
const autoSyncContext = canAutoIdlePlayQueuePull(status, orbitRole);
const localQueueSyncPaused = autoSyncContext && idlePullSuspended;
const needsQueuePull = status === 'connected'
&& Boolean(activeServerId)
&& (
(Boolean(playbackServerId) && activeServerId !== playbackServerId)
|| isQueueHandoffPending()
|| localQueueSyncPaused
);
const queueHandoffReason = status === 'connected'
&& Boolean(activeServerId)
&& Boolean(playbackServerId)
&& activeServerId !== playbackServerId;
const ledVariant = status === 'checking'
? 'checking'
: status === 'disconnected'
@@ -81,6 +97,8 @@ export function usePlayQueueSyncLedState(status: ConnectionStatus) {
return {
ledVariant,
needsQueuePull,
localQueueSyncPaused,
queueHandoffReason,
pullInFlight,
syncRingVisible,
pullFromActiveServer,