From e039fd8c7ff6bc3b53542ed777e6e4a515450af0 Mon Sep 17 00:00:00 2001 From: Juha Paananen Date: Mon, 11 Mar 2024 20:21:14 +0200 Subject: [PATCH] Create board clone on the server Also adds support for CRDTs in creating with templateId, as cloning now uses the same mechanism --- backend/src/common-event-handler.ts | 9 +++++- frontend/src/board/header/BoardViewHeader.tsx | 4 ++- frontend/src/store/crdt-store.ts | 31 ++----------------- playwright/src/tests/board.spec.ts | 1 + 4 files changed, 14 insertions(+), 31 deletions(-) diff --git a/backend/src/common-event-handler.ts b/backend/src/common-event-handler.ts index 78646a05e..851232b46 100644 --- a/backend/src/common-event-handler.ts +++ b/backend/src/common-event-handler.ts @@ -1,6 +1,8 @@ -import { AppEvent, Board, checkBoardAccess, defaultBoardSize } from "../../common/src/domain" +import * as Y from "yjs" +import { AppEvent, Board, CrdtEnabled, checkBoardAccess, defaultBoardSize } from "../../common/src/domain" import { addBoard } from "./board-state" import { fetchBoard } from "./board-store" +import { yWebSocketServer } from "./board-yjs-server" import { MessageHandlerResult } from "./connection-handler" import { getAuthenticatedUserFromJWT } from "./http-session" import { @@ -94,6 +96,11 @@ export async function handleCommonEvent(socket: WsWrapper, appEvent: AppEvent): } } const board = { ...defaultBoardSize, items: {}, connections: [], ...template, ...payload, serial: 0 } + if (template && template.crdt === CrdtEnabled) { + const templateDoc = await yWebSocketServer.docs.getYDocAndWaitForFetch(template.id) + const newDoc = await yWebSocketServer.docs.getYDocAndWaitForFetch(board.id) + Y.applyUpdate(newDoc, Y.encodeStateAsUpdate(templateDoc)) + } await addBoard(board) socket.send(toBuffer({ action: "board.add.ack", boardId: board.id })) } diff --git a/frontend/src/board/header/BoardViewHeader.tsx b/frontend/src/board/header/BoardViewHeader.tsx index 6bb578d09..0864eed53 100644 --- a/frontend/src/board/header/BoardViewHeader.tsx +++ b/frontend/src/board/header/BoardViewHeader.tsx @@ -12,6 +12,7 @@ import { SharingModalDialog } from "./SharingModalDialog" import { UserInfoView } from "./UserInfoView" import { Rect } from "../../../../common/src/geometry" import { CRDTStore } from "../../store/crdt-store" +import * as uuid from "uuid" export function BoardViewHeader({ usersOnBoard, @@ -44,8 +45,9 @@ export function BoardViewHeader({ const navigator = getNavigator() function makeCopy() { const newBoard = { - ...crdtStore.cloneBoard(board.get()), + id: uuid.v4(), name: `${nameAtom.get()} copy`, + templateId: board.get()!.id, accessPolicy: defaultAccessPolicy(sessionState.get(), false), } createBoardAndNavigate(newBoard, dispatch, navigator, eventsFromServer) diff --git a/frontend/src/store/crdt-store.ts b/frontend/src/store/crdt-store.ts index 4ab5f94e2..66380c908 100644 --- a/frontend/src/store/crdt-store.ts +++ b/frontend/src/store/crdt-store.ts @@ -1,15 +1,9 @@ import * as L from "lonna" -import * as uuid from "uuid" import { IndexeddbPersistence } from "y-indexeddb" import { WebsocketProvider } from "y-websocket" import * as Y from "yjs" -import { Board, Id, Item, PersistableBoardItemEvent } from "../../../common/src/domain" -import { - augmentBoardWithCRDT, - augmentItemsWithCRDT, - getCRDTField, - importItemsIntoCRDT, -} from "../../../common/src/board-crdt-helper" +import { augmentItemsWithCRDT, getCRDTField, importItemsIntoCRDT } from "../../../common/src/board-crdt-helper" +import { Id, Item, PersistableBoardItemEvent } from "../../../common/src/domain" import { getWebSocketRootUrl } from "./server-connection" type BoardCRDT = ReturnType @@ -123,29 +117,8 @@ export function CRDTStore( return boardCrdt.augmentItems(items) } - function cloneBoard(board: Board): Board { - const newId = uuid.v4() - if (!boardCrdt || boardCrdt.boardId !== board.id) { - return { - ...board, - id: newId, - } - } - - const newBoard = { - ...augmentBoardWithCRDT(boardCrdt.doc, board), - id: newId, - } - - const temporaryBoardCrdt = BoardCRDT(newId, online, localBoardItemEvents, getSocketRoot, WebSocketPolyfill) - importItemsIntoCRDT(temporaryBoardCrdt.doc, Object.values(newBoard.items)) - temporaryBoardCrdt.disconnect() - return newBoard - } - return { getBoardCrdt, augmentItems, - cloneBoard, } } diff --git a/playwright/src/tests/board.spec.ts b/playwright/src/tests/board.spec.ts index a14472ea8..4869d2e2a 100644 --- a/playwright/src/tests/board.spec.ts +++ b/playwright/src/tests/board.spec.ts @@ -298,6 +298,7 @@ test.describe("Basic board functionality", () => { testWithBothBoardTypes("Clone the board", async ({ board, page }) => { const semigroups = await board.createArea(500, 200, "Semigroups") const functors = await board.createNoteWithText(200, 200, "Functors") + await sleep(1000) await board.cloneBoard() await board.assertBoardName("Clone the board copy") await expect(semigroups).toBeVisible()