Update multiplayer on www

This commit is contained in:
Steve Ruiz 2021-10-16 21:44:58 +01:00
parent abcdcd8dae
commit 3f5f613e09
2 changed files with 53 additions and 28 deletions

View file

@ -1,3 +1,17 @@
## 0.0.117
### Core
- Improves multiplayer features.
### TLDraw
- Fixes bugs in text, arrows, stickies.
- Adds start binding for new arrows.
- Adds copy painting (alt + shift + drag).
- Adds side clonig.
- Adds clone dragging.
## 0.0.116 ## 0.0.116
### Core ### Core

View file

@ -12,6 +12,7 @@ interface TLDrawUserPresence extends Presence {
const client = createClient({ const client = createClient({
publicApiKey: process.env.NEXT_PUBLIC_LIVEBLOCKS_PUBLIC_API_KEY, publicApiKey: process.env.NEXT_PUBLIC_LIVEBLOCKS_PUBLIC_API_KEY,
throttle: 80,
}) })
export default function MultiplayerEditor({ id }: { id: string }) { export default function MultiplayerEditor({ id }: { id: string }) {
@ -62,32 +63,28 @@ function Editor({ id }: { id: string }) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
window.tlstate = tlstate window.tlstate = tlstate
tlstate.loadRoom(id)
setTlstate(tlstate) setTlstate(tlstate)
}, []) }, [])
const handleChange = React.useCallback( const handleChange = React.useCallback(
(_tlstate: TLDrawState, state: Data, reason: string) => { (_tlstate: TLDrawState, state: Data, reason: string) => {
// If the client updates its document, update the room's document / gtag // If the client updates its document, update the room's document
if (reason.startsWith('command')) { if (reason.startsWith('command') || reason.startsWith('undo') || reason.startsWith('redo')) {
gtag.event({
action: reason,
category: 'editor',
label: '',
value: 0,
})
doc?.update({ uuid: docId, document: state.document }) doc?.update({ uuid: docId, document: state.document })
} }
// When the client updates its presence, update the room // When the client updates its presence, update the room
if (state.room && (reason === 'patch:room:self:update' || reason === 'patch:selected')) { // if (state.room && (reason === 'patch:room:self:update' || reason === 'patch:selected')) {
const room = client.getRoom(id) // const room = client.getRoom(ROOM_ID)
if (!room) return // if (!room) return
const { userId, users } = state.room // const { userId, users } = state.room
room.updatePresence({ id: userId, user: users[userId] }) // room.updatePresence({ id: userId, user: users[userId] })
} // }
}, },
[docId, doc, id] [docId, doc]
) )
React.useEffect(() => { React.useEffect(() => {
@ -96,14 +93,12 @@ function Editor({ id }: { id: string }) {
if (!room) return if (!room) return
if (!doc) return if (!doc) return
if (!tlstate) return if (!tlstate) return
if (!tlstate.state.room) return
// Update the user's presence with the user from state // Update the user's presence with the user from state
const { users, userId } = tlstate.state.room const { users, userId } = tlstate.state.room
room.updatePresence({
id: userId, room.updatePresence({ id: userId, user: users[userId] })
user: users[userId],
shapes: Object.fromEntries(tlstate.selectedIds.map((id) => [id, tlstate.getShape(id)])),
})
// Subscribe to presence changes; when others change, update the state // Subscribe to presence changes; when others change, update the state
room.subscribe<TLDrawUserPresence>('others', (others) => { room.subscribe<TLDrawUserPresence>('others', (others) => {
@ -125,6 +120,7 @@ function Editor({ id }: { id: string }) {
function handleDocumentUpdates() { function handleDocumentUpdates() {
if (!doc) return if (!doc) return
if (!tlstate) return if (!tlstate) return
if (!tlstate.state.room) return
const docObject = doc.toObject() const docObject = doc.toObject()
@ -134,13 +130,13 @@ function Editor({ id }: { id: string }) {
} else { } else {
tlstate.updateUsers( tlstate.updateUsers(
Object.values(tlstate.state.room.users).map((user) => { Object.values(tlstate.state.room.users).map((user) => {
const activeShapes = user.activeShapes // const activeShapes = user.activeShapes
.map((shape) => docObject.document.pages[tlstate.currentPageId].shapes[shape.id]) // .map((shape) => docObject.document.pages[tlstate.currentPageId].shapes[shape.id])
.filter(Boolean) // .filter(Boolean)
return { return {
...user, ...user,
activeShapes: activeShapes, // activeShapes: activeShapes,
selectedIds: activeShapes.map((shape) => shape.id), selectedIds: user.selectedIds, // activeShapes.map((shape) => shape.id),
} }
}) })
) )
@ -148,7 +144,8 @@ function Editor({ id }: { id: string }) {
} }
function handleExit() { function handleExit() {
room?.broadcastEvent({ name: 'exit', userId: tlstate?.state.room.userId }) if (!(tlstate && tlstate.state.room)) return
room?.broadcastEvent({ name: 'exit', userId: tlstate.state.room.userId })
} }
window.addEventListener('beforeunload', handleExit) window.addEventListener('beforeunload', handleExit)
@ -165,13 +162,27 @@ function Editor({ id }: { id: string }) {
} }
}, [doc, docId, tlstate, id]) }, [doc, docId, tlstate, id])
const handleUserChange = React.useCallback(
(tlstate: TLDrawState, user: TLDrawUser) => {
const room = client.getRoom(id)
room?.updatePresence({ id: tlstate.state.room?.userId, user })
},
[id]
)
if (error) return <div>Error: {error.message}</div> if (error) return <div>Error: {error.message}</div>
if (doc === null) return <div>Loading...</div> if (doc === null) return <div>Loading...</div>
return ( return (
<div className="tldraw"> <div className="tldraw">
<TLDraw onMount={handleMount} onChange={handleChange} showPages={false} autofocus /> <TLDraw
onMount={handleMount}
onChange={handleChange}
onUserChange={handleUserChange}
showPages={false}
autofocus
/>
</div> </div>
) )
} }