diff --git a/src/pages/Toolbar.tsx b/src/pages/Toolbar.tsx index 0bc14f1..47a87d8 100644 --- a/src/pages/Toolbar.tsx +++ b/src/pages/Toolbar.tsx @@ -98,6 +98,15 @@ export default function Toolbar() { refetchInterval: 5_000, }) + const { data: vfsList } = useQuery({ + queryKey: ['vfs', 'list'], + queryFn: async () => { + const response = await rclone('/vfs/list') + return response?.vfses ?? [] + }, + refetchInterval: 5_000, + }) + const remotes = useMemo(() => remotesQuery.data ?? [], [remotesQuery.data]) const remoteTypes = useMemo(() => remoteTypesQuery.data ?? {}, [remoteTypesQuery.data]) @@ -115,12 +124,13 @@ export default function Toolbar() { useEffect(() => { console.log(`${mountList?.length} mounts`) console.log(`${serveList?.length} serves`) + console.log(`${vfsList?.length} vfses`) const { results } = runToolbarEngine(searchStringDebounced, remotes, remoteTypes) startTransition(() => { setEngineResults(results) }) - }, [mountList, serveList, searchStringDebounced, remotes, remoteTypes]) + }, [mountList, serveList, vfsList, searchStringDebounced, remotes, remoteTypes]) useEffect(() => { let unlisten: (() => void) | undefined @@ -190,16 +200,25 @@ export default function Toolbar() { }, []) const handleExecute = useCallback(async (result: ResolvedToolbarResult) => { - await closeToolbar() - setSearchString('') + let keepOpen = false + try { const action = result.resolve() await action.onPress(result.args, { openWindow, + updateText: (text: string) => { + setSearchString(text) + keepOpen = true + }, }) } catch (error) { console.error('[Toolbar] Failed to execute action', error) } + + if (!keepOpen) { + await closeToolbar() + setSearchString('') + } }, []) const executeShortcutAtIndex = useCallback( diff --git a/toolbar/actions.ts b/toolbar/actions.ts index c35b687..f248895 100644 --- a/toolbar/actions.ts +++ b/toolbar/actions.ts @@ -755,6 +755,7 @@ const actions: ToolbarActionDefinition[] = [ ) ) } + results.push(createBaseResult('Back', 'Return to menu', { _action: 'back' }, 50)) return results } @@ -780,15 +781,17 @@ const actions: ToolbarActionDefinition[] = [ return results }, - onPress: async (args) => { + onPress: async (args, context) => { + if (args._action === 'back') { + context.updateText('') + return + } + const remote = typeof args.remote === 'string' && args.remote.length > 0 ? args.remote : undefined if (!remote) { - await notify({ - title: 'Error', - body: 'Please specify a remote to browse', - }) + context.updateText('Browse ') return } @@ -1146,6 +1149,115 @@ const actions: ToolbarActionDefinition[] = [ await getCurrentWindow().emit('close-app') }, }, + { + id: 'vfs', + label: 'VFS', + description: COMMAND_DESCRIPTIONS.vfs, + keywords: COMMAND_KEYWORDS.vfs, + getDefaultResult: () => createBaseResult('VFS', 'Specify a cache to forget', {}, 35), + getResults: ({ query }) => { + if (query && !matchesKeyword(query, COMMAND_KEYWORDS.vfs)) { + return [] + } + + const results: ToolbarActionResult[] = [] + + const activeVfses = queryClient.getQueryData(['vfs', 'list']) as string[] | undefined + + if (activeVfses && activeVfses.length > 0) { + for (const vfs of activeVfses) { + results.push( + createBaseResult( + `Forget ${vfs}`, + 'Clear the VFS directory cache', + { _action: 'forget', _fs: vfs }, + 180 + ) + ) + } + + if (activeVfses.length >= 2) { + results.push( + createBaseResult( + `Forget All VFS Caches (${activeVfses.length} active)`, + 'Clear all VFS directory caches', + { _action: 'forget_all' }, + 170 + ) + ) + } + } else { + results.push( + createBaseResult('Back', 'No active VFS caches', { _action: 'back' }, 35) + ) + } + + return results + }, + onPress: async (args, context) => { + if (!args._action) { + context.updateText('VFS ') + return + } + + if (args._action === 'back') { + context.updateText('') + return + } + + if (args._action === 'forget') { + const fs = args._fs as string + try { + await rclone('/vfs/forget', { + params: { + query: { + fs, + }, + }, + }) + await notify({ + title: 'VFS Cache Cleared', + body: `Directory cache for ${fs} has been cleared`, + }) + queryClient.setQueryData( + ['vfs', 'list'], + (old: string[] | undefined) => old?.filter((v) => v !== fs) ?? [] + ) + } catch (error) { + console.error('[toolbar] failed to forget VFS cache', error) + await message( + error instanceof Error ? error.message : 'Failed to clear VFS cache', + { + title: 'VFS Forget', + kind: 'error', + } + ) + } + return + } + + if (args._action === 'forget_all') { + try { + await rclone('/vfs/forget') + await notify({ + title: 'All VFS Caches Cleared', + body: 'All VFS directory caches have been cleared', + }) + queryClient.setQueryData(['vfs', 'list'], []) + } catch (error) { + console.error('[toolbar] failed to forget all VFS caches', error) + await message( + error instanceof Error ? error.message : 'Failed to clear all VFS caches', + { + title: 'VFS Forget All', + kind: 'error', + } + ) + } + return + } + }, + }, ] export function getToolbarActions(): ToolbarActionDefinition[] { diff --git a/toolbar/constants.ts b/toolbar/constants.ts index c85108b..fb70a8b 100644 --- a/toolbar/constants.ts +++ b/toolbar/constants.ts @@ -24,6 +24,7 @@ const REMOTE_EDIT_DESCRIPTION = 'Edit a remote.' const REMOTE_AUTO_MOUNT_DESCRIPTION = 'Configure auto mount options for a remote.' const REMOTE_LIST_DESCRIPTION = 'Show all configured remotes.' const QUIT_DESCRIPTION = 'Quit the application.' +const VFS_DESCRIPTION = 'Forget the local cache for one or all remotes.' export const COMMAND_CONFIG = { copy: { route: '/copy', windowLabel: 'Copy' }, @@ -47,6 +48,7 @@ export const COMMAND_CONFIG = { remoteAutoMount: {}, remoteList: {}, quit: {}, + vfs: {}, } as const export const COMMAND_DESCRIPTIONS: Record = { @@ -71,6 +73,7 @@ export const COMMAND_DESCRIPTIONS: Record = { remoteAutoMount: REMOTE_AUTO_MOUNT_DESCRIPTION, remoteList: REMOTE_LIST_DESCRIPTION, quit: QUIT_DESCRIPTION, + vfs: VFS_DESCRIPTION, } export const COMMAND_KEYWORDS: Record = { @@ -95,4 +98,5 @@ export const COMMAND_KEYWORDS: Record = { remoteAutoMount: ['mount', 'remote', 'update', 'change'], remoteList: ['remote', 'list', 'show'], quit: ['quit', 'exit', 'close'], + vfs: ['vfs', 'cache', 'forget'], } diff --git a/toolbar/types.ts b/toolbar/types.ts index 60d39ef..0a40900 100644 --- a/toolbar/types.ts +++ b/toolbar/types.ts @@ -20,6 +20,7 @@ export type ToolbarCommandId = | 'remoteAutoMount' | 'quit' | 'remoteList' + | 'vfs' export type ToolbarActionArgs = Record @@ -32,6 +33,7 @@ export interface ToolbarActionResult { export interface ToolbarActionOnPressContext { openWindow: (options: { name: string; url: string }) => Promise + updateText: (text: string) => void } export interface ToolbarActionPath {