diff --git a/apps/app/src-tauri/Cargo.lock b/apps/app/src-tauri/Cargo.lock index 32a35469..160ed674 100644 --- a/apps/app/src-tauri/Cargo.lock +++ b/apps/app/src-tauri/Cargo.lock @@ -462,7 +462,6 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" name = "cherit" version = "0.0.1" dependencies = [ - "futures", "log", "serde", "serde_json", @@ -474,7 +473,6 @@ dependencies = [ "tauri-plugin-log", "tauri-plugin-os", "tauri-plugin-store", - "urlencoding", ] [[package]] @@ -1049,21 +1047,6 @@ dependencies = [ "new_debug_unreachable", ] -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - [[package]] name = "futures-channel" version = "0.3.31" @@ -1071,7 +1054,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", - "futures-sink", ] [[package]] @@ -1139,7 +1121,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ - "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -4627,12 +4608,6 @@ dependencies = [ "serde", ] -[[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" diff --git a/apps/app/src-tauri/Cargo.toml b/apps/app/src-tauri/Cargo.toml index 27906868..3ce6e515 100644 --- a/apps/app/src-tauri/Cargo.toml +++ b/apps/app/src-tauri/Cargo.toml @@ -30,5 +30,3 @@ tauri-plugin-fs = "2" tauri-plugin-store = "2" tauri-plugin-dialog = "2" tauri-plugin-os = "2" -urlencoding = "2" -futures = "0.3.31" diff --git a/apps/app/src-tauri/src/lib.rs b/apps/app/src-tauri/src/lib.rs index 95fe9873..d415dab1 100644 --- a/apps/app/src-tauri/src/lib.rs +++ b/apps/app/src-tauri/src/lib.rs @@ -1,194 +1,3 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Clone)] -pub struct FileNode { - pub name: String, - pub path: String, - pub is_directory: bool, - pub children: Vec, -} - -fn sort_nodes(nodes: &mut Vec) { - nodes.sort_by(|a, b| { - if a.is_directory != b.is_directory { - return if a.is_directory { - std::cmp::Ordering::Less - } else { - std::cmp::Ordering::Greater - }; - } - a.name.to_lowercase().cmp(&b.name.to_lowercase()) - }); - - for node in nodes { - if !node.children.is_empty() { - sort_nodes(&mut node.children); - } - } -} - -#[cfg(not(target_os = "android"))] -fn build_tree_recursive_desktop(path_str: &str) -> std::io::Result> { - use std::fs; - let mut nodes = Vec::new(); - let entries = fs::read_dir(path_str)?; - - for entry in entries { - if let Ok(entry) = entry { - if let Ok(metadata) = entry.metadata() { - let file_name = entry.file_name().to_string_lossy().to_string(); - - let is_directory = metadata.is_dir(); - let starts_with_dot = file_name.starts_with('.'); - let ends_with_md = file_name.ends_with(".md"); - - if (is_directory && !starts_with_dot) || ends_with_md { - let path = entry.path().to_string_lossy().to_string(); - let mut children = Vec::new(); - - if is_directory { - if let Ok(sub_children) = build_tree_recursive_desktop(&path) { - children = sub_children; - } - } - - nodes.push(FileNode { - name: file_name.trim_end_matches(".md").to_string(), - path, - is_directory, - children, - }); - } - } - } - } - Ok(nodes) -} - -#[cfg(target_os = "android")] -fn build_tree_recursive_android( - app: tauri::AppHandle, - path: String, - document_top_tree_uri: Option, -) -> std::pin::Pin, String>> + Send>> { - Box::pin(async move { - use tauri_plugin_android_fs::AndroidFsExt; - use tauri_plugin_android_fs::FileUri; - use tauri_plugin_android_fs::EntryOptions; - use futures::future::join_all; - - let api = app.android_fs(); - - let json_obj = serde_json::json!({ - "uri": path, - "documentTopTreeUri": document_top_tree_uri - }); - let file_uri = FileUri::from_json_str(&json_obj.to_string()) - .map_err(|e| format!("Failed to create FileUri: {}", e))?; - - let options = EntryOptions { - uri: false, - name: true, - last_modified: false, - len: false, - mime_type: false, - }; - - let entries = api.read_dir_with_options(&file_uri, options) - .map_err(|e| e.to_string())?; - - let mut futures = Vec::new(); - let mut nodes = Vec::new(); - - for entry in entries { - let is_directory = entry.is_dir(); - let name_opt = entry.name(); - - if let Some(name_str) = name_opt { - let name = name_str.to_string(); - let starts_with_dot = name.starts_with('.'); - let ends_with_md = name.ends_with(".md"); - - if (is_directory && !starts_with_dot) || ends_with_md { - let path_uri = format!("{}%2F{}", path, urlencoding::encode(&name)); - - if is_directory { - let app_clone = app.clone(); - let path_clone = path_uri.clone(); - let doc_uri_clone = document_top_tree_uri.clone(); - let name_clone = name.clone(); - - futures.push(async move { - let children_res = build_tree_recursive_android( - app_clone, - path_clone.clone(), - doc_uri_clone - ).await; - - match children_res { - Ok(children) => Some(FileNode { - name: name_clone.trim_end_matches(".md").to_string(), - path: path_clone, - is_directory: true, - children, - }), - Err(_) => None - } - }); - } else { - nodes.push(FileNode { - name: name.trim_end_matches(".md").to_string(), - path: path_uri, - is_directory: false, - children: vec![], - }); - } - } - } - } - - let results = join_all(futures).await; - for res in results { - if let Some(node) = res { - nodes.push(node); - } - } - - Ok(nodes) - }) -} - -#[tauri::command] -async fn build_file_tree( - app: tauri::AppHandle, - path: String, - document_top_tree_uri: Option, -) -> Result, String> { - let nodes; - - #[cfg(target_os = "android")] - { - let mut unsorted_nodes = build_tree_recursive_android(app, path, document_top_tree_uri).await?; - sort_nodes(&mut unsorted_nodes); - nodes = unsorted_nodes; - } - - #[cfg(not(target_os = "android"))] - { - // Suppress unused variable warnings on desktop - let _ = app; - let _ = document_top_tree_uri; - nodes = tauri::async_runtime::spawn_blocking(move || { - let mut n = build_tree_recursive_desktop(&path)?; - sort_nodes(&mut n); - Ok(n) - }).await.map_err(|e| e.to_string())? - .map_err(|e: std::io::Error| e.to_string())?; - } - - Ok(nodes) -} - #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() @@ -197,7 +6,6 @@ pub fn run() { .plugin(tauri_plugin_store::Builder::new().build()) .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_android_fs::init()) - .invoke_handler(tauri::generate_handler![build_file_tree]) .setup(|app| { if cfg!(debug_assertions) { app.handle().plugin( diff --git a/apps/app/src/components/sidebar/file_manager/index.svelte b/apps/app/src/components/sidebar/file_manager/index.svelte index 4ec568c8..915cde41 100644 --- a/apps/app/src/components/sidebar/file_manager/index.svelte +++ b/apps/app/src/components/sidebar/file_manager/index.svelte @@ -24,7 +24,6 @@ $effect(() => { if (file_tree) sort_file_tree(file_tree); }); - let collapsed_state: boolean = $state(true); $effect(() => { if (!root_path) return; diff --git a/apps/app/src/components/sidebar/file_manager/items_renderer.svelte b/apps/app/src/components/sidebar/file_manager/items_renderer.svelte index d0287b4e..1b19514a 100644 --- a/apps/app/src/components/sidebar/file_manager/items_renderer.svelte +++ b/apps/app/src/components/sidebar/file_manager/items_renderer.svelte @@ -257,7 +257,7 @@ ondragstart={(e) => handle_drag_start(e, node)} ondragend={reset_dnd} oncontextmenu={(e) => handle_node_right_click(e, node)} - class="{opened_filenode?.path === node.path ? 'bg-base-content/10' : ''} + class="{opened_filenode?.path === node.path ? 'bg-base-content/10' : ''} {dragged_node?.path === node.path ? 'opacity-50' : ''} py-0.75 w-full hover:text-[color-mix(in_srgb,var(--color-base-content)_85%,black)] truncate block" onclick={(e) => { diff --git a/apps/app/src/lib/file_tree/builder.ts b/apps/app/src/lib/file_tree/builder.ts index 52bc83a2..c4bede33 100644 --- a/apps/app/src/lib/file_tree/builder.ts +++ b/apps/app/src/lib/file_tree/builder.ts @@ -1,42 +1,85 @@ +import { readDir, type DirEntry } from '@tauri-apps/plugin-fs'; import { type FileNode, type GenericPath } from '@/types'; -import { invoke } from '@tauri-apps/api/core'; +import { + AndroidFs, + type AndroidEntryMetadataWithUri, +} from 'tauri-plugin-android-fs-api'; +import { join } from '@tauri-apps/api/path'; +import { current_platform } from './utils'; export async function build_file_tree_from_fs({ path, document_top_tree_uri, }: GenericPath): Promise { - try { - return await invoke('build_file_tree', { - path, - documentTopTreeUri: document_top_tree_uri || null, + let entries: DirEntry[] | AndroidEntryMetadataWithUri[] | undefined; + let base_nodes: FileNode[] | undefined; + + if (current_platform == 'android') { + if (!document_top_tree_uri) + throw new Error('Document top tree URI is not set'); + entries = await AndroidFs.readDir({ + uri: path, + documentTopTreeUri: document_top_tree_uri, }); - } catch (error) { - console.error('Failed to build file tree:', error); - throw error; + base_nodes = await transform_android_entries_to_filenode(entries, path); + } else { + entries = await readDir(path); + base_nodes = await transform_entries_to_filenode(entries, path); } -} -// These functions are no longer needed but kept if needed for other parts of the app -// or we can remove them if we are sure they are unused. -// Based on the task, we are replacing the logic. -// I'll comment them out or remove them if I'm sure. -// The user said "replace it with the js build file tree function". -// I'll keep the exports but empty or commented if I want to be safe, or just remove them. -// "and replace it with the js build file tree function" -> Replace the implementation of `build_file_tree_from_fs`. -// I'll remove the helper functions as they were only used by `build_file_tree_from_fs`. + const nodes = await Promise.all( + base_nodes.map(async (n) => { + if (!n.is_directory) return n; + const children = await build_file_tree_from_fs({ + path: n.path, + document_top_tree_uri, + }); + return { + ...n, + children, + }; + }) + ); + return nodes; +} export async function transform_entries_to_filenode( - entries: any[], + entries: DirEntry[], base_dir_path: string ): Promise { - // Deprecated: logic moved to Rust - return []; + const nodes = await Promise.all( + entries + .filter( + (entry) => + (entry.isDirectory && !entry.name.startsWith('.')) || + entry.name.endsWith('.md') + ) + .map(async (entry) => ({ + name: entry.name.replace(/\.md$/, ''), + path: await join(base_dir_path, entry.name), + is_directory: entry.isDirectory, + children: [], + })) + ); + return nodes; } - export async function transform_android_entries_to_filenode( - entries: any[], + entries: AndroidEntryMetadataWithUri[], base_dir_path: string ): Promise { - // Deprecated: logic moved to Rust - return []; + const nodes = await Promise.all( + entries + .filter( + (entry) => + (entry.type === 'Dir' && !entry.name.startsWith('.')) || + entry.name.endsWith('.md') + ) + .map(async (entry) => ({ + name: entry.name.replace(/\.md$/, ''), + path: `${base_dir_path}%2F${encodeURIComponent(entry.name)}`, + is_directory: entry.type === 'Dir', + children: [], + })) + ); + return nodes; } diff --git a/apps/app/src/lib/file_tree/utils/file_tree_utils.ts b/apps/app/src/lib/file_tree/utils/file_tree_utils.ts index 13e34997..ca77ad8e 100644 --- a/apps/app/src/lib/file_tree/utils/file_tree_utils.ts +++ b/apps/app/src/lib/file_tree/utils/file_tree_utils.ts @@ -1,5 +1,4 @@ import { type FileNode } from '@/types'; - export function find_unused_name( base_name: string, subtree: FileNode[], @@ -12,7 +11,6 @@ export function find_unused_name( base_name = `Untitled ${++i}`; return base_name; } - export function sort_file_tree(nodes: FileNode[]): FileNode[] { // Sort array in-place nodes.sort((a, b) => {