Files
tolaria/src/hooks/useDeleteActions.ts
lucaronin 5b1ecd7a94 refactor: remove tab bar — single note open at a time
Replace the multi-tab model with single-note navigation. One note is
open at a time; clicking any note (sidebar, wikilink, relationship)
replaces the current note in the editor. Back/Forward history still
works via Cmd+[/].

Removed: TabBar component, useClosedTabHistory, tab reorder/close/
reopen logic, Cmd+W/Cmd+Shift+T shortcuts, Close Tab and Reopen
Closed Tab menu items, tabLayout utility, and all related tests.

Simplified: useTabManagement (single-note), useAppNavigation (no tab
lookup), useKeyboardNavigation (note nav only), useNoteCreation (no
close-tab cleanup), useDeleteActions (deselect instead of close tab),
Editor (no TabBar render), Rust menu (removed 2 menu items).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 17:24:49 +01:00

107 lines
3.4 KiB
TypeScript

import { useCallback, useMemo, useState } from 'react'
import { invoke } from '@tauri-apps/api/core'
import { isTauri, mockInvoke } from '../mock-tauri'
import type { VaultEntry } from '../types'
interface ConfirmDeleteState {
title: string
message: string
confirmLabel?: string
onConfirm: () => void
}
interface UseDeleteActionsInput {
vaultPath: string
entries: VaultEntry[]
/** Called to deselect the note if it is currently open. */
onDeselectNote: (path: string) => void
removeEntry: (path: string) => void
setToastMessage: (msg: string | null) => void
}
export function useDeleteActions({
vaultPath,
entries,
onDeselectNote,
removeEntry,
setToastMessage,
}: UseDeleteActionsInput) {
const [confirmDelete, setConfirmDelete] = useState<ConfirmDeleteState | null>(null)
const trashedCount = useMemo(() => entries.filter(e => e.trashed).length, [entries])
const deleteNoteFromDisk = useCallback(async (path: string) => {
try {
if (isTauri()) await invoke('delete_note', { path })
else await mockInvoke('delete_note', { path })
onDeselectNote(path)
removeEntry(path)
return true
} catch (e) {
setToastMessage(`Failed to delete note: ${e}`)
return false
}
}, [onDeselectNote, removeEntry, setToastMessage])
const handleDeleteNote = useCallback(async (path: string) => {
setConfirmDelete({
title: 'Delete permanently?',
message: 'This note will be permanently deleted. This cannot be undone.',
onConfirm: async () => {
setConfirmDelete(null)
const ok = await deleteNoteFromDisk(path)
if (ok) setToastMessage('Note permanently deleted')
},
})
}, [deleteNoteFromDisk, setToastMessage])
const handleBulkDeletePermanently = useCallback((paths: string[]) => {
const count = paths.length
setConfirmDelete({
title: `Delete ${count} ${count === 1 ? 'note' : 'notes'} permanently?`,
message: `${count === 1 ? 'This note' : `These ${count} notes`} will be permanently deleted. This cannot be undone.`,
onConfirm: async () => {
setConfirmDelete(null)
let ok = 0
for (const path of paths) {
if (await deleteNoteFromDisk(path)) ok++
}
if (ok > 0) setToastMessage(`${ok} note${ok > 1 ? 's' : ''} permanently deleted`)
},
})
}, [deleteNoteFromDisk, setToastMessage])
const handleEmptyTrash = useCallback(() => {
if (trashedCount === 0) return
setConfirmDelete({
title: 'Empty Trash?',
message: `Permanently delete all ${trashedCount} trashed ${trashedCount === 1 ? 'note' : 'notes'}? This cannot be undone.`,
confirmLabel: 'Empty Trash',
onConfirm: async () => {
setConfirmDelete(null)
try {
const tauriInvoke = isTauri() ? invoke : mockInvoke
const deleted = await tauriInvoke<string[]>('empty_trash', { vaultPath })
for (const path of deleted) {
onDeselectNote(path)
removeEntry(path)
}
setToastMessage(`${deleted.length} note${deleted.length !== 1 ? 's' : ''} permanently deleted`)
} catch (e) {
setToastMessage(`Failed to empty trash: ${e}`)
}
},
})
}, [trashedCount, vaultPath, onDeselectNote, removeEntry, setToastMessage])
return {
confirmDelete,
setConfirmDelete,
trashedCount,
deleteNoteFromDisk,
handleDeleteNote,
handleBulkDeletePermanently,
handleEmptyTrash,
}
}