From 3c34c7fecc49f01465cf6cfa88d94738a791ee21 Mon Sep 17 00:00:00 2001 From: hexastack Date: Thu, 24 Oct 2024 08:20:00 +0100 Subject: [PATCH 1/9] fix: incomplete blocks removal during delete operation --- .../components/visual-editor/v2/Diagrams.tsx | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/frontend/src/components/visual-editor/v2/Diagrams.tsx b/frontend/src/components/visual-editor/v2/Diagrams.tsx index 8e2a478..8412072 100644 --- a/frontend/src/components/visual-editor/v2/Diagrams.tsx +++ b/frontend/src/components/visual-editor/v2/Diagrams.tsx @@ -399,48 +399,52 @@ const Diagrams = () => { } else { // Block Delete Case const ids = id.includes(",") ? id.split(",") : [id]; - const deletePromises = ids.map((id) => { - const block = getBlockFromCache(id); - return deleteBlock(id, { + for (const blockId of ids) { + const block = getBlockFromCache(blockId); + + await deleteBlock(blockId, { onSuccess() { // Update all linked blocks to remove any reference to the deleted block - [ + const linkedBlockIds = [ ...(block?.nextBlocks || []), ...(block?.previousBlocks || []), ...(block?.attachedBlock ? [block.attachedBlock] : []), ...(block?.attachedToBlock ? [block.attachedToBlock] : []), - ] - .map((bid) => getBlockFromCache(bid)) - .filter((b) => !!b) - .forEach((b) => { + ]; + + for (const linkedBlockId of linkedBlockIds) { + const linkedBlock = getBlockFromCache(linkedBlockId); + + if (linkedBlock) { updateCachedBlock({ - id: b.id, + id: linkedBlock.id, payload: { - ...b, - nextBlocks: b.nextBlocks?.filter( - (nextBlockId) => nextBlockId !== id, + ...linkedBlock, + nextBlocks: linkedBlock.nextBlocks?.filter( + (nextBlockId) => nextBlockId !== blockId, ), - previousBlocks: b.previousBlocks?.filter( - (previousBlockId) => previousBlockId !== id, + previousBlocks: linkedBlock.previousBlocks?.filter( + (previousBlockId) => previousBlockId !== blockId, ), attachedBlock: - b.attachedBlock === id ? undefined : b.attachedBlock, - attachedToBlock: - b.attachedToBlock === id + linkedBlock.attachedBlock === blockId ? undefined - : b.attachedToBlock, + : linkedBlock.attachedBlock, + attachedToBlock: + linkedBlock.attachedToBlock === blockId + ? undefined + : linkedBlock.attachedToBlock, }, strategy: "overwrite", }); - }); + } + } - deleteCachedBlock(id); + deleteCachedBlock(blockId); }, }); - }); - - await Promise.all(deletePromises); + } } deleteCallbackRef.current?.(); From 55fe415fd87dacf41714bffe6395cb0cc114eb26 Mon Sep 17 00:00:00 2001 From: hexastack Date: Tue, 29 Oct 2024 17:51:56 +0100 Subject: [PATCH 2/9] refactor: use deleteMany for multiple blocks removal --- api/src/chat/controllers/block.controller.ts | 25 +++ .../components/visual-editor/v2/Diagrams.tsx | 205 +++++++++--------- 2 files changed, 124 insertions(+), 106 deletions(-) diff --git a/api/src/chat/controllers/block.controller.ts b/api/src/chat/controllers/block.controller.ts index fd59e8a..3cc9dc9 100644 --- a/api/src/chat/controllers/block.controller.ts +++ b/api/src/chat/controllers/block.controller.ts @@ -323,4 +323,29 @@ export class BlockController extends BaseController< } return result; } + + /** + * Deletes multiple blocks by their IDs. + * @param ids - IDs of blocks to be deleted. + * @returns A Promise that resolves to the deletion result. + */ + @CsrfCheck(true) + @Delete('') + @HttpCode(204) + async deleteMany(@Body('ids') ids: string[]): Promise { + if (!ids || ids.length === 0) { + throw new BadRequestException('No IDs provided for deletion.'); + } + const deleteResult = await this.blockService.deleteMany({ + _id: { $in: ids }, + }); + + if (deleteResult.deletedCount === 0) { + this.logger.warn(`Unable to delete blocks with provided IDs: ${ids}`); + throw new NotFoundException('Blocks with provided IDs not found'); + } + + this.logger.log(`Successfully deleted blocks with IDs: ${ids}`); + return deleteResult; + } } diff --git a/frontend/src/components/visual-editor/v2/Diagrams.tsx b/frontend/src/components/visual-editor/v2/Diagrams.tsx index 8412072..f831164 100644 --- a/frontend/src/components/visual-editor/v2/Diagrams.tsx +++ b/frontend/src/components/visual-editor/v2/Diagrams.tsx @@ -43,6 +43,7 @@ import { MoveDialog } from "@/app-components/dialogs/MoveDialog"; import { CategoryDialog } from "@/components/categories/CategoryDialog"; import { isSameEntity } from "@/hooks/crud/helpers"; import { useDelete, useDeleteFromCache } from "@/hooks/crud/useDelete"; +import { useDeleteMany } from "@/hooks/crud/useDeleteMany"; import { useFind } from "@/hooks/crud/useFind"; import { useGetFromCache } from "@/hooks/crud/useGet"; import { useUpdate, useUpdateCache } from "@/hooks/crud/useUpdate"; @@ -120,6 +121,12 @@ const Diagrams = () => { }, invalidate: false, }); + const { mutateAsync: deleteBlocks } = useDeleteMany(EntityType.BLOCK, { + onSuccess: () => { + deleteDialogCtl.closeDialog(); + setSelectedBlockId(undefined); + }, + }); const { mutateAsync: updateBlock } = useUpdate(EntityType.BLOCK, { invalidate: false, }); @@ -312,19 +319,17 @@ const Diagrams = () => { const handleDeleteButton = () => { const selectedEntities = engine?.getModel().getSelectedEntities(); - const ids = selectedEntities?.map((model) => model.getID()).join(","); + const ids = selectedEntities?.map((model) => model.getID()); - if (ids && selectedEntities) { + if (ids && selectedEntities && ids.length > 0) { deleteCallbackRef.current = () => { - if (selectedEntities.length > 0) { - selectedEntities.forEach((model) => { - model.setLocked(false); - model.remove(); - }); - engine?.repaintCanvas(); - } + selectedEntities.forEach((model) => { + model.setLocked(false); + model.remove(); + }); + engine?.repaintCanvas(); }; - deleteDialogCtl.openDialog(ids); + deleteDialogCtl.openDialog(ids.join(",")); } }; const handleMoveButton = () => { @@ -339,112 +344,100 @@ const Diagrams = () => { const id = deleteDialogCtl?.data; if (id) { - // Check if it's a link id - if (id.length === 36) { - // Remove link + update nextBlocks + TODO update port state - const link = model?.getLink(id) as any; - const sourceId = link?.sourcePort.parent.options.id; - const targetId = link?.targetPort.parent.options.id; + const ids = id.split(","); - if (link?.sourcePort.options.label === BlockPorts.nextBlocksOutPort) { - // Next/previous Link Delete - const previousData = getBlockFromCache(sourceId); - const nextBlocks = [...(previousData?.nextBlocks || [])]; + if (ids.length > 1) { + await deleteBlocks(ids, { + onSuccess: () => { + ids.forEach((blockId) => { + const block = getBlockFromCache(blockId); - await updateBlock( - { - id: sourceId, - params: { - nextBlocks: nextBlocks.filter((block) => block !== targetId), - }, - }, - { - onSuccess() { - updateCachedBlock({ - id: targetId, - preprocess: ({ previousBlocks = [], ...rest }) => ({ - ...rest, - previousBlocks: previousBlocks.filter( - (previousBlock) => previousBlock !== sourceId, - ), - }), - }); - }, - }, - ); - } else if ( - link?.sourcePort.options.label === BlockPorts.attachmentOutPort - ) { - // Attached / AttachedTo Link Delete - await updateBlock( - { - id: sourceId, - params: { - attachedBlock: null, - }, - }, - { - onSuccess() { - updateCachedBlock({ - id: targetId, - preprocess: (oldData) => ({ - ...oldData, - attachedToBlock: null, - }), - }); - }, - }, - ); - } - } else { - // Block Delete Case - const ids = id.includes(",") ? id.split(",") : [id]; + if (block) { + // Update all linked blocks to remove references to deleted blocks + const linkedBlockIds = [ + ...(block?.nextBlocks || []), + ...(block?.previousBlocks || []), + ...(block?.attachedBlock ? [block.attachedBlock] : []), + ...(block?.attachedToBlock ? [block.attachedToBlock] : []), + ]; - for (const blockId of ids) { - const block = getBlockFromCache(blockId); + linkedBlockIds.forEach((linkedBlockId) => { + const linkedBlock = getBlockFromCache(linkedBlockId); - await deleteBlock(blockId, { - onSuccess() { - // Update all linked blocks to remove any reference to the deleted block - const linkedBlockIds = [ - ...(block?.nextBlocks || []), - ...(block?.previousBlocks || []), - ...(block?.attachedBlock ? [block.attachedBlock] : []), - ...(block?.attachedToBlock ? [block.attachedToBlock] : []), - ]; - - for (const linkedBlockId of linkedBlockIds) { - const linkedBlock = getBlockFromCache(linkedBlockId); - - if (linkedBlock) { - updateCachedBlock({ - id: linkedBlock.id, - payload: { - ...linkedBlock, - nextBlocks: linkedBlock.nextBlocks?.filter( - (nextBlockId) => nextBlockId !== blockId, - ), - previousBlocks: linkedBlock.previousBlocks?.filter( - (previousBlockId) => previousBlockId !== blockId, - ), - attachedBlock: - linkedBlock.attachedBlock === blockId + if (linkedBlock) { + updateCachedBlock({ + id: linkedBlock.id, + payload: { + ...linkedBlock, + nextBlocks: linkedBlock.nextBlocks?.filter( + (nextBlockId) => !ids.includes(nextBlockId), + ), + previousBlocks: linkedBlock.previousBlocks?.filter( + (previousBlockId) => !ids.includes(previousBlockId), + ), + attachedBlock: ids.includes( + linkedBlock.attachedBlock || "", + ) ? undefined : linkedBlock.attachedBlock, - attachedToBlock: - linkedBlock.attachedToBlock === blockId + attachedToBlock: ids.includes( + linkedBlock.attachedToBlock || "", + ) ? undefined : linkedBlock.attachedToBlock, - }, - strategy: "overwrite", - }); - } + }, + strategy: "overwrite", + }); + } + }); + deleteCachedBlock(blockId); } + }); + }, + }); + } else { + const blockId = ids[0]; + const block = getBlockFromCache(blockId); - deleteCachedBlock(blockId); - }, - }); - } + await deleteBlock(blockId, { + onSuccess() { + const linkedBlockIds = [ + ...(block?.nextBlocks || []), + ...(block?.previousBlocks || []), + ...(block?.attachedBlock ? [block.attachedBlock] : []), + ...(block?.attachedToBlock ? [block.attachedToBlock] : []), + ]; + + linkedBlockIds.forEach((linkedBlockId) => { + const linkedBlock = getBlockFromCache(linkedBlockId); + + if (linkedBlock) { + updateCachedBlock({ + id: linkedBlock.id, + payload: { + ...linkedBlock, + nextBlocks: linkedBlock.nextBlocks?.filter( + (nextBlockId) => nextBlockId !== blockId, + ), + previousBlocks: linkedBlock.previousBlocks?.filter( + (previousBlockId) => previousBlockId !== blockId, + ), + attachedBlock: + linkedBlock.attachedBlock === blockId + ? undefined + : linkedBlock.attachedBlock, + attachedToBlock: + linkedBlock.attachedToBlock === blockId + ? undefined + : linkedBlock.attachedToBlock, + }, + strategy: "overwrite", + }); + } + }); + deleteCachedBlock(blockId); + }, + }); } deleteCallbackRef.current?.(); From 066124acc580e8fdbb25126f5d4eacd44b1e47d4 Mon Sep 17 00:00:00 2001 From: hexastack Date: Wed, 30 Oct 2024 09:56:48 +0100 Subject: [PATCH 3/9] fix: revert moving link --- .../components/visual-editor/v2/Diagrams.tsx | 228 +++++++++++------- 1 file changed, 142 insertions(+), 86 deletions(-) diff --git a/frontend/src/components/visual-editor/v2/Diagrams.tsx b/frontend/src/components/visual-editor/v2/Diagrams.tsx index f831164..647908d 100644 --- a/frontend/src/components/visual-editor/v2/Diagrams.tsx +++ b/frontend/src/components/visual-editor/v2/Diagrams.tsx @@ -344,100 +344,156 @@ const Diagrams = () => { const id = deleteDialogCtl?.data; if (id) { - const ids = id.split(","); + // Handle link deletion + if (id.length === 36) { + const link = model?.getLink(id) as any; + const sourceId = link?.sourcePort.parent.options.id; + const targetId = link?.targetPort.parent.options.id; - if (ids.length > 1) { - await deleteBlocks(ids, { - onSuccess: () => { - ids.forEach((blockId) => { - const block = getBlockFromCache(blockId); + if (link?.sourcePort.options.label === BlockPorts.nextBlocksOutPort) { + const previousData = getBlockFromCache(sourceId); + const nextBlocks = [...(previousData?.nextBlocks || [])]; - if (block) { - // Update all linked blocks to remove references to deleted blocks - const linkedBlockIds = [ - ...(block?.nextBlocks || []), - ...(block?.previousBlocks || []), - ...(block?.attachedBlock ? [block.attachedBlock] : []), - ...(block?.attachedToBlock ? [block.attachedToBlock] : []), - ]; + await updateBlock( + { + id: sourceId, + params: { + nextBlocks: nextBlocks.filter((block) => block !== targetId), + }, + }, + { + onSuccess() { + updateCachedBlock({ + id: targetId, + preprocess: ({ previousBlocks = [], ...rest }) => ({ + ...rest, + previousBlocks: previousBlocks.filter( + (previousBlock) => previousBlock !== sourceId, + ), + }), + }); + }, + }, + ); + } else if ( + link?.sourcePort.options.label === BlockPorts.attachmentOutPort + ) { + await updateBlock( + { + id: sourceId, + params: { + attachedBlock: null, + }, + }, + { + onSuccess() { + updateCachedBlock({ + id: targetId, + preprocess: (oldData) => ({ + ...oldData, + attachedToBlock: null, + }), + }); + }, + }, + ); + } + } else { + const ids = id.split(","); - linkedBlockIds.forEach((linkedBlockId) => { - const linkedBlock = getBlockFromCache(linkedBlockId); + if (ids.length > 1) { + await deleteBlocks(ids, { + onSuccess: () => { + ids.forEach((blockId) => { + const block = getBlockFromCache(blockId); - if (linkedBlock) { - updateCachedBlock({ - id: linkedBlock.id, - payload: { - ...linkedBlock, - nextBlocks: linkedBlock.nextBlocks?.filter( - (nextBlockId) => !ids.includes(nextBlockId), - ), - previousBlocks: linkedBlock.previousBlocks?.filter( - (previousBlockId) => !ids.includes(previousBlockId), - ), - attachedBlock: ids.includes( - linkedBlock.attachedBlock || "", - ) + if (block) { + // Update all linked blocks to remove references to deleted blocks + const linkedBlockIds = [ + ...(block?.nextBlocks || []), + ...(block?.previousBlocks || []), + ...(block?.attachedBlock ? [block.attachedBlock] : []), + ...(block?.attachedToBlock ? [block.attachedToBlock] : []), + ]; + + linkedBlockIds.forEach((linkedBlockId) => { + const linkedBlock = getBlockFromCache(linkedBlockId); + + if (linkedBlock) { + updateCachedBlock({ + id: linkedBlock.id, + payload: { + ...linkedBlock, + nextBlocks: linkedBlock.nextBlocks?.filter( + (nextBlockId) => !ids.includes(nextBlockId), + ), + previousBlocks: linkedBlock.previousBlocks?.filter( + (previousBlockId) => !ids.includes(previousBlockId), + ), + attachedBlock: ids.includes( + linkedBlock.attachedBlock || "", + ) + ? undefined + : linkedBlock.attachedBlock, + attachedToBlock: ids.includes( + linkedBlock.attachedToBlock || "", + ) + ? undefined + : linkedBlock.attachedToBlock, + }, + strategy: "overwrite", + }); + } + }); + deleteCachedBlock(blockId); + } + }); + }, + }); + } else { + const blockId = ids[0]; + const block = getBlockFromCache(blockId); + + await deleteBlock(blockId, { + onSuccess() { + const linkedBlockIds = [ + ...(block?.nextBlocks || []), + ...(block?.previousBlocks || []), + ...(block?.attachedBlock ? [block.attachedBlock] : []), + ...(block?.attachedToBlock ? [block.attachedToBlock] : []), + ]; + + linkedBlockIds.forEach((linkedBlockId) => { + const linkedBlock = getBlockFromCache(linkedBlockId); + + if (linkedBlock) { + updateCachedBlock({ + id: linkedBlock.id, + payload: { + ...linkedBlock, + nextBlocks: linkedBlock.nextBlocks?.filter( + (nextBlockId) => nextBlockId !== blockId, + ), + previousBlocks: linkedBlock.previousBlocks?.filter( + (previousBlockId) => previousBlockId !== blockId, + ), + attachedBlock: + linkedBlock.attachedBlock === blockId ? undefined : linkedBlock.attachedBlock, - attachedToBlock: ids.includes( - linkedBlock.attachedToBlock || "", - ) + attachedToBlock: + linkedBlock.attachedToBlock === blockId ? undefined : linkedBlock.attachedToBlock, - }, - strategy: "overwrite", - }); - } - }); - deleteCachedBlock(blockId); - } - }); - }, - }); - } else { - const blockId = ids[0]; - const block = getBlockFromCache(blockId); - - await deleteBlock(blockId, { - onSuccess() { - const linkedBlockIds = [ - ...(block?.nextBlocks || []), - ...(block?.previousBlocks || []), - ...(block?.attachedBlock ? [block.attachedBlock] : []), - ...(block?.attachedToBlock ? [block.attachedToBlock] : []), - ]; - - linkedBlockIds.forEach((linkedBlockId) => { - const linkedBlock = getBlockFromCache(linkedBlockId); - - if (linkedBlock) { - updateCachedBlock({ - id: linkedBlock.id, - payload: { - ...linkedBlock, - nextBlocks: linkedBlock.nextBlocks?.filter( - (nextBlockId) => nextBlockId !== blockId, - ), - previousBlocks: linkedBlock.previousBlocks?.filter( - (previousBlockId) => previousBlockId !== blockId, - ), - attachedBlock: - linkedBlock.attachedBlock === blockId - ? undefined - : linkedBlock.attachedBlock, - attachedToBlock: - linkedBlock.attachedToBlock === blockId - ? undefined - : linkedBlock.attachedToBlock, - }, - strategy: "overwrite", - }); - } - }); - deleteCachedBlock(blockId); - }, - }); + }, + strategy: "overwrite", + }); + } + }); + deleteCachedBlock(blockId); + }, + }); + } } deleteCallbackRef.current?.(); From 4f5280ff9cc61ff3873f380ed852eae10172ec03 Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Thu, 21 Nov 2024 17:20:12 +0100 Subject: [PATCH 4/9] refactor: block and link deletion logic --- .../components/visual-editor/v2/Diagrams.tsx | 298 +++++++++--------- 1 file changed, 142 insertions(+), 156 deletions(-) diff --git a/frontend/src/components/visual-editor/v2/Diagrams.tsx b/frontend/src/components/visual-editor/v2/Diagrams.tsx index 647908d..69e8490 100644 --- a/frontend/src/components/visual-editor/v2/Diagrams.tsx +++ b/frontend/src/components/visual-editor/v2/Diagrams.tsx @@ -317,6 +317,137 @@ const Diagrams = () => { ), ]); + const handleLinkDeletion = async (linkId: string) => { + const link = model?.getLink(linkId) as any; + const sourceId = link?.sourcePort.parent.options.id; + const targetId = link?.targetPort.parent.options.id; + + if (link?.sourcePort.options.label === BlockPorts.nextBlocksOutPort) { + await removeNextBlockLink(sourceId, targetId); + } else if ( + link?.sourcePort.options.label === BlockPorts.attachmentOutPort + ) { + await removeAttachmentLink(sourceId, targetId); + } + }; + const removeNextBlockLink = async (sourceId: string, targetId: string) => { + const previousData = getBlockFromCache(sourceId); + const nextBlocks = [...(previousData?.nextBlocks || [])]; + + await updateBlock( + { + id: sourceId, + params: { + nextBlocks: nextBlocks.filter((block) => block !== targetId), + }, + }, + { + onSuccess() { + updateCachedBlock({ + id: targetId, + preprocess: ({ previousBlocks = [], ...rest }) => ({ + ...rest, + previousBlocks: previousBlocks.filter( + (block) => block !== sourceId, + ), + }), + }); + }, + }, + ); + }; + const removeAttachmentLink = async (sourceId: string, targetId: string) => { + await updateBlock( + { + id: sourceId, + params: { attachedBlock: null }, + }, + { + onSuccess() { + updateCachedBlock({ + id: targetId, + preprocess: (oldData) => ({ ...oldData, attachedToBlock: null }), + }); + }, + }, + ); + }; + const handleBlockDeletion = async (id: string) => { + const ids = id.split(","); + + if (ids.length > 1) { + await deleteMultipleBlocks(ids); + } else { + await deleteSingleBlock(ids[0]); + } + }; + const deleteMultipleBlocks = async (ids: string[]) => { + await deleteBlocks(ids, { + onSuccess: () => { + ids.forEach((blockId) => { + const block = getBlockFromCache(blockId); + + if (block) { + updateLinkedBlocks(block, ids); + deleteCachedBlock(blockId); + } + }); + }, + }); + }; + const deleteSingleBlock = async (blockId: string) => { + const block = getBlockFromCache(blockId); + + await deleteBlock(blockId, { + onSuccess() { + if (block) { + updateLinkedBlocks(block, [blockId]); + deleteCachedBlock(blockId); + } + }, + }); + }; + const updateLinkedBlocks = (block: IBlock, deletedIds: string[]) => { + const linkedBlockIds = [ + ...(block?.nextBlocks || []), + ...(block?.previousBlocks || []), + ...(block?.attachedBlock ? [block.attachedBlock] : []), + ...(block?.attachedToBlock ? [block.attachedToBlock] : []), + ]; + + linkedBlockIds.forEach((linkedBlockId) => { + const linkedBlock = getBlockFromCache(linkedBlockId); + + if (linkedBlock) { + updateCachedBlock({ + id: linkedBlock.id, + payload: { + ...linkedBlock, + nextBlocks: linkedBlock.nextBlocks?.filter( + (nextBlockId) => !deletedIds.includes(nextBlockId), + ), + previousBlocks: linkedBlock.previousBlocks?.filter( + (previousBlockId) => !deletedIds.includes(previousBlockId), + ), + attachedBlock: deletedIds.includes(linkedBlock.attachedBlock || "") + ? undefined + : linkedBlock.attachedBlock, + attachedToBlock: deletedIds.includes( + linkedBlock.attachedToBlock || "", + ) + ? undefined + : linkedBlock.attachedToBlock, + }, + strategy: "overwrite", + }); + } + }); + }; + const cleanupAfterDeletion = () => { + deleteCallbackRef.current?.(); + deleteCallbackRef.current = () => {}; + deleteDialogCtl.closeDialog(); + }; const handleDeleteButton = () => { const selectedEntities = engine?.getModel().getSelectedEntities(); const ids = selectedEntities?.map((model) => model.getID()); @@ -343,163 +474,18 @@ const Diagrams = () => { const onDelete = async () => { const id = deleteDialogCtl?.data; - if (id) { - // Handle link deletion - if (id.length === 36) { - const link = model?.getLink(id) as any; - const sourceId = link?.sourcePort.parent.options.id; - const targetId = link?.targetPort.parent.options.id; - - if (link?.sourcePort.options.label === BlockPorts.nextBlocksOutPort) { - const previousData = getBlockFromCache(sourceId); - const nextBlocks = [...(previousData?.nextBlocks || [])]; - - await updateBlock( - { - id: sourceId, - params: { - nextBlocks: nextBlocks.filter((block) => block !== targetId), - }, - }, - { - onSuccess() { - updateCachedBlock({ - id: targetId, - preprocess: ({ previousBlocks = [], ...rest }) => ({ - ...rest, - previousBlocks: previousBlocks.filter( - (previousBlock) => previousBlock !== sourceId, - ), - }), - }); - }, - }, - ); - } else if ( - link?.sourcePort.options.label === BlockPorts.attachmentOutPort - ) { - await updateBlock( - { - id: sourceId, - params: { - attachedBlock: null, - }, - }, - { - onSuccess() { - updateCachedBlock({ - id: targetId, - preprocess: (oldData) => ({ - ...oldData, - attachedToBlock: null, - }), - }); - }, - }, - ); - } - } else { - const ids = id.split(","); - - if (ids.length > 1) { - await deleteBlocks(ids, { - onSuccess: () => { - ids.forEach((blockId) => { - const block = getBlockFromCache(blockId); - - if (block) { - // Update all linked blocks to remove references to deleted blocks - const linkedBlockIds = [ - ...(block?.nextBlocks || []), - ...(block?.previousBlocks || []), - ...(block?.attachedBlock ? [block.attachedBlock] : []), - ...(block?.attachedToBlock ? [block.attachedToBlock] : []), - ]; - - linkedBlockIds.forEach((linkedBlockId) => { - const linkedBlock = getBlockFromCache(linkedBlockId); - - if (linkedBlock) { - updateCachedBlock({ - id: linkedBlock.id, - payload: { - ...linkedBlock, - nextBlocks: linkedBlock.nextBlocks?.filter( - (nextBlockId) => !ids.includes(nextBlockId), - ), - previousBlocks: linkedBlock.previousBlocks?.filter( - (previousBlockId) => !ids.includes(previousBlockId), - ), - attachedBlock: ids.includes( - linkedBlock.attachedBlock || "", - ) - ? undefined - : linkedBlock.attachedBlock, - attachedToBlock: ids.includes( - linkedBlock.attachedToBlock || "", - ) - ? undefined - : linkedBlock.attachedToBlock, - }, - strategy: "overwrite", - }); - } - }); - deleteCachedBlock(blockId); - } - }); - }, - }); - } else { - const blockId = ids[0]; - const block = getBlockFromCache(blockId); - - await deleteBlock(blockId, { - onSuccess() { - const linkedBlockIds = [ - ...(block?.nextBlocks || []), - ...(block?.previousBlocks || []), - ...(block?.attachedBlock ? [block.attachedBlock] : []), - ...(block?.attachedToBlock ? [block.attachedToBlock] : []), - ]; - - linkedBlockIds.forEach((linkedBlockId) => { - const linkedBlock = getBlockFromCache(linkedBlockId); - - if (linkedBlock) { - updateCachedBlock({ - id: linkedBlock.id, - payload: { - ...linkedBlock, - nextBlocks: linkedBlock.nextBlocks?.filter( - (nextBlockId) => nextBlockId !== blockId, - ), - previousBlocks: linkedBlock.previousBlocks?.filter( - (previousBlockId) => previousBlockId !== blockId, - ), - attachedBlock: - linkedBlock.attachedBlock === blockId - ? undefined - : linkedBlock.attachedBlock, - attachedToBlock: - linkedBlock.attachedToBlock === blockId - ? undefined - : linkedBlock.attachedToBlock, - }, - strategy: "overwrite", - }); - } - }); - deleteCachedBlock(blockId); - }, - }); - } - } - - deleteCallbackRef.current?.(); - deleteCallbackRef.current = () => {}; - deleteDialogCtl.closeDialog(); + if (!id) { + return; } + const isLink = id.length === 36; + + if (isLink) { + await handleLinkDeletion(id); + } else { + await handleBlockDeletion(id); + } + + cleanupAfterDeletion(); }; const onMove = async (newCategoryId?: string) => { if (!newCategoryId) { From ee024afa057123592f8121ac028174afeb370c69 Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Thu, 21 Nov 2024 18:08:47 +0100 Subject: [PATCH 5/9] refactor: minor changes --- .../components/visual-editor/v2/Diagrams.tsx | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/visual-editor/v2/Diagrams.tsx b/frontend/src/components/visual-editor/v2/Diagrams.tsx index 69e8490..5519917 100644 --- a/frontend/src/components/visual-editor/v2/Diagrams.tsx +++ b/frontend/src/components/visual-editor/v2/Diagrams.tsx @@ -372,23 +372,21 @@ const Diagrams = () => { }, ); }; - const handleBlockDeletion = async (id: string) => { - const ids = id.split(","); - - if (ids.length > 1) { - await deleteMultipleBlocks(ids); + const handleBlockDeletion = async (blockIds: string[]) => { + if (blockIds.length > 1) { + await deleteMultipleBlocks(blockIds); } else { - await deleteSingleBlock(ids[0]); + await deleteSingleBlock(blockIds[0]); } }; - const deleteMultipleBlocks = async (ids: string[]) => { - await deleteBlocks(ids, { + const deleteMultipleBlocks = async (blockIds: string[]) => { + await deleteBlocks(blockIds, { onSuccess: () => { - ids.forEach((blockId) => { + blockIds.forEach((blockId) => { const block = getBlockFromCache(blockId); if (block) { - updateLinkedBlocks(block, ids); + updateLinkedBlocks(block, blockIds); deleteCachedBlock(blockId); } }); @@ -407,13 +405,14 @@ const Diagrams = () => { }, }); }; + const getLinkedBlockIds = (block: IBlock): string[] => [ + ...(block?.nextBlocks || []), + ...(block?.previousBlocks || []), + ...(block?.attachedBlock ? [block.attachedBlock] : []), + ...(block?.attachedToBlock ? [block.attachedToBlock] : []), + ]; const updateLinkedBlocks = (block: IBlock, deletedIds: string[]) => { - const linkedBlockIds = [ - ...(block?.nextBlocks || []), - ...(block?.previousBlocks || []), - ...(block?.attachedBlock ? [block.attachedBlock] : []), - ...(block?.attachedToBlock ? [block.attachedToBlock] : []), - ]; + const linkedBlockIds = getLinkedBlockIds(block); linkedBlockIds.forEach((linkedBlockId) => { const linkedBlock = getBlockFromCache(linkedBlockId); @@ -478,11 +477,12 @@ const Diagrams = () => { return; } const isLink = id.length === 36; + const listIds = id.split(","); if (isLink) { - await handleLinkDeletion(id); + await handleLinkDeletion(listIds[0]); } else { - await handleBlockDeletion(id); + await handleBlockDeletion(listIds); } cleanupAfterDeletion(); From 09b32019497930ce2fa884c64a7e98a95996a607 Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Thu, 21 Nov 2024 18:53:22 +0100 Subject: [PATCH 6/9] refactor: ids to be array by default --- .../components/visual-editor/v2/Diagrams.tsx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/visual-editor/v2/Diagrams.tsx b/frontend/src/components/visual-editor/v2/Diagrams.tsx index 5519917..681c5ea 100644 --- a/frontend/src/components/visual-editor/v2/Diagrams.tsx +++ b/frontend/src/components/visual-editor/v2/Diagrams.tsx @@ -71,7 +71,7 @@ const Diagrams = () => { const [engine, setEngine] = useState(); const [canvas, setCanvas] = useState(); const [selectedBlockId, setSelectedBlockId] = useState(); - const deleteDialogCtl = useDialog(false); + const deleteDialogCtl = useDialog(false); const moveDialogCtl = useDialog(false); const addCategoryDialogCtl = useDialog(false); const { mutateAsync: updateBlocks } = useUpdateMany(EntityType.BLOCK); @@ -198,7 +198,7 @@ const Diagrams = () => { setter: setSelectedBlockId, updateFn: updateBlock, onRemoveNode: (ids, next) => { - deleteDialogCtl.openDialog(ids.join(",")); + deleteDialogCtl.openDialog(ids); deleteCallbackRef.current = next; }, onDbClickNode: (event, id) => { @@ -459,7 +459,7 @@ const Diagrams = () => { }); engine?.repaintCanvas(); }; - deleteDialogCtl.openDialog(ids.join(",")); + deleteDialogCtl.openDialog(ids); } }; const handleMoveButton = () => { @@ -471,18 +471,17 @@ const Diagrams = () => { } }; const onDelete = async () => { - const id = deleteDialogCtl?.data; + const ids = deleteDialogCtl?.data; - if (!id) { + if (!ids || ids?.length === 0) { return; } - const isLink = id.length === 36; - const listIds = id.split(","); + const isLink = ids[0].length === 36; if (isLink) { - await handleLinkDeletion(listIds[0]); + await handleLinkDeletion(ids[0]); } else { - await handleBlockDeletion(listIds); + await handleBlockDeletion(ids); } cleanupAfterDeletion(); From 978562900e2355d8a4e92f57b899ff154c77ce28 Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Fri, 22 Nov 2024 17:56:22 +0100 Subject: [PATCH 7/9] refactor: minor updates --- api/src/chat/controllers/block.controller.ts | 2 +- .../components/visual-editor/v2/Diagrams.tsx | 23 ++----------------- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/api/src/chat/controllers/block.controller.ts b/api/src/chat/controllers/block.controller.ts index 3cc9dc9..0cdc057 100644 --- a/api/src/chat/controllers/block.controller.ts +++ b/api/src/chat/controllers/block.controller.ts @@ -332,7 +332,7 @@ export class BlockController extends BaseController< @CsrfCheck(true) @Delete('') @HttpCode(204) - async deleteMany(@Body('ids') ids: string[]): Promise { + async deleteBlocksByIds(@Body('ids') ids: string[]): Promise { if (!ids || ids.length === 0) { throw new BadRequestException('No IDs provided for deletion.'); } diff --git a/frontend/src/components/visual-editor/v2/Diagrams.tsx b/frontend/src/components/visual-editor/v2/Diagrams.tsx index 681c5ea..3c44e79 100644 --- a/frontend/src/components/visual-editor/v2/Diagrams.tsx +++ b/frontend/src/components/visual-editor/v2/Diagrams.tsx @@ -372,14 +372,7 @@ const Diagrams = () => { }, ); }; - const handleBlockDeletion = async (blockIds: string[]) => { - if (blockIds.length > 1) { - await deleteMultipleBlocks(blockIds); - } else { - await deleteSingleBlock(blockIds[0]); - } - }; - const deleteMultipleBlocks = async (blockIds: string[]) => { + const handleBlocksDeletion = async (blockIds: string[]) => { await deleteBlocks(blockIds, { onSuccess: () => { blockIds.forEach((blockId) => { @@ -393,18 +386,6 @@ const Diagrams = () => { }, }); }; - const deleteSingleBlock = async (blockId: string) => { - const block = getBlockFromCache(blockId); - - await deleteBlock(blockId, { - onSuccess() { - if (block) { - updateLinkedBlocks(block, [blockId]); - deleteCachedBlock(blockId); - } - }, - }); - }; const getLinkedBlockIds = (block: IBlock): string[] => [ ...(block?.nextBlocks || []), ...(block?.previousBlocks || []), @@ -481,7 +462,7 @@ const Diagrams = () => { if (isLink) { await handleLinkDeletion(ids[0]); } else { - await handleBlockDeletion(ids); + await handleBlocksDeletion(ids); } cleanupAfterDeletion(); From 332a2b7a704261da0ec7dc3a8707744fb74b0ad8 Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Fri, 22 Nov 2024 18:12:50 +0100 Subject: [PATCH 8/9] revert: revert controller name --- api/src/chat/controllers/block.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/chat/controllers/block.controller.ts b/api/src/chat/controllers/block.controller.ts index 0cdc057..3cc9dc9 100644 --- a/api/src/chat/controllers/block.controller.ts +++ b/api/src/chat/controllers/block.controller.ts @@ -332,7 +332,7 @@ export class BlockController extends BaseController< @CsrfCheck(true) @Delete('') @HttpCode(204) - async deleteBlocksByIds(@Body('ids') ids: string[]): Promise { + async deleteMany(@Body('ids') ids: string[]): Promise { if (!ids || ids.length === 0) { throw new BadRequestException('No IDs provided for deletion.'); } From 4f4f2a4275d81e0ea9ad732b8be31017ed0e56c5 Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Fri, 22 Nov 2024 18:31:34 +0100 Subject: [PATCH 9/9] fix: typing issues --- .../src/app-components/dialogs/DeleteDialog.tsx | 11 +++++------ .../components/visual-editor/v2/Diagrams.tsx | 17 ++++------------- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/frontend/src/app-components/dialogs/DeleteDialog.tsx b/frontend/src/app-components/dialogs/DeleteDialog.tsx index d509793..a2196ed 100644 --- a/frontend/src/app-components/dialogs/DeleteDialog.tsx +++ b/frontend/src/app-components/dialogs/DeleteDialog.tsx @@ -8,25 +8,24 @@ import ErrorIcon from "@mui/icons-material/Error"; import { + Button, Dialog, DialogActions, + DialogContent, Grid, Typography, - DialogContent, - Button, } from "@mui/material"; -import { FC } from "react"; import { DialogTitle } from "@/app-components/dialogs/DialogTitle"; import { DialogControl } from "@/hooks/useDialog"; import { useTranslate } from "@/hooks/useTranslate"; -export type DeleteDialogProps = DialogControl; -export const DeleteDialog: FC = ({ +export type DeleteDialogProps = DialogControl; +export const DeleteDialog = ({ open, callback, closeDialog: closeFunction, -}: DeleteDialogProps) => { +}: DeleteDialogProps) => { const { t } = useTranslate(); return ( diff --git a/frontend/src/components/visual-editor/v2/Diagrams.tsx b/frontend/src/components/visual-editor/v2/Diagrams.tsx index 3c44e79..3d309d6 100644 --- a/frontend/src/components/visual-editor/v2/Diagrams.tsx +++ b/frontend/src/components/visual-editor/v2/Diagrams.tsx @@ -42,7 +42,7 @@ import { DeleteDialog } from "@/app-components/dialogs"; import { MoveDialog } from "@/app-components/dialogs/MoveDialog"; import { CategoryDialog } from "@/components/categories/CategoryDialog"; import { isSameEntity } from "@/hooks/crud/helpers"; -import { useDelete, useDeleteFromCache } from "@/hooks/crud/useDelete"; +import { useDeleteFromCache } from "@/hooks/crud/useDelete"; import { useDeleteMany } from "@/hooks/crud/useDeleteMany"; import { useFind } from "@/hooks/crud/useFind"; import { useGetFromCache } from "@/hooks/crud/useGet"; @@ -90,10 +90,7 @@ const Diagrams = () => { const { data: categories } = useFind( { entity: EntityType.CATEGORY }, { - initialPaginationState: { - page: 0, - pageSize: 999, // @TODO: We need to display all categories - }, + hasCount: false, initialSortState: [{ field: "createdAt", sort: "asc" }], }, { @@ -114,13 +111,6 @@ const Diagrams = () => { const { mutateAsync: updateCategory } = useUpdate(EntityType.CATEGORY, { invalidate: false, }); - const { mutateAsync: deleteBlock } = useDelete(EntityType.BLOCK, { - onSuccess() { - deleteDialogCtl.closeDialog(); - setSelectedBlockId(undefined); - }, - invalidate: false, - }); const { mutateAsync: deleteBlocks } = useDeleteMany(EntityType.BLOCK, { onSuccess: () => { deleteDialogCtl.closeDialog(); @@ -188,6 +178,7 @@ const Diagrams = () => { if (categories?.length > 0 && !selectedCategoryId) { setSelectedCategoryId(categories[0].id); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { @@ -525,7 +516,7 @@ const Diagrams = () => { - + {...deleteDialogCtl} callback={onDelete} />