@netless/window-manager 0.4.0-canary.6 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/.idea/inspectionProfiles/Project_Default.xml +7 -0
  2. package/.idea/modules.xml +8 -0
  3. package/.idea/vcs.xml +6 -0
  4. package/.idea/window-manager.iml +12 -0
  5. package/.vscode/settings.json +1 -0
  6. package/CHANGELOG.md +39 -2
  7. package/README.md +3 -0
  8. package/dist/App/MagixEvent/index.d.ts +29 -0
  9. package/dist/App/Storage/index.d.ts +19 -6
  10. package/dist/App/Storage/typings.d.ts +4 -0
  11. package/dist/AppContext.d.ts +39 -17
  12. package/dist/AppListener.d.ts +2 -1
  13. package/dist/AppManager.d.ts +25 -11
  14. package/dist/AppProxy.d.ts +7 -8
  15. package/dist/AttributesDelegate.d.ts +2 -2
  16. package/dist/BoxManager.d.ts +6 -3
  17. package/dist/BuiltinApps.d.ts +5 -0
  18. package/dist/ContainerResizeObserver.d.ts +10 -0
  19. package/dist/Cursor/Cursor.d.ts +10 -12
  20. package/dist/Cursor/index.d.ts +6 -16
  21. package/dist/Helper.d.ts +7 -0
  22. package/dist/Register/storage.d.ts +5 -1
  23. package/dist/Utils/AppCreateQueue.d.ts +11 -0
  24. package/dist/Utils/Common.d.ts +7 -2
  25. package/dist/Utils/RoomHacker.d.ts +3 -3
  26. package/dist/{MainView.d.ts → View/MainView.d.ts} +5 -6
  27. package/dist/View/ViewManager.d.ts +13 -0
  28. package/dist/constants.d.ts +5 -7
  29. package/dist/index.d.ts +35 -16
  30. package/dist/index.es.js +41 -1
  31. package/dist/index.es.js.map +1 -1
  32. package/dist/index.umd.js +41 -1
  33. package/dist/index.umd.js.map +1 -1
  34. package/dist/style.css +1 -1
  35. package/dist/typings.d.ts +3 -2
  36. package/docs/advanced.md +39 -0
  37. package/docs/api.md +69 -6
  38. package/docs/concept.md +9 -0
  39. package/docs/replay.md +40 -0
  40. package/package.json +7 -6
  41. package/src/App/MagixEvent/index.ts +68 -0
  42. package/src/App/Storage/index.ts +90 -44
  43. package/src/App/Storage/typings.ts +8 -0
  44. package/src/AppContext.ts +61 -25
  45. package/src/AppListener.ts +28 -16
  46. package/src/AppManager.ts +260 -81
  47. package/src/AppProxy.ts +53 -64
  48. package/src/AttributesDelegate.ts +2 -2
  49. package/src/BoxManager.ts +40 -24
  50. package/src/BuiltinApps.ts +23 -0
  51. package/src/ContainerResizeObserver.ts +62 -0
  52. package/src/Cursor/Cursor.svelte +25 -21
  53. package/src/Cursor/Cursor.ts +25 -38
  54. package/src/Cursor/icons.ts +2 -0
  55. package/src/Cursor/index.ts +45 -139
  56. package/src/Helper.ts +41 -0
  57. package/src/Register/index.ts +25 -16
  58. package/src/Register/loader.ts +2 -2
  59. package/src/Register/storage.ts +6 -1
  60. package/src/Utils/AppCreateQueue.ts +54 -0
  61. package/src/Utils/Common.ts +69 -14
  62. package/src/Utils/RoomHacker.ts +44 -14
  63. package/src/{MainView.ts → View/MainView.ts} +25 -36
  64. package/src/View/ViewManager.ts +52 -0
  65. package/src/constants.ts +6 -4
  66. package/src/image/laser-pointer-cursor.svg +17 -0
  67. package/src/index.ts +164 -99
  68. package/src/shim.d.ts +5 -0
  69. package/src/style.css +12 -1
  70. package/src/typings.ts +3 -2
  71. package/vite.config.js +8 -2
  72. package/dist/Base/Context.d.ts +0 -13
  73. package/dist/Base/index.d.ts +0 -7
  74. package/dist/Utils/CameraStore.d.ts +0 -15
  75. package/dist/ViewManager.d.ts +0 -29
  76. package/dist/sdk.d.ts +0 -14
  77. package/src/Base/Context.ts +0 -49
  78. package/src/Base/index.ts +0 -10
  79. package/src/Utils/CameraStore.ts +0 -72
  80. package/src/sdk.ts +0 -39
  81. package/src/viewManager.ts +0 -177
package/src/AppManager.ts CHANGED
@@ -1,25 +1,31 @@
1
- import pRetry from "p-retry";
2
- import { AppAttributes, AppStatus, Events, MagixEventName } from "./constants";
1
+ import { AppAttributes, AppStatus, Events, MagixEventName, ROOT_DIR } from "./constants";
2
+ import { AppCreateQueue } from "./Utils/AppCreateQueue";
3
3
  import { AppListeners } from "./AppListener";
4
4
  import { AppProxy } from "./AppProxy";
5
- import { autorun, isPlayer, isRoom, ScenePathType, ViewVisionMode } from "white-web-sdk";
6
- import { callbacks, emitter, WindowManager, reconnectRefresher } from "./index";
7
- import { CameraStore } from "./Utils/CameraStore";
8
- import { genAppId, makeValidScenePath, setScenePath } from "./Utils/Common";
5
+ import { appRegister } from "./Register";
6
+ import { autorun, isPlayer, isRoom, ScenePathType } from "white-web-sdk";
7
+ import { callbacks, emitter, reconnectRefresher, WindowManager } from "./index";
8
+ import { get, isInteger, orderBy } from "lodash";
9
9
  import { log } from "./Utils/log";
10
- import { MainViewProxy } from "./MainView";
10
+ import { MainViewProxy } from "./View/MainView";
11
11
  import { onObjectRemoved, safeListenPropsUpdated } from "./Utils/Reactive";
12
- import { sortBy } from "lodash";
13
12
  import { store } from "./AttributesDelegate";
14
- import { ViewManager } from "./ViewManager";
13
+ import { ViewManager } from "./View/ViewManager";
14
+ import {
15
+ entireScenes,
16
+ genAppId,
17
+ makeValidScenePath,
18
+ parseSceneDir,
19
+ setScenePath,
20
+ setViewFocusScenePath,
21
+ } from "./Utils/Common";
15
22
  import type { ReconnectRefresher } from "./ReconnectRefresher";
16
23
  import type { BoxManager } from "./BoxManager";
17
- import type { Displayer, DisplayerState, Room } from "white-web-sdk";
24
+ import type { Displayer, DisplayerState, Room, ScenesCallbacksNode, View } from "white-web-sdk";
18
25
  import type { AddAppParams, BaseInsertParams, TeleBoxRect, EmitterEvent } from "./index";
19
26
 
20
27
  export class AppManager {
21
28
  public displayer: Displayer;
22
- public cameraStore: CameraStore;
23
29
  public viewManager: ViewManager;
24
30
  public appProxies: Map<string, AppProxy> = new Map();
25
31
  public appStatus: Map<string, AppStatus> = new Map();
@@ -27,10 +33,16 @@ export class AppManager {
27
33
  public mainViewProxy: MainViewProxy;
28
34
  public refresher?: ReconnectRefresher;
29
35
  public isReplay = this.windowManger.isReplay;
36
+ public mainViewScenesLength = 0;
30
37
 
31
38
  private appListeners: AppListeners;
32
39
  public boxManager?: BoxManager;
33
40
 
41
+ private _prevSceneIndex: number | undefined;
42
+ private _prevFocused: string | undefined;
43
+ private callbacksNode: ScenesCallbacksNode | null;
44
+ private appCreateQueue = new AppCreateQueue();
45
+
34
46
  constructor(public windowManger: WindowManager) {
35
47
  this.displayer = windowManger.displayer;
36
48
  this.store.setContext({
@@ -38,9 +50,8 @@ export class AppManager {
38
50
  safeSetAttributes: attributes => this.safeSetAttributes(attributes),
39
51
  safeUpdateAttributes: (keys, val) => this.safeUpdateAttributes(keys, val),
40
52
  });
41
- this.cameraStore = new CameraStore();
42
53
  this.mainViewProxy = new MainViewProxy(this);
43
- this.viewManager = new ViewManager(this);
54
+ this.viewManager = new ViewManager(this.displayer);
44
55
  this.appListeners = new AppListeners(this);
45
56
  this.displayer.callbacks.on(this.eventName, this.displayerStateListener);
46
57
  this.appListeners.addListeners();
@@ -60,6 +71,70 @@ export class AppManager {
60
71
  this.onAppDelete(this.attributes.apps);
61
72
  });
62
73
  }
74
+ emitter.on("removeScenes", scenePath => {
75
+ if (scenePath === ROOT_DIR) {
76
+ this.setMainViewScenePath(ROOT_DIR);
77
+ return;
78
+ }
79
+ const mainViewScenePath = this.store.getMainViewScenePath();
80
+ if (this.room && mainViewScenePath) {
81
+ if (mainViewScenePath === scenePath) {
82
+ this.setMainViewScenePath(ROOT_DIR);
83
+ }
84
+ }
85
+ });
86
+ this.callbacksNode = this.displayer.createScenesCallback(ROOT_DIR, {
87
+ onAddScene: scenesCallback => {
88
+ this.mainViewScenesLength = scenesCallback.scenes.length;
89
+ callbacks.emit("mainViewScenesLengthChange", this.mainViewScenesLength);
90
+ },
91
+ onRemoveScene: scenesCallback => {
92
+ this.mainViewScenesLength = scenesCallback.scenes.length;
93
+ callbacks.emit("mainViewScenesLengthChange", this.mainViewScenesLength);
94
+ },
95
+ });
96
+ if (this.callbacksNode) {
97
+ this.mainViewScenesLength = this.callbacksNode.scenes.length;
98
+ }
99
+ }
100
+
101
+ private get eventName() {
102
+ return isRoom(this.displayer) ? "onRoomStateChanged" : "onPlayerStateChanged";
103
+ }
104
+
105
+ public get attributes() {
106
+ return this.windowManger.attributes;
107
+ }
108
+
109
+ public get canOperate() {
110
+ return this.windowManger.canOperate;
111
+ }
112
+
113
+ public get room() {
114
+ return isRoom(this.displayer) ? (this.displayer as Room) : undefined;
115
+ }
116
+
117
+ public get mainView() {
118
+ return this.mainViewProxy.view;
119
+ }
120
+
121
+ public get focusApp() {
122
+ if (this.store.focus) {
123
+ return this.appProxies.get(this.store.focus);
124
+ }
125
+ }
126
+
127
+ public get uid() {
128
+ return this.room?.uid || "";
129
+ }
130
+
131
+ public getMainViewSceneDir() {
132
+ const scenePath = this.store.getMainViewScenePath();
133
+ if (scenePath) {
134
+ return parseSceneDir(scenePath);
135
+ } else {
136
+ throw new Error("[WindowManager]: mainViewSceneDir not found");
137
+ }
63
138
  }
64
139
 
65
140
  private async onCreated() {
@@ -98,6 +173,38 @@ export class AppManager {
98
173
  }
99
174
  });
100
175
  });
176
+ this.refresher?.add("mainViewIndex", () => {
177
+ return autorun(() => {
178
+ const mainSceneIndex = get(this.attributes, "_mainSceneIndex");
179
+ if (mainSceneIndex !== undefined && this._prevSceneIndex !== mainSceneIndex) {
180
+ callbacks.emit("mainViewSceneIndexChange", mainSceneIndex);
181
+ this._prevSceneIndex = mainSceneIndex;
182
+ }
183
+ });
184
+ });
185
+ this.refresher?.add("focusedChange", () => {
186
+ return autorun(() => {
187
+ const focused = get(this.attributes, "focus");
188
+ if (this._prevFocused !== focused) {
189
+ callbacks.emit("focusedChange", focused);
190
+ this.disposePrevFocusViewRedoUndoListeners(this._prevFocused);
191
+ setTimeout(() => {
192
+ this.addRedoUndoListeners(focused);
193
+ }, 0);
194
+ this._prevFocused = focused;
195
+ if (focused !== undefined) {
196
+ this.boxManager?.focusBox({ appId: focused });
197
+ // 确保 focus 修改的时候, appProxy 已经创建
198
+ setTimeout(() => {
199
+ const appProxy = this.appProxies.get(focused);
200
+ if (appProxy) {
201
+ appRegister.notifyApp(appProxy.kind, "focus", { appId: focused });
202
+ }
203
+ }, 0);
204
+ }
205
+ }
206
+ });
207
+ });
101
208
  if (!this.attributes.apps || Object.keys(this.attributes.apps).length === 0) {
102
209
  const mainScenePath = this.store.getMainViewScenePath();
103
210
  if (!mainScenePath) return;
@@ -108,8 +215,61 @@ export class AppManager {
108
215
  }
109
216
  this.displayerWritableListener(!this.room?.isWritable);
110
217
  this.displayer.callbacks.on("onEnableWriteNowChanged", this.displayerWritableListener);
218
+ this._prevFocused = this.attributes.focus;
219
+ this.addRedoUndoListeners(this.attributes.focus);
111
220
  }
112
221
 
222
+ private disposePrevFocusViewRedoUndoListeners = (prevFocused: string | undefined) => {
223
+ if (prevFocused === undefined) {
224
+ this.mainView.callbacks.off("onCanRedoStepsUpdate", this.onCanRedoStepsUpdate);
225
+ this.mainView.callbacks.off("onCanUndoStepsUpdate", this.onCanRedoStepsUpdate);
226
+ } else {
227
+ const appProxy = this.appProxies.get(prevFocused);
228
+ if (appProxy) {
229
+ appProxy.view?.callbacks.off("onCanRedoStepsUpdate", this.onCanRedoStepsUpdate);
230
+ appProxy.view?.callbacks.off("onCanUndoStepsUpdate", this.onCanUndoStepsUpdate);
231
+ }
232
+ }
233
+ };
234
+
235
+ private addRedoUndoListeners = (focused: string | undefined) => {
236
+ if (focused === undefined) {
237
+ this.addViewCallbacks(
238
+ this.mainView,
239
+ this.onCanRedoStepsUpdate,
240
+ this.onCanUndoStepsUpdate
241
+ );
242
+ } else {
243
+ const focusApp = this.appProxies.get(focused);
244
+ if (focusApp && focusApp.view) {
245
+ this.addViewCallbacks(
246
+ focusApp.view,
247
+ this.onCanRedoStepsUpdate,
248
+ this.onCanUndoStepsUpdate
249
+ );
250
+ }
251
+ }
252
+ };
253
+
254
+ private addViewCallbacks = (
255
+ view: View,
256
+ redoListener: (steps: number) => void,
257
+ undoListener: (steps: number) => void
258
+ ) => {
259
+ redoListener(view.canRedoSteps);
260
+ undoListener(view.canUndoSteps);
261
+ view.callbacks.on("onCanRedoStepsUpdate", redoListener);
262
+ view.callbacks.on("onCanUndoStepsUpdate", undoListener);
263
+ };
264
+
265
+ private onCanRedoStepsUpdate = (steps: number) => {
266
+ callbacks.emit("canRedoStepsChange", steps);
267
+ };
268
+
269
+ private onCanUndoStepsUpdate = (steps: number) => {
270
+ callbacks.emit("canUndoStepsChange", steps);
271
+ };
272
+
113
273
  /**
114
274
  * 插件更新 attributes 时的回调
115
275
  *
@@ -125,19 +285,18 @@ export class AppManager {
125
285
  createdAt: apps[appId].createdAt,
126
286
  };
127
287
  });
128
- for (const { id } of sortBy(appsWithCreatedAt, "createdAt")) {
288
+ for (const { id } of orderBy(appsWithCreatedAt, "createdAt", "asc")) {
129
289
  if (!this.appProxies.has(id) && !this.appStatus.has(id)) {
130
290
  const app = apps[id];
131
291
 
132
- pRetry(
133
- async () => {
134
- this.appStatus.set(id, AppStatus.StartCreate);
135
- // 防御 appAttributes 有可能为 undefined 的情况,这里做一个重试
136
- const appAttributes = this.attributes[id];
137
- if (!appAttributes) {
138
- throw new Error("appAttributes is undefined");
139
- }
140
- await this.baseInsertApp(
292
+ this.appStatus.set(id, AppStatus.StartCreate);
293
+ try {
294
+ const appAttributes = this.attributes[id];
295
+ if (!appAttributes) {
296
+ throw new Error("appAttributes is undefined");
297
+ }
298
+ this.appCreateQueue.push(() => {
299
+ return this.baseInsertApp(
141
300
  {
142
301
  kind: app.kind,
143
302
  options: app.options,
@@ -146,13 +305,11 @@ export class AppManager {
146
305
  id,
147
306
  false
148
307
  );
149
- this.focusByAttributes(apps);
150
- },
151
- { retries: 3 }
152
- ).catch(err => {
153
- console.warn(`[WindowManager]: Insert App Error`, err);
154
- this.appStatus.delete(id);
155
- });
308
+ });
309
+ this.focusByAttributes(apps);
310
+ } catch (error) {
311
+ console.warn(`[WindowManager]: Insert App Error`, error);
312
+ }
156
313
  }
157
314
  }
158
315
  }
@@ -188,15 +345,19 @@ export class AppManager {
188
345
  mainView.disableCameraTransform = disableCameraTransform;
189
346
  mainView.divElement = divElement;
190
347
  if (!mainView.focusScenePath) {
191
- this.store.setMainViewFocusPath(mainView);
192
- }
193
- if (this.store.focus === undefined && mainView.mode !== ViewVisionMode.Writable) {
194
- this.viewManager.switchMainViewToWriter();
348
+ this.setMainViewFocusPath();
195
349
  }
196
- this.mainViewProxy.addMainViewListener();
197
350
  emitter.emit("mainViewMounted");
198
351
  }
199
352
 
353
+ public setMainViewFocusPath(scenePath?: string) {
354
+ const focusScenePath = scenePath || this.store.getMainViewScenePath();
355
+ if (focusScenePath) {
356
+ const view = setViewFocusScenePath(this.mainView, focusScenePath);
357
+ return view?.focusScenePath === focusScenePath;
358
+ }
359
+ }
360
+
200
361
  public async addApp(params: AddAppParams, isDynamicPPT: boolean): Promise<string | undefined> {
201
362
  log("addApp", params);
202
363
  const { appId, needFocus } = await this.beforeAddApp(params, isDynamicPPT);
@@ -272,10 +433,6 @@ export class AppManager {
272
433
  }
273
434
  });
274
435
  }
275
- if (state.roomMembers) {
276
- this.windowManger.cursorManager?.setRoomMembers(state.roomMembers);
277
- this.windowManger.cursorManager?.cleanMemberAttributes(state.roomMembers);
278
- }
279
436
  this.appProxies.forEach(appProxy => {
280
437
  appProxy.appEmitter.emit("roomStateChange", state);
281
438
  });
@@ -295,41 +452,15 @@ export class AppManager {
295
452
  appProxy.emitAppIsWritableChange();
296
453
  });
297
454
  if (isWritable === true) {
298
- if (!this.store.focus) {
299
- this.mainViewProxy.switchViewModeToWriter();
300
- }
301
455
  this.mainView.disableCameraTransform = false;
456
+ if (this.room && this.room.disableSerialization === true) {
457
+ this.room.disableSerialization = false;
458
+ }
302
459
  } else {
303
460
  this.mainView.disableCameraTransform = true;
304
461
  }
305
462
  };
306
463
 
307
- private get eventName() {
308
- return isRoom(this.displayer) ? "onRoomStateChanged" : "onPlayerStateChanged";
309
- }
310
-
311
- public get attributes() {
312
- return this.windowManger.attributes;
313
- }
314
-
315
- public get canOperate() {
316
- return this.windowManger.canOperate;
317
- }
318
-
319
- public get room() {
320
- return isRoom(this.displayer) ? (this.displayer as Room) : undefined;
321
- }
322
-
323
- public get mainView() {
324
- return this.mainViewProxy.view;
325
- }
326
-
327
- public get focusApp() {
328
- if (this.store.focus) {
329
- return this.appProxies.get(this.store.focus);
330
- }
331
- }
332
-
333
464
  public safeSetAttributes(attributes: any) {
334
465
  this.windowManger.safeSetAttributes(attributes);
335
466
  }
@@ -341,37 +472,74 @@ export class AppManager {
341
472
  public async setMainViewScenePath(scenePath: string) {
342
473
  if (this.room) {
343
474
  const scenePathType = this.displayer.scenePathType(scenePath);
475
+ const sceneDir = parseSceneDir(scenePath);
476
+ if (sceneDir !== ROOT_DIR) {
477
+ throw new Error(`[WindowManager]: main view scenePath must in root dir "/"`);
478
+ }
344
479
  if (scenePathType === ScenePathType.None) {
345
480
  throw new Error(`[WindowManager]: ${scenePath} not valid scene`);
346
481
  } else if (scenePathType === ScenePathType.Page) {
347
482
  await this._setMainViewScenePath(scenePath);
348
483
  } else if (scenePathType === ScenePathType.Dir) {
349
484
  const validScenePath = makeValidScenePath(this.displayer, scenePath);
350
- await this._setMainViewScenePath(validScenePath);
485
+ if (validScenePath) {
486
+ await this._setMainViewScenePath(validScenePath);
487
+ }
351
488
  }
352
489
  }
353
490
  }
354
491
 
355
492
  private async _setMainViewScenePath(scenePath: string) {
356
- this.safeSetAttributes({ _mainScenePath: scenePath });
357
- await this.viewManager.switchMainViewToWriter();
358
- setScenePath(this.room, scenePath);
359
- this.store.setMainViewFocusPath(this.mainView);
360
- this.dispatchInternalEvent(Events.SetMainViewScenePath, { nextScenePath: scenePath });
493
+ const success = this.setMainViewFocusPath(scenePath);
494
+ if (success) {
495
+ this.safeSetAttributes({ _mainScenePath: scenePath });
496
+ this.store.setMainViewFocusPath(this.mainView);
497
+ this.updateSceneIndex();
498
+ this.dispatchSetMainViewScenePath(scenePath);
499
+ }
361
500
  }
362
501
 
502
+ private updateSceneIndex = () => {
503
+ const scenePath = this.store.getMainViewScenePath() as string;
504
+ const sceneDir = parseSceneDir(scenePath);
505
+ const scenes = entireScenes(this.displayer)[sceneDir];
506
+ if (scenes.length) {
507
+ // "/ppt3/1" -> "1"
508
+ const pageName = scenePath.replace(sceneDir, "").replace("/", "");
509
+ const index = scenes.findIndex(scene => scene.name === pageName);
510
+ if (isInteger(index) && index >= 0) {
511
+ this.safeSetAttributes({ _mainSceneIndex: index });
512
+ }
513
+ }
514
+ };
515
+
363
516
  public async setMainViewSceneIndex(index: number) {
364
517
  if (this.room) {
365
- this.safeSetAttributes({ _mainSceneIndex: index });
366
- await this.viewManager.switchMainViewToWriter();
367
- this.room.setSceneIndex(index);
368
- const nextScenePath = this.room.state.sceneState.scenePath;
369
- this.store.setMainViewScenePath(nextScenePath);
370
- this.store.setMainViewFocusPath(this.mainView);
371
- this.dispatchInternalEvent(Events.SetMainViewScenePath, { nextScenePath });
518
+ if (this.store.getMainViewSceneIndex() === index) return;
519
+ const mainViewScenePath = this.store.getMainViewScenePath() as string;
520
+ if (mainViewScenePath) {
521
+ const sceneDir = parseSceneDir(mainViewScenePath);
522
+ const scenePath = makeValidScenePath(this.displayer, sceneDir, index);
523
+ if (scenePath) {
524
+ const success = this.setMainViewFocusPath(scenePath);
525
+ if (success) {
526
+ this.store.setMainViewScenePath(scenePath);
527
+ this.safeSetAttributes({ _mainSceneIndex: index });
528
+ this.dispatchSetMainViewScenePath(scenePath);
529
+ }
530
+ } else {
531
+ throw new Error(`[WindowManager]: ${sceneDir}: ${index} not valid index`);
532
+ }
533
+ }
372
534
  }
373
535
  }
374
536
 
537
+ private dispatchSetMainViewScenePath(scenePath: string): void {
538
+ this.dispatchInternalEvent(Events.SetMainViewScenePath, { nextScenePath: scenePath });
539
+ // 兼容 15 的 SDK, 需要 room 的当前 ScenePath
540
+ setScenePath(this.room, scenePath);
541
+ }
542
+
375
543
  public getAppInitPath(appId: string): string | undefined {
376
544
  const attrs = this.store.getAppAttributes(appId);
377
545
  if (attrs) {
@@ -439,6 +607,7 @@ export class AppManager {
439
607
  const reconnected = appProxies.map(appProxy => {
440
608
  return appProxy.onReconnected();
441
609
  });
610
+ this.mainViewProxy.onReconnect();
442
611
  await Promise.all(reconnected);
443
612
  }
444
613
 
@@ -455,6 +624,11 @@ export class AppManager {
455
624
  });
456
625
  }
457
626
 
627
+ public findMemberByUid = (uid: string) => {
628
+ const roomMembers = this.room?.state.roomMembers;
629
+ return roomMembers?.find(member => member.payload?.uid === uid);
630
+ };
631
+
458
632
  public destroy() {
459
633
  this.displayer.callbacks.off(this.eventName, this.displayerStateListener);
460
634
  this.displayer.callbacks.off("onEnableWriteNowChanged", this.displayerWritableListener);
@@ -471,5 +645,10 @@ export class AppManager {
471
645
  this.refresher?.destroy();
472
646
  this.mainViewProxy.destroy();
473
647
  callbacks.clearListeners();
648
+ this.callbacksNode?.dispose();
649
+ this.appCreateQueue.destroy();
650
+ this.disposePrevFocusViewRedoUndoListeners(this._prevFocused);
651
+ this._prevFocused = undefined;
652
+ this._prevSceneIndex = undefined;
474
653
  }
475
654
  }