1.20.0 (#797)
* Edit Farsi translations (#788) * Add a Ukrainian translation (#786) * Add a Ukrainian translation * Clarify some strings in the Ukrainian translation * feat: change dock position (#774) * feat: change dock position * fix grid row and column * add top position * fix responsive for the top position * change content side * fix overflowing menu * [improvement] theme on body (#790) * Update Tldraw.tsx * Add theme on body, adjust dark page options dialog * fix test * Preparing for global integration (#775) * Update translations.ts * Create en.json * Make main translation default * Remove unused locale property of translation Co-authored-by: Steve Ruiz <steveruizok@gmail.com> * Fix language menu * Update ar.json (#793) * feature/add Hebrew translations (#792) * hebrew translations * pr fixes Co-authored-by: Steve Ruiz <steveruizok@gmail.com> * fix toolspanel item position (#791) * fix toolspanel item position * add translation Co-authored-by: Steve Ruiz <steveruizok@gmail.com> * Add remote caching * Adds link to translation guide (#794) * Update ar.json (#795) * [feature] readonly link (#796) * Copy readonly link * Update [id].tsx * Add readonly label * update psuedohash * Update utils.ts Co-authored-by: Baahar Ebrahimi <108254874+Baahaarmast@users.noreply.github.com> Co-authored-by: walking-octopus <46994949+walking-octopus@users.noreply.github.com> Co-authored-by: Judicael <46365844+judicaelandria@users.noreply.github.com> Co-authored-by: Ali Alhaidary <75235623+ali-alhaidary@users.noreply.github.com> Co-authored-by: gadi246 <gadi246@gmail.com>
This commit is contained in:
parent
5ac091339a
commit
315112459c
11 changed files with 343 additions and 24 deletions
151
apps/www/hooks/useReadOnlyMultiplayerState.ts
Normal file
151
apps/www/hooks/useReadOnlyMultiplayerState.ts
Normal file
|
@ -0,0 +1,151 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import React, { useState, useRef, useCallback } from 'react'
|
||||
import type { TldrawApp, TDUser, TDShape, TDBinding, TDAsset } from '@tldraw/tldraw'
|
||||
import { Storage, useRedo, useUndo, useRoom, useUpdateMyPresence } from '../utils/liveblocks'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { LiveMap } from '@liveblocks/client'
|
||||
|
||||
declare const window: Window & { app: TldrawApp }
|
||||
|
||||
export function useReadOnlyMultiplayerState(roomId: string) {
|
||||
const [app, setApp] = useState<TldrawApp>()
|
||||
const [error, setError] = useState<Error>()
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
const room = useRoom()
|
||||
const onUndo = useUndo()
|
||||
const onRedo = useRedo()
|
||||
const updateMyPresence = useUpdateMyPresence()
|
||||
|
||||
const rIsPaused = useRef(false)
|
||||
|
||||
const rLiveShapes = useRef<Storage['shapes']>()
|
||||
const rLiveBindings = useRef<Storage['bindings']>()
|
||||
const rLiveAssets = useRef<Storage['assets']>()
|
||||
|
||||
// Callbacks --------------
|
||||
|
||||
// Put the state into the window, for debugging.
|
||||
const onMount = useCallback(
|
||||
(app: TldrawApp) => {
|
||||
app.loadRoom(roomId)
|
||||
app.pause() // Turn off the app's own undo / redo stack
|
||||
window.app = app
|
||||
setApp(app)
|
||||
},
|
||||
[roomId]
|
||||
)
|
||||
|
||||
// Handle presence updates when the user's pointer / selection changes
|
||||
const onChangePresence = useCallback(
|
||||
(app: TldrawApp, user: TDUser) => {
|
||||
updateMyPresence({ id: app.room?.userId, user })
|
||||
},
|
||||
[updateMyPresence]
|
||||
)
|
||||
|
||||
// Document Changes --------
|
||||
|
||||
React.useEffect(() => {
|
||||
const unsubs: (() => void)[] = []
|
||||
if (!(app && room)) return
|
||||
// Handle errors
|
||||
unsubs.push(room.subscribe('error', (error) => setError(error)))
|
||||
|
||||
// Handle changes to other users' presence
|
||||
unsubs.push(
|
||||
room.subscribe('others', (others, event) => {
|
||||
if (event.type === 'leave') {
|
||||
if (event.user.presence) {
|
||||
app?.removeUser(event.user.presence.id)
|
||||
}
|
||||
} else {
|
||||
app.updateUsers(
|
||||
others
|
||||
.toArray()
|
||||
.filter((other) => other.presence)
|
||||
.map((other) => other.presence!.user)
|
||||
.filter(Boolean)
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
let stillAlive = true
|
||||
|
||||
// Setup the document's storage and subscriptions
|
||||
async function setupDocument() {
|
||||
const storage = await room.getStorage()
|
||||
|
||||
// Migrate previous versions
|
||||
const version = storage.root.get('version')
|
||||
|
||||
// Initialize (get or create) maps for shapes/bindings/assets
|
||||
|
||||
let lShapes = storage.root.get('shapes')
|
||||
if (!lShapes || !('_serialize' in lShapes)) {
|
||||
storage.root.set('shapes', new LiveMap())
|
||||
lShapes = storage.root.get('shapes')
|
||||
}
|
||||
rLiveShapes.current = lShapes
|
||||
|
||||
let lBindings = storage.root.get('bindings')
|
||||
if (!lBindings || !('_serialize' in lBindings)) {
|
||||
storage.root.set('bindings', new LiveMap())
|
||||
lBindings = storage.root.get('bindings')
|
||||
}
|
||||
rLiveBindings.current = lBindings
|
||||
|
||||
let lAssets = storage.root.get('assets')
|
||||
if (!lAssets || !('_serialize' in lAssets)) {
|
||||
storage.root.set('assets', new LiveMap())
|
||||
lAssets = storage.root.get('assets')
|
||||
}
|
||||
rLiveAssets.current = lAssets
|
||||
|
||||
// Save the version number for future migrations
|
||||
storage.root.set('version', 2.1)
|
||||
|
||||
// Subscribe to changes
|
||||
const handleChanges = () => {
|
||||
app?.replacePageContent(
|
||||
Object.fromEntries(lShapes.entries()),
|
||||
Object.fromEntries(lBindings.entries()),
|
||||
Object.fromEntries(lAssets.entries())
|
||||
)
|
||||
}
|
||||
|
||||
if (stillAlive) {
|
||||
unsubs.push(room.subscribe(lShapes, handleChanges))
|
||||
|
||||
// Update the document with initial content
|
||||
handleChanges()
|
||||
|
||||
// Zoom to fit the content
|
||||
app.zoomToFit()
|
||||
if (app.zoom > 1) {
|
||||
app.resetZoom()
|
||||
}
|
||||
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
setupDocument()
|
||||
|
||||
return () => {
|
||||
stillAlive = false
|
||||
unsubs.forEach((unsub) => unsub())
|
||||
}
|
||||
}, [room, app])
|
||||
|
||||
return {
|
||||
onUndo,
|
||||
onRedo,
|
||||
onMount,
|
||||
onChangePresence,
|
||||
error,
|
||||
loading,
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue