mirror of
https://github.com/nimbold/Firelink.git
synced 2026-07-26 12:08:27 +00:00
feat: implement native OS file management and trash utilities
This commit is contained in:
Generated
+84
-6
@@ -1382,6 +1382,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tower-http",
|
||||
"trash",
|
||||
"ts-rs",
|
||||
"url",
|
||||
"uuid",
|
||||
@@ -5479,6 +5480,24 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trash"
|
||||
version = "5.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7602e0c7d66ec2d92a8c917219fbc7894039efa2063b9064260110828a356f46"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"libc",
|
||||
"log 0.4.32",
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"scopeguard",
|
||||
"urlencoding",
|
||||
"windows 0.56.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tray-icon"
|
||||
version = "0.23.1"
|
||||
@@ -5653,6 +5672,12 @@ dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "urlpattern"
|
||||
version = "0.3.0"
|
||||
@@ -5979,8 +6004,8 @@ dependencies = [
|
||||
"webview2-com-sys",
|
||||
"windows 0.61.3",
|
||||
"windows-core 0.61.2",
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-implement 0.60.2",
|
||||
"windows-interface 0.59.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6051,6 +6076,16 @@ dependencies = [
|
||||
"windows-version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.56.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132"
|
||||
dependencies = [
|
||||
"windows-core 0.56.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.61.3"
|
||||
@@ -6094,14 +6129,26 @@ dependencies = [
|
||||
"windows-core 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.56.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6"
|
||||
dependencies = [
|
||||
"windows-implement 0.56.0",
|
||||
"windows-interface 0.56.0",
|
||||
"windows-result 0.1.2",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-implement 0.60.2",
|
||||
"windows-interface 0.59.3",
|
||||
"windows-link 0.1.3",
|
||||
"windows-result 0.3.4",
|
||||
"windows-strings 0.4.2",
|
||||
@@ -6113,8 +6160,8 @@ version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-implement 0.60.2",
|
||||
"windows-interface 0.59.3",
|
||||
"windows-link 0.2.1",
|
||||
"windows-result 0.4.1",
|
||||
"windows-strings 0.5.1",
|
||||
@@ -6142,6 +6189,17 @@ dependencies = [
|
||||
"windows-threading 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.56.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.2"
|
||||
@@ -6153,6 +6211,17 @@ dependencies = [
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.56.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.3"
|
||||
@@ -6218,6 +6287,15 @@ dependencies = [
|
||||
"windows-strings 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.3.4"
|
||||
|
||||
@@ -56,6 +56,7 @@ url = "2"
|
||||
tauri-plugin-store = "2.4.3"
|
||||
log = "0.4.32"
|
||||
tauri-plugin-log = "2"
|
||||
trash = "5"
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
cocoa = "0.25"
|
||||
objc = "0.2.7"
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
use std::process::Command;
|
||||
use std::path::Path;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn reveal_in_file_manager(path: String) -> Result<(), String> {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
Command::new("open")
|
||||
.arg("-R")
|
||||
.arg(&path)
|
||||
.spawn()
|
||||
.map_err(|e| format!("Failed to reveal file: {}", e))?;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
Command::new("explorer.exe")
|
||||
.arg(format!("/select,\"{}\"", path))
|
||||
.spawn()
|
||||
.map_err(|e| format!("Failed to reveal file: {}", e))?;
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
||||
{
|
||||
let parent = Path::new(&path).parent().unwrap_or(Path::new(""));
|
||||
Command::new("xdg-open")
|
||||
.arg(parent)
|
||||
.spawn()
|
||||
.map_err(|e| format!("Failed to reveal file: {}", e))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn open_downloaded_file(path: String) -> Result<(), String> {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
Command::new("open")
|
||||
.arg(&path)
|
||||
.spawn()
|
||||
.map_err(|e| format!("Failed to open file: {}", e))?;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
Command::new("cmd")
|
||||
.args(["/c", "start", "", &path])
|
||||
.spawn()
|
||||
.map_err(|e| format!("Failed to open file: {}", e))?;
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
||||
{
|
||||
Command::new("xdg-open")
|
||||
.arg(&path)
|
||||
.spawn()
|
||||
.map_err(|e| format!("Failed to open file: {}", e))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn trash_download_assets(path: String, partial_paths: Vec<String>) -> Result<(), String> {
|
||||
let p = Path::new(&path);
|
||||
if p.exists() {
|
||||
if let Err(e) = trash::delete(p) {
|
||||
return Err(format!("Failed to trash primary file: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
for partial in partial_paths {
|
||||
let p_partial = Path::new(&partial);
|
||||
if p_partial.exists() {
|
||||
if let Err(e) = trash::delete(p_partial) {
|
||||
return Err(format!("Failed to trash partial file: {}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -423,6 +423,7 @@ pub mod download;
|
||||
mod ipc;
|
||||
mod parity;
|
||||
pub mod error;
|
||||
pub mod commands;
|
||||
pub use error::AppError;
|
||||
|
||||
// Retained only for compatibility with the optional aria2 diagnostic monitor.
|
||||
@@ -1704,6 +1705,7 @@ pub fn run() {
|
||||
set_keychain_password, get_keychain_password, delete_keychain_password,
|
||||
check_file_exists, delete_file, toggle_tray_icon, set_extension_pairing_token,
|
||||
set_extension_frontend_ready, set_concurrent_limit, set_global_speed_limit, remove_download,
|
||||
commands::reveal_in_file_manager, commands::open_downloaded_file, commands::trash_download_assets,
|
||||
parity::get_system_proxy, parity::get_file_category, parity::check_for_updates, parity::is_supported_media, parity::get_supported_media_domains,
|
||||
parity::create_category_directories
|
||||
])
|
||||
|
||||
@@ -255,7 +255,7 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter }) => {
|
||||
setContextMenu(null);
|
||||
try {
|
||||
const fullPath = await resolvePath(contextItem.destination || '~/Downloads', contextItem.fileName);
|
||||
await invoke('open_file', { path: fullPath });
|
||||
await invoke('open_downloaded_file', { path: fullPath });
|
||||
} catch (e) {
|
||||
console.error("Failed to open file:", e);
|
||||
}
|
||||
@@ -271,7 +271,7 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter }) => {
|
||||
setContextMenu(null);
|
||||
try {
|
||||
const fullPath = await resolvePath(contextItem.destination || '~/Downloads', contextItem.fileName);
|
||||
await invoke('show_in_folder', { path: fullPath });
|
||||
await invoke('reveal_in_file_manager', { path: fullPath });
|
||||
} catch (e) {
|
||||
console.error("Failed to show in folder:", e);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,9 @@ type CommandMap = {
|
||||
test_deno: { args: undefined; result: string };
|
||||
open_file: { args: { path: string }; result: void };
|
||||
show_in_folder: { args: { path: string }; result: void };
|
||||
reveal_in_file_manager: { args: { path: string }; result: void };
|
||||
open_downloaded_file: { args: { path: string }; result: void };
|
||||
trash_download_assets: { args: { path: string; partialPaths: string[] }; result: void };
|
||||
start_download: { args: StartDownloadArgs; result: void };
|
||||
start_media_download: { args: StartMediaDownloadArgs; result: void };
|
||||
pause_download: { args: { id: string }; result: void };
|
||||
|
||||
@@ -204,17 +204,22 @@ export const useDownloadStore = create<DownloadState>((set, get) => ({
|
||||
const item = get().downloads.find(d => d.id === id);
|
||||
if (item && item.status === 'downloading') {
|
||||
try {
|
||||
const filepath = item.destination ? `${item.destination}/${item.fileName}` : null;
|
||||
await invoke('remove_download', { id, filepath });
|
||||
// Just cancel the active download via the backend, don't delete files via remove_download
|
||||
await invoke('remove_download', { id, filepath: null });
|
||||
} catch (e) {
|
||||
console.error("Failed to terminate download on deletion:", e);
|
||||
}
|
||||
} else if (item && deleteFile) {
|
||||
}
|
||||
|
||||
if (item && deleteFile) {
|
||||
try {
|
||||
const filepath = item.destination ? `${item.destination}/${item.fileName}` : null;
|
||||
await invoke('remove_download', { id, filepath });
|
||||
if (filepath) {
|
||||
const partialPaths = [`${filepath}.aria2`, `${filepath}.part`];
|
||||
await invoke('trash_download_assets', { path: filepath, partialPaths });
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to delete file from disk:", e);
|
||||
console.error("Failed to trash file from disk:", e);
|
||||
}
|
||||
}
|
||||
set((state) => ({
|
||||
|
||||
Reference in New Issue
Block a user