mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 14:35:41 +00:00
fix(device-sync): auto-import manifest on mount, clear view on disconnect
- Read psysonic-sync.json automatically when DeviceSync page opens and drive is already connected (no manual folder re-select needed) - Always replace sources from manifest when choosing a folder, so switching between sticks loads the correct album list - Hide source list and status badges when drive is disconnected - Reset import flag on disconnect so re-plugging triggers a fresh import - Fix unused `mut` warning in cancel_device_sync Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1896,7 +1896,7 @@ async fn calculate_sync_payload(
|
|||||||
/// Signals a running `sync_batch_to_device` job to stop after its current tracks finish.
|
/// Signals a running `sync_batch_to_device` job to stop after its current tracks finish.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn cancel_device_sync(job_id: String, app: tauri::AppHandle) {
|
fn cancel_device_sync(job_id: String, app: tauri::AppHandle) {
|
||||||
if let Ok(mut flags) = sync_cancel_flags().lock() {
|
if let Ok(flags) = sync_cancel_flags().lock() {
|
||||||
if let Some(flag) = flags.get(&job_id) {
|
if let Some(flag) = flags.get(&job_id) {
|
||||||
flag.store(true, Ordering::Relaxed);
|
flag.store(true, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-15
@@ -162,6 +162,30 @@ export default function DeviceSync() {
|
|||||||
// Scan device on mount and when targetDir changes
|
// Scan device on mount and when targetDir changes
|
||||||
useEffect(() => { scanDevice(); }, [scanDevice]);
|
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]);
|
||||||
|
|
||||||
// Compute expected paths for each source (for status comparison)
|
// Compute expected paths for each source (for status comparison)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!targetDir || sources.length === 0) {
|
if (!targetDir || sources.length === 0) {
|
||||||
@@ -353,19 +377,18 @@ export default function DeviceSync() {
|
|||||||
if (sel) {
|
if (sel) {
|
||||||
const dir = sel as string;
|
const dir = sel as string;
|
||||||
setTargetDir(dir);
|
setTargetDir(dir);
|
||||||
// If the device has a psysonic-sync.json and localStorage has no sources yet,
|
// If the device has a psysonic-sync.json, always import it — replacing any
|
||||||
// auto-import so the list is populated when switching machines.
|
// sources from a previous device so switching sticks works correctly.
|
||||||
if (useDeviceSyncStore.getState().sources.length === 0) {
|
try {
|
||||||
try {
|
const manifest = await invoke<{ version: number; sources: DeviceSyncSource[] } | null>(
|
||||||
const manifest = await invoke<{ version: number; sources: DeviceSyncSource[] } | null>(
|
'read_device_manifest', { destDir: dir }
|
||||||
'read_device_manifest', { destDir: dir }
|
);
|
||||||
);
|
if (manifest?.sources?.length) {
|
||||||
if (manifest?.sources?.length) {
|
useDeviceSyncStore.getState().clearSources();
|
||||||
manifest.sources.forEach(s => useDeviceSyncStore.getState().addSource(s));
|
manifest.sources.forEach(s => useDeviceSyncStore.getState().addSource(s));
|
||||||
showToast(t('deviceSync.manifestImported', { count: manifest.sources.length }), 4000, 'info');
|
showToast(t('deviceSync.manifestImported', { count: manifest.sources.length }), 4000, 'info');
|
||||||
}
|
}
|
||||||
} catch { /* no manifest, that's fine */ }
|
} catch { /* no manifest, that's fine */ }
|
||||||
}
|
|
||||||
// Trigger a device scan after folder change
|
// Trigger a device scan after folder change
|
||||||
setTimeout(() => scanDevice(), 100);
|
setTimeout(() => scanDevice(), 100);
|
||||||
}
|
}
|
||||||
@@ -733,7 +756,7 @@ export default function DeviceSync() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Status summary badges */}
|
{/* Status summary badges */}
|
||||||
{sources.length > 0 && (
|
{sources.length > 0 && driveDetected && (
|
||||||
<div className="device-sync-status-summary">
|
<div className="device-sync-status-summary">
|
||||||
{syncedCount > 0 && (
|
{syncedCount > 0 && (
|
||||||
<span className="device-sync-badge synced">
|
<span className="device-sync-badge synced">
|
||||||
@@ -753,7 +776,7 @@ export default function DeviceSync() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{sources.length === 0 ? (
|
{sources.length === 0 || !driveDetected ? (
|
||||||
<p className="device-sync-empty">{t('deviceSync.noSourcesSelected')}</p>
|
<p className="device-sync-empty">{t('deviceSync.noSourcesSelected')}</p>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Reference in New Issue
Block a user