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 () => {
|
const handleLeaveShare = async () => {
|
||||||
if (!selectedDoc || !currentUser) return;
|
if (!selectedDoc || !currentUser) return;
|
||||||
if (
|
const totalBlocks = selectedDoc.blocks?.length || 0;
|
||||||
!window.confirm(
|
const subpointsCount = (selectedDoc.blocks || []).filter((b) => (b.level || 0) > 0).length;
|
||||||
'Möchtest du diese freigegebene Notiz wirklich aus deinem Workspace entfernen?'
|
const title = selectedDoc.title || 'Geteilte Notiz';
|
||||||
)
|
|
||||||
)
|
let confirmMsg = 'Möchtest du diese freigegebene Notiz wirklich aus deinem Workspace entfernen?';
|
||||||
return;
|
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 {
|
try {
|
||||||
await api.deleteDocument(selectedDoc.id);
|
await api.deleteDocument(selectedDoc.id);
|
||||||
setShowShareModal(false);
|
setShowShareModal(false);
|
||||||
@@ -691,7 +697,21 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteDoc = async (id: string) => {
|
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);
|
triggerHaptic(20);
|
||||||
await api.deleteDocument(id);
|
await api.deleteDocument(id);
|
||||||
const remaining = docs.filter((d) => d.id !== 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
|
// Delete block and all its descendant sub-bullets with confirmation
|
||||||
const deleteBlockAndFocusPrev = (index: number) => {
|
const removeBlock = (blockId: string): boolean => {
|
||||||
if (isReadOnly || !selectedDoc || selectedDoc.blocks.length <= 1) return;
|
if (isReadOnly || !selectedDoc) return false;
|
||||||
const prevBlock = selectedDoc.blocks[index - 1] || selectedDoc.blocks[index + 1];
|
const blocks = selectedDoc.blocks;
|
||||||
const targetId = selectedDoc.blocks[index].id;
|
const index = blocks.findIndex((b) => b.id === blockId);
|
||||||
removeBlock(targetId);
|
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) {
|
if (prevBlock) {
|
||||||
setLastFocusedBlockId(prevBlock.id);
|
setLastFocusedBlockId(prevBlock.id);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const el = blockInputRefs.current[prevBlock.id];
|
const el = blockInputRefs.current[prevBlock.id];
|
||||||
if (el) {
|
if (el) {
|
||||||
el.focus();
|
el.focus();
|
||||||
// Put cursor at end of text
|
if ('setSelectionRange' in el && typeof el.value === 'string') {
|
||||||
if ('setSelectionRange' in el) {
|
|
||||||
el.setSelectionRange(el.value.length, el.value.length);
|
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>) => {
|
const updateBlock = (blockId: string, patch: Partial<DocumentBlock>) => {
|
||||||
@@ -982,12 +1056,6 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
|||||||
persistDocument({ ...selectedDoc, blocks: nextBlocks });
|
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) => {
|
const toggleCollapse = (blockId: string) => {
|
||||||
if (!selectedDoc) return;
|
if (!selectedDoc) return;
|
||||||
const block = selectedDoc.blocks.find((b) => b.id === blockId);
|
const block = selectedDoc.blocks.find((b) => b.id === blockId);
|
||||||
@@ -1050,13 +1118,13 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
|||||||
indentBlock(index);
|
indentBlock(index);
|
||||||
}
|
}
|
||||||
} else if (e.key === 'Backspace' && (!block.content || block.content === '')) {
|
} 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();
|
e.preventDefault();
|
||||||
setSlashBlockId(null);
|
setSlashBlockId(null);
|
||||||
deleteBlockAndFocusPrev(index);
|
deleteBlockAndFocusPrev(index);
|
||||||
} else if ((block.level || 0) > 0) {
|
|
||||||
e.preventDefault();
|
|
||||||
outdentBlock(block.id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in new issue
Block a user