diff --git a/client/src/App.scss b/client/src/App.scss index b0640c2..8eaedf4 100644 --- a/client/src/App.scss +++ b/client/src/App.scss @@ -28,7 +28,7 @@ a { $border-radius: 9px; $padding: 9px; $height: 40px; -$left: 50px; +$left: 110px; .container { z-index: 2; diff --git a/client/src/components/Editor.tsx b/client/src/components/Editor.tsx index 764aef2..b795b3e 100644 --- a/client/src/components/Editor.tsx +++ b/client/src/components/Editor.tsx @@ -1,18 +1,12 @@ -import { FormEvent, useState } from "react"; -import { Tldraw, useFileSystem } from "@tldraw/tldraw"; +import { FormEvent, useCallback, useState } from "react"; +import { Tldraw, TldrawApp, useFileSystem } from "@tldraw/tldraw"; import { CustomCursor } from "./Cursor"; import { useAssets } from "../hooks/useAssets"; import { useMultiplayer } from "../hooks/useMultiplayer"; -import { useSingleplayer } from "../hooks/useSingleplayer"; -import { cloneDoc, initPersistence, initProvider, newDoc } from "../utils/yjs"; +import { cloneDoc, initProvider, newDoc } from "../utils/yjs"; import { v4 as uuidv4, validate as uuidValidate } from "uuid"; import PropTypes from "prop-types"; -import { - Multiplayer, - MultiplayerReadOnly, - Settings, - Singleplayer, -} from "../types/types"; +import { Multiplayer, Settings, Singleplayer } from "../types/types"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faArrowRightFromBracket, @@ -57,10 +51,11 @@ function Editor({ const [useLocalDoc, setUseLocalDoc] = useState(false); language = language || "en"; - initPersistence(idbName, localDoc); + readOnly = readOnly || false; let editor = ( - ) : ( + editor = ( ); } @@ -185,22 +174,32 @@ function Editor({ ); } -function SingleplayerEditor({ apiUrl, doc, language, readOnly }: Singleplayer) { +function SingleplayerEditor({ + apiUrl, + idbName, + doc, + language, + readOnly, +}: Singleplayer) { const fileSystemEvents = useFileSystem(); const { onAssetCreate, onAssetDelete, onAssetUpload } = useAssets(apiUrl); - const { ...events } = useSingleplayer(doc, language); + + const onMount = useCallback((app: TldrawApp) => { + app.setSetting("language", language); + app.setSetting("keepStyleMenuOpen", true); + }, []); return ( ); } @@ -211,6 +210,7 @@ function MultiplayerEditor({ provider, roomId, language, + readOnly, }: Multiplayer) { const { onSaveProjectAs, onSaveProject } = useFileSystem(); const { onAssetCreate, onAssetDelete, onAssetUpload } = useAssets(apiUrl); @@ -222,34 +222,12 @@ function MultiplayerEditor({ components={components} showPages={false} showMultiplayerMenu={false} - onAssetCreate={onAssetCreate} + onAssetCreate={readOnly ? undefined : onAssetCreate} onAssetDelete={onAssetDelete} onAssetUpload={onAssetUpload} onSaveProjectAs={onSaveProjectAs} onSaveProject={onSaveProject} - {...events} - /> - ); -} - -function MultiplayerReadOnlyEditor({ - doc, - provider, - roomId, - language, -}: MultiplayerReadOnly) { - const { onSaveProjectAs, onSaveProject } = useFileSystem(); - const { ...events } = useMultiplayer(doc, provider, roomId, language); - - return ( - ); diff --git a/client/src/hooks/useSingleplayer.ts b/client/src/hooks/useSingleplayer.ts deleted file mode 100644 index eef37df..0000000 --- a/client/src/hooks/useSingleplayer.ts +++ /dev/null @@ -1,136 +0,0 @@ -import type { TDAsset, TDBinding, TDShape, TldrawApp } from "@tldraw/tldraw"; -import { useCallback, useEffect, useState } from "react"; -import { useHotkeys } from "react-hotkeys-hook"; -import * as Y from "yjs"; -import { getDocData } from "../utils/yjs"; - -export function useSingleplayer(doc: Y.Doc, language: string) { - const [app, setApp] = useState(); - const [loading, setLoading] = useState(true); - - const { yShapes, yBindings, yAssets, undoManager } = getDocData(doc); - - const onUndo = useCallback(() => { - undoManager.undo(); - }, []); - const onRedo = useCallback(() => { - undoManager.redo(); - }, []); - - // Callbacks -------------- - - // Put the state into the window, for debugging. - const onMount = useCallback((app: TldrawApp) => { - app.setSetting("language", language); - app.setSetting("keepStyleMenuOpen", true); - app.pause(); // Turn off the app's own undo / redo stack - setApp(app); - }, []); - - // Update the live shapes when the app's shapes change. - const onChangePage = useCallback( - ( - app: TldrawApp, - shapes: Record, - bindings: Record, - assets: Record - ) => { - doc.transact(() => { - if (!(yShapes && yBindings && yAssets)) return; - - Object.entries(shapes).forEach(([id, shape]) => { - if (!shape) { - yShapes.delete(id); - } else { - yShapes.set(shape.id, shape); - } - }); - - Object.entries(bindings).forEach(([id, binding]) => { - if (!binding) { - yBindings.delete(id); - } else { - yBindings.set(binding.id, binding); - } - }); - - Object.entries(assets).forEach(([id, asset]) => { - if (!asset) { - yAssets.delete(id); - } else { - yAssets.set(asset.id, asset); - } - }); - }); - }, - [] - ); - - // Document Changes -------- - - useEffect(() => { - if (!app) return; - - // Subscribe to changes - function handleChanges() { - if (!app) return; - - app.replacePageContent( - Object.fromEntries(yShapes.entries()), - Object.fromEntries(yBindings.entries()), - Object.fromEntries(yAssets.entries()) - ); - } - - async function setup() { - yShapes.observe(handleChanges); - handleChanges(); - setLoading(false); - } - - setup(); - - return () => { - yShapes.unobserveDeep(handleChanges); - }; - }, [app]); - - const onSessionStart = useCallback(() => {}, []); - - const onSessionEnd = useCallback(() => {}, []); - - useHotkeys( - "ctrl+shift+l;,⌘+shift+l", - () => { - if (window.confirm("Reset the document?")) { - undoManager.stopCapturing(); - doc.transact(() => { - if (!(yShapes && yBindings && yAssets)) return; - - yShapes.forEach((shape) => { - yShapes.delete(shape.id); - }); - - yBindings.forEach((shape) => { - yBindings.delete(shape.id); - }); - - yAssets.forEach((shape) => { - yAssets.delete(shape.id); - }); - }); - } - }, - [] - ); - - return { - onMount, - onUndo, - onRedo, - onChangePage, - onSessionStart, - onSessionEnd, - loading, - }; -} diff --git a/client/src/types/types.ts b/client/src/types/types.ts index cddc522..cdcdc99 100644 --- a/client/src/types/types.ts +++ b/client/src/types/types.ts @@ -15,6 +15,7 @@ export type Settings = { export type Singleplayer = { apiUrl: string; + idbName: string; doc: Y.Doc; language: string; readOnly: boolean; @@ -26,11 +27,5 @@ export type Multiplayer = { provider: WebsocketProvider; roomId: string; language: string; -}; - -export type MultiplayerReadOnly = { - doc: Y.Doc; - provider: WebsocketProvider; - roomId: string; - language: string; + readOnly: boolean; };