diff --git a/core/src/location/file_path_helper/mod.rs b/core/src/location/file_path_helper/mod.rs index 4be0982f0..325c52545 100644 --- a/core/src/location/file_path_helper/mod.rs +++ b/core/src/location/file_path_helper/mod.rs @@ -79,6 +79,7 @@ file_path::select!(file_path_walker { date_modified inode size_in_bytes_bytes + hidden }); file_path::select!(file_path_to_handle_custom_uri { pub_id @@ -128,12 +129,13 @@ pub struct FilePathMetadata { pub hidden: bool, } -pub fn path_is_hidden(path: &Path, metadata: &Metadata) -> bool { +pub fn path_is_hidden(path: impl AsRef, metadata: &Metadata) -> bool { #[cfg(target_family = "unix")] { use std::ffi::OsStr; let _ = metadata; // just to avoid warnings on Linux if path + .as_ref() .file_name() .and_then(OsStr::to_str) .map(|s| s.starts_with('.')) @@ -147,6 +149,7 @@ pub fn path_is_hidden(path: &Path, metadata: &Metadata) -> bool { { use std::os::macos::fs::MetadataExt; + // https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemDetails/FileSystemDetails.html#:~:text=UF_HIDDEN const UF_HIDDEN: u32 = 0x8000; if (metadata.st_flags() & UF_HIDDEN) == UF_HIDDEN { @@ -171,7 +174,10 @@ pub fn path_is_hidden(path: &Path, metadata: &Metadata) -> bool { } impl FilePathMetadata { - pub async fn from_path(path: &Path, metadata: &Metadata) -> Result { + pub async fn from_path( + path: impl AsRef, + metadata: &Metadata, + ) -> Result { let inode = { #[cfg(target_family = "unix")] { @@ -180,13 +186,13 @@ impl FilePathMetadata { #[cfg(target_family = "windows")] { - get_inode_from_path(&path).await? + get_inode_from_path(path.as_ref()).await? } }; Ok(Self { inode, - hidden: path_is_hidden(path, metadata), + hidden: path_is_hidden(path.as_ref(), metadata), size_in_bytes: metadata.len(), created_at: metadata.created_or_now().into(), modified_at: metadata.modified_or_now().into(), diff --git a/core/src/location/indexer/mod.rs b/core/src/location/indexer/mod.rs index 274769ef7..8d89ac132 100644 --- a/core/src/location/indexer/mod.rs +++ b/core/src/location/indexer/mod.rs @@ -232,6 +232,10 @@ async fn execute_indexer_update_step( (date_modified::NAME, json!(entry.metadata.modified_at)), date_modified::set(Some(entry.metadata.modified_at.into())), ), + ( + (hidden::NAME, json!(entry.metadata.hidden)), + hidden::set(Some(entry.metadata.hidden)), + ), ] .into_iter() .unzip(); diff --git a/core/src/location/indexer/rules/seed.rs b/core/src/location/indexer/rules/seed.rs index 0313fbfc9..117401b35 100644 --- a/core/src/location/indexer/rules/seed.rs +++ b/core/src/location/indexer/rules/seed.rs @@ -104,7 +104,7 @@ pub fn no_os_protected() -> SystemIndexerRule { "C:/Users/*/ntuser.dat*", "C:/Users/*/{ntuser.ini,ntuser.dat,NTUSER.DAT}", // User special folders (most of these the user dont even have permission to access) - "C:/Users/*/{Cookies,AppData,NetHood,Recent,PrintHood,SendTo,Templates,Start Menu,Application Data,Local Settings}", + "C:/Users/*/{Cookies,AppData,NetHood,Recent,PrintHood,SendTo,Templates,Start Menu,Application Data,Local Settings,My Documents}", // System special folders "C:/{$Recycle.Bin,$WinREAgent,Documents and Settings,Program Files,Program Files (x86),ProgramData,Recovery,PerfLogs,Windows,Windows.old}", // NTFS internal dir, can exists on any drive diff --git a/core/src/location/indexer/walk.rs b/core/src/location/indexer/walk.rs index 784bc567a..f060aaea3 100644 --- a/core/src/location/indexer/walk.rs +++ b/core/src/location/indexer/walk.rs @@ -296,7 +296,7 @@ where indexed_paths.insert(WalkingEntry { iso_file_path: iso_file_path_factory(root, true)?, - maybe_metadata: Some(FilePathMetadata::from_path(root, &metadata).await?), + maybe_metadata: Some(FilePathMetadata::from_path(&root, &metadata).await?), }); } @@ -378,7 +378,7 @@ where // Datetimes stored in DB loses a bit of precision, so we need to check against a delta // instead of using != operator || DateTime::::from(metadata.modified_at) - *date_modified - > Duration::milliseconds(1) + > Duration::milliseconds(1) || file_path.hidden.is_none() || metadata.hidden != file_path.hidden.unwrap_or_default() ) // We ignore the size of directories because it is not reliable, we need to // calculate it ourselves later @@ -528,7 +528,7 @@ where let Ok(metadata) = entry .metadata() .await - .map_err(|e| errors.push(FileIOError::from((entry.path(), e)).into())) + .map_err(|e| errors.push(FileIOError::from((¤t_path, e)).into())) else { continue 'entries; }; @@ -575,7 +575,7 @@ where // Then we mark this directory the be walked in too if let Some(ref mut to_walk) = maybe_to_walk { to_walk.push_back(ToWalkEntry { - path: entry.path(), + path: current_path.clone(), parent_dir_accepted_by_its_children: accept_by_children_dir, maybe_parent: Some(path.clone()), }); @@ -640,7 +640,7 @@ where continue; }; - let Ok(metadata) = FilePathMetadata::from_path(ancestor, &metadata) + let Ok(metadata) = FilePathMetadata::from_path(&ancestor, &metadata) .await .map_err(|e| errors.push(e.into())) else { diff --git a/core/src/location/manager/watcher/utils.rs b/core/src/location/manager/watcher/utils.rs index d0db96d92..ffbde062d 100644 --- a/core/src/location/manager/watcher/utils.rs +++ b/core/src/location/manager/watcher/utils.rs @@ -7,7 +7,7 @@ use crate::{ check_file_path_exists, create_file_path, file_path_with_object, filter_existing_file_path_params, isolated_file_path_data::extract_normalized_materialized_path_str, - loose_find_existing_file_path_params, FilePathError, FilePathMetadata, + loose_find_existing_file_path_params, path_is_hidden, FilePathError, FilePathMetadata, IsolatedFilePathData, MetadataExt, }, find_location, @@ -123,7 +123,7 @@ pub(super) async fn create_dir( library, iso_file_path, None, - FilePathMetadata::from_path(path, metadata).await?, + FilePathMetadata::from_path(&path, metadata).await?, ) .await?; @@ -176,7 +176,7 @@ async fn inner_create_file( let iso_file_path = IsolatedFilePathData::new(location_id, location_path, path, false)?; let extension = iso_file_path.extension.to_string(); - let metadata = FilePathMetadata::from_path(path, metadata).await?; + let metadata = FilePathMetadata::from_path(&path, metadata).await?; // First we check if already exist a file with this same inode number // if it does, we just update it @@ -416,6 +416,7 @@ async fn inner_update_file( } }; + let is_hidden = path_is_hidden(full_path, &fs_metadata); if file_path.cas_id != cas_id { let (sync_params, db_params): (Vec<_>, Vec<_>) = { use file_path::*; @@ -470,6 +471,16 @@ async fn inner_update_file( ((inode::NAME, serde_json::Value::Null), None) } }, + { + if is_hidden != file_path.hidden.unwrap_or_default() { + ( + (hidden::NAME, json!(inode)), + Some(hidden::set(Some(is_hidden))), + ) + } else { + ((hidden::NAME, serde_json::Value::Null), None) + } + }, ] .into_iter() .filter_map(|(sync_param, maybe_db_param)| { @@ -578,6 +589,26 @@ async fn inner_update_file( invalidate_query!(library, "search.paths"); invalidate_query!(library, "search.objects"); + } else if is_hidden != file_path.hidden.unwrap_or_default() { + sync.write_ops( + db, + ( + vec![sync.shared_update( + prisma_sync::file_path::SyncId { + pub_id: file_path.pub_id.clone(), + }, + file_path::hidden::NAME, + json!(is_hidden), + )], + db.file_path().update( + file_path::pub_id::equals(file_path.pub_id.clone()), + vec![file_path::hidden::set(Some(is_hidden))], + ), + ), + ) + .await?; + + invalidate_query!(library, "search.paths"); } Ok(()) @@ -649,7 +680,7 @@ pub(super) async fn rename( trace!("Updated {updated} file_paths"); } - let metadata = FilePathMetadata::from_path(new_path, &new_path_metadata).await?; + let is_hidden = path_is_hidden(new_path, &new_path_metadata); library .db @@ -663,7 +694,7 @@ pub(super) async fn rename( file_path::date_modified::set(Some( DateTime::::from(new_path_metadata.modified_or_now()).into(), )), - file_path::hidden::set(Some(metadata.hidden)), + file_path::hidden::set(Some(is_hidden)), ], ) .exec() diff --git a/core/src/location/manager/watcher/windows.rs b/core/src/location/manager/watcher/windows.rs index e4c082723..0875af4c5 100644 --- a/core/src/location/manager/watcher/windows.rs +++ b/core/src/location/manager/watcher/windows.rs @@ -131,11 +131,11 @@ impl<'lib> EventHandler<'lib> for WindowsEventHandler<'lib> { ) .await?; } else { - let metadata = fs::metadata(&paths[0]) - .await - .map_err(|e| FileIOError::from((&paths[0], e)))?; - let path = paths.remove(0); + let metadata = fs::metadata(&path) + .await + .map_err(|e| FileIOError::from((&path, e)))?; + if metadata.is_dir() { // Don't need to dispatch a recalculate directory event as `create_dir` dispatches // a `scan_location_sub_path` function, which recalculates the size already diff --git a/interface/app/$libraryId/ephemeral.tsx b/interface/app/$libraryId/ephemeral.tsx index 8be1f521b..d2693dd21 100644 --- a/interface/app/$libraryId/ephemeral.tsx +++ b/interface/app/$libraryId/ephemeral.tsx @@ -46,7 +46,7 @@ const EphemeralExplorer = memo((props: { args: PathParams }) => { 'search.ephemeralPaths', { path: path ?? (os === 'windows' ? 'C:\\' : '/'), - withHiddenFiles: false, + withHiddenFiles: settingsSnapshot.showHiddenFiles, order: settingsSnapshot.order } ], @@ -63,13 +63,6 @@ const EphemeralExplorer = memo((props: { args: PathParams }) => { const ret: ExplorerItem[] = []; for (const item of query.data.entries) { - if ( - !settingsSnapshot.showHiddenFiles && - item.type === 'NonIndexedPath' && - item.item.hidden - ) - continue; - if (settingsSnapshot.layoutMode !== 'media') ret.push(item); else { const { kind } = getExplorerItemData(item);