mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
refactor(device-sync): G.76 — extract browser + device-scan + job-events hooks + choose-folder util (cluster) (#643)
Four-cut cluster pulling the remaining lifecycle code out of DeviceSync.tsx. 928 → 638 LOC (−290). useDeviceSyncBrowser — playlists / randomAlbums / artists state + their three loaders + the tab-switch useEffect that lazy-loads on first visit + the 300 ms debounced album-search useEffect + the expandedArtistIds / artistAlbumsMap / loadingArtistIds state with toggleArtistExpand. Takes activeTab + search + a resetSearch callback (so the tab-switch effect can clear the search input the page still owns). useDeviceSyncDeviceScan — scanDevice useCallback + the on-mount useEffect + the auto-import-manifest useEffect (with the manifestImportedRef gate so it only fires once per drive plug-in) + the clean-on-unplug useEffect that clears deviceFilePaths and resets the import flag. useDeviceSyncJobEvents — the device:sync:progress and device:sync:complete event listeners. Complete handler dispatches the toast, writes the manifest, generates per-playlist m3u8 files (through fetchTracksForSource + trackToSyncInfo), and triggers scanDevice. Cancelled state is preserved by re-calling useDeviceSyncJobStore.cancel() after complete(). runDeviceSyncChooseFolder — the openDialog → setTargetDir → optional manifest auto-import → scanDevice timer flow. DeviceSync drops every direct import that those hooks now own (getPlaylists, getArtists, getArtist, getAlbumList, searchSubsonic, listen, openDialog, useEffect, useRef, SubsonicPlaylist / SubsonicArtist / SubsonicAlbum type imports). Pure code move otherwise — no behaviour change.
This commit is contained in:
committed by
GitHub
parent
c7946f26b6
commit
6fcf2259f6
@@ -0,0 +1,64 @@
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { useDeviceSyncStore, type DeviceSyncSource } from '../store/deviceSyncStore';
|
||||
import { showToast } from '../utils/toast';
|
||||
|
||||
export interface DeviceSyncDeviceScanResult {
|
||||
scanDevice: () => Promise<void>;
|
||||
}
|
||||
|
||||
export function useDeviceSyncDeviceScan(
|
||||
targetDir: string | null,
|
||||
sourcesLength: number,
|
||||
driveDetected: boolean,
|
||||
t: TFunction,
|
||||
): DeviceSyncDeviceScanResult {
|
||||
const setDeviceFilePaths = useDeviceSyncStore.getState().setDeviceFilePaths;
|
||||
const setScanning = useDeviceSyncStore.getState().setScanning;
|
||||
|
||||
const scanDevice = useCallback(async () => {
|
||||
if (!targetDir || sourcesLength === 0) {
|
||||
setDeviceFilePaths([]);
|
||||
return;
|
||||
}
|
||||
setScanning(true);
|
||||
try {
|
||||
const files = await invoke<string[]>('list_device_dir_files', { dir: targetDir });
|
||||
setDeviceFilePaths(files);
|
||||
} catch {
|
||||
setDeviceFilePaths([]);
|
||||
} finally {
|
||||
setScanning(false);
|
||||
}
|
||||
}, [targetDir, sourcesLength, setDeviceFilePaths, setScanning]);
|
||||
|
||||
// Scan device on mount and when targetDir changes
|
||||
useEffect(() => { scanDevice(); }, [scanDevice]);
|
||||
|
||||
// Auto-import manifest when page loads and drive is already connected
|
||||
const manifestImportedRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (!targetDir || !driveDetected || manifestImportedRef.current) return;
|
||||
manifestImportedRef.current = true;
|
||||
invoke<{ version: number; sources: DeviceSyncSource[] } | null>(
|
||||
'read_device_manifest', { destDir: targetDir }
|
||||
).then(manifest => {
|
||||
if (manifest?.sources?.length) {
|
||||
useDeviceSyncStore.getState().clearSources();
|
||||
manifest.sources.forEach(s => useDeviceSyncStore.getState().addSource(s));
|
||||
showToast(t('deviceSync.manifestImported', { count: manifest.sources.length }), 4000, 'info');
|
||||
}
|
||||
}).catch(() => {});
|
||||
}, [targetDir, driveDetected, t]);
|
||||
|
||||
// Clear device file list and reset import flag when stick is unplugged
|
||||
useEffect(() => {
|
||||
if (!driveDetected) {
|
||||
setDeviceFilePaths([]);
|
||||
manifestImportedRef.current = false;
|
||||
}
|
||||
}, [driveDetected, setDeviceFilePaths]);
|
||||
|
||||
return { scanDevice };
|
||||
}
|
||||
Reference in New Issue
Block a user