Преглед на файлове

Update useMultiplayerState.ts

main
Steve Ruiz преди 3 години
родител
ревизия
2a833dd408
променени са 1 файла, в които са добавени 79 реда и са изтрити 94 реда
  1. 79
    94
      apps/www/hooks/useMultiplayerState.ts

+ 79
- 94
apps/www/hooks/useMultiplayerState.ts Целия файл

@@ -11,21 +11,73 @@ export function useMultiplayerState(roomId: string) {
11 11
   const [app, setApp] = React.useState<TldrawApp>()
12 12
   const [error, setError] = React.useState<Error>()
13 13
   const [loading, setLoading] = React.useState(true)
14
-  const rExpectingUpdate = React.useRef(false)
15 14
 
16 15
   const room = useRoom()
17 16
   const onUndo = useUndo()
18 17
   const onRedo = useRedo()
19 18
   const updateMyPresence = useUpdateMyPresence()
20 19
 
21
-  // Document Changes --------
22
-
23 20
   const rLiveShapes = React.useRef<LiveMap<string, TDShape>>()
24 21
   const rLiveBindings = React.useRef<LiveMap<string, TDBinding>>()
25 22
 
23
+  // Callbacks --------------
24
+
25
+  // Put the state into the window, for debugging.
26
+  const onMount = React.useCallback(
27
+    (app: TldrawApp) => {
28
+      app.loadRoom(roomId)
29
+      app.pause() // Turn off the app's own undo / redo stack
30
+      window.app = app
31
+      setApp(app)
32
+    },
33
+    [roomId]
34
+  )
35
+
36
+  // Update the live shapes when the app's shapes change.
37
+  const onChangePage = React.useCallback(
38
+    (
39
+      app: TldrawApp,
40
+      shapes: Record<string, TDShape | undefined>,
41
+      bindings: Record<string, TDBinding | undefined>
42
+    ) => {
43
+      room.batch(() => {
44
+        const lShapes = rLiveShapes.current
45
+        const lBindings = rLiveBindings.current
46
+
47
+        if (!(lShapes && lBindings)) return
48
+
49
+        Object.entries(shapes).forEach(([id, shape]) => {
50
+          if (!shape) {
51
+            lShapes.delete(id)
52
+          } else {
53
+            lShapes.set(shape.id, shape)
54
+          }
55
+        })
56
+
57
+        Object.entries(bindings).forEach(([id, binding]) => {
58
+          if (!binding) {
59
+            lBindings.delete(id)
60
+          } else {
61
+            lBindings.set(binding.id, binding)
62
+          }
63
+        })
64
+      })
65
+    },
66
+    [room]
67
+  )
68
+
69
+  // Handle presence updates when the user's pointer / selection changes
70
+  const onChangePresence = React.useCallback(
71
+    (app: TldrawApp, user: TDUser) => {
72
+      updateMyPresence({ id: app.room?.userId, user })
73
+    },
74
+    [updateMyPresence]
75
+  )
76
+
77
+  // Document Changes --------
78
+
26 79
   React.useEffect(() => {
27 80
     const unsubs: (() => void)[] = []
28
-
29 81
     if (!(app && room)) return
30 82
     // Handle errors
31 83
     unsubs.push(room.subscribe('error', (error) => setError(error)))
@@ -67,6 +119,8 @@ export function useMultiplayerState(roomId: string) {
67 119
     window.addEventListener('beforeunload', handleExit)
68 120
     unsubs.push(() => window.removeEventListener('beforeunload', handleExit))
69 121
 
122
+    let stillAlive = true
123
+
70 124
     // Setup the document's storage and subscriptions
71 125
     async function setupDocument() {
72 126
       const storage = await room.getStorage<any>()
@@ -87,25 +141,6 @@ export function useMultiplayerState(roomId: string) {
87 141
       }
88 142
       rLiveBindings.current = lBindings
89 143
 
90
-      // Subscribe to changes
91
-      function handleChanges() {
92
-        if (rExpectingUpdate.current) {
93
-          rExpectingUpdate.current = false
94
-          return
95
-        }
96
-
97
-        app?.replacePageContent(
98
-          Object.fromEntries(lShapes.entries()),
99
-          Object.fromEntries(lBindings.entries())
100
-        )
101
-      }
102
-
103
-      unsubs.push(room.subscribe(lShapes, handleChanges))
104
-      unsubs.push(room.subscribe(lBindings, handleChanges))
105
-
106
-      // Update the document with initial content
107
-      handleChanges()
108
-
109 144
       // Migrate previous versions
110 145
       const version = storage.root.get('version')
111 146
 
@@ -121,7 +156,7 @@ export function useMultiplayerState(roomId: string) {
121 156
           migrated?: boolean
122 157
         }>
123 158
 
124
-        // No doc? No problem. This was likely
159
+        // No doc? No problem. This was likely a newer document
125 160
         if (doc) {
126 161
           const {
127 162
             document: {
@@ -129,91 +164,41 @@ export function useMultiplayerState(roomId: string) {
129 164
                 page: { shapes, bindings },
130 165
               },
131 166
             },
132
-          } = doc.toObject() as { document: TDDocument }
133
-
134
-          for (const key in shapes) {
135
-            const shape = shapes[key]
136
-            lShapes.set(shape.id, shape)
137
-          }
167
+          } = doc.toObject()
138 168
 
139
-          for (const key in bindings) {
140
-            const binding = bindings[key]
141
-            lBindings.set(binding.id, binding)
142
-          }
169
+          Object.values(shapes).forEach((shape) => lShapes.set(shape.id, shape))
170
+          Object.values(bindings).forEach((binding) => lBindings.set(binding.id, binding))
143 171
         }
144 172
       }
145 173
 
146 174
       // Save the version number for future migrations
147 175
       storage.root.set('version', 2)
148 176
 
149
-      setLoading(false)
150
-    }
177
+      // Subscribe to changes
178
+      const handleChanges = () => {
179
+        app?.replacePageContent(
180
+          Object.fromEntries(lShapes.entries()),
181
+          Object.fromEntries(lBindings.entries())
182
+        )
183
+      }
184
+
185
+      if (stillAlive) {
186
+        unsubs.push(room.subscribe(lShapes, handleChanges))
187
+
188
+        // Update the document with initial content
189
+        handleChanges()
151 190
 
191
+        setLoading(false)
192
+      }
193
+    }
152 194
     setupDocument()
153 195
 
154 196
     return () => {
197
+      stillAlive = false
155 198
       unsubs.forEach((unsub) => unsub())
156 199
     }
157 200
   }, [room, app])
158 201
 
159
-  // Callbacks --------------
160
-
161
-  // Put the state into the window, for debugging.
162
-  const onMount = React.useCallback(
163
-    (app: TldrawApp) => {
164
-      app.loadRoom(roomId)
165
-      app.pause() // Turn off the app's own undo / redo stack
166
-      window.app = app
167
-      setApp(app)
168
-    },
169
-    [roomId]
170
-  )
171
-
172
-  // Update the live shapes when the app's shapes change.
173
-  const onChangePage = React.useCallback(
174
-    (
175
-      app: TldrawApp,
176
-      shapes: Record<string, TDShape | undefined>,
177
-      bindings: Record<string, TDBinding | undefined>
178
-    ) => {
179
-      room.batch(() => {
180
-        const lShapes = rLiveShapes.current
181
-        const lBindings = rLiveBindings.current
182
-
183
-        if (!(lShapes && lBindings)) return
184
-
185
-        for (const id in shapes) {
186
-          const shape = shapes[id]
187
-          if (!shape) {
188
-            lShapes.delete(id)
189
-          } else {
190
-            lShapes.set(shape.id, shape)
191
-          }
192
-        }
193
-
194
-        for (const id in bindings) {
195
-          const binding = bindings[id]
196
-          if (!binding) {
197
-            lBindings.delete(id)
198
-          } else {
199
-            lBindings.set(binding.id, binding)
200
-          }
201
-        }
202
-
203
-        rExpectingUpdate.current = true
204
-      })
205
-    },
206
-    [room]
207
-  )
208
-
209
-  // Handle presence updates when the user's pointer / selection changes
210
-  const onChangePresence = React.useCallback(
211
-    (app: TldrawApp, user: TDUser) => {
212
-      updateMyPresence({ id: app.room?.userId, user })
213
-    },
214
-    [updateMyPresence]
215
-  )
216
-
217 202
   return {
218 203
     onUndo,
219 204
     onRedo,

Loading…
Отказ
Запис