feat(workspace): confirm deletion of notes and bullets with nested subpoints
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 56s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 56s
This commit is contained in:
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user