diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 340425a8e7..eb34872db4 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -98,7 +98,10 @@ "broken_relations_to_be_deleted": "Following relations will be broken and deleted ({{ relationCount}})", "cancel": "Cancel", "ok": "OK", - "deleted_relation_text": "Note {{- note}} (to be deleted) is referenced by relation {{- relation}} originating from {{- source}}." + "deleted_relation_text": "Note {{- note}} (to be deleted) is referenced by relation {{- relation}} originating from {{- source}}.", + "no_clones_message": "The selected notes have no clones.", + "delete_clones_with_count": "Also delete {{count}} other clone(s) (can be undone in recent changes)", + "and_more_clones": "and {{count}} more..." }, "export": { "export_note_title": "Export note", diff --git a/apps/client/src/widgets/dialogs/delete_notes.tsx b/apps/client/src/widgets/dialogs/delete_notes.tsx index c58d440b72..a8f0f5adb8 100644 --- a/apps/client/src/widgets/dialogs/delete_notes.tsx +++ b/apps/client/src/widgets/dialogs/delete_notes.tsx @@ -11,6 +11,11 @@ import Button from "../react/Button.jsx"; import Alert from "../react/Alert.jsx"; import { useTriliumEvent } from "../react/hooks.jsx"; +interface CloneInfo { + totalCloneCount: number; + clonePaths: string[]; +} + export interface ResolveOptions { proceed: boolean; deleteAllClones?: boolean; @@ -34,8 +39,9 @@ export default function DeleteNotesDialog() { const [ deleteAllClones, setDeleteAllClones ] = useState(false); const [ eraseNotes, setEraseNotes ] = useState(!!opts.forceDeleteAllClones); const [ brokenRelations, setBrokenRelations ] = useState([]); - const [ noteIdsToBeDeleted, setNoteIdsToBeDeleted ] = useState([]); + const [ noteIdsToBeDeleted, setNoteIdsToBeDeleted ] = useState([]); const [ shown, setShown ] = useState(false); + const [ cloneInfo, setCloneInfo ] = useState({ totalCloneCount: 0, clonePaths: [] }); const okButtonRef = useRef(null); useTriliumEvent("showDeleteNotesDialog", (opts) => { @@ -43,11 +49,53 @@ export default function DeleteNotesDialog() { setShown(true); }) + // Calculate clone information when branches change + useEffect(() => { + const { branchIdsToDelete } = opts; + if (!branchIdsToDelete || branchIdsToDelete.length === 0) { + setCloneInfo({ totalCloneCount: 0, clonePaths: [] }); + return; + } + + async function calculateCloneInfo() { + const branches = froca.getBranches(branchIdsToDelete!, true); + const uniqueNoteIds = [...new Set(branches.map(b => b.noteId))]; + const notes = await froca.getNotes(uniqueNoteIds); + + let totalCloneCount = 0; + const clonePaths: string[] = []; + + for (const note of notes) { + const parentBranches = note.getParentBranches(); + // Clones are additional parent branches beyond the one being deleted + const otherBranches = parentBranches.filter(b => !branchIdsToDelete!.includes(b.branchId)); + + if (otherBranches.length > 0) { + totalCloneCount += otherBranches.length; + + // Get paths for preview (limit to first 5 total) + for (const branch of otherBranches) { + if (clonePaths.length >= 5) break; + const pathHtml = (await link.createLink(note.noteId, { + showNotePath: true, + referenceLink: false + })).html(); + clonePaths.push(pathHtml); + } + } + } + + setCloneInfo({ totalCloneCount, clonePaths }); + } + + calculateCloneInfo(); + }, [opts.branchIdsToDelete]); + useEffect(() => { const { branchIdsToDelete, forceDeleteAllClones } = opts; if (!branchIdsToDelete || branchIdsToDelete.length === 0) { return; - } + } server.post("delete-notes-preview", { branchIdsToDelete, @@ -81,8 +129,10 @@ export default function DeleteNotesDialog() { } show={shown} > - ; } } + +interface DeleteAllClonesOptionProps { + cloneInfo: CloneInfo; + deleteAllClones: boolean; + setDeleteAllClones: (value: boolean) => void; +} + +function DeleteAllClonesOption({ cloneInfo, deleteAllClones, setDeleteAllClones }: DeleteAllClonesOptionProps) { + const { totalCloneCount, clonePaths } = cloneInfo; + + if (totalCloneCount === 0) { + return ( +
+ {t("delete_notes.no_clones_message")} +
+ ); + } + + return ( +
+ + {clonePaths.length > 0 && ( +
    + {clonePaths.map((path, index) => ( +
  • + ))} + {totalCloneCount > clonePaths.length && ( +
  • {t("delete_notes.and_more_clones", { count: totalCloneCount - clonePaths.length })}
  • + )} +
+ )} +
+ ); +}