Skip to content

Commit

Permalink
Allow an authenticated user to enable collaborative editor for new board
Browse files Browse the repository at this point in the history
Otherwise boards will use the legacy editor and no crdts.
  • Loading branch information
raimohanska committed Feb 25, 2024
1 parent 0dbf313 commit 408b865
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 12 deletions.
3 changes: 1 addition & 2 deletions YJS_CRDT_WIP.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ The Y.js based collaborative editing support is under construction.

Must-haves

- Undo buffer integration. Editor has its own local undo but we should also add the full edit as a global undo item
- Board level flag for CRDT or other controlled way of rolling this out
- APIs
- Manage session on the server side: terminate YJS sockets when websocket session is terminated
- Mobile check
Expand All @@ -24,6 +22,7 @@ Must-haves

Nice-to-haves

- Undo buffer integration. Editor has its own local undo but we should also add the full edit as a global undo item
- Persistence: consider storing CRDT snapshot
- Persistence: make sure the compactor works
- UI: Show proper username by the cursor when hovering. Now shows some large number
Expand Down
3 changes: 3 additions & 0 deletions common/src/board-reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ export function boardReducer(
board.items,
(items) => {
event.items.forEach((item) => {
if (board.crdt && isTextItem(item)) {
item = { ...item, crdt: board.crdt }
}
if (
item.containerId &&
!findItem(board)(item.containerId) &&
Expand Down
11 changes: 5 additions & 6 deletions common/src/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ export function optional<T extends t.Type<any>>(c: T) {
return t.union([c, t.undefined, t.null])
}

type CrdtMode = undefined | 1

export type BoardAttributes = {
id: Id
name: string
width: number
height: number
accessPolicy?: BoardAccessPolicy
crdt?: CrdtMode
}

export type BoardContents = {
Expand All @@ -34,7 +37,7 @@ export type Board = BoardAttributes &
serial: Serial
}

export type BoardStub = Pick<Board, "id" | "name" | "accessPolicy"> & { templateId?: Id }
export type BoardStub = Pick<Board, "id" | "name" | "accessPolicy" | "crdt"> & { templateId?: Id }

export const AccessLevelCodec = t.union([
t.literal("admin"),
Expand Down Expand Up @@ -134,7 +137,7 @@ export type TextItemProperties = ItemProperties & {
text: string
fontSize?: number
align?: Align
crdt?: 1
crdt?: CrdtMode
textAsDelta?: QuillDelta
}
export type NoteShape = "round" | "square" | "rect" | "diamond"
Expand Down Expand Up @@ -401,8 +404,6 @@ export function newSimilarNote(note: Note) {
return newNote("HELLO", note.color, 20, 20, note.width, note.height, note.shape)
}

const CRDT_CURRENT = 1

export function newText(
text: string = "HELLO",
x: number = 20,
Expand All @@ -422,7 +423,6 @@ export function newText(
z,
color: "none",
locked: false,
crdt: CRDT_CURRENT,
}
}

Expand All @@ -444,7 +444,6 @@ export function newContainer(
z,
color: "white",
locked: false,
crdt: CRDT_CURRENT,
}
}

Expand Down
19 changes: 19 additions & 0 deletions frontend/src/components/BoardCrdtModeSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Fragment, h } from "harmaja"
import * as L from "lonna"
import { Checkbox } from "./components"

type BoardCrdtModeSelectorProps = {
useCollaborativeEditing: L.Atom<boolean>
}
export const BoardCrdtModeSelector = ({ useCollaborativeEditing }: BoardCrdtModeSelectorProps) => {
return (
<div className="board-access-editor">
<div className="restrict-toggle">
<Checkbox checked={useCollaborativeEditing} />
<span>
<label htmlFor="domain-restrict">Experimental: use collaborative text editor</label>
</span>
</div>
</div>
)
}
30 changes: 26 additions & 4 deletions frontend/src/dashboard/DashboardView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ import { getNavigator, Link } from "harmaja-router"
import * as L from "lonna"
import * as R from "ramda"
import * as uuid from "uuid"
import { BoardAccessPolicy, EventFromServer, exampleBoard, RecentBoard } from "../../../common/src/domain"
import {
Board,
BoardAccessPolicy,
BoardStub,
EventFromServer,
exampleBoard,
RecentBoard,
} from "../../../common/src/domain"
import { BOARD_PATH, createBoardAndNavigate, Routes } from "../board-navigation"
import { localStorageAtom } from "../board/local-storage-atom"
import { IS_TOUCHSCREEN } from "../board/touchScreen"
Expand All @@ -13,6 +20,7 @@ import { signIn, signOut } from "../google-auth"
import { Dispatch } from "../store/board-store"
import { RecentBoards } from "../store/recent-boards"
import { canLogin, defaultAccessPolicy, UserSessionState } from "../store/user-session-store"
import { BoardCrdtModeSelector } from "../components/BoardCrdtModeSelector"

export const DashboardView = ({
sessionState,
Expand Down Expand Up @@ -255,13 +263,18 @@ const Welcome = ({
const CreateBoardOptions = ({
accessPolicy,
sessionState,
useCollaborativeEditing,
}: {
accessPolicy: L.Atom<BoardAccessPolicy | undefined>
sessionState: L.Property<UserSessionState>
useCollaborativeEditing: L.Atom<boolean>
}) => {
return L.view(sessionState, (s) =>
s.status === "logged-in" ? (
<BoardAccessPolicyEditor {...{ accessPolicy, user: s }} />
<>
<BoardCrdtModeSelector {...{ useCollaborativeEditing }} />
<BoardAccessPolicyEditor {...{ accessPolicy, user: s }} />
</>
) : (
<small className="anonymousBoardDisclaimer">
Anonymously created boards are accessible to anyone with a link. You may <a onClick={signIn}>sign in</a>{" "}
Expand Down Expand Up @@ -291,10 +304,16 @@ const CreateBoard = ({
accessPolicy.set(defaultAccessPolicy(s, false))
})
const hasRecentBoards = L.view(recentBoards.recentboards, (bs) => bs.length > 0)
const useCollaborativeEditing = L.atom(false)

function onSubmit(e: JSX.FormEvent) {
e.preventDefault()
const newBoard = { name: boardName.get(), id: uuid.v4(), accessPolicy: accessPolicy.get() }
const newBoard: BoardStub = {
name: boardName.get(),
id: uuid.v4(),
accessPolicy: accessPolicy.get(),
crdt: useCollaborativeEditing.get() ? 1 : undefined,
}
createBoardAndNavigate(newBoard, dispatch, navigator, eventsFromServer)
}

Expand All @@ -307,7 +326,10 @@ const CreateBoard = ({
Create
</button>
</div>
{L.view(disabled, (d) => !d && <CreateBoardOptions {...{ accessPolicy, sessionState }} />)}
{L.view(
disabled,
(d) => !d && <CreateBoardOptions {...{ accessPolicy, sessionState, useCollaborativeEditing }} />,
)}
</form>
)
}

0 comments on commit 408b865

Please sign in to comment.