mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
122 lines
4.1 KiB
TypeScript
122 lines
4.1 KiB
TypeScript
import { useCallback, useEffect, useMemo, useState, useSyncExternalStore } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import type { ConnectionStatus } from '../hooks/useConnectionStatus';
|
|
import { pullPlayQueueFromActiveServer } from '../store/applyServerPlayQueue';
|
|
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';
|
|
|
|
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);
|
|
const currentTrackId = usePlayerStore(s => s.currentTrack?.id);
|
|
|
|
const playbackServerId = useMemo(
|
|
() => getPlaybackServerId(),
|
|
[activeServerId, queueItems, queueIndex, currentTrackId],
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (activeServerId && playbackServerId && activeServerId === playbackServerId) {
|
|
clearQueueHandoffPending();
|
|
}
|
|
}, [activeServerId, playbackServerId]);
|
|
|
|
const autoSyncContext = canAutoIdlePlayQueuePull(status, orbitRole);
|
|
const localQueueSyncPaused = autoSyncContext && idlePullSuspended && !isPlaying;
|
|
|
|
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'
|
|
? 'disconnected'
|
|
: needsQueuePull
|
|
? 'queue-handoff'
|
|
: 'connected';
|
|
|
|
const pullFromActiveServer = useCallback(async () => {
|
|
if (status !== 'connected' || pullInFlight) return;
|
|
if (orbitRole === 'host' || orbitRole === 'guest') return;
|
|
if (currentRadio) return;
|
|
|
|
setPullInFlight(true);
|
|
try {
|
|
const result = await pullPlayQueueFromActiveServer();
|
|
switch (result) {
|
|
case 'noop':
|
|
showToast(t('connection.queueSynced'), 2500, 'info');
|
|
break;
|
|
case 'empty':
|
|
showToast(t('connection.queuePullEmpty'), 4000, 'info');
|
|
break;
|
|
case 'applied':
|
|
showToast(t('connection.queuePullSuccess'), 3000, 'info');
|
|
break;
|
|
case 'error':
|
|
showToast(t('connection.queuePullFailed'), 5000, 'error');
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} finally {
|
|
setPullInFlight(false);
|
|
}
|
|
}, [currentRadio, orbitRole, pullInFlight, status, t]);
|
|
|
|
const syncRingVisible = status === 'connected' && (needsQueuePull || pullInFlight);
|
|
|
|
return {
|
|
ledVariant,
|
|
needsQueuePull,
|
|
localQueueSyncPaused,
|
|
queueHandoffReason,
|
|
pullInFlight,
|
|
syncRingVisible,
|
|
pullFromActiveServer,
|
|
};
|
|
}
|
|
|
|
export function canAutoIdlePlayQueuePull(
|
|
status: ConnectionStatus,
|
|
orbitRole: string | null,
|
|
): boolean {
|
|
if (status !== 'connected') return false;
|
|
if (orbitRole === 'host' || orbitRole === 'guest') return false;
|
|
if (usePlayerStore.getState().currentRadio) return false;
|
|
if (queueIsMultiServer()) return false;
|
|
const activeId = useAuthStore.getState().activeServerId;
|
|
const playbackId = getPlaybackServerId();
|
|
if (!activeId || !playbackId || activeId !== playbackId) return false;
|
|
return true;
|
|
}
|