mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
72 lines
2.7 KiB
TypeScript
72 lines
2.7 KiB
TypeScript
import { useEffect, useMemo, useState } from 'react';
|
|
import { computeSyncPaths } from '@/lib/api/syncfs';
|
|
import { fetchTracksForSource } from '@/features/playback/utils/playback/fetchTracksForSource';
|
|
import { trackToSyncInfo, type SyncStatus } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
|
import type { DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
|
|
|
|
export interface DeviceSyncSourceStatusesResult {
|
|
sourcePathsMap: Map<string, string[]>;
|
|
sourceStatuses: Map<string, SyncStatus>;
|
|
}
|
|
|
|
export function useDeviceSyncSourceStatuses(
|
|
targetDir: string | null,
|
|
sources: DeviceSyncSource[],
|
|
pendingDeletion: string[],
|
|
deviceFilePaths: string[],
|
|
): DeviceSyncSourceStatusesResult {
|
|
// Map source IDs → computed device paths (for status derivation)
|
|
const [sourcePathsMap, setSourcePathsMap] = useState<Map<string, string[]>>(new Map());
|
|
|
|
// Compute expected paths for each source (for status comparison)
|
|
useEffect(() => {
|
|
if (!targetDir || sources.length === 0) {
|
|
// React Compiler set-state-in-effect rule: state set from an async result resolved in this effect.
|
|
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
setSourcePathsMap(new Map());
|
|
return;
|
|
}
|
|
// Path schema is fixed in the Rust backend now — no template parameter.
|
|
let cancelled = false;
|
|
(async () => {
|
|
const map = new Map<string, string[]>();
|
|
await Promise.all(sources.map(async source => {
|
|
if (cancelled) return;
|
|
try {
|
|
const tracks = await fetchTracksForSource(source);
|
|
const paths = await computeSyncPaths({
|
|
tracks: tracks.map((tr, idx) => trackToSyncInfo(
|
|
tr, '',
|
|
source.type === 'playlist' ? { name: source.name, index: idx + 1 } : undefined,
|
|
)),
|
|
destDir: targetDir,
|
|
});
|
|
map.set(source.id, paths);
|
|
} catch {
|
|
map.set(source.id, []);
|
|
}
|
|
}));
|
|
if (!cancelled) setSourcePathsMap(map);
|
|
})();
|
|
return () => { cancelled = true; };
|
|
}, [targetDir, sources]);
|
|
|
|
// Derive sync status per source
|
|
const sourceStatuses = useMemo(() => {
|
|
const deviceSet = new Set(deviceFilePaths);
|
|
const statuses = new Map<string, SyncStatus>();
|
|
for (const source of sources) {
|
|
if (pendingDeletion.includes(source.id)) {
|
|
statuses.set(source.id, 'deletion');
|
|
} else {
|
|
const paths = sourcePathsMap.get(source.id) ?? [];
|
|
const allSynced = paths.length > 0 && paths.every(p => deviceSet.has(p));
|
|
statuses.set(source.id, allSynced ? 'synced' : 'pending');
|
|
}
|
|
}
|
|
return statuses;
|
|
}, [sources, pendingDeletion, sourcePathsMap, deviceFilePaths]);
|
|
|
|
return { sourcePathsMap, sourceStatuses };
|
|
}
|