@netless/window-manager 0.4.7 → 0.4.9-canary.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 (44) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +1 -0
  3. package/dist/AppContext.d.ts +4 -4
  4. package/dist/AppManager.d.ts +12 -9
  5. package/dist/BoxManager.d.ts +4 -3
  6. package/dist/ContainerResizeObserver.d.ts +1 -1
  7. package/dist/Helper.d.ts +2 -0
  8. package/dist/InternalEmitter.d.ts +41 -0
  9. package/dist/ReconnectRefresher.d.ts +1 -1
  10. package/dist/RedoUndo.d.ts +18 -0
  11. package/dist/Utils/AppCreateQueue.d.ts +2 -0
  12. package/dist/Utils/Common.d.ts +1 -1
  13. package/dist/View/MainView.d.ts +1 -0
  14. package/dist/callback.d.ts +21 -0
  15. package/dist/index.d.ts +5 -52
  16. package/dist/index.es.js +5 -5
  17. package/dist/index.es.js.map +1 -1
  18. package/dist/index.umd.js +5 -5
  19. package/dist/index.umd.js.map +1 -1
  20. package/dist/style.css +1 -1
  21. package/docs/advanced.md +13 -0
  22. package/docs/api.md +15 -1
  23. package/docs/develop-app.md +50 -0
  24. package/package.json +3 -3
  25. package/src/AppContext.ts +51 -33
  26. package/src/AppListener.ts +2 -1
  27. package/src/AppManager.ts +90 -82
  28. package/src/AppProxy.ts +8 -4
  29. package/src/BoxManager.ts +7 -13
  30. package/src/ContainerResizeObserver.ts +1 -1
  31. package/src/Cursor/Cursor.ts +2 -1
  32. package/src/Cursor/index.ts +4 -2
  33. package/src/Helper.ts +6 -0
  34. package/src/InternalEmitter.ts +27 -0
  35. package/src/ReconnectRefresher.ts +1 -1
  36. package/src/RedoUndo.ts +88 -0
  37. package/src/Register/index.ts +1 -0
  38. package/src/Register/loader.ts +1 -1
  39. package/src/Utils/AppCreateQueue.ts +10 -0
  40. package/src/Utils/Common.ts +2 -2
  41. package/src/Utils/RoomHacker.ts +3 -3
  42. package/src/View/MainView.ts +13 -1
  43. package/src/callback.ts +23 -0
  44. package/src/index.ts +23 -57
package/src/AppManager.ts CHANGED
@@ -4,13 +4,18 @@ import { AppListeners } from "./AppListener";
4
4
  import { AppProxy } from "./AppProxy";
5
5
  import { appRegister } from "./Register";
6
6
  import { autorun, isPlayer, isRoom, ScenePathType } from "white-web-sdk";
7
- import { callbacks, emitter, reconnectRefresher, WindowManager } from "./index";
7
+ import { callbacks } from "./callback";
8
+ import { emitter } from "./InternalEmitter";
8
9
  import { get, isInteger, orderBy } from "lodash";
9
10
  import { log } from "./Utils/log";
10
11
  import { MainViewProxy } from "./View/MainView";
11
12
  import { onObjectRemoved, safeListenPropsUpdated } from "./Utils/Reactive";
13
+ import { reconnectRefresher, WindowManager } from "./index";
14
+ import { RedoUndo } from "./RedoUndo";
15
+ import { SideEffectManager } from "side-effect-manager";
12
16
  import { store } from "./AttributesDelegate";
13
17
  import { ViewManager } from "./View/ViewManager";
18
+ import type { EmitterEvent } from "./InternalEmitter";
14
19
  import {
15
20
  entireScenes,
16
21
  genAppId,
@@ -21,9 +26,14 @@ import {
21
26
  } from "./Utils/Common";
22
27
  import type { ReconnectRefresher } from "./ReconnectRefresher";
23
28
  import type { BoxManager } from "./BoxManager";
24
- import type { Displayer, DisplayerState, Room, ScenesCallbacksNode, View } from "white-web-sdk";
25
- import type { AddAppParams, BaseInsertParams, TeleBoxRect, EmitterEvent } from "./index";
26
-
29
+ import type {
30
+ Displayer,
31
+ DisplayerState,
32
+ Room,
33
+ ScenesCallbacksNode,
34
+ SceneState,
35
+ } from "white-web-sdk";
36
+ import type { AddAppParams, BaseInsertParams, TeleBoxRect } from "./index";
27
37
  export class AppManager {
28
38
  public displayer: Displayer;
29
39
  public viewManager: ViewManager;
@@ -43,6 +53,10 @@ export class AppManager {
43
53
  private callbacksNode: ScenesCallbacksNode | null = null;
44
54
  private appCreateQueue = new AppCreateQueue();
45
55
 
56
+ private sideEffectManager = new SideEffectManager();
57
+
58
+ public sceneState: SceneState | null = null;
59
+
46
60
  constructor(public windowManger: WindowManager) {
47
61
  this.displayer = windowManger.displayer;
48
62
  this.store.setContext({
@@ -50,6 +64,7 @@ export class AppManager {
50
64
  safeSetAttributes: attributes => this.safeSetAttributes(attributes),
51
65
  safeUpdateAttributes: (keys, val) => this.safeUpdateAttributes(keys, val),
52
66
  });
67
+
53
68
  this.mainViewProxy = new MainViewProxy(this);
54
69
  this.viewManager = new ViewManager(this.displayer);
55
70
  this.appListeners = new AppListeners(this);
@@ -60,6 +75,17 @@ export class AppManager {
60
75
  this.refresher.setRoom(this.room);
61
76
  this.refresher.setContext({ emitter });
62
77
 
78
+ this.sideEffectManager.add(() => {
79
+ return () => {
80
+ this.appCreateQueue.destroy();
81
+ this.mainViewProxy.destroy();
82
+ this.refresher?.destroy();
83
+ this.viewManager.destroy();
84
+ this.boxManager?.destroy();
85
+ this.callbacksNode?.dispose();
86
+ };
87
+ });
88
+
63
89
  emitter.once("onCreated").then(() => this.onCreated());
64
90
  emitter.on("onReconnected", () => this.onReconnected());
65
91
  if (isPlayer(this.displayer)) {
@@ -75,6 +101,8 @@ export class AppManager {
75
101
  if (scenePath === ROOT_DIR) {
76
102
  this.setMainViewScenePath(ROOT_DIR);
77
103
  this.createRootDirScenesCallback();
104
+ this.onRootDirRemoved();
105
+ emitter.emit("rootDirRemoved");
78
106
  return;
79
107
  }
80
108
  const mainViewScenePath = this.store.getMainViewScenePath();
@@ -87,6 +115,20 @@ export class AppManager {
87
115
  this.createRootDirScenesCallback();
88
116
  }
89
117
 
118
+ /**
119
+ * 根目录被删除时所有的 scene 都会被删除.
120
+ * 所以需要关掉所有开启了 view 的 app
121
+ */
122
+ private onRootDirRemoved() {
123
+ this.appProxies.forEach(appProxy => {
124
+ if (appProxy.view) {
125
+ this.closeApp(appProxy.id);
126
+ }
127
+ });
128
+ // 删除了根目录的 scenes 之后 mainview 需要重新绑定, 否则主白板会不能渲染
129
+ this.mainViewProxy.rebind();
130
+ }
131
+
90
132
  private createRootDirScenesCallback = () => {
91
133
  let isRecreate = false;
92
134
  if (this.callbacksNode) {
@@ -94,22 +136,39 @@ export class AppManager {
94
136
  isRecreate = true;
95
137
  }
96
138
  this.callbacksNode = this.displayer.createScenesCallback(ROOT_DIR, {
97
- onAddScene: scenesCallback => {
98
- this.mainViewScenesLength = scenesCallback.scenes.length;
99
- callbacks.emit("mainViewScenesLengthChange", this.mainViewScenesLength);
100
- },
101
- onRemoveScene: scenesCallback => {
102
- this.mainViewScenesLength = scenesCallback.scenes.length;
103
- callbacks.emit("mainViewScenesLengthChange", this.mainViewScenesLength);
104
- },
139
+ onAddScene: this.onSceneChange,
140
+ onRemoveScene: this.onSceneChange,
105
141
  });
106
142
  if (this.callbacksNode) {
143
+ this.updateSceneState(this.callbacksNode);
107
144
  this.mainViewScenesLength = this.callbacksNode.scenes.length;
108
145
  if (isRecreate) {
109
146
  callbacks.emit("mainViewScenesLengthChange", this.callbacksNode.scenes.length);
110
147
  }
111
148
  }
112
- }
149
+ };
150
+
151
+ private onSceneChange = (node: ScenesCallbacksNode) => {
152
+ this.mainViewScenesLength = node.scenes.length;
153
+ this.updateSceneState(node);
154
+ callbacks.emit("mainViewScenesLengthChange", this.mainViewScenesLength);
155
+ };
156
+
157
+ private updateSceneState = (node: ScenesCallbacksNode) => {
158
+ const currentIndex = this.store.getMainViewSceneIndex() || 0;
159
+ const sceneName = node.scenes[currentIndex];
160
+ this.sceneState = {
161
+ scenePath: `${ROOT_DIR}${sceneName}`,
162
+ contextPath: node.path,
163
+ index: currentIndex,
164
+ scenes: node.scenes.map(scene => {
165
+ return {
166
+ name: scene,
167
+ };
168
+ }),
169
+ sceneName: sceneName,
170
+ };
171
+ };
113
172
 
114
173
  private get eventName() {
115
174
  return isRoom(this.displayer) ? "onRoomStateChanged" : "onPlayerStateChanged";
@@ -152,7 +211,7 @@ export class AppManager {
152
211
 
153
212
  private async onCreated() {
154
213
  await this.attributesUpdateCallback(this.attributes.apps);
155
- this.boxManager?.updateManagerRect();
214
+ emitter.emit("updateManagerRect");
156
215
  emitter.onAny(this.boxEventListener);
157
216
  this.refresher?.add("apps", () => {
158
217
  return safeListenPropsUpdated(
@@ -191,6 +250,9 @@ export class AppManager {
191
250
  const mainSceneIndex = get(this.attributes, "_mainSceneIndex");
192
251
  if (mainSceneIndex !== undefined && this._prevSceneIndex !== mainSceneIndex) {
193
252
  callbacks.emit("mainViewSceneIndexChange", mainSceneIndex);
253
+ if (this.callbacksNode) {
254
+ this.updateSceneState(this.callbacksNode);
255
+ }
194
256
  this._prevSceneIndex = mainSceneIndex;
195
257
  }
196
258
  });
@@ -200,10 +262,7 @@ export class AppManager {
200
262
  const focused = get(this.attributes, "focus");
201
263
  if (this._prevFocused !== focused) {
202
264
  callbacks.emit("focusedChange", focused);
203
- this.disposePrevFocusViewRedoUndoListeners(this._prevFocused);
204
- setTimeout(() => {
205
- this.addRedoUndoListeners(focused);
206
- }, 0);
265
+ emitter.emit("focusedChange", { focused, prev: this._prevFocused });
207
266
  this._prevFocused = focused;
208
267
  if (focused !== undefined) {
209
268
  this.boxManager?.focusBox({ appId: focused });
@@ -229,59 +288,16 @@ export class AppManager {
229
288
  this.displayerWritableListener(!this.room?.isWritable);
230
289
  this.displayer.callbacks.on("onEnableWriteNowChanged", this.displayerWritableListener);
231
290
  this._prevFocused = this.attributes.focus;
232
- this.addRedoUndoListeners(this.attributes.focus);
233
- }
234
-
235
- private disposePrevFocusViewRedoUndoListeners = (prevFocused: string | undefined) => {
236
- if (prevFocused === undefined) {
237
- this.mainView.callbacks.off("onCanRedoStepsUpdate", this.onCanRedoStepsUpdate);
238
- this.mainView.callbacks.off("onCanUndoStepsUpdate", this.onCanRedoStepsUpdate);
239
- } else {
240
- const appProxy = this.appProxies.get(prevFocused);
241
- if (appProxy) {
242
- appProxy.view?.callbacks.off("onCanRedoStepsUpdate", this.onCanRedoStepsUpdate);
243
- appProxy.view?.callbacks.off("onCanUndoStepsUpdate", this.onCanUndoStepsUpdate);
244
- }
245
- }
246
- };
247
-
248
- private addRedoUndoListeners = (focused: string | undefined) => {
249
- if (focused === undefined) {
250
- this.addViewCallbacks(
251
- this.mainView,
252
- this.onCanRedoStepsUpdate,
253
- this.onCanUndoStepsUpdate
254
- );
255
- } else {
256
- const focusApp = this.appProxies.get(focused);
257
- if (focusApp && focusApp.view) {
258
- this.addViewCallbacks(
259
- focusApp.view,
260
- this.onCanRedoStepsUpdate,
261
- this.onCanUndoStepsUpdate
262
- );
263
- }
264
- }
265
- };
266
291
 
267
- private addViewCallbacks = (
268
- view: View,
269
- redoListener: (steps: number) => void,
270
- undoListener: (steps: number) => void
271
- ) => {
272
- redoListener(view.canRedoSteps);
273
- undoListener(view.canUndoSteps);
274
- view.callbacks.on("onCanRedoStepsUpdate", redoListener);
275
- view.callbacks.on("onCanUndoStepsUpdate", undoListener);
276
- };
277
-
278
- private onCanRedoStepsUpdate = (steps: number) => {
279
- callbacks.emit("canRedoStepsChange", steps);
280
- };
281
-
282
- private onCanUndoStepsUpdate = (steps: number) => {
283
- callbacks.emit("canUndoStepsChange", steps);
284
- };
292
+ this.sideEffectManager.add(() => {
293
+ const redoUndo = new RedoUndo({
294
+ mainView: () => this.mainViewProxy.view,
295
+ focus: () => this.attributes.focus,
296
+ getAppProxy: id => this.appProxies.get(id),
297
+ });
298
+ return () => redoUndo.destroy();
299
+ });
300
+ }
285
301
 
286
302
  /**
287
303
  * 插件更新 attributes 时的回调
@@ -292,6 +308,9 @@ export class AppManager {
292
308
  public async attributesUpdateCallback(apps: any) {
293
309
  if (apps && WindowManager.container) {
294
310
  const appIds = Object.keys(apps);
311
+ if (appIds.length === 0) {
312
+ this.appCreateQueue.emitReady();
313
+ }
295
314
  const appsWithCreatedAt = appIds.map(appId => {
296
315
  return {
297
316
  id: appId,
@@ -637,11 +656,6 @@ export class AppManager {
637
656
  });
638
657
  }
639
658
 
640
- public findMemberByUid = (uid: string) => {
641
- const roomMembers = this.room?.state.roomMembers;
642
- return roomMembers?.find(member => member.payload?.uid === uid);
643
- };
644
-
645
659
  public destroy() {
646
660
  this.displayer.callbacks.off(this.eventName, this.displayerStateListener);
647
661
  this.displayer.callbacks.off("onEnableWriteNowChanged", this.displayerWritableListener);
@@ -653,14 +667,8 @@ export class AppManager {
653
667
  appProxy.destroy(true, false, true);
654
668
  });
655
669
  }
656
- this.viewManager.destroy();
657
- this.boxManager?.destroy();
658
- this.refresher?.destroy();
659
- this.mainViewProxy.destroy();
660
670
  callbacks.clearListeners();
661
- this.callbacksNode?.dispose();
662
- this.appCreateQueue.destroy();
663
- this.disposePrevFocusViewRedoUndoListeners(this._prevFocused);
671
+ this.sideEffectManager.flushAll();
664
672
  this._prevFocused = undefined;
665
673
  this._prevSceneIndex = undefined;
666
674
  }
package/src/AppProxy.ts CHANGED
@@ -5,7 +5,7 @@ import { appRegister } from "./Register";
5
5
  import { autorun } from "white-web-sdk";
6
6
  import { BoxManagerNotFoundError } from "./Utils/error";
7
7
  import { debounce, get } from "lodash";
8
- import { emitter } from "./index";
8
+ import { emitter } from "./InternalEmitter";
9
9
  import { Fields } from "./AttributesDelegate";
10
10
  import { entireScenes, getScenePath, removeScenes, setScenePath, setViewFocusScenePath } from "./Utils/Common";
11
11
  import { log } from "./Utils/log";
@@ -127,7 +127,7 @@ export class AppProxy {
127
127
  } else {
128
128
  throw new Error(`[WindowManager]: app load failed ${params.kind} ${params.src}`);
129
129
  }
130
- this.boxManager?.updateManagerRect();
130
+ emitter.emit("updateManagerRect")
131
131
  return {
132
132
  appId: this.id,
133
133
  app: appImpl,
@@ -363,8 +363,12 @@ export class AppProxy {
363
363
  ) {
364
364
  if (this.status === "destroyed") return;
365
365
  this.status = "destroyed";
366
- await appRegister.notifyApp(this.kind, "destroy", { appId: this.id });
367
- await this.appEmitter.emit("destroy", { error });
366
+ try {
367
+ await appRegister.notifyApp(this.kind, "destroy", { appId: this.id });
368
+ await this.appEmitter.emit("destroy", { error });
369
+ } catch (error) {
370
+ console.error("[WindowManager]: notifyApp error", error.message, error.stack);
371
+ }
368
372
  this.appEmitter.clearListeners();
369
373
  emitter.emit(`destroy-${this.id}` as any, { error });
370
374
  if (needCloseBox) {
package/src/BoxManager.ts CHANGED
@@ -1,12 +1,8 @@
1
1
  import { AppAttributes, Events, MIN_HEIGHT, MIN_WIDTH } from "./constants";
2
2
  import { debounce } from "lodash";
3
- import { emitter, WindowManager } from "./index";
4
- import {
5
- TELE_BOX_STATE,
6
- TeleBoxCollector,
7
- TeleBoxManager,
8
- } from "@netless/telebox-insider";
9
- import type { AddAppOptions, AppInitState, EmitterType, CallbacksType } from "./index";
3
+ import { TELE_BOX_STATE, TeleBoxCollector, TeleBoxManager } from "@netless/telebox-insider";
4
+ import { WindowManager } from "./index";
5
+ import type { AddAppOptions, AppInitState } from "./index";
10
6
  import type {
11
7
  TeleBoxManagerUpdateConfig,
12
8
  TeleBoxManagerCreateConfig,
@@ -19,6 +15,8 @@ import type {
19
15
  import type Emittery from "emittery";
20
16
  import type { NetlessApp } from "./typings";
21
17
  import type { View } from "white-web-sdk";
18
+ import type { CallbacksType } from "./callback";
19
+ import type { EmitterType } from "./InternalEmitter";
22
20
 
23
21
  export { TELE_BOX_STATE };
24
22
 
@@ -156,13 +154,10 @@ export class BoxManager {
156
154
  this.teleBoxManager.events.on("z_index", box => {
157
155
  this.context.updateAppState(box.id, AppAttributes.ZIndex, box.zIndex);
158
156
  });
159
- emitter.on("playgroundSizeChange", this.playgroundSizeChangeListener);
157
+ emitter.on("playgroundSizeChange", () => this.updateManagerRect());
158
+ emitter.on("updateManagerRect", () => this.updateManagerRect());
160
159
  }
161
160
 
162
- private playgroundSizeChangeListener = () => {
163
- this.updateManagerRect();
164
- };
165
-
166
161
  private get mainView() {
167
162
  return this.context.getMainView();
168
163
  }
@@ -406,7 +401,6 @@ export class BoxManager {
406
401
  }
407
402
 
408
403
  public destroy() {
409
- emitter.off("playgroundSizeChange", this.playgroundSizeChangeListener);
410
404
  this.teleBoxManager.destroy();
411
405
  }
412
406
  }
@@ -1,6 +1,6 @@
1
1
  import { ResizeObserver as ResizeObserverPolyfill } from "@juggle/resize-observer";
2
2
  import { WindowManager } from "./index";
3
- import type { EmitterType } from "./index";
3
+ import type { EmitterType } from "./InternalEmitter";
4
4
 
5
5
  const ResizeObserver = window.ResizeObserver || ResizeObserverPolyfill;
6
6
 
@@ -1,6 +1,7 @@
1
1
  import App from "./Cursor.svelte";
2
2
  import { ApplianceMap } from "./icons";
3
3
  import { ApplianceNames } from "white-web-sdk";
4
+ import { findMemberByUid } from "../Helper";
4
5
  import { omit } from "lodash";
5
6
  import type { Position } from "../AttributesDelegate";
6
7
  import type { RoomMember } from "white-web-sdk";
@@ -164,7 +165,7 @@ export class Cursor {
164
165
  }
165
166
 
166
167
  public updateMember() {
167
- this.member = this.manager.findMemberByUid(this.memberId);
168
+ this.member = findMemberByUid(this.manager.room, this.memberId);
168
169
  this.updateComponent();
169
170
  return this.member;
170
171
  }
@@ -1,9 +1,10 @@
1
1
  import { ApplianceNames } from "white-web-sdk";
2
2
  import { Cursor } from "./Cursor";
3
3
  import { CursorState, Events } from "../constants";
4
- import { emitter, WindowManager } from "../index";
4
+ import { emitter } from "../InternalEmitter";
5
5
  import { SideEffectManager } from "side-effect-manager";
6
6
  import { throttle } from "lodash";
7
+ import { WindowManager } from "../index";
7
8
  import type { CursorMovePayload } from "../index";
8
9
  import type { PositionType } from "../AttributesDelegate";
9
10
  import type { Point, RoomMember, View } from "white-web-sdk";
@@ -65,7 +66,8 @@ export class CursorManager {
65
66
  };
66
67
 
67
68
  private canMoveCursor(member: RoomMember | undefined) {
68
- const isLaserPointer = member?.memberState.currentApplianceName === ApplianceNames.laserPointer;
69
+ const isLaserPointer =
70
+ member?.memberState.currentApplianceName === ApplianceNames.laserPointer;
69
71
  // 激光笔教具在不开启光标的情况下也要显示
70
72
  return this.enableCursor || isLaserPointer;
71
73
  }
package/src/Helper.ts CHANGED
@@ -3,6 +3,7 @@ import { REQUIRE_VERSION } from "./constants";
3
3
  import { WhiteVersion } from "white-web-sdk";
4
4
  import { WhiteWebSDKInvalidError } from "./Utils/error";
5
5
  import { WindowManager } from "./index";
6
+ import type { Room } from "white-web-sdk";
6
7
 
7
8
  export const setupWrapper = (
8
9
  root: HTMLElement
@@ -39,3 +40,8 @@ export const checkVersion = () => {
39
40
  throw new WhiteWebSDKInvalidError(REQUIRE_VERSION);
40
41
  }
41
42
  };
43
+
44
+ export const findMemberByUid = (room: Room | undefined, uid: string) => {
45
+ const roomMembers = room?.state.roomMembers;
46
+ return roomMembers?.find(member => member.payload?.uid === uid);
47
+ };
@@ -0,0 +1,27 @@
1
+ import Emittery from "emittery";
2
+ import type { AppInitState, CursorMovePayload } from "./index";
3
+
4
+
5
+ export type EmitterEvent = {
6
+ onCreated: undefined;
7
+ InitReplay: AppInitState;
8
+ move: { appId: string; x: number; y: number };
9
+ focus: { appId: string };
10
+ close: { appId: string };
11
+ resize: { appId: string; width: number; height: number; x?: number; y?: number };
12
+ error: Error;
13
+ seek: number;
14
+ mainViewMounted: undefined;
15
+ observerIdChange: number;
16
+ boxStateChange: string;
17
+ playgroundSizeChange: DOMRect;
18
+ onReconnected: void;
19
+ removeScenes: string;
20
+ cursorMove: CursorMovePayload;
21
+ updateManagerRect: undefined;
22
+ focusedChange: { focused: string | undefined; prev: string | undefined };
23
+ rootDirRemoved: undefined;
24
+ };
25
+
26
+ export type EmitterType = Emittery<EmitterEvent>;
27
+ export const emitter: EmitterType = new Emittery();
@@ -2,7 +2,7 @@ import { debounce, isFunction } from "lodash";
2
2
  import { log } from "./Utils/log";
3
3
  import { RoomPhase } from "white-web-sdk";
4
4
  import type { Room } from "white-web-sdk";
5
- import type { EmitterType } from "./index";
5
+ import type { EmitterType } from "./InternalEmitter";
6
6
 
7
7
  export type ReconnectRefresherContext = {
8
8
  emitter: EmitterType;
@@ -0,0 +1,88 @@
1
+ import { callbacks } from "./callback";
2
+ import { emitter } from "./InternalEmitter";
3
+ import type { View } from "white-web-sdk";
4
+ import type { AppProxy } from "./AppProxy";
5
+
6
+ export type RedoUndoContext = {
7
+ mainView: () => View;
8
+ focus: () => string | undefined;
9
+ getAppProxy: (id: string) => AppProxy | undefined;
10
+ };
11
+
12
+ export class RedoUndo {
13
+ constructor(private context: RedoUndoContext) {
14
+ emitter.on("focusedChange", changed => {
15
+ this.disposePrevFocusViewRedoUndoListeners(changed.prev);
16
+ setTimeout(() => {
17
+ this.addRedoUndoListeners(changed.focused);
18
+ }, 0);
19
+ });
20
+ emitter.on("rootDirRemoved", () => {
21
+ this.disposePrevFocusViewRedoUndoListeners(context.focus());
22
+ this.addRedoUndoListeners(context.focus());
23
+ });
24
+ this.addRedoUndoListeners(context.focus());
25
+ }
26
+
27
+ private addRedoUndoListeners = (focused: string | undefined) => {
28
+ if (focused === undefined) {
29
+ this.addViewCallbacks(
30
+ this.context.mainView(),
31
+ this.onCanRedoStepsUpdate,
32
+ this.onCanUndoStepsUpdate
33
+ );
34
+ } else {
35
+ const focusApp = this.context.getAppProxy(focused);
36
+ if (focusApp && focusApp.view) {
37
+ this.addViewCallbacks(
38
+ focusApp.view,
39
+ this.onCanRedoStepsUpdate,
40
+ this.onCanUndoStepsUpdate
41
+ );
42
+ }
43
+ }
44
+ };
45
+
46
+ private addViewCallbacks = (
47
+ view: View,
48
+ redoListener: (steps: number) => void,
49
+ undoListener: (steps: number) => void
50
+ ) => {
51
+ redoListener(view.canRedoSteps);
52
+ undoListener(view.canUndoSteps);
53
+ view.callbacks.on("onCanRedoStepsUpdate", redoListener);
54
+ view.callbacks.on("onCanUndoStepsUpdate", undoListener);
55
+ };
56
+
57
+ private disposeViewCallbacks = (view: View) => {
58
+ view.callbacks.off("onCanRedoStepsUpdate", this.onCanRedoStepsUpdate);
59
+ view.callbacks.off("onCanUndoStepsUpdate", this.onCanUndoStepsUpdate);
60
+ };
61
+
62
+ private onCanRedoStepsUpdate = (steps: number) => {
63
+ callbacks.emit("canRedoStepsChange", steps);
64
+ };
65
+
66
+ private onCanUndoStepsUpdate = (steps: number) => {
67
+ callbacks.emit("canUndoStepsChange", steps);
68
+ };
69
+
70
+ private disposePrevFocusViewRedoUndoListeners = (prevFocused: string | undefined) => {
71
+ let view: View | undefined = undefined;
72
+ if (prevFocused === undefined) {
73
+ view = this.context.mainView();
74
+ } else {
75
+ const appProxy = this.context.getAppProxy(prevFocused);
76
+ if (appProxy && appProxy.view) {
77
+ view = appProxy.view;
78
+ }
79
+ }
80
+ if (view) {
81
+ this.disposeViewCallbacks(view);
82
+ }
83
+ };
84
+
85
+ public destroy() {
86
+ this.disposePrevFocusViewRedoUndoListeners(this.context.focus());
87
+ }
88
+ }
@@ -15,6 +15,7 @@ class AppRegister {
15
15
  public appClasses: Map<string, () => Promise<NetlessApp>> = new Map();
16
16
 
17
17
  public async register(params: RegisterParams): Promise<void> {
18
+ this.appClassesCache.delete(params.kind);
18
19
  this.registered.set(params.kind, params);
19
20
 
20
21
  const srcOrAppOrFunction = params.src;
@@ -1,4 +1,4 @@
1
- import { callbacks } from "../index";
1
+ import { callbacks } from "../callback";
2
2
  import { getItem, setItem } from "./storage";
3
3
  import type { NetlessApp } from "../typings";
4
4
 
@@ -1,3 +1,4 @@
1
+ import { callbacks } from "../callback";
1
2
  import type { AppProxy } from "../AppProxy";
2
3
 
3
4
  export type Invoker = () => Promise<AppProxy | undefined>;
@@ -6,6 +7,7 @@ export class AppCreateQueue {
6
7
  private list: Invoker[] = [];
7
8
  private currentInvoker: Invoker | undefined;
8
9
  private timer: number | undefined;
10
+ public isEmit = false;
9
11
 
10
12
  private initInterval() {
11
13
  return setInterval(() => {
@@ -37,6 +39,7 @@ export class AppCreateQueue {
37
39
  this.currentInvoker = undefined;
38
40
  if (this.list.length === 0) {
39
41
  clearInterval(this.timer);
42
+ this.emitReady();
40
43
  }
41
44
  })
42
45
  .catch(error => {
@@ -46,6 +49,13 @@ export class AppCreateQueue {
46
49
  }
47
50
  }
48
51
 
52
+ public emitReady() {
53
+ if (!this.isEmit) {
54
+ callbacks.emit("ready");
55
+ }
56
+ this.isEmit = true;
57
+ }
58
+
49
59
  public destroy() {
50
60
  if (this.timer) {
51
61
  clearInterval(this.timer);
@@ -1,9 +1,9 @@
1
1
  import { appRegister } from "../Register";
2
2
  import { debounce } from "lodash";
3
- import { emitter } from "../index";
3
+ import { emitter } from "../InternalEmitter";
4
4
  import { ScenePathType } from "white-web-sdk";
5
5
  import { v4 } from "uuid";
6
- import type { PublicEvent } from "../index";
6
+ import type { PublicEvent } from "../callback";
7
7
  import type { Displayer, ViewVisionMode, Room, View } from "white-web-sdk";
8
8
  import type Emittery from "emittery";
9
9
  import { ROOT_DIR } from "../constants";
@@ -1,4 +1,4 @@
1
- import { emitter } from "../index";
1
+ import { emitter } from "../InternalEmitter";
2
2
  import { isPlayer } from "white-web-sdk";
3
3
  import type { WindowManager } from "../index";
4
4
  import type { Camera, Room, Player, PlayerSeekingResult } from "white-web-sdk";
@@ -23,13 +23,13 @@ export const replaceRoomFunction = (room: Room | Player, manager: WindowManager)
23
23
 
24
24
  Object.defineProperty(room, "canUndoSteps", {
25
25
  get() {
26
- return manager.mainView.canUndoSteps;
26
+ return manager.canUndoSteps;
27
27
  },
28
28
  });
29
29
 
30
30
  Object.defineProperty(room, "canRedoSteps", {
31
31
  get() {
32
- return manager.mainView.canRedoSteps;
32
+ return manager.canRedoSteps;
33
33
  },
34
34
  });
35
35
 
@@ -1,7 +1,8 @@
1
1
  import { AnimationMode, reaction } from "white-web-sdk";
2
- import { callbacks, emitter } from "../index";
2
+ import { callbacks } from "../callback";
3
3
  import { createView } from "./ViewManager";
4
4
  import { debounce, isEmpty, isEqual } from "lodash";
5
+ import { emitter } from "../InternalEmitter";
5
6
  import { Fields } from "../AttributesDelegate";
6
7
  import { setViewFocusScenePath } from "../Utils/Common";
7
8
  import { SideEffectManager } from "side-effect-manager";
@@ -110,6 +111,17 @@ export class MainViewProxy {
110
111
  }
111
112
  }
112
113
 
114
+ public rebind(): void {
115
+ const divElement = this.mainView.divElement;
116
+ const disableCameraTransform = this.mainView.disableCameraTransform;
117
+ this.stop();
118
+ this.mainView.release();
119
+ this.mainView = this.createMainView();
120
+ this.mainView.disableCameraTransform = disableCameraTransform;
121
+ this.mainView.divElement = divElement;
122
+ this.start();
123
+ }
124
+
113
125
  private onCameraUpdatedByDevice = (camera: Camera) => {
114
126
  this.store.setMainViewCamera({ ...camera, id: this.manager.uid });
115
127
  if (!isEqual(this.mainViewSize, { ...this.mainView.size, id: this.manager.uid })) {