浏览代码

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
   const [app, setApp] = React.useState<TldrawApp>()
11
   const [app, setApp] = React.useState<TldrawApp>()
12
   const [error, setError] = React.useState<Error>()
12
   const [error, setError] = React.useState<Error>()
13
   const [loading, setLoading] = React.useState(true)
13
   const [loading, setLoading] = React.useState(true)
14
-  const rExpectingUpdate = React.useRef(false)
15
 
14
 
16
   const room = useRoom()
15
   const room = useRoom()
17
   const onUndo = useUndo()
16
   const onUndo = useUndo()
18
   const onRedo = useRedo()
17
   const onRedo = useRedo()
19
   const updateMyPresence = useUpdateMyPresence()
18
   const updateMyPresence = useUpdateMyPresence()
20
 
19
 
21
-  // Document Changes --------
22
-
23
   const rLiveShapes = React.useRef<LiveMap<string, TDShape>>()
20
   const rLiveShapes = React.useRef<LiveMap<string, TDShape>>()
24
   const rLiveBindings = React.useRef<LiveMap<string, TDBinding>>()
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
   React.useEffect(() => {
79
   React.useEffect(() => {
27
     const unsubs: (() => void)[] = []
80
     const unsubs: (() => void)[] = []
28
-
29
     if (!(app && room)) return
81
     if (!(app && room)) return
30
     // Handle errors
82
     // Handle errors
31
     unsubs.push(room.subscribe('error', (error) => setError(error)))
83
     unsubs.push(room.subscribe('error', (error) => setError(error)))
67
     window.addEventListener('beforeunload', handleExit)
119
     window.addEventListener('beforeunload', handleExit)
68
     unsubs.push(() => window.removeEventListener('beforeunload', handleExit))
120
     unsubs.push(() => window.removeEventListener('beforeunload', handleExit))
69
 
121
 
122
+    let stillAlive = true
123
+
70
     // Setup the document's storage and subscriptions
124
     // Setup the document's storage and subscriptions
71
     async function setupDocument() {
125
     async function setupDocument() {
72
       const storage = await room.getStorage<any>()
126
       const storage = await room.getStorage<any>()
87
       }
141
       }
88
       rLiveBindings.current = lBindings
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
       // Migrate previous versions
144
       // Migrate previous versions
110
       const version = storage.root.get('version')
145
       const version = storage.root.get('version')
111
 
146
 
121
           migrated?: boolean
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
         if (doc) {
160
         if (doc) {
126
           const {
161
           const {
127
             document: {
162
             document: {
129
                 page: { shapes, bindings },
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
       // Save the version number for future migrations
174
       // Save the version number for future migrations
147
       storage.root.set('version', 2)
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
     setupDocument()
194
     setupDocument()
153
 
195
 
154
     return () => {
196
     return () => {
197
+      stillAlive = false
155
       unsubs.forEach((unsub) => unsub())
198
       unsubs.forEach((unsub) => unsub())
156
     }
199
     }
157
   }, [room, app])
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
   return {
202
   return {
218
     onUndo,
203
     onUndo,
219
     onRedo,
204
     onRedo,

正在加载...
取消
保存