feat(workspace): confirm deletion of notes and bullets with nested subpoints
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 56s

This commit is contained in:
sandrodach committed 2026-10-25 11:26:36 +01:00
1 parent 920716be25
commit 0affb827a4
1 file changed
+94 -26
@@ -611,12 +611,18 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
const handleLeaveShare = async () => {
if (!selectedDoc || !currentUser) return;
if (
!window.confirm(
'Möchtest du diese freigegebene Notiz wirklich aus deinem Workspace entfernen?'
)
)
return;
const totalBlocks = selectedDoc.blocks?.length || 0;
const subpointsCount = (selectedDoc.blocks || []).filter((b) => (b.level || 0) > 0).length;
const title = selectedDoc.title || 'Geteilte Notiz';
let confirmMsg = 'Möchtest du diese freigegebene Notiz wirklich aus deinem Workspace entfernen?';
if (totalBlocks > 0 && subpointsCount > 0) {
confirmMsg = `Möchtest du die geteilte Notiz "${title}" mit allen ${totalBlocks} Punkten (inklusive ${subpointsCount} Unterpunkten) wirklich aus deinem Workspace entfernen?`;
} else if (totalBlocks > 0) {
confirmMsg = `Möchtest du die geteilte Notiz "${title}" wirklich aus deinem Workspace entfernen?`;
}
if (!window.confirm(confirmMsg)) return;
try {
await api.deleteDocument(selectedDoc.id);
setShowShareModal(false);
@@ -691,7 +697,21 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
};
const handleDeleteDoc = async (id: string) => {
if (!window.confirm('Dokument wirklich löschen?')) return;
const docToDelete = docs.find((d) => d.id === id) || (selectedDoc?.id === id ? selectedDoc : null);
const title = docToDelete?.title || selectedDoc?.title || 'Notiz';
const totalBlocks = docToDelete?.blocks?.length || 0;
const subpointsCount = (docToDelete?.blocks || []).filter((b) => (b.level || 0) > 0).length;
let confirmMsg = `Möchtest du die Notiz "${title}" wirklich löschen?`;
if (totalBlocks > 0) {
if (subpointsCount > 0) {
confirmMsg = `Möchtest du die Notiz "${title}" mit allen ${totalBlocks} Aufzählungspunkten (inklusive ${subpointsCount} Unterpunkten) wirklich unwiderruflich löschen?`;
} else {
confirmMsg = `Möchtest du die Notiz "${title}" mit allen ${totalBlocks} Aufzählungspunkten wirklich löschen?`;
}
}
if (!window.confirm(confirmMsg)) return;
triggerHaptic(20);
await api.deleteDocument(id);
const remaining = docs.filter((d) => d.id !== id);
@@ -953,25 +973,79 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
}
};
// Delete block and focus the preceding one
const deleteBlockAndFocusPrev = (index: number) => {
if (isReadOnly || !selectedDoc || selectedDoc.blocks.length <= 1) return;
const prevBlock = selectedDoc.blocks[index - 1] || selectedDoc.blocks[index + 1];
const targetId = selectedDoc.blocks[index].id;
removeBlock(targetId);
// Delete block and all its descendant sub-bullets with confirmation
const removeBlock = (blockId: string): boolean => {
if (isReadOnly || !selectedDoc) return false;
const blocks = selectedDoc.blocks;
const index = blocks.findIndex((b) => b.id === blockId);
if (index === -1) return false;
const targetBlock = blocks[index];
const targetLevel = targetBlock.level || 0;
// Find all descendant sub-bullets directly or indirectly nested under this block
const descendantIds: string[] = [];
for (let i = index + 1; i < blocks.length; i++) {
const cur = blocks[i];
const curLevel = cur.level || 0;
if (curLevel <= targetLevel) break;
descendantIds.push(cur.id);
}
if (descendantIds.length > 0) {
const count = descendantIds.length;
const snippet = targetBlock.content?.trim() ? ` "${targetBlock.content.trim().slice(0, 30)}..."` : '';
const confirmText =
count === 1
? `Dieser Aufzählungspunkt${snippet} enthält 1 Unterpunkt. Möchtest du ihn mitsamt dem Unterpunkt wirklich löschen?`
: `Dieser Aufzählungspunkt${snippet} enthält ${count} Unterpunkte. Möchtest du ihn mitsamt allen ${count} Unterpunkten wirklich löschen?`;
if (!window.confirm(confirmText)) {
return false;
}
}
triggerHaptic(15);
const idsToDelete = new Set([blockId, ...descendantIds]);
let nextBlocks = blocks.filter((b) => !idsToDelete.has(b.id));
if (nextBlocks.length === 0) {
nextBlocks = [
{
id: `blk_${Date.now()}_1`,
type: 'bullet',
content: '',
level: 0,
},
];
}
// Determine fallback block to focus
const prevBlock = index > 0 ? blocks[index - 1] : nextBlocks[0];
persistDocument({ ...selectedDoc, blocks: nextBlocks });
if (prevBlock) {
setLastFocusedBlockId(prevBlock.id);
setTimeout(() => {
const el = blockInputRefs.current[prevBlock.id];
if (el) {
el.focus();
// Put cursor at end of text
if ('setSelectionRange' in el) {
if ('setSelectionRange' in el && typeof el.value === 'string') {
el.setSelectionRange(el.value.length, el.value.length);
}
}
}, 20);
}, 30);
}
return true;
};
// Delete block and focus the preceding one
const deleteBlockAndFocusPrev = (index: number) => {
if (isReadOnly || !selectedDoc) return;
const targetBlock = selectedDoc.blocks[index];
if (!targetBlock) return;
removeBlock(targetBlock.id);
};
const updateBlock = (blockId: string, patch: Partial<DocumentBlock>) => {
@@ -982,12 +1056,6 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
persistDocument({ ...selectedDoc, blocks: nextBlocks });
};
const removeBlock = (blockId: string) => {
if (isReadOnly || !selectedDoc || selectedDoc.blocks.length <= 1) return;
const nextBlocks = selectedDoc.blocks.filter((b) => b.id !== blockId);
persistDocument({ ...selectedDoc, blocks: nextBlocks });
};
const toggleCollapse = (blockId: string) => {
if (!selectedDoc) return;
const block = selectedDoc.blocks.find((b) => b.id === blockId);
@@ -1050,13 +1118,13 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
indentBlock(index);
}
} else if (e.key === 'Backspace' && (!block.content || block.content === '')) {
if (selectedDoc?.blocks && selectedDoc.blocks.length > 1) {
if ((block.level || 0) > 0) {
e.preventDefault();
outdentBlock(block.id);
} else if (selectedDoc?.blocks && selectedDoc.blocks.length > 1) {
e.preventDefault();
setSlashBlockId(null);
deleteBlockAndFocusPrev(index);
} else if ((block.level || 0) > 0) {
e.preventDefault();
outdentBlock(block.id);
}
}
};