diff --git a/client/src/features/editor/Editor.tsx b/client/src/features/editor/Editor.tsx index 82892bdd..42956749 100644 --- a/client/src/features/editor/Editor.tsx +++ b/client/src/features/editor/Editor.tsx @@ -65,6 +65,7 @@ export const Editor = ({ onTitleChange, pageId, serializedEditorData }: EditorPr sendBlockUpdateOperation, sendBlockDeleteOperation, sendBlockInsertOperation, + sendCharInsertOperation, }); const { handleKeyDown } = useMarkdownGrammer({ diff --git a/client/src/features/editor/components/block/Block.tsx b/client/src/features/editor/components/block/Block.tsx index 0c28ecbe..3baa4827 100644 --- a/client/src/features/editor/components/block/Block.tsx +++ b/client/src/features/editor/components/block/Block.tsx @@ -20,7 +20,7 @@ interface BlockProps { onClick: (blockId: BlockId, e: React.MouseEvent) => void; onAnimationSelect: (blockId: BlockId, animation: AnimationType) => void; onTypeSelect: (blockId: BlockId, type: ElementType) => void; - onCopySelect: () => void; + onCopySelect: (blockId: BlockId) => void; onDeleteSelect: (blockId: BlockId) => void; } @@ -88,7 +88,7 @@ export const Block: React.FC = memo( }; const handleCopySelect = () => { - onCopySelect(); + onCopySelect(block.id); }; const handleDeleteSelect = () => { diff --git a/client/src/features/editor/hooks/useBlockOption.ts b/client/src/features/editor/hooks/useBlockOption.ts index 68fc8ee8..595c65e6 100644 --- a/client/src/features/editor/hooks/useBlockOption.ts +++ b/client/src/features/editor/hooks/useBlockOption.ts @@ -1,10 +1,11 @@ -import { EditorCRDT } from "@noctaCrdt/Crdt"; +import { BlockCRDT, EditorCRDT } from "@noctaCrdt/Crdt"; import { AnimationType, ElementType, RemoteBlockDeleteOperation, RemoteBlockInsertOperation, RemoteBlockUpdateOperation, + RemoteCharInsertOperation, } from "@noctaCrdt/Interfaces"; import { BlockId } from "@noctaCrdt/NodeId"; import { BlockLinkedList } from "node_modules/@noctaCrdt/LinkedList"; @@ -24,6 +25,7 @@ interface useBlockOptionSelectProps { sendBlockUpdateOperation: (operation: RemoteBlockUpdateOperation) => void; sendBlockDeleteOperation: (operation: RemoteBlockDeleteOperation) => void; sendBlockInsertOperation: (operation: RemoteBlockInsertOperation) => void; + sendCharInsertOperation: (operation: RemoteCharInsertOperation) => void; } export const useBlockOptionSelect = ({ @@ -34,6 +36,7 @@ export const useBlockOptionSelect = ({ sendBlockUpdateOperation, sendBlockDeleteOperation, sendBlockInsertOperation, + sendCharInsertOperation, }: useBlockOptionSelectProps) => { const handleTypeSelect = (blockId: BlockId, type: ElementType) => { const block = editorState.linkedList.getNode(blockId); @@ -73,8 +76,48 @@ export const useBlockOptionSelect = ({ })); }; - // TODO 복제 - const handleCopySelect = () => {}; + const handleCopySelect = (blockId: BlockId) => { + const currentBlock = editorState.linkedList.getNode(blockId); + if (!currentBlock) return; + + const currentIndex = editorCRDT.LinkedList.spread().findIndex((block) => + block.id.equals(blockId), + ); + + const operation = editorCRDT.localInsert(currentIndex + 1, ""); + operation.node.type = currentBlock.type; + operation.node.indent = currentBlock.indent; + operation.node.animation = currentBlock.animation; + operation.node.style = currentBlock.style; + operation.node.icon = currentBlock.icon; + operation.node.crdt = new BlockCRDT(editorCRDT.client); + + // 먼저 새로운 블록을 만들고 + sendBlockInsertOperation({ node: operation.node, pageId }); + + // 내부 문자 노드 복사 + currentBlock.crdt.LinkedList.spread().forEach((char, index) => { + const insertOperation = operation.node.crdt.localInsert( + index, + char.value, + operation.node.id, + pageId, + ); + sendCharInsertOperation(insertOperation); + }); + + // 여기서 update를 한번 더 해주면 된다. (block의 속성 (animation, type, style, icon)을 복사하기 위함) + sendBlockUpdateOperation({ + node: operation.node, + pageId, + }); + + setEditorState((prev) => ({ + clock: editorCRDT.clock, + linkedList: editorCRDT.LinkedList, + currentBlock: operation.node.id || prev.currentBlock, + })); + }; const handleDeleteSelect = (blockId: BlockId) => { const currentIndex = editorCRDT.LinkedList.spread().findIndex((block) =>