From f387f2f0761c2fdd44d1152387cb8f863dda6f6c Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Fri, 25 Aug 2023 05:04:50 +0800 Subject: [PATCH] [ENG-992] Add `search.pathsCount` route (#1244) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit search.pathsCount route Co-authored-by: VĂ­tor Vasconcellos --- core/src/api/search.rs | 94 ++++++++++++++-------- interface/app/$libraryId/Explorer/store.ts | 7 ++ interface/app/$libraryId/ephemeral.tsx | 12 +-- packages/client/src/core.ts | 7 +- 4 files changed, 77 insertions(+), 43 deletions(-) diff --git a/core/src/api/search.rs b/core/src/api/search.rs index 26ff2f825..6a91e23b6 100644 --- a/core/src/api/search.rs +++ b/core/src/api/search.rs @@ -19,7 +19,6 @@ use prisma_client_rust::{operator, or}; use rspc::{alpha::AlphaRouter, ErrorCode}; use serde::{Deserialize, Serialize}; use specta::Type; -use tracing::trace; use super::{Ctx, R}; @@ -275,14 +274,23 @@ impl ObjectFilterArgs { pub fn mount() -> AlphaRouter { R.router() - .procedure("ephemeral-paths", { + .procedure("ephemeralPaths", { + #[derive(Serialize, Deserialize, Type, Debug, Clone)] + #[serde(rename_all = "camelCase", tag = "field", content = "value")] + enum NonIndexedPathOrdering { + Name(SortOrder), + SizeInBytes(SortOrder), + DateCreated(SortOrder), + DateModified(SortOrder), + } + #[derive(Deserialize, Type, Debug)] #[serde(rename_all = "camelCase")] struct NonIndexedPath { path: PathBuf, with_hidden_files: bool, #[specta(optional)] - order: Option, + order: Option, } R.with2(library()).query( @@ -297,54 +305,50 @@ pub fn mount() -> AlphaRouter { if let Some(order) = order { match order { - FilePathSearchOrdering::Name(order) => { + NonIndexedPathOrdering::Name(order) => { paths.entries.sort_unstable_by(|path1, path2| { - if let SortOrder::Desc = order { - path2 - .name() - .to_lowercase() - .cmp(&path1.name().to_lowercase()) - } else { - path1 - .name() - .to_lowercase() - .cmp(&path2.name().to_lowercase()) + let one = path1.name().to_lowercase(); + let two = path2.name().to_lowercase(); + + match order { + SortOrder::Desc => two.cmp(&one), + SortOrder::Asc => one.cmp(&two), } }); } - FilePathSearchOrdering::SizeInBytes(order) => { + NonIndexedPathOrdering::SizeInBytes(order) => { paths.entries.sort_unstable_by(|path1, path2| { - if let SortOrder::Desc = order { - path2.size_in_bytes().cmp(&path1.size_in_bytes()) - } else { - path1.size_in_bytes().cmp(&path2.size_in_bytes()) + let one = path1.size_in_bytes(); + let two = path2.size_in_bytes(); + + match order { + SortOrder::Desc => two.cmp(&one), + SortOrder::Asc => one.cmp(&two), } }); } - FilePathSearchOrdering::DateCreated(order) => { + NonIndexedPathOrdering::DateCreated(order) => { paths.entries.sort_unstable_by(|path1, path2| { - if let SortOrder::Desc = order { - path2.date_created().cmp(&path1.date_created()) - } else { - path1.date_created().cmp(&path2.date_created()) + let one = path1.date_created(); + let two = path2.date_created(); + + match order { + SortOrder::Desc => two.cmp(&one), + SortOrder::Asc => one.cmp(&two), } }); } - FilePathSearchOrdering::DateModified(order) => { + NonIndexedPathOrdering::DateModified(order) => { paths.entries.sort_unstable_by(|path1, path2| { - if let SortOrder::Desc = order { - path2.date_modified().cmp(&path1.date_modified()) - } else { - path1.date_modified().cmp(&path2.date_modified()) + let one = path1.date_modified(); + let two = path2.date_modified(); + + match order { + SortOrder::Desc => two.cmp(&one), + SortOrder::Asc => one.cmp(&two), } }); } - FilePathSearchOrdering::DateIndexed(_) => { - trace!("Can't order by indexed date on ephemeral paths route, ignoring...") - } - FilePathSearchOrdering::Object(_) => { - trace!("Receive an Object sort ordeding at ephemeral paths route, ignoring...") - } } } @@ -441,6 +445,26 @@ pub fn mount() -> AlphaRouter { }, ) }) + .procedure("pathsCount", { + #[derive(Deserialize, Type, Debug)] + #[serde(rename_all = "camelCase")] + #[specta(inline)] + struct Args { + #[serde(default)] + filter: FilePathFilterArgs, + } + + R.with2(library()) + .query(|(_, library), Args { filter }| async move { + let Library { db, .. } = library.as_ref(); + + Ok(db + .file_path() + .count(filter.into_params(db).await?) + .exec() + .await? as u32) + }) + }) .procedure("objects", { #[derive(Deserialize, Type, Debug)] #[serde(rename_all = "camelCase")] diff --git a/interface/app/$libraryId/Explorer/store.ts b/interface/app/$libraryId/Explorer/store.ts index 26fbcab71..023402a28 100644 --- a/interface/app/$libraryId/Explorer/store.ts +++ b/interface/app/$libraryId/Explorer/store.ts @@ -159,3 +159,10 @@ export const objectOrderingKeysSchema = z.union([ z.literal('dateAccessed').describe('Date Accessed'), z.literal('kind').describe('Kind') ]); + +export const nonIndexedPathOrderingSchema = z.union([ + z.literal('name').describe('Name'), + z.literal('sizeInBytes').describe('Size'), + z.literal('dateCreated').describe('Date Created'), + z.literal('dateModified').describe('Date Modified') +]); diff --git a/interface/app/$libraryId/ephemeral.tsx b/interface/app/$libraryId/ephemeral.tsx index 38f5197ee..613b8e3db 100644 --- a/interface/app/$libraryId/ephemeral.tsx +++ b/interface/app/$libraryId/ephemeral.tsx @@ -1,5 +1,5 @@ import { Suspense, memo, useDeferredValue, useMemo } from 'react'; -import { type FilePathSearchOrdering, getExplorerItemData, useLibraryQuery } from '@sd/client'; +import { type NonIndexedPathOrdering, getExplorerItemData, useLibraryQuery } from '@sd/client'; import { Tooltip } from '@sd/ui'; import { type PathParams, PathParamsSchema } from '~/app/route-schemas'; import { useOperatingSystem, useZodSearchParams } from '~/hooks'; @@ -8,8 +8,8 @@ import { ExplorerContextProvider } from './Explorer/Context'; import { DefaultTopBarOptions } from './Explorer/TopBarOptions'; import { createDefaultExplorerSettings, - filePathOrderingKeysSchema, - getExplorerStore + getExplorerStore, + nonIndexedPathOrderingSchema } from './Explorer/store'; import { useExplorer, useExplorerSettings } from './Explorer/useExplorer'; import { TopBarPortal } from './TopBar/Portal'; @@ -22,7 +22,7 @@ const EphemeralExplorer = memo((props: { args: PathParams }) => { const explorerSettings = useExplorerSettings({ settings: useMemo( () => - createDefaultExplorerSettings({ + createDefaultExplorerSettings({ order: { field: 'name', value: 'Asc' @@ -30,14 +30,14 @@ const EphemeralExplorer = memo((props: { args: PathParams }) => { }), [] ), - orderingKeys: filePathOrderingKeysSchema + orderingKeys: nonIndexedPathOrderingSchema }); const settingsSnapshot = explorerSettings.useSettingsSnapshot(); const query = useLibraryQuery( [ - 'search.ephemeral-paths', + 'search.ephemeralPaths', { path: path ?? (os === 'windows' ? 'C:\\' : '/'), withHiddenFiles: true, diff --git a/packages/client/src/core.ts b/packages/client/src/core.ts index b2f1be5b9..b41eef871 100644 --- a/packages/client/src/core.ts +++ b/packages/client/src/core.ts @@ -25,9 +25,10 @@ export type Procedures = { { key: "notifications.dismissAll", input: never, result: null } | { key: "notifications.get", input: never, result: Notification[] } | { key: "preferences.get", input: LibraryArgs, result: LibraryPreferences } | - { key: "search.ephemeral-paths", input: LibraryArgs, result: NonIndexedFileSystemEntries } | + { key: "search.ephemeralPaths", input: LibraryArgs, result: NonIndexedFileSystemEntries } | { key: "search.objects", input: LibraryArgs, result: SearchData } | { key: "search.paths", input: LibraryArgs, result: SearchData } | + { key: "search.pathsCount", input: LibraryArgs<{ filter?: FilePathFilterArgs }>, result: number } | { key: "sync.messages", input: LibraryArgs, result: CRDTOperation[] } | { key: "tags.get", input: LibraryArgs, result: Tag | null } | { key: "tags.getForObject", input: LibraryArgs, result: Tag[] } | @@ -236,10 +237,12 @@ export type NodeState = ({ id: string; name: string; p2p_port: number | null; p2 export type NonIndexedFileSystemEntries = { entries: ExplorerItem[]; errors: Error[] } -export type NonIndexedPath = { path: string; withHiddenFiles: boolean; order?: FilePathSearchOrdering | null } +export type NonIndexedPath = { path: string; withHiddenFiles: boolean; order?: NonIndexedPathOrdering | null } export type NonIndexedPathItem = { path: string; name: string; extension: string; kind: number; is_dir: boolean; date_created: string; date_modified: string; size_in_bytes_bytes: number[] } +export type NonIndexedPathOrdering = { field: "name"; value: SortOrder } | { field: "sizeInBytes"; value: SortOrder } | { field: "dateCreated"; value: SortOrder } | { field: "dateModified"; value: SortOrder } + /** * Represents a single notification. */