From 22751b96a343b97ed278543c9a23364eb20cd6a8 Mon Sep 17 00:00:00 2001 From: Juha Paananen Date: Wed, 27 Mar 2024 21:38:53 +0200 Subject: [PATCH] Attach user nicknames to text cursors --- frontend/src/store/board-store.ts | 1 + frontend/src/store/crdt-store.ts | 39 +++++++++++++++++++++++-------- frontend/src/style/board.scss | 7 ++++++ perf-tester/src/index.ts | 7 ++++++ 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/frontend/src/store/board-store.ts b/frontend/src/store/board-store.ts index 0c258585..5383ab87 100644 --- a/frontend/src/store/board-store.ts +++ b/frontend/src/store/board-store.ts @@ -568,6 +568,7 @@ export function BoardStore( L.view(state, (s) => s.board?.id), L.view(state, (s) => s.status === "online"), localBoardItemEvents, + sessionInfo, getWebSocketRootUrl, WebSocketPolyfill, ) diff --git a/frontend/src/store/crdt-store.ts b/frontend/src/store/crdt-store.ts index 66380c90..5a8feb36 100644 --- a/frontend/src/store/crdt-store.ts +++ b/frontend/src/store/crdt-store.ts @@ -5,6 +5,7 @@ import * as Y from "yjs" import { augmentItemsWithCRDT, getCRDTField, importItemsIntoCRDT } from "../../../common/src/board-crdt-helper" import { Id, Item, PersistableBoardItemEvent } from "../../../common/src/domain" import { getWebSocketRootUrl } from "./server-connection" +import { UserSessionState } from "./user-session-store" type BoardCRDT = ReturnType export type WebSocketPolyfill = @@ -87,15 +88,29 @@ export function CRDTStore( currentBoardId: L.Property, online: L.Property, localBoardItemEvents: L.EventStream, + sessionState: L.Property, getSocketRoot: () => string = getWebSocketRootUrl, WebSocketPolyfill: WebSocketPolyfill = WebSocket as any, ) { - let boardCrdt: BoardCRDT | undefined = undefined + let boardCrdt = L.atom(undefined) currentBoardId.forEach((boardId) => { - if (boardCrdt && boardCrdt.boardId !== boardId) { - boardCrdt.disconnect() - boardCrdt = undefined + boardCrdt.modify((prev) => { + if (!prev || prev.boardId === boardId) { + return prev + } + prev.disconnect() + }) + }) + + const userInfo = L.view(sessionState, (state) => ({ + name: state.nickname, + color: "#35b2dc", + })) + + L.view(userInfo, boardCrdt, (u, c) => [u, c] as const).forEach(([u, c]) => { + if (c) { + c.awareness.setLocalStateField("user", u) } }) @@ -104,17 +119,21 @@ export function CRDTStore( 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) - } - return boardCrdt + boardCrdt.modify((prev) => { + if (prev) { + return prev + } + return BoardCRDT(boardId, online, localBoardItemEvents, getSocketRoot, WebSocketPolyfill) + }) + return boardCrdt.get()! } function augmentItems(boardId: Id, items: Item[]): Item[] { - if (!boardCrdt || boardCrdt.boardId !== boardId) { + const bc = boardCrdt.get() + if (!bc || bc.boardId !== boardId) { return items } - return boardCrdt.augmentItems(items) + return bc.augmentItems(items) } return { diff --git a/frontend/src/style/board.scss b/frontend/src/style/board.scss index a9a684a2..8ae09078 100644 --- a/frontend/src/style/board.scss +++ b/frontend/src/style/board.scss @@ -176,6 +176,7 @@ > .quill-editor { width: 100%; height: fit-content; + overflow: visible; > .ql-editor { padding: 0.1em; ol, @@ -183,6 +184,12 @@ padding: 0; } } + .ql-cursors { + .ql-cursor-name { + margin: 0.1em 0.4em 0.05em; + font-size: min(1rem, 0.7em); + } + } } } diff --git a/perf-tester/src/index.ts b/perf-tester/src/index.ts index 56e9fdd7..f568027b 100644 --- a/perf-tester/src/index.ts +++ b/perf-tester/src/index.ts @@ -58,6 +58,13 @@ function createTester(nickname: string, boardId: string) { L.constant(boardId), connection.connected, localEvents.pipe(L.filter(isPersistableBoardItemEvent)).applyScope(L.globalScope), + L.constant({ + status: "anonymous", + sessionId: null, + nickname: nickname, + nicknameSetByUser: true, + loginSupported: false, + }), () => WS_ROOT, MyWebSocket as any, )