@netless/window-manager 0.4.0-canary.2 → 0.4.0-canary.23

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 (82) 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 +29 -1
  7. package/README.md +1 -0
  8. package/dist/App/MagixEvent/index.d.ts +29 -0
  9. package/dist/App/Storage/StorageEvent.d.ts +8 -0
  10. package/dist/App/Storage/index.d.ts +39 -0
  11. package/dist/App/Storage/typings.d.ts +22 -0
  12. package/dist/App/Storage/utils.d.ts +5 -0
  13. package/dist/AppContext.d.ts +40 -16
  14. package/dist/AppListener.d.ts +1 -1
  15. package/dist/AppManager.d.ts +15 -11
  16. package/dist/AppProxy.d.ts +7 -6
  17. package/dist/AttributesDelegate.d.ts +2 -2
  18. package/dist/BoxManager.d.ts +6 -3
  19. package/dist/BuiltinApps.d.ts +5 -0
  20. package/dist/ContainerResizeObserver.d.ts +10 -0
  21. package/dist/Cursor/Cursor.d.ts +8 -11
  22. package/dist/Cursor/index.d.ts +5 -16
  23. package/dist/Helper.d.ts +6 -0
  24. package/dist/ReconnectRefresher.d.ts +0 -1
  25. package/dist/Register/storage.d.ts +5 -1
  26. package/dist/Utils/Common.d.ts +7 -2
  27. package/dist/Utils/Reactive.d.ts +1 -1
  28. package/dist/Utils/RoomHacker.d.ts +1 -1
  29. package/dist/{MainView.d.ts → View/MainView.d.ts} +5 -6
  30. package/dist/View/ViewManager.d.ts +13 -0
  31. package/dist/constants.d.ts +3 -7
  32. package/dist/index.d.ts +25 -10
  33. package/dist/index.es.js +41 -1
  34. package/dist/index.es.js.map +1 -1
  35. package/dist/index.umd.js +41 -1
  36. package/dist/index.umd.js.map +1 -1
  37. package/dist/style.css +1 -1
  38. package/dist/typings.d.ts +3 -2
  39. package/docs/api.md +36 -6
  40. package/docs/concept.md +9 -0
  41. package/package.json +7 -6
  42. package/src/App/MagixEvent/index.ts +68 -0
  43. package/src/App/Storage/StorageEvent.ts +21 -0
  44. package/src/App/Storage/index.ts +289 -0
  45. package/src/App/Storage/typings.ts +23 -0
  46. package/src/App/Storage/utils.ts +17 -0
  47. package/src/AppContext.ts +66 -24
  48. package/src/AppListener.ts +15 -14
  49. package/src/AppManager.ts +146 -63
  50. package/src/AppProxy.ts +52 -53
  51. package/src/AttributesDelegate.ts +2 -2
  52. package/src/BoxManager.ts +40 -24
  53. package/src/BuiltinApps.ts +23 -0
  54. package/src/ContainerResizeObserver.ts +62 -0
  55. package/src/Cursor/Cursor.ts +22 -36
  56. package/src/Cursor/index.ts +39 -139
  57. package/src/Helper.ts +30 -0
  58. package/src/ReconnectRefresher.ts +0 -5
  59. package/src/Register/index.ts +25 -16
  60. package/src/Register/loader.ts +2 -2
  61. package/src/Register/storage.ts +6 -1
  62. package/src/Utils/Common.ts +66 -13
  63. package/src/Utils/Reactive.ts +9 -3
  64. package/src/Utils/RoomHacker.ts +42 -13
  65. package/src/{MainView.ts → View/MainView.ts} +25 -36
  66. package/src/View/ViewManager.ts +52 -0
  67. package/src/constants.ts +3 -4
  68. package/src/index.ts +96 -72
  69. package/src/shim.d.ts +5 -0
  70. package/src/style.css +7 -1
  71. package/src/typings.ts +3 -2
  72. package/vite.config.js +8 -2
  73. package/dist/Base/Context.d.ts +0 -13
  74. package/dist/Base/index.d.ts +0 -7
  75. package/dist/Utils/CameraStore.d.ts +0 -15
  76. package/dist/ViewManager.d.ts +0 -29
  77. package/dist/sdk.d.ts +0 -14
  78. package/src/Base/Context.ts +0 -49
  79. package/src/Base/index.ts +0 -10
  80. package/src/Utils/CameraStore.ts +0 -72
  81. package/src/sdk.ts +0 -39
  82. package/src/viewManager.ts +0 -177
package/src/AppContext.ts CHANGED
@@ -6,18 +6,20 @@ import {
6
6
  unlistenDisposed,
7
7
  unlistenUpdated,
8
8
  toJS
9
- } from 'white-web-sdk';
9
+ } from 'white-web-sdk';
10
10
  import { BoxNotCreatedError } from './Utils/error';
11
- import type { Room, SceneDefinition, View } from "white-web-sdk";
11
+ import type { Room, SceneDefinition, View, EventListener as WhiteEventListener } from "white-web-sdk";
12
12
  import type { ReadonlyTeleBox } from "@netless/telebox-insider";
13
13
  import type Emittery from "emittery";
14
14
  import type { BoxManager } from "./BoxManager";
15
15
  import type { AppEmitterEvent } from "./index";
16
16
  import type { AppManager } from "./AppManager";
17
17
  import type { AppProxy } from "./AppProxy";
18
+ import { Storage } from './App/Storage';
19
+ import type { MagixEventAddListener, MagixEventDispatcher, MagixEventRemoveListener } from './App/MagixEvent';
18
20
 
19
- export class AppContext<TAttrs extends Record<string, any>, AppOptions = any> {
20
- public readonly emitter: Emittery<AppEmitterEvent<TAttrs>>;
21
+ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOptions = any> {
22
+ public readonly emitter: Emittery<AppEmitterEvent<TAttributes>>;
21
23
  public readonly mobxUtils = {
22
24
  autorun,
23
25
  reaction,
@@ -39,45 +41,45 @@ export class AppContext<TAttrs extends Record<string, any>, AppOptions = any> {
39
41
  private boxManager: BoxManager,
40
42
  public appId: string,
41
43
  private appProxy: AppProxy,
42
- private appOptions?: AppOptions | (() => AppOptions),
44
+ private appOptions?: TAppOptions | (() => TAppOptions),
43
45
  ) {
44
46
  this.emitter = appProxy.appEmitter;
45
47
  this.isAddApp = appProxy.isAddApp;
46
48
  }
47
49
 
48
- public getDisplayer() {
50
+ public getDisplayer = () => {
49
51
  return this.manager.displayer;
50
52
  }
51
53
 
52
- public getAttributes(): TAttrs | undefined {
54
+ /** @deprecated Use context.storage.state instead. */
55
+ public getAttributes = (): TAttributes | undefined => {
53
56
  return this.appProxy.attributes;
54
57
  }
55
58
 
56
- public getScenes(): SceneDefinition[] | undefined {
59
+ public getScenes = (): SceneDefinition[] | undefined => {
57
60
  const appAttr = this.store.getAppAttributes(this.appId);
58
61
  if (appAttr?.isDynamicPPT) {
59
- const appProxy = this.manager.appProxies.get(this.appId);
60
- if (appProxy) {
61
- return appProxy.scenes;
62
- }
62
+ return this.appProxy.scenes;
63
63
  } else {
64
64
  return appAttr?.options["scenes"];
65
65
  }
66
66
  }
67
67
 
68
- public getView(): View | undefined {
68
+ public getView = (): View | undefined => {
69
69
  return this.appProxy.view;
70
70
  }
71
71
 
72
- public getInitScenePath() {
72
+ public getInitScenePath = () => {
73
73
  return this.manager.getAppInitPath(this.appId);
74
74
  }
75
75
 
76
- public getIsWritable(): boolean {
76
+ /** Get App writable status. */
77
+ public getIsWritable = (): boolean => {
77
78
  return this.manager.canOperate;
78
79
  }
79
80
 
80
- public getBox(): ReadonlyTeleBox {
81
+ /** Get the App Window UI box. */
82
+ public getBox = (): ReadonlyTeleBox => {
81
83
  const box = this.boxManager.getBox(this.appId);
82
84
  if (box) {
83
85
  return box;
@@ -86,27 +88,30 @@ export class AppContext<TAttrs extends Record<string, any>, AppOptions = any> {
86
88
  }
87
89
  }
88
90
 
89
- public getRoom(): Room | undefined {
91
+ public getRoom = (): Room | undefined => {
90
92
  return this.manager.room;
91
93
  }
92
94
 
93
- public setAttributes(attributes: TAttrs) {
95
+ /** @deprecated Use context.storage.setState instead. */
96
+ public setAttributes = (attributes: TAttributes) => {
94
97
  this.manager.safeSetAttributes({ [this.appId]: attributes });
95
98
  }
96
99
 
97
- public updateAttributes(keys: string[], value: any) {
100
+ /** @deprecated Use context.storage.setState instead. */
101
+ public updateAttributes = (keys: string[], value: any) => {
98
102
  if (this.manager.attributes[this.appId]) {
99
103
  this.manager.safeUpdateAttributes([this.appId, ...keys], value);
100
104
  }
101
105
  }
102
106
 
103
- public async setScenePath(scenePath: string): Promise<void> {
107
+ public setScenePath = async (scenePath: string): Promise<void> => {
104
108
  if (!this.appProxy.box) return;
105
109
  this.appProxy.setFullPath(scenePath);
106
- this.appProxy.context.switchAppToWriter(this.appId);
110
+ // 兼容 15 版本 SDK 的切页
111
+ this.getRoom()?.setScenePath(scenePath);
107
112
  }
108
113
 
109
- public mountView(dom: HTMLDivElement): void {
114
+ public mountView = (dom: HTMLDivElement): void => {
110
115
  const view = this.getView();
111
116
  if (view) {
112
117
  view.divElement = dom;
@@ -117,7 +122,44 @@ export class AppContext<TAttrs extends Record<string, any>, AppOptions = any> {
117
122
  }
118
123
  }
119
124
 
120
- public getAppOptions(): AppOptions | undefined {
121
- return typeof this.appOptions === 'function' ? (this.appOptions as () => AppOptions)() : this.appOptions
125
+ /** Get the local App options. */
126
+ public getAppOptions = (): TAppOptions | undefined => {
127
+ return typeof this.appOptions === 'function' ? (this.appOptions as () => TAppOptions)() : this.appOptions
122
128
  }
129
+
130
+ private _storage?: Storage<TAttributes>
131
+
132
+ /** Main Storage for attributes. */
133
+ public get storage(): Storage<TAttributes> {
134
+ if (!this._storage) {
135
+ this._storage = new Storage(this);
136
+ }
137
+ return this._storage;
138
+ }
139
+
140
+ /**
141
+ * Create separated storages for flexible state management.
142
+ * @param storeId Namespace for the storage. Storages of the same namespace share the same data.
143
+ * @param defaultState Default state for initial storage creation.
144
+ * @returns
145
+ */
146
+ public createStorage = <TState>(storeId: string, defaultState?: TState): Storage<TState> => {
147
+ const storage = new Storage(this, storeId, defaultState);
148
+ this.emitter.on("destroy", () => {
149
+ storage.destroy();
150
+ });
151
+ return storage;
152
+ }
153
+
154
+ /** Dispatch events to other clients (and self). */
155
+ public dispatchMagixEvent: MagixEventDispatcher<TMagixEventPayloads> = (this.manager.displayer as Room).dispatchMagixEvent.bind(this.manager.displayer)
156
+
157
+ /** Listen to events from others clients (and self messages). */
158
+ public addMagixEventListener: MagixEventAddListener<TMagixEventPayloads> = (event, handler, options) => {
159
+ this.manager.displayer.addMagixEventListener(event, handler as WhiteEventListener, options);
160
+ return () => this.manager.displayer.removeMagixEventListener(event, handler as WhiteEventListener);
161
+ }
162
+
163
+ /** Remove a Magix event listener. */
164
+ public removeMagixEventListener = this.manager.displayer.removeMagixEventListener.bind(this.manager.displayer) as MagixEventRemoveListener<TMagixEventPayloads>
123
165
  }
@@ -1,9 +1,9 @@
1
- import { callbacks } from './index';
2
- import { Events, MagixEventName } from './constants';
1
+ import { callbacks, emitter } from "./index";
2
+ import { Events, MagixEventName } from "./constants";
3
+ import { setViewFocusScenePath } from "./Utils/Common";
3
4
  import type { Event } from "white-web-sdk";
4
5
  import type { AppManager } from "./AppManager";
5
6
  import type { TeleBoxState } from "@netless/telebox-insider";
6
- import { setViewFocusScenePath } from './Utils/Common';
7
7
 
8
8
  export class AppListeners {
9
9
  private displayer = this.manager.displayer;
@@ -34,10 +34,6 @@ export class AppListeners {
34
34
  this.appResizeHandler(data.payload);
35
35
  break;
36
36
  }
37
- case Events.SwitchViewsToFreedom: {
38
- this.switchViewsToFreedomHandler();
39
- break;
40
- }
41
37
  case Events.AppBoxStateChange: {
42
38
  this.boxStateChangeHandler(data.payload);
43
39
  break;
@@ -50,6 +46,10 @@ export class AppListeners {
50
46
  this.moveCameraToContainHandler(data.payload);
51
47
  break;
52
48
  }
49
+ case Events.CursorMove: {
50
+ this.cursorMoveHandler(data.payload);
51
+ break;
52
+ }
53
53
  default:
54
54
  break;
55
55
  }
@@ -65,19 +65,20 @@ export class AppListeners {
65
65
  this.manager.room?.refreshViewSize();
66
66
  };
67
67
 
68
- private switchViewsToFreedomHandler = () => {
69
- this.manager.viewManager.freedomAllViews();
70
- };
71
-
72
68
  private boxStateChangeHandler = (state: TeleBoxState) => {
73
69
  callbacks.emit("boxStateChange", state);
74
- }
70
+ };
75
71
 
76
72
  private setMainViewScenePathHandler = ({ nextScenePath }: { nextScenePath: string }) => {
77
73
  setViewFocusScenePath(this.manager.mainView, nextScenePath);
78
- }
74
+ callbacks.emit("mainViewScenePathChange", nextScenePath);
75
+ };
79
76
 
80
77
  private moveCameraToContainHandler = (payload: any) => {
81
78
  this.manager.mainView.moveCameraToContain(payload);
82
- }
79
+ };
80
+
81
+ private cursorMoveHandler = (payload: any) => {
82
+ emitter.emit("cursorMove", payload);
83
+ };
83
84
  }
package/src/AppManager.ts CHANGED
@@ -2,25 +2,30 @@ import pRetry from "p-retry";
2
2
  import { AppAttributes, AppStatus, Events, MagixEventName } from "./constants";
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 } from "./index";
7
- import { CameraStore } from "./Utils/CameraStore";
8
- import { genAppId, makeValidScenePath, setScenePath } from "./Utils/Common";
5
+ import { autorun, isPlayer, isRoom, ScenePathType } from "white-web-sdk";
6
+ import { callbacks, emitter, WindowManager, reconnectRefresher } from "./index";
7
+ import {
8
+ entireScenes,
9
+ genAppId,
10
+ makeValidScenePath,
11
+ parseSceneDir,
12
+ setScenePath,
13
+ setViewFocusScenePath,
14
+ } from "./Utils/Common";
9
15
  import { log } from "./Utils/log";
10
- import { MainViewProxy } from "./MainView";
16
+ import { MainViewProxy } from "./View/MainView";
11
17
  import { onObjectRemoved, safeListenPropsUpdated } from "./Utils/Reactive";
12
- import { reconnectRefresher } from "./ReconnectRefresher";
13
- import { sortBy } from "lodash";
18
+ import { get, isInteger, sortBy } from "lodash";
14
19
  import { store } from "./AttributesDelegate";
15
- import { ViewManager } from "./ViewManager";
20
+ import { ViewManager } from "./View/ViewManager";
16
21
  import type { ReconnectRefresher } from "./ReconnectRefresher";
17
22
  import type { BoxManager } from "./BoxManager";
18
23
  import type { Displayer, DisplayerState, Room } from "white-web-sdk";
19
24
  import type { AddAppParams, BaseInsertParams, TeleBoxRect, EmitterEvent } from "./index";
25
+ import { appRegister } from "./Register";
20
26
 
21
27
  export class AppManager {
22
28
  public displayer: Displayer;
23
- public cameraStore: CameraStore;
24
29
  public viewManager: ViewManager;
25
30
  public appProxies: Map<string, AppProxy> = new Map();
26
31
  public appStatus: Map<string, AppStatus> = new Map();
@@ -32,6 +37,9 @@ export class AppManager {
32
37
  private appListeners: AppListeners;
33
38
  public boxManager?: BoxManager;
34
39
 
40
+ private _prevSceneIndex: number | undefined;
41
+ private _prevFocused: string | undefined;
42
+
35
43
  constructor(public windowManger: WindowManager) {
36
44
  this.displayer = windowManger.displayer;
37
45
  this.store.setContext({
@@ -39,9 +47,8 @@ export class AppManager {
39
47
  safeSetAttributes: attributes => this.safeSetAttributes(attributes),
40
48
  safeUpdateAttributes: (keys, val) => this.safeUpdateAttributes(keys, val),
41
49
  });
42
- this.cameraStore = new CameraStore();
43
50
  this.mainViewProxy = new MainViewProxy(this);
44
- this.viewManager = new ViewManager(this);
51
+ this.viewManager = new ViewManager(this.displayer);
45
52
  this.appListeners = new AppListeners(this);
46
53
  this.displayer.callbacks.on(this.eventName, this.displayerStateListener);
47
54
  this.appListeners.addListeners();
@@ -61,6 +68,48 @@ export class AppManager {
61
68
  this.onAppDelete(this.attributes.apps);
62
69
  });
63
70
  }
71
+ emitter.on("removeScenes", scenePath => {
72
+ if (scenePath === "/") {
73
+ this.setMainViewScenePath("/");
74
+ return;
75
+ }
76
+ const mainViewScenePath = this.store.getMainViewScenePath();
77
+ if (this.room && mainViewScenePath) {
78
+ if (mainViewScenePath === scenePath) {
79
+ this.setMainViewScenePath("/");
80
+ }
81
+ }
82
+ });
83
+ }
84
+
85
+ private get eventName() {
86
+ return isRoom(this.displayer) ? "onRoomStateChanged" : "onPlayerStateChanged";
87
+ }
88
+
89
+ public get attributes() {
90
+ return this.windowManger.attributes;
91
+ }
92
+
93
+ public get canOperate() {
94
+ return this.windowManger.canOperate;
95
+ }
96
+
97
+ public get room() {
98
+ return isRoom(this.displayer) ? (this.displayer as Room) : undefined;
99
+ }
100
+
101
+ public get mainView() {
102
+ return this.mainViewProxy.view;
103
+ }
104
+
105
+ public get focusApp() {
106
+ if (this.store.focus) {
107
+ return this.appProxies.get(this.store.focus);
108
+ }
109
+ }
110
+
111
+ public get uid() {
112
+ return this.room?.uid || "";
64
113
  }
65
114
 
66
115
  private async onCreated() {
@@ -99,6 +148,25 @@ export class AppManager {
99
148
  }
100
149
  });
101
150
  });
151
+ this.refresher?.add("mainViewIndex", () => {
152
+ return autorun(() => {
153
+ const mainSceneIndex = get(this.attributes, "_mainSceneIndex");
154
+ if (mainSceneIndex !== undefined && this._prevSceneIndex !== mainSceneIndex) {
155
+ callbacks.emit("mainViewSceneIndexChange", mainSceneIndex);
156
+ this._prevSceneIndex = mainSceneIndex;
157
+ }
158
+ });
159
+ });
160
+ this.refresher?.add("focusedChange", () => {
161
+ return autorun(() => {
162
+ const focused = get(this.attributes, "focus");
163
+ if (this._prevFocused !== focused) {
164
+ this.boxManager?.focusBox({ appId: focused });
165
+ callbacks.emit("focusedChange", focused);
166
+ this._prevFocused = focused;
167
+ }
168
+ });
169
+ });
102
170
  if (!this.attributes.apps || Object.keys(this.attributes.apps).length === 0) {
103
171
  const mainScenePath = this.store.getMainViewScenePath();
104
172
  if (!mainScenePath) return;
@@ -109,6 +177,7 @@ export class AppManager {
109
177
  }
110
178
  this.displayerWritableListener(!this.room?.isWritable);
111
179
  this.displayer.callbacks.on("onEnableWriteNowChanged", this.displayerWritableListener);
180
+ this._prevFocused = this.attributes.focus;
112
181
  }
113
182
 
114
183
  /**
@@ -189,15 +258,19 @@ export class AppManager {
189
258
  mainView.disableCameraTransform = disableCameraTransform;
190
259
  mainView.divElement = divElement;
191
260
  if (!mainView.focusScenePath) {
192
- this.store.setMainViewFocusPath(mainView);
193
- }
194
- if (this.store.focus === undefined && mainView.mode !== ViewVisionMode.Writable) {
195
- this.viewManager.switchMainViewToWriter();
261
+ this.setMainViewFocusPath();
196
262
  }
197
- this.mainViewProxy.addMainViewListener();
198
263
  emitter.emit("mainViewMounted");
199
264
  }
200
265
 
266
+ public setMainViewFocusPath(scenePath?: string) {
267
+ const focusScenePath = scenePath || this.store.getMainViewScenePath();
268
+ if (focusScenePath) {
269
+ const view = setViewFocusScenePath(this.mainView, focusScenePath);
270
+ return view?.focusScenePath === focusScenePath;
271
+ }
272
+ }
273
+
201
274
  public async addApp(params: AddAppParams, isDynamicPPT: boolean): Promise<string | undefined> {
202
275
  log("addApp", params);
203
276
  const { appId, needFocus } = await this.beforeAddApp(params, isDynamicPPT);
@@ -273,17 +346,13 @@ export class AppManager {
273
346
  }
274
347
  });
275
348
  }
276
- if (state.roomMembers) {
277
- this.windowManger.cursorManager?.setRoomMembers(state.roomMembers);
278
- this.windowManger.cursorManager?.cleanMemberAttributes(state.roomMembers);
279
- }
280
349
  this.appProxies.forEach(appProxy => {
281
350
  appProxy.appEmitter.emit("roomStateChange", state);
282
351
  });
283
352
  emitter.emit("observerIdChange", this.displayer.observerId);
284
353
  };
285
354
 
286
- private displayerWritableListener = (isReadonly: boolean) => {
355
+ public displayerWritableListener = (isReadonly: boolean) => {
287
356
  const isWritable = !isReadonly;
288
357
  const isManualWritable =
289
358
  this.windowManger.readonly === undefined || this.windowManger.readonly === false;
@@ -296,41 +365,15 @@ export class AppManager {
296
365
  appProxy.emitAppIsWritableChange();
297
366
  });
298
367
  if (isWritable === true) {
299
- if (!this.store.focus) {
300
- this.mainViewProxy.switchViewModeToWriter();
301
- }
302
368
  this.mainView.disableCameraTransform = false;
369
+ if (this.room && this.room.disableSerialization === true) {
370
+ this.room.disableSerialization = false;
371
+ }
303
372
  } else {
304
373
  this.mainView.disableCameraTransform = true;
305
374
  }
306
375
  };
307
376
 
308
- private get eventName() {
309
- return isRoom(this.displayer) ? "onRoomStateChanged" : "onPlayerStateChanged";
310
- }
311
-
312
- public get attributes() {
313
- return this.windowManger.attributes;
314
- }
315
-
316
- public get canOperate() {
317
- return this.windowManger.canOperate;
318
- }
319
-
320
- public get room() {
321
- return isRoom(this.displayer) ? (this.displayer as Room) : undefined;
322
- }
323
-
324
- public get mainView() {
325
- return this.mainViewProxy.view;
326
- }
327
-
328
- public get focusApp() {
329
- if (this.store.focus) {
330
- return this.appProxies.get(this.store.focus);
331
- }
332
- }
333
-
334
377
  public safeSetAttributes(attributes: any) {
335
378
  this.windowManger.safeSetAttributes(attributes);
336
379
  }
@@ -348,28 +391,57 @@ export class AppManager {
348
391
  await this._setMainViewScenePath(scenePath);
349
392
  } else if (scenePathType === ScenePathType.Dir) {
350
393
  const validScenePath = makeValidScenePath(this.displayer, scenePath);
351
- await this._setMainViewScenePath(validScenePath);
394
+ if (validScenePath) {
395
+ await this._setMainViewScenePath(validScenePath);
396
+ }
352
397
  }
353
398
  }
354
399
  }
355
400
 
356
401
  private async _setMainViewScenePath(scenePath: string) {
357
- this.safeSetAttributes({ _mainScenePath: scenePath });
358
- await this.viewManager.switchMainViewToWriter();
359
- setScenePath(this.room, scenePath);
360
- this.store.setMainViewFocusPath(this.mainView);
361
- this.dispatchInternalEvent(Events.SetMainViewScenePath, { nextScenePath: scenePath });
402
+ const success = this.setMainViewFocusPath(scenePath);
403
+ if (success) {
404
+ this.safeSetAttributes({ _mainScenePath: scenePath });
405
+ this.store.setMainViewFocusPath(this.mainView);
406
+ this.updateSceneIndex();
407
+ this.dispatchInternalEvent(Events.SetMainViewScenePath, { nextScenePath: scenePath });
408
+ }
362
409
  }
363
410
 
411
+ private updateSceneIndex = () => {
412
+ const scenePath = this.store.getMainViewScenePath() as string;
413
+ const sceneDir = parseSceneDir(scenePath);
414
+ const scenes = entireScenes(this.displayer)[sceneDir];
415
+ if (scenes.length) {
416
+ // "/ppt3/1" -> "1"
417
+ const pageName = scenePath.replace(sceneDir, "").replace("/", "");
418
+ const index = scenes.findIndex(scene => scene.name === pageName);
419
+ if (isInteger(index) && index >= 0) {
420
+ this.safeSetAttributes({ _mainSceneIndex: index });
421
+ }
422
+ }
423
+ };
424
+
364
425
  public async setMainViewSceneIndex(index: number) {
365
426
  if (this.room) {
366
- this.safeSetAttributes({ _mainSceneIndex: index });
367
- await this.viewManager.switchMainViewToWriter();
368
- this.room.setSceneIndex(index);
369
- const nextScenePath = this.room.state.sceneState.scenePath;
370
- this.store.setMainViewScenePath(nextScenePath);
371
- this.store.setMainViewFocusPath(this.mainView);
372
- this.dispatchInternalEvent(Events.SetMainViewScenePath, { nextScenePath });
427
+ if (this.store.getMainViewSceneIndex() === index) return;
428
+ const mainViewScenePath = this.store.getMainViewScenePath() as string;
429
+ if (mainViewScenePath) {
430
+ const sceneDir = parseSceneDir(mainViewScenePath);
431
+ const scenePath = makeValidScenePath(this.displayer, sceneDir, index);
432
+ if (scenePath) {
433
+ const success = this.setMainViewFocusPath(scenePath);
434
+ if (success) {
435
+ this.store.setMainViewScenePath(scenePath);
436
+ this.safeSetAttributes({ _mainSceneIndex: index });
437
+ this.dispatchInternalEvent(Events.SetMainViewScenePath, {
438
+ nextScenePath: scenePath,
439
+ });
440
+ }
441
+ } else {
442
+ throw new Error(`[WindowManager]: ${sceneDir}: ${index} not valid index`);
443
+ }
444
+ }
373
445
  }
374
446
  }
375
447
 
@@ -398,6 +470,10 @@ export class AppManager {
398
470
  }
399
471
  case "focus": {
400
472
  this.windowManger.safeSetAttributes({ focus: payload.appId });
473
+ const appProxy = this.appProxies.get(payload.appId);
474
+ if (appProxy) {
475
+ appRegister.notifyApp(appProxy.kind, "focus", { appId: payload.appId });
476
+ }
401
477
  break;
402
478
  }
403
479
  case "resize": {
@@ -440,6 +516,7 @@ export class AppManager {
440
516
  const reconnected = appProxies.map(appProxy => {
441
517
  return appProxy.onReconnected();
442
518
  });
519
+ this.mainViewProxy.onReconnect();
443
520
  await Promise.all(reconnected);
444
521
  }
445
522
 
@@ -456,6 +533,11 @@ export class AppManager {
456
533
  });
457
534
  }
458
535
 
536
+ public findMemberByUid = (uid: string) => {
537
+ const roomMembers = this.room?.state.roomMembers;
538
+ return roomMembers?.find(member => member.payload?.uid === uid);
539
+ };
540
+
459
541
  public destroy() {
460
542
  this.displayer.callbacks.off(this.eventName, this.displayerStateListener);
461
543
  this.displayer.callbacks.off("onEnableWriteNowChanged", this.displayerWritableListener);
@@ -472,5 +554,6 @@ export class AppManager {
472
554
  this.refresher?.destroy();
473
555
  this.mainViewProxy.destroy();
474
556
  callbacks.clearListeners();
557
+ this._prevSceneIndex = undefined;
475
558
  }
476
559
  }