From 081d8ff988fd11f30bb2a562027f66903a11ffc7 Mon Sep 17 00:00:00 2001 From: Juha Paananen Date: Mon, 11 Mar 2024 18:46:08 +0200 Subject: [PATCH] Disconnect crdt-store from previous document when changing boards --- frontend/src/store/board-store.ts | 1 + frontend/src/store/crdt-store.ts | 58 ++++++++++++++++++++++--------- perf-tester/src/index.ts | 1 + 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/frontend/src/store/board-store.ts b/frontend/src/store/board-store.ts index 5068887d..8a9551c6 100644 --- a/frontend/src/store/board-store.ts +++ b/frontend/src/store/board-store.ts @@ -553,6 +553,7 @@ export function BoardStore( const localBoardItemEvents = uiEvents.pipe(L.filter(isPersistableBoardItemEvent, globalScope)) const crdtStore = CRDTStore( + L.view(state, (s) => s.board?.id), L.view(state, (s) => s.status === "online"), localBoardItemEvents, getWebSocketRootUrl, diff --git a/frontend/src/store/crdt-store.ts b/frontend/src/store/crdt-store.ts index bac1f651..4ab5f94e 100644 --- a/frontend/src/store/crdt-store.ts +++ b/frontend/src/store/crdt-store.ts @@ -37,12 +37,6 @@ function BoardCRDT( return getCRDTField(doc, itemId, fieldName) } - localBoardItemEvents.pipe(L.filter((e) => e.boardId === boardId)).forEach((event) => { - if (event.action === "item.add") { - importItemsIntoCRDT(doc, event.items, { fallbackToText: true }) - } - }) - function augmentItems(items: Item[]): Item[] { return augmentItemsWithCRDT(doc, items) } @@ -60,16 +54,35 @@ function BoardCRDT( WebSocketPolyfill, }) - online.onChange((c) => (c ? provider.connect() : provider.disconnect())) + const disconnected = L.bus() + online.pipe(L.changes, L.takeUntil(disconnected)).forEach((c) => (c ? provider.connect() : provider.disconnect())) + + localBoardItemEvents + .pipe( + L.takeUntil(disconnected), + L.filter((e) => e.boardId === boardId), + ) + .forEach((event) => { + if (event.action === "item.add") { + importItemsIntoCRDT(doc, event.items, { fallbackToText: true }) + } + }) provider.on("status", (event: any) => { - console.log("YJS Provider status", event.status) + console.log("YJS Provider status", boardId, event.status) }) + function disconnect() { + console.log("Disconnecting YJS provider for board", boardId) + provider.destroy() + } + return { + boardId, doc, getField, augmentItems, + disconnect, awareness: provider.awareness, } } @@ -77,33 +90,42 @@ function BoardCRDT( export type CRDTStore = ReturnType export function CRDTStore( + currentBoardId: L.Property, online: L.Property, localBoardItemEvents: L.EventStream, getSocketRoot: () => string = getWebSocketRootUrl, WebSocketPolyfill: WebSocketPolyfill = WebSocket as any, ) { - const boards = new Map() + let boardCrdt: BoardCRDT | undefined = undefined + + currentBoardId.forEach((boardId) => { + if (boardCrdt && boardCrdt.boardId !== boardId) { + boardCrdt.disconnect() + boardCrdt = undefined + } + }) + function getBoardCrdt(boardId: Id): BoardCRDT { - let boardCrdt = boards.get(boardId) - if (!boardCrdt) { + if (boardId != currentBoardId.get()) { + throw Error(`Requested CRDT for board ${boardId} but current board is ${currentBoardId.get()}`) + } + + if (!boardCrdt || boardCrdt.boardId !== boardId) { boardCrdt = BoardCRDT(boardId, online, localBoardItemEvents, getSocketRoot, WebSocketPolyfill) - boards.set(boardId, boardCrdt) } return boardCrdt } function augmentItems(boardId: Id, items: Item[]): Item[] { - const boardCrdt = boards.get(boardId) - if (!boardCrdt) { + if (!boardCrdt || boardCrdt.boardId !== boardId) { return items } return boardCrdt.augmentItems(items) } function cloneBoard(board: Board): Board { - const boardCrdt = boards.get(board.id) const newId = uuid.v4() - if (!boardCrdt) { + if (!boardCrdt || boardCrdt.boardId !== board.id) { return { ...board, id: newId, @@ -115,7 +137,9 @@ export function CRDTStore( id: newId, } - importItemsIntoCRDT(getBoardCrdt(newId).doc, Object.values(newBoard.items)) + const temporaryBoardCrdt = BoardCRDT(newId, online, localBoardItemEvents, getSocketRoot, WebSocketPolyfill) + importItemsIntoCRDT(temporaryBoardCrdt.doc, Object.values(newBoard.items)) + temporaryBoardCrdt.disconnect() return newBoard } diff --git a/perf-tester/src/index.ts b/perf-tester/src/index.ts index c17ed238..683f106e 100644 --- a/perf-tester/src/index.ts +++ b/perf-tester/src/index.ts @@ -55,6 +55,7 @@ function createTester(nickname: string, boardId: string) { } const crdtStore = CRDTStore( + L.constant(boardId), connection.connected, localEvents.pipe(L.filter(isPersistableBoardItemEvent)).applyScope(L.globalScope), () => WS_ROOT,