mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 06:25:41 +00:00
c7946f26b6
Four-cut cluster pulling the orchestrators out of DeviceSync.tsx.
1179 → 905 LOC (−274).
useDeviceSyncDrives — drives state + drivesLoading + refreshDrives
callback + the 5 s polling useEffect + the activeDrive memo that
matches targetDir against any detected drive's mount_point.
Returns { drives, drivesLoading, activeDrive, driveDetected,
refreshDrives }.
useDeviceSyncSourceStatuses — owns sourcePathsMap state + the
useEffect that computes per-source paths through compute_sync_paths
(parallel for all sources, with the cancellation guard), and the
derived sourceStatuses Map keyed on 'synced' / 'pending' /
'deletion'.
runDeviceSyncMigration — runDeviceSyncMigrationPreview (read v1
manifest's filenameTemplate, fetch album-source tracks, compute
new paths via Rust + old paths via JS legacy template, diff into
pairs + collisions + unchanged) and runDeviceSyncMigrationExecute
(invoke rename_device_files, bump manifest to v2, rescan device).
Exports MigrationPhase / MigrationPair / MigrationResult types so
the page state stays typed.
runDeviceSyncExecution — runDeviceSyncSummaryPrompt (input
validation + invoke calculate_sync_payload through the subsonic
client) and runDeviceSyncExecute (delete pending sources, re-write
playlist m3u8 even when nothing to download, fire sync_batch_to_device
with the right toast variants on space / mount / generic errors).
SyncDelta type moves with it.
DeviceSync drops the inline definitions; closeMigration stays
inline (six lines, no real win extracting it). Pure code move
otherwise — no behaviour change.
46 lines
1.4 KiB
TypeScript
46 lines
1.4 KiB
TypeScript
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
import { invoke } from '@tauri-apps/api/core';
|
|
import type { RemovableDrive } from '../utils/deviceSyncHelpers';
|
|
|
|
export interface DeviceSyncDrivesResult {
|
|
drives: RemovableDrive[];
|
|
drivesLoading: boolean;
|
|
activeDrive: RemovableDrive | null;
|
|
driveDetected: boolean;
|
|
refreshDrives: () => Promise<void>;
|
|
}
|
|
|
|
export function useDeviceSyncDrives(targetDir: string | null): DeviceSyncDrivesResult {
|
|
const [drives, setDrives] = useState<RemovableDrive[]>([]);
|
|
const [drivesLoading, setDrivesLoading] = useState(false);
|
|
|
|
const refreshDrives = useCallback(async () => {
|
|
setDrivesLoading(true);
|
|
try {
|
|
const result = await invoke<RemovableDrive[]>('get_removable_drives');
|
|
setDrives(result);
|
|
} catch {
|
|
setDrives([]);
|
|
} finally {
|
|
setDrivesLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
// Fetch drives on mount, then poll every 5 seconds
|
|
useEffect(() => {
|
|
refreshDrives();
|
|
const interval = setInterval(refreshDrives, 5000);
|
|
return () => clearInterval(interval);
|
|
}, [refreshDrives]);
|
|
|
|
// Detect if the current targetDir is on a detected removable drive
|
|
const activeDrive = useMemo(() => {
|
|
if (!targetDir) return null;
|
|
return drives.find(d => targetDir.startsWith(d.mount_point)) ?? null;
|
|
}, [targetDir, drives]);
|
|
|
|
const driveDetected = activeDrive !== null;
|
|
|
|
return { drives, drivesLoading, activeDrive, driveDetected, refreshDrives };
|
|
}
|