@netless/window-manager 0.4.7 → 0.4.8-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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netless/window-manager",
3
- "version": "0.4.7",
3
+ "version": "0.4.8-canary.0",
4
4
  "description": "",
5
5
  "main": "dist/index.es.js",
6
6
  "module": "dist/index.es.js",
@@ -54,7 +54,7 @@
54
54
  "rollup-plugin-analyzer": "^4.0.0",
55
55
  "rollup-plugin-styles": "^3.14.1",
56
56
  "svelte": "^3.42.4",
57
- "typescript": "^4.3.5",
57
+ "typescript": "^4.5.5",
58
58
  "vite": "^2.5.3",
59
59
  "white-web-sdk": "2.16.10"
60
60
  }
package/src/AppManager.ts CHANGED
@@ -9,6 +9,8 @@ import { get, isInteger, orderBy } from "lodash";
9
9
  import { log } from "./Utils/log";
10
10
  import { MainViewProxy } from "./View/MainView";
11
11
  import { onObjectRemoved, safeListenPropsUpdated } from "./Utils/Reactive";
12
+ import { RedoUndo } from "./RedoUndo";
13
+ import { SideEffectManager } from "side-effect-manager";
12
14
  import { store } from "./AttributesDelegate";
13
15
  import { ViewManager } from "./View/ViewManager";
14
16
  import {
@@ -21,7 +23,7 @@ import {
21
23
  } from "./Utils/Common";
22
24
  import type { ReconnectRefresher } from "./ReconnectRefresher";
23
25
  import type { BoxManager } from "./BoxManager";
24
- import type { Displayer, DisplayerState, Room, ScenesCallbacksNode, View } from "white-web-sdk";
26
+ import type { Displayer, DisplayerState, Room, ScenesCallbacksNode } from "white-web-sdk";
25
27
  import type { AddAppParams, BaseInsertParams, TeleBoxRect, EmitterEvent } from "./index";
26
28
 
27
29
  export class AppManager {
@@ -43,6 +45,8 @@ export class AppManager {
43
45
  private callbacksNode: ScenesCallbacksNode | null = null;
44
46
  private appCreateQueue = new AppCreateQueue();
45
47
 
48
+ private sideEffectManager = new SideEffectManager();
49
+
46
50
  constructor(public windowManger: WindowManager) {
47
51
  this.displayer = windowManger.displayer;
48
52
  this.store.setContext({
@@ -50,6 +54,7 @@ export class AppManager {
50
54
  safeSetAttributes: attributes => this.safeSetAttributes(attributes),
51
55
  safeUpdateAttributes: (keys, val) => this.safeUpdateAttributes(keys, val),
52
56
  });
57
+
53
58
  this.mainViewProxy = new MainViewProxy(this);
54
59
  this.viewManager = new ViewManager(this.displayer);
55
60
  this.appListeners = new AppListeners(this);
@@ -60,6 +65,17 @@ export class AppManager {
60
65
  this.refresher.setRoom(this.room);
61
66
  this.refresher.setContext({ emitter });
62
67
 
68
+ this.sideEffectManager.add(() => {
69
+ return () => {
70
+ this.appCreateQueue.destroy();
71
+ this.mainViewProxy.destroy();
72
+ this.refresher?.destroy();
73
+ this.viewManager.destroy();
74
+ this.boxManager?.destroy();
75
+ this.callbacksNode?.dispose();
76
+ };
77
+ });
78
+
63
79
  emitter.once("onCreated").then(() => this.onCreated());
64
80
  emitter.on("onReconnected", () => this.onReconnected());
65
81
  if (isPlayer(this.displayer)) {
@@ -75,6 +91,8 @@ export class AppManager {
75
91
  if (scenePath === ROOT_DIR) {
76
92
  this.setMainViewScenePath(ROOT_DIR);
77
93
  this.createRootDirScenesCallback();
94
+ this.onRootDirRemoved();
95
+ emitter.emit("rootDirRemoved");
78
96
  return;
79
97
  }
80
98
  const mainViewScenePath = this.store.getMainViewScenePath();
@@ -87,6 +105,20 @@ export class AppManager {
87
105
  this.createRootDirScenesCallback();
88
106
  }
89
107
 
108
+ /**
109
+ * 根目录被删除时所有的 scene 都会被删除.
110
+ * 所以需要关掉所有开启了 view 的 app
111
+ */
112
+ private onRootDirRemoved() {
113
+ this.appProxies.forEach(appProxy => {
114
+ if (appProxy.view) {
115
+ this.closeApp(appProxy.id);
116
+ }
117
+ });
118
+ // 删除了根目录的 scenes 之后 mainview 需要重新绑定, 否则主白板会不能渲染
119
+ this.mainViewProxy.rebind();
120
+ }
121
+
90
122
  private createRootDirScenesCallback = () => {
91
123
  let isRecreate = false;
92
124
  if (this.callbacksNode) {
@@ -109,7 +141,7 @@ export class AppManager {
109
141
  callbacks.emit("mainViewScenesLengthChange", this.callbacksNode.scenes.length);
110
142
  }
111
143
  }
112
- }
144
+ };
113
145
 
114
146
  private get eventName() {
115
147
  return isRoom(this.displayer) ? "onRoomStateChanged" : "onPlayerStateChanged";
@@ -152,7 +184,7 @@ export class AppManager {
152
184
 
153
185
  private async onCreated() {
154
186
  await this.attributesUpdateCallback(this.attributes.apps);
155
- this.boxManager?.updateManagerRect();
187
+ emitter.emit("updateManagerRect");
156
188
  emitter.onAny(this.boxEventListener);
157
189
  this.refresher?.add("apps", () => {
158
190
  return safeListenPropsUpdated(
@@ -200,10 +232,7 @@ export class AppManager {
200
232
  const focused = get(this.attributes, "focus");
201
233
  if (this._prevFocused !== focused) {
202
234
  callbacks.emit("focusedChange", focused);
203
- this.disposePrevFocusViewRedoUndoListeners(this._prevFocused);
204
- setTimeout(() => {
205
- this.addRedoUndoListeners(focused);
206
- }, 0);
235
+ emitter.emit("focusedChange", { focused, prev: this._prevFocused });
207
236
  this._prevFocused = focused;
208
237
  if (focused !== undefined) {
209
238
  this.boxManager?.focusBox({ appId: focused });
@@ -229,59 +258,16 @@ export class AppManager {
229
258
  this.displayerWritableListener(!this.room?.isWritable);
230
259
  this.displayer.callbacks.on("onEnableWriteNowChanged", this.displayerWritableListener);
231
260
  this._prevFocused = this.attributes.focus;
232
- this.addRedoUndoListeners(this.attributes.focus);
233
- }
234
261
 
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
-
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
- };
262
+ this.sideEffectManager.add(() => {
263
+ const redoUndo = new RedoUndo({
264
+ mainView: () => this.mainViewProxy.view,
265
+ focus: () => this.attributes.focus,
266
+ getAppProxy: id => this.appProxies.get(id),
267
+ });
268
+ return () => redoUndo.destroy();
269
+ });
270
+ }
285
271
 
286
272
  /**
287
273
  * 插件更新 attributes 时的回调
@@ -637,11 +623,6 @@ export class AppManager {
637
623
  });
638
624
  }
639
625
 
640
- public findMemberByUid = (uid: string) => {
641
- const roomMembers = this.room?.state.roomMembers;
642
- return roomMembers?.find(member => member.payload?.uid === uid);
643
- };
644
-
645
626
  public destroy() {
646
627
  this.displayer.callbacks.off(this.eventName, this.displayerStateListener);
647
628
  this.displayer.callbacks.off("onEnableWriteNowChanged", this.displayerWritableListener);
@@ -653,14 +634,8 @@ export class AppManager {
653
634
  appProxy.destroy(true, false, true);
654
635
  });
655
636
  }
656
- this.viewManager.destroy();
657
- this.boxManager?.destroy();
658
- this.refresher?.destroy();
659
- this.mainViewProxy.destroy();
660
637
  callbacks.clearListeners();
661
- this.callbacksNode?.dispose();
662
- this.appCreateQueue.destroy();
663
- this.disposePrevFocusViewRedoUndoListeners(this._prevFocused);
638
+ this.sideEffectManager.flushAll();
664
639
  this._prevFocused = undefined;
665
640
  this._prevSceneIndex = undefined;
666
641
  }
package/src/AppProxy.ts CHANGED
@@ -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,11 +1,7 @@
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";
3
+ import { TELE_BOX_STATE, TeleBoxCollector, TeleBoxManager } from "@netless/telebox-insider";
4
+ import { WindowManager } from "./index";
9
5
  import type { AddAppOptions, AppInitState, EmitterType, CallbacksType } from "./index";
10
6
  import type {
11
7
  TeleBoxManagerUpdateConfig,
@@ -156,13 +152,10 @@ export class BoxManager {
156
152
  this.teleBoxManager.events.on("z_index", box => {
157
153
  this.context.updateAppState(box.id, AppAttributes.ZIndex, box.zIndex);
158
154
  });
159
- emitter.on("playgroundSizeChange", this.playgroundSizeChangeListener);
155
+ emitter.on("playgroundSizeChange", () => this.updateManagerRect());
156
+ emitter.on("updateManagerRect", () => this.updateManagerRect());
160
157
  }
161
158
 
162
- private playgroundSizeChangeListener = () => {
163
- this.updateManagerRect();
164
- };
165
-
166
159
  private get mainView() {
167
160
  return this.context.getMainView();
168
161
  }
@@ -406,7 +399,6 @@ export class BoxManager {
406
399
  }
407
400
 
408
401
  public destroy() {
409
- emitter.off("playgroundSizeChange", this.playgroundSizeChangeListener);
410
402
  this.teleBoxManager.destroy();
411
403
  }
412
404
  }
@@ -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
  }
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,87 @@
1
+ import { callbacks, emitter } from "./index";
2
+ import type { View } from "white-web-sdk";
3
+ import type { AppProxy } from "./AppProxy";
4
+
5
+ export type RedoUndoContext = {
6
+ mainView: () => View;
7
+ focus: () => string | undefined;
8
+ getAppProxy: (id: string) => AppProxy | undefined;
9
+ };
10
+
11
+ export class RedoUndo {
12
+ constructor(private context: RedoUndoContext) {
13
+ emitter.on("focusedChange", changed => {
14
+ this.disposePrevFocusViewRedoUndoListeners(changed.prev);
15
+ setTimeout(() => {
16
+ this.addRedoUndoListeners(changed.focused);
17
+ }, 0);
18
+ });
19
+ emitter.on("rootDirRemoved", () => {
20
+ this.disposePrevFocusViewRedoUndoListeners(context.focus());
21
+ this.addRedoUndoListeners(context.focus());
22
+ });
23
+ this.addRedoUndoListeners(context.focus());
24
+ }
25
+
26
+ private addRedoUndoListeners = (focused: string | undefined) => {
27
+ if (focused === undefined) {
28
+ this.addViewCallbacks(
29
+ this.context.mainView(),
30
+ this.onCanRedoStepsUpdate,
31
+ this.onCanUndoStepsUpdate
32
+ );
33
+ } else {
34
+ const focusApp = this.context.getAppProxy(focused);
35
+ if (focusApp && focusApp.view) {
36
+ this.addViewCallbacks(
37
+ focusApp.view,
38
+ this.onCanRedoStepsUpdate,
39
+ this.onCanUndoStepsUpdate
40
+ );
41
+ }
42
+ }
43
+ };
44
+
45
+ private addViewCallbacks = (
46
+ view: View,
47
+ redoListener: (steps: number) => void,
48
+ undoListener: (steps: number) => void
49
+ ) => {
50
+ redoListener(view.canRedoSteps);
51
+ undoListener(view.canUndoSteps);
52
+ view.callbacks.on("onCanRedoStepsUpdate", redoListener);
53
+ view.callbacks.on("onCanUndoStepsUpdate", undoListener);
54
+ };
55
+
56
+ private disposeViewCallbacks = (view: View) => {
57
+ view.callbacks.off("onCanRedoStepsUpdate", this.onCanRedoStepsUpdate);
58
+ view.callbacks.off("onCanUndoStepsUpdate", this.onCanUndoStepsUpdate);
59
+ };
60
+
61
+ private onCanRedoStepsUpdate = (steps: number) => {
62
+ callbacks.emit("canRedoStepsChange", steps);
63
+ };
64
+
65
+ private onCanUndoStepsUpdate = (steps: number) => {
66
+ callbacks.emit("canUndoStepsChange", steps);
67
+ };
68
+
69
+ private disposePrevFocusViewRedoUndoListeners = (prevFocused: string | undefined) => {
70
+ let view: View | undefined = undefined;
71
+ if (prevFocused === undefined) {
72
+ view = this.context.mainView();
73
+ } else {
74
+ const appProxy = this.context.getAppProxy(prevFocused);
75
+ if (appProxy && appProxy.view) {
76
+ view = appProxy.view;
77
+ }
78
+ }
79
+ if (view) {
80
+ this.disposeViewCallbacks(view);
81
+ }
82
+ };
83
+
84
+ public destroy() {
85
+ this.disposePrevFocusViewRedoUndoListeners(this.context.focus());
86
+ }
87
+ }
@@ -110,6 +110,17 @@ export class MainViewProxy {
110
110
  }
111
111
  }
112
112
 
113
+ public rebind(): void {
114
+ const divElement = this.mainView.divElement;
115
+ const disableCameraTransform = this.mainView.disableCameraTransform;
116
+ this.stop();
117
+ this.mainView.release();
118
+ this.mainView = this.createMainView();
119
+ this.mainView.disableCameraTransform = disableCameraTransform;
120
+ this.mainView.divElement = divElement;
121
+ this.start();
122
+ }
123
+
113
124
  private onCameraUpdatedByDevice = (camera: Camera) => {
114
125
  this.store.setMainViewCamera({ ...camera, id: this.manager.uid });
115
126
  if (!isEqual(this.mainViewSize, { ...this.mainView.size, id: this.manager.uid })) {
package/src/index.ts CHANGED
@@ -138,6 +138,9 @@ export type EmitterEvent = {
138
138
  onReconnected: void;
139
139
  removeScenes: string;
140
140
  cursorMove: CursorMovePayload;
141
+ updateManagerRect: undefined;
142
+ focusedChange: { focused: string | undefined, prev: string | undefined };
143
+ rootDirRemoved: undefined;
141
144
  };
142
145
 
143
146
  export type EmitterType = Emittery<EmitterEvent>;
@@ -374,7 +377,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
374
377
  }
375
378
  }
376
379
  }
377
- this.boxManager?.updateManagerRect();
380
+ emitter.emit("updateManagerRect");
378
381
  this.appManager?.refresh();
379
382
  this.appManager?.resetMaximized();
380
383
  this.appManager?.resetMinimized();