Skip to content

Commit 1afbed3

Browse files
committed
Report folder scan progress to UI
Emit progress events from the Tauri backend and display them in the React UI. Backend: add total_files and an AtomicUsize completed counter, increment it per-file and emit a "folder-scan-progress" event with completed/total; also increase concurrent worker semaphore from 4 to 8 and adjust the checksum task to clone the window handle and return a result variable. Frontend: listen for "folder-scan-progress", store progress in state, show a determinate LinearProgress and a caption while scanning, initialize an indeterminate progress at scan start, and clear it when finished.
1 parent 0cf985d commit 1afbed3

2 files changed

Lines changed: 44 additions & 4 deletions

File tree

src-tauri/src/main.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use std::collections::HashMap;
44
use std::path::Path;
55
use std::process::Command;
6-
use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
6+
use std::sync::{Arc, atomic::{AtomicBool, AtomicUsize, Ordering}};
77
use tauri::command;
88
use tokio::sync::Semaphore;
99
#[cfg(not(target_os = "windows"))]
@@ -341,6 +341,8 @@ async fn scan_folder(window: Window, state: tauri::State<'_, AppState>, folder_p
341341
// Process files concurrently with a semaphore to limit parallelism
342342
let sem = Arc::new(Semaphore::new(8)); // Increased concurrency from 4 to 8
343343
let mut handles = Vec::new();
344+
let total_files = files_to_process.len();
345+
let completed_files = Arc::new(AtomicUsize::new(0));
344346

345347
for file_path in files_to_process {
346348
if state.cancel.load(Ordering::SeqCst) {
@@ -351,14 +353,23 @@ async fn scan_folder(window: Window, state: tauri::State<'_, AppState>, folder_p
351353
let cancel_flag = state.cancel.clone();
352354
let file_path_clone = file_path.clone();
353355
let algorithms_clone = algorithms.clone(); // Clone algorithms for each task
356+
let completed_clone = completed_files.clone();
354357

355358
let handle = tokio::spawn(async move {
356359
// permit is dropped at end of scope to release semaphore
357360
let _permit = permit;
358-
match do_calculate_checksums(win, cancel_flag.clone(), file_path_clone.clone(), algorithms_clone).await {
361+
let result = match do_calculate_checksums(win.clone(), cancel_flag.clone(), file_path_clone.clone(), algorithms_clone).await {
359362
Ok(checksums) => Ok((file_path_clone, checksums)),
360363
Err(e) => Err((file_path_clone, e)),
361-
}
364+
};
365+
366+
let current = completed_clone.fetch_add(1, Ordering::SeqCst) + 1;
367+
let _ = win.emit("folder-scan-progress", serde_json::json!({
368+
"completed": current,
369+
"total": total_files
370+
}));
371+
372+
result
362373
});
363374

364375
handles.push(handle);

src/App.tsx

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,26 @@ const FolderScanTab = ({ folderPath, setFolderPath, selectedAlgorithms, handleAl
890890
const [includeSubfolders, setIncludeSubfolders] = useState(true);
891891
const [includeHidden, setIncludeHidden] = useState(false);
892892
const [isScanning, setIsScanning] = useState(false);
893+
const [progress, setProgress] = useState<{ completed: number; total: number } | null>(null);
894+
895+
useEffect(() => {
896+
let unlisten: any;
897+
(async () => {
898+
unlisten = await listen('folder-scan-progress', (event: any) => {
899+
const payload = event.payload as any;
900+
setProgress({
901+
completed: payload.completed || 0,
902+
total: payload.total || 0
903+
});
904+
});
905+
})();
906+
907+
return () => {
908+
if (unlisten && typeof unlisten.then === 'function') {
909+
unlisten.then((f: any) => f());
910+
}
911+
};
912+
}, []);
893913

894914
const handleFolderSelect = async () => {
895915
const selected = await openDialog({
@@ -911,6 +931,7 @@ const FolderScanTab = ({ folderPath, setFolderPath, selectedAlgorithms, handleAl
911931
}
912932

913933
setIsScanning(true);
934+
setProgress({ completed: 0, total: 100 }); // Indeterminate at first, total won't be 100 actually unless 100 files, but we update soon
914935
setFiles([]);
915936
try {
916937
const scannedFiles = await invoke("scan_folder", {
@@ -924,6 +945,7 @@ const FolderScanTab = ({ folderPath, setFolderPath, selectedAlgorithms, handleAl
924945
if (e !== "Cancelled") showAlert('Error', `Folder scan failed: ${e}`);
925946
} finally {
926947
setIsScanning(false);
948+
setProgress(null);
927949
}
928950
};
929951

@@ -1014,7 +1036,14 @@ const FolderScanTab = ({ folderPath, setFolderPath, selectedAlgorithms, handleAl
10141036
<Button variant="outlined" color="error" onClick={() => invoke('cancel_hashing')}>Cancel</Button>
10151037
)}
10161038
</Box>
1017-
{isScanning && <LinearProgress sx={{ mt: 2 }} />}
1039+
{isScanning && progress && (
1040+
<Box sx={{ mt: 2 }}>
1041+
<LinearProgress variant={progress.total > 0 ? "determinate" : "indeterminate"} value={progress.total > 0 ? (progress.completed / progress.total) * 100 : 0} />
1042+
<Typography variant="caption" sx={{ mt: 0.5, display: 'block', textAlign: 'center' }}>
1043+
{progress.total > 0 ? `${progress.completed} / ${progress.total} files completed (${Math.round((progress.completed / progress.total) * 100)}%)` : 'Scanning...'}
1044+
</Typography>
1045+
</Box>
1046+
)}
10181047

10191048
<TableContainer component={Paper} sx={{ mt: 2 }}>
10201049
<Table>

0 commit comments

Comments
 (0)