@netless/window-manager 1.0.0-canary.2 → 1.0.0-canary.22

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 (50) hide show
  1. package/__mocks__/white-web-sdk.ts +10 -1
  2. package/dist/App/AppContext.d.ts +11 -7
  3. package/dist/App/AppProxy.d.ts +35 -7
  4. package/dist/App/{WhiteBoardView.d.ts → WhiteboardView.d.ts} +10 -1
  5. package/dist/App/index.d.ts +2 -1
  6. package/dist/App/type.d.ts +21 -0
  7. package/dist/AppManager.d.ts +5 -5
  8. package/dist/AttributesDelegate.d.ts +11 -16
  9. package/dist/BoxManager.d.ts +7 -6
  10. package/dist/Cursor/index.d.ts +0 -1
  11. package/dist/InternalEmitter.d.ts +3 -2
  12. package/dist/Page/PageController.d.ts +1 -0
  13. package/dist/ReconnectRefresher.d.ts +1 -1
  14. package/dist/Utils/Common.d.ts +1 -0
  15. package/dist/View/CameraSynchronizer.d.ts +9 -9
  16. package/dist/View/MainView.d.ts +18 -7
  17. package/dist/View/ViewSync.d.ts +24 -0
  18. package/dist/constants.d.ts +6 -2
  19. package/dist/index.cjs.js +12 -12
  20. package/dist/index.d.ts +19 -2
  21. package/dist/index.es.js +803 -425
  22. package/dist/index.umd.js +12 -12
  23. package/dist/style.css +1 -1
  24. package/docs/app-context.md +98 -64
  25. package/docs/develop-app.md +2 -5
  26. package/docs/mirgrate-to-1.0.md +28 -0
  27. package/package.json +3 -3
  28. package/pnpm-lock.yaml +9 -9
  29. package/src/App/AppContext.ts +43 -21
  30. package/src/App/AppProxy.ts +247 -79
  31. package/src/App/{WhiteBoardView.ts → WhiteboardView.ts} +38 -4
  32. package/src/App/index.ts +2 -1
  33. package/src/App/type.ts +22 -0
  34. package/src/AppManager.ts +38 -31
  35. package/src/AttributesDelegate.ts +18 -18
  36. package/src/BoxManager.ts +28 -22
  37. package/src/Cursor/index.ts +0 -2
  38. package/src/InternalEmitter.ts +3 -2
  39. package/src/Page/PageController.ts +1 -0
  40. package/src/PageState.ts +1 -1
  41. package/src/ReconnectRefresher.ts +7 -2
  42. package/src/Utils/Common.ts +6 -0
  43. package/src/Utils/Reactive.ts +27 -26
  44. package/src/Utils/RoomHacker.ts +3 -0
  45. package/src/View/CameraSynchronizer.ts +43 -30
  46. package/src/View/MainView.ts +106 -81
  47. package/src/View/ViewSync.ts +110 -0
  48. package/src/constants.ts +5 -1
  49. package/src/index.ts +59 -15
  50. package/src/style.css +8 -0
@@ -1,6 +1,6 @@
1
- import { listenUpdated, unlistenUpdated, reaction, UpdateEventKind } from "white-web-sdk";
2
- import type { AkkoObjectUpdatedProperty , AkkoObjectUpdatedListener } from "white-web-sdk";
3
1
  import { isObject } from "lodash";
2
+ import { listenUpdated, reaction, unlistenUpdated, UpdateEventKind } from "white-web-sdk";
3
+ import type { AkkoObjectUpdatedProperty, AkkoObjectUpdatedListener } from "white-web-sdk";
4
4
 
5
5
  // 兼容 13 和 14 版本 SDK
6
6
  export const onObjectByEvent = (event: UpdateEventKind) => {
@@ -12,7 +12,7 @@ export const onObjectByEvent = (event: UpdateEventKind) => {
12
12
  if (kinds.includes(event)) {
13
13
  func();
14
14
  }
15
- }
15
+ };
16
16
  listenUpdated(object, listener);
17
17
  func();
18
18
  return () => unlistenUpdated(object, listener);
@@ -21,43 +21,44 @@ export const onObjectByEvent = (event: UpdateEventKind) => {
21
21
  () => object,
22
22
  () => {
23
23
  func();
24
- }, {
24
+ },
25
+ {
25
26
  fireImmediately: true,
26
27
  }
27
- )
28
+ );
28
29
  }
29
- }
30
- }
30
+ };
31
+ };
31
32
 
32
33
  export const safeListenPropsUpdated = <T>(
33
34
  getProps: () => T,
34
35
  callback: AkkoObjectUpdatedListener<T>,
35
36
  onDestroyed?: (props: unknown) => void
36
- ) => {
37
+ ) => {
37
38
  let disposeListenUpdated: (() => void) | null = null;
38
39
  const disposeReaction = reaction(
39
- getProps,
40
- () => {
41
- if (disposeListenUpdated) {
42
- disposeListenUpdated();
43
- disposeListenUpdated = null;
44
- }
45
- const props = getProps();
46
- if (isObject(props)) {
47
- disposeListenUpdated = () => unlistenUpdated(props, callback);
48
- listenUpdated(props, callback);
49
- } else {
50
- onDestroyed?.(props);
51
- }
52
- },
53
- { fireImmediately: true }
40
+ getProps,
41
+ () => {
42
+ if (disposeListenUpdated) {
43
+ disposeListenUpdated();
44
+ disposeListenUpdated = null;
45
+ }
46
+ const props = getProps();
47
+ if (isObject(props)) {
48
+ disposeListenUpdated = () => unlistenUpdated(props, callback);
49
+ listenUpdated(props, callback);
50
+ } else {
51
+ onDestroyed?.(props);
52
+ }
53
+ },
54
+ { fireImmediately: true }
54
55
  );
55
56
 
56
57
  return () => {
57
- disposeListenUpdated?.();
58
- disposeReaction();
58
+ disposeListenUpdated?.();
59
+ disposeReaction();
59
60
  };
60
- }
61
+ };
61
62
 
62
63
  export const onObjectRemoved = onObjectByEvent(UpdateEventKind.Removed);
63
64
  export const onObjectInserted = onObjectByEvent(UpdateEventKind.Inserted);
@@ -56,6 +56,9 @@ export const replaceRoomFunction = (room: Room | Player, manager: WindowManager)
56
56
  room.lockImages = (...args) => manager.lockImages(...args);
57
57
 
58
58
  delegateRemoveScenes(room, manager);
59
+ if (!(room as any).dynamicPpt.slideStateAdapter.pptHandler) {
60
+ (room as any).dynamicPpt.slideStateAdapter.pptHandler = manager.createPPTHandler();
61
+ }
59
62
  }
60
63
  };
61
64
 
@@ -1,32 +1,32 @@
1
1
  import { AnimationMode } from "white-web-sdk";
2
- import { delay, throttle } from "lodash";
2
+ import { debounce, delay, isEqual, pick, throttle } from "lodash";
3
3
  import type { TeleBoxRect } from "@netless/telebox-insider";
4
- import type { Camera, View, Size } from "white-web-sdk";
5
- import type { MainViewSize } from "../AttributesDelegate";
4
+ import type { Camera, View } from "white-web-sdk";
5
+ import type { ICamera, ISize } from "../AttributesDelegate";
6
6
 
7
- export type SaveCamera = (camera: Camera) => void;
7
+ export type SaveCamera = (camera: ICamera) => void;
8
8
 
9
9
  export class CameraSynchronizer {
10
- protected remoteCamera?: Camera;
11
- protected remoteSize?: MainViewSize;
10
+ public remoteCamera?: ICamera;
11
+ public remoteSize?: ISize;
12
12
  protected rect?: TeleBoxRect;
13
13
  protected view?: View;
14
14
 
15
15
  constructor(protected saveCamera: SaveCamera) {}
16
16
 
17
- public setRect(rect: TeleBoxRect) {
17
+ public setRect = debounce((rect: TeleBoxRect) => {
18
18
  this.rect = rect;
19
19
  if (this.remoteCamera && this.remoteSize) {
20
20
  this.onRemoteUpdate(this.remoteCamera, this.remoteSize);
21
21
  }
22
- }
22
+ }, 10);
23
23
 
24
24
  public setView(view: View) {
25
25
  this.view = view;
26
26
  }
27
27
 
28
28
  // 远端 Camera 或者 size 更新
29
- public onRemoteUpdate = throttle((camera: Camera, size: MainViewSize) => {
29
+ public onRemoteUpdate = throttle((camera: ICamera, size: ISize) => {
30
30
  this.remoteCamera = camera;
31
31
  this.remoteSize = size;
32
32
  if (this.remoteSize && this.rect) {
@@ -37,31 +37,44 @@ export class CameraSynchronizer {
37
37
  scale = this.rect.height / size.height;
38
38
  }
39
39
  const nextScale = camera.scale * scale;
40
- const moveCamera = () => this.view?.moveCamera({
41
- centerX: camera.centerX,
42
- centerY: camera.centerY,
43
- scale: nextScale,
44
- animationMode: AnimationMode.Immediately,
45
- });
40
+ const moveCamera = () => {
41
+ const config: Partial<Camera> & { animationMode: AnimationMode } = {
42
+ scale: nextScale,
43
+ animationMode: AnimationMode.Immediately,
44
+ }
45
+ if (camera.centerX !== null) {
46
+ config.centerX = camera.centerX;
47
+ }
48
+ if (camera.centerY !== null) {
49
+ config.centerY = camera.centerY;
50
+ }
51
+ this.view?.moveCamera(config);
52
+ }
53
+ moveCamera();
46
54
  // TODO 直接调用 moveCamera 依然会出现 camera 错误的情况,这里暂时加一个 delay 保证 camera 是对的, 后续需要 SDK 进行修改
47
- delay(moveCamera, 10);
55
+ delay(moveCamera, 50);
48
56
  }
49
- }, 50);
50
-
51
-
52
- public onLocalCameraUpdate(camera: Camera) {
53
- this.saveCamera(camera);
54
- }
57
+ }, 10);
55
58
 
56
- // 本地 Size 更新, 先匹配 camera 到新的 size 然后再发送 camera 数据到远端
57
- public onLocalSizeUpdate(size: Size) {
58
- if (this.rect && this.view) {
59
+ public onRemoteSizeUpdate(size: ISize) {
60
+ this.remoteSize = size;
61
+ const needMoveCamera = !isEqual(pick(this.rect, ["width", "height"]), pick(size, ["width", "height"]));
62
+ if (this.rect && this.remoteCamera && needMoveCamera) {
59
63
  const scale = this.rect.width / size.width;
60
- const nextScale = this.view.camera.scale * scale;
61
- this.view.moveCamera({
62
- scale: nextScale,
63
- animationMode: AnimationMode.Immediately
64
- });
64
+ const nextScale = this.remoteCamera.scale * scale;
65
+ const moveCamera = () => {
66
+ this.view?.moveCamera({
67
+ scale: nextScale,
68
+ animationMode: AnimationMode.Immediately,
69
+ })
70
+ };
71
+ moveCamera();
72
+ delay(moveCamera, 50);
65
73
  }
66
74
  }
75
+
76
+ public onLocalCameraUpdate(camera: ICamera) {
77
+ this.saveCamera(camera);
78
+ this.remoteCamera = camera;
79
+ }
67
80
  }
@@ -1,73 +1,96 @@
1
- import { reaction } from "white-web-sdk";
2
1
  import { callbacks } from "../callback";
3
2
  import { createView } from "./ViewManager";
4
3
  import { debounce, get, isEqual } from "lodash";
5
4
  import { emitter } from "../InternalEmitter";
6
5
  import { Events } from "../constants";
7
6
  import { Fields } from "../AttributesDelegate";
8
- import { setViewFocusScenePath } from "../Utils/Common";
7
+ import { reaction, toJS } from "white-web-sdk";
8
+ import { releaseView, setScenePath, setViewFocusScenePath } from "../Utils/Common";
9
9
  import { SideEffectManager } from "side-effect-manager";
10
- import type { Camera, Size, View } from "white-web-sdk";
10
+ import { Val } from "value-enhancer";
11
+ import { ViewSync } from "./ViewSync";
12
+ import type { ICamera, ISize } from "../AttributesDelegate";
13
+ import type { Size, View } from "white-web-sdk";
11
14
  import type { AppManager } from "../AppManager";
12
- import { CameraSynchronizer } from "./CameraSynchronizer";
13
15
 
14
16
  export class MainViewProxy {
15
17
  private started = false;
16
18
  private mainViewIsAddListener = false;
17
19
  private mainView: View;
18
20
  private store = this.manager.store;
19
- private synchronizer: CameraSynchronizer;
20
21
 
21
22
  private sideEffectManager = new SideEffectManager();
22
23
 
24
+ public camera$ = new Val<ICamera | undefined>(undefined);
25
+ public size$ = new Val<ISize | undefined>(undefined);
26
+ public view$ = new Val<View | undefined>(undefined);
27
+
28
+ public viewSync?: ViewSync;
29
+
23
30
  constructor(private manager: AppManager) {
24
- this.synchronizer = new CameraSynchronizer(
25
- camera => this.store.setMainViewCamera({ ...camera, id: this.manager.uid })
26
- );
27
31
  this.mainView = this.createMainView();
28
- this.moveCameraSizeByAttributes();
29
32
  emitter.once("mainViewMounted").then(() => {
30
33
  this.addMainViewListener();
31
34
  this.start();
32
35
  this.ensureCameraAndSize();
33
36
  this.startListenWritableChange();
34
37
  });
35
- this.sideEffectManager.add(() => {
36
- return emitter.on("containerSizeRatioUpdate", this.onUpdateContainerSizeRatio);
37
- });
38
- this.sideEffectManager.add(() => {
39
- return emitter.on("startReconnect", () => {
40
- if (!(this.mainView as any).didRelease) {
41
- this.mainView.release();
38
+ this.sideEffectManager.add(() => [
39
+ emitter.on("startReconnect", () => {
40
+ releaseView(this.mainView);
41
+ }),
42
+ ]);
43
+ this.createViewSync();
44
+ this.sideEffectManager.add(() => emitter.on("focusedChange", ({ focused }) => {
45
+ if (focused === undefined) {
46
+ const scenePath = this.store.getMainViewScenePath();
47
+ if (scenePath) {
48
+ setScenePath(this.manager.room, scenePath);
42
49
  }
50
+ }
51
+ }));
52
+ }
53
+
54
+ public createViewSync = () => {
55
+ if (this.manager.boxManager && !this.viewSync) {
56
+ this.viewSync = new ViewSync({
57
+ uid: this.manager.uid,
58
+ view$: this.view$,
59
+ camera$: this.camera$,
60
+ size$: this.size$,
61
+ stageRect$: this.manager.boxManager?.stageRect$,
62
+ viewMode$: this.manager.windowManger.viewMode$,
63
+ storeCamera: this.storeCamera,
64
+ storeSize: this.storeSize,
43
65
  });
44
- });
45
- const rect = this.manager.boxManager?.stageRect;
46
- if (rect) {
47
- this.synchronizer.setRect(rect);
48
66
  }
49
- this.sideEffectManager.add(() => {
50
- return emitter.on("playgroundSizeChange", rect => {
51
- this.synchronizer.setRect(rect);
52
- this.synchronizer.onLocalSizeUpdate(rect);
53
- });
54
- });
55
- }
67
+ };
56
68
 
57
69
  private startListenWritableChange = () => {
58
- this.sideEffectManager.add(() => {
59
- return emitter.on("writableChange", isWritable => {
70
+ this.sideEffectManager.add(() =>
71
+ emitter.on("writableChange", isWritable => {
60
72
  if (isWritable) {
61
73
  this.ensureCameraAndSize();
62
74
  }
63
- });
64
- });
75
+ })
76
+ );
65
77
  };
66
78
 
67
79
  public ensureCameraAndSize() {
68
80
  if (!this.mainViewCamera || !this.mainViewSize) {
69
81
  this.manager.dispatchInternalEvent(Events.InitMainViewCamera);
70
- this.setCameraAndSize();
82
+ this.storeCamera({
83
+ id: this.manager.uid,
84
+ ...this.view.camera
85
+ });
86
+ const stageRect = this.manager.boxManager?.stageRect;
87
+ if (stageRect && !this.mainViewSize) {
88
+ this.storeSize({
89
+ id: this.manager.uid,
90
+ width: stageRect.width,
91
+ height: stageRect.height
92
+ });
93
+ }
71
94
  }
72
95
  }
73
96
 
@@ -83,55 +106,73 @@ export class MainViewProxy {
83
106
  return get(this.view, ["didRelease"]);
84
107
  }
85
108
 
86
- private moveCameraSizeByAttributes() {
87
- this.synchronizer.onRemoteUpdate(this.mainViewCamera, this.mainViewSize);
88
- }
89
-
90
109
  public start() {
91
110
  if (this.started) return;
92
- this.sizeChangeHandler(this.mainViewSize);
93
111
  this.addCameraListener();
94
112
  this.addCameraReaction();
95
113
  this.started = true;
96
114
  }
97
115
 
98
116
  public addCameraReaction = () => {
99
- this.manager.refresher?.add(Fields.MainViewCamera, this.cameraReaction);
117
+ this.manager.refresher.add(Fields.MainViewCamera, this.cameraReaction);
118
+ this.manager.refresher.add(Fields.MainViewSize, this.sizeReaction);
100
119
  };
101
120
 
102
- public setCameraAndSize(): void {
103
- const stageSize = this.getStageSize();
104
- if (stageSize) {
105
- const camera = { ...this.mainView.camera, id: this.manager.uid };
106
- const size = { ...stageSize, id: this.manager.uid };
107
- this.store.setMainViewCameraAndSize(camera, size);
121
+ public storeCurrentCamera = () => {
122
+ const iCamera = this.view.camera;
123
+ this.storeCamera({
124
+ id: this.manager.uid,
125
+ ...iCamera
126
+ });
127
+ }
128
+
129
+ public storeCurrentSize = () => {
130
+ const rect = this.manager.boxManager?.stageRect;
131
+ if (rect) {
132
+ this.storeSize({
133
+ id: this.manager.uid,
134
+ width: rect.width,
135
+ height: rect.height
136
+ });
108
137
  }
109
138
  }
110
139
 
140
+ public storeCamera = (camera: ICamera) => {
141
+ this.store.setMainViewCamera(camera);
142
+ };
143
+
144
+ public storeSize = (size: ISize) => {
145
+ this.store.setMainViewSize(size);
146
+ };
147
+
111
148
  private cameraReaction = () => {
112
149
  return reaction(
113
150
  () => this.mainViewCamera,
114
151
  camera => {
115
- if (camera && camera.id !== this.manager.uid) {
116
- this.synchronizer.onRemoteUpdate(camera, this.mainViewSize);
152
+ if (camera) {
153
+ const rawCamera = toJS(camera);
154
+ if (!isEqual(rawCamera, this.camera$.value)) {
155
+ this.camera$.setValue(rawCamera);
156
+ }
117
157
  }
118
158
  },
119
159
  { fireImmediately: true }
120
160
  );
121
161
  };
122
162
 
123
- public sizeChangeHandler = debounce((size: Size) => {
124
- if (size) {
125
- this.synchronizer.onLocalSizeUpdate(size);
126
- }
127
- }, 30);
128
-
129
- public onUpdateContainerSizeRatio = () => {
130
- const size = this.store.getMainViewSize();
131
- this.sizeChangeHandler(size);
132
- if (size.id === this.manager.uid) {
133
- this.setCameraAndSize();
134
- }
163
+ private sizeReaction = () => {
164
+ return reaction(
165
+ () => this.mainViewSize,
166
+ size => {
167
+ if (size) {
168
+ const rawSize = toJS(size);
169
+ if (!isEqual(rawSize, this.size$.value)) {
170
+ this.size$.setValue(rawSize);
171
+ }
172
+ }
173
+ },
174
+ { fireImmediately: true }
175
+ );
135
176
  };
136
177
 
137
178
  public get view(): View {
@@ -148,7 +189,7 @@ export class MainViewProxy {
148
189
  if (mainViewScenePath) {
149
190
  setViewFocusScenePath(mainView, mainViewScenePath);
150
191
  }
151
- this.synchronizer.setView(mainView);
192
+ this.view$.setValue(mainView);
152
193
  return mainView;
153
194
  }
154
195
 
@@ -171,9 +212,7 @@ export class MainViewProxy {
171
212
  const divElement = this.mainView.divElement;
172
213
  const disableCameraTransform = this.mainView.disableCameraTransform;
173
214
  this.stop();
174
- if (!this.didRelease) {
175
- this.mainView.release();
176
- }
215
+ releaseView(this.mainView);
177
216
  this.removeMainViewListener();
178
217
  this.mainView = this.createMainView();
179
218
  this.mainView.disableCameraTransform = disableCameraTransform;
@@ -182,21 +221,6 @@ export class MainViewProxy {
182
221
  this.start();
183
222
  }
184
223
 
185
- private onCameraUpdatedByDevice = (camera: Camera) => {
186
- this.synchronizer.onLocalCameraUpdate(camera);
187
- const size = this.getStageSize();
188
- if (size && !isEqual(size, this.mainViewSize)) {
189
- this.setMainViewSize(size);
190
- }
191
- };
192
-
193
- private getStageSize(): Size | undefined {
194
- const stage = this.manager.boxManager?.stageRect;
195
- if (stage) {
196
- return { width: stage.width, height: stage.height };
197
- }
198
- }
199
-
200
224
  public addMainViewListener(): void {
201
225
  if (this.mainViewIsAddListener) return;
202
226
  if (this.view.divElement) {
@@ -229,13 +253,11 @@ export class MainViewProxy {
229
253
  }, 50);
230
254
 
231
255
  private addCameraListener() {
232
- this.view.callbacks.on("onCameraUpdatedByDevice", this.onCameraUpdatedByDevice);
233
256
  this.view.callbacks.on("onCameraUpdated", this.onCameraOrSizeUpdated);
234
257
  this.view.callbacks.on("onSizeUpdated", this.onCameraOrSizeUpdated);
235
258
  }
236
259
 
237
260
  private removeCameraListener() {
238
- this.view.callbacks.off("onCameraUpdatedByDevice", this.onCameraUpdatedByDevice);
239
261
  this.view.callbacks.off("onCameraUpdated", this.onCameraOrSizeUpdated);
240
262
  this.view.callbacks.off("onSizeUpdated", this.onCameraOrSizeUpdated);
241
263
  }
@@ -246,12 +268,15 @@ export class MainViewProxy {
246
268
 
247
269
  public stop() {
248
270
  this.removeCameraListener();
249
- this.manager.refresher?.remove(Fields.MainViewCamera);
250
- this.manager.refresher?.remove(Fields.MainViewSize);
271
+ this.manager.refresher.remove(Fields.MainViewCamera);
272
+ this.manager.refresher.remove(Fields.MainViewSize);
251
273
  this.started = false;
252
274
  }
253
275
 
254
276
  public destroy() {
277
+ this.camera$.destroy();
278
+ this.size$.destroy();
279
+ this.view$.destroy();
255
280
  this.removeMainViewListener();
256
281
  this.stop();
257
282
  this.sideEffectManager.flushAll();
@@ -0,0 +1,110 @@
1
+ import { AnimationMode, ViewMode } from "white-web-sdk";
2
+ import { CameraSynchronizer } from "./CameraSynchronizer";
3
+ import { combine } from "value-enhancer";
4
+ import { isEqual } from "lodash";
5
+ import { SideEffectManager } from "side-effect-manager";
6
+ import type { Camera, View } from "white-web-sdk";
7
+ import type { Val, ReadonlyVal } from "value-enhancer";
8
+ import type { ICamera, ISize } from "../AttributesDelegate";
9
+ import type { TeleBoxRect } from "@netless/telebox-insider";
10
+
11
+ export type ViewSyncContext = {
12
+ uid: string;
13
+ // 远端 camera
14
+ camera$: Val<ICamera | undefined, boolean>;
15
+ // 远端 size
16
+ size$: Val<ISize | undefined>;
17
+
18
+ stageRect$: ReadonlyVal<TeleBoxRect>;
19
+
20
+ viewMode$?: Val<ViewMode>;
21
+
22
+ storeCamera: (camera: ICamera) => void;
23
+
24
+ storeSize: (size: ISize) => void;
25
+
26
+ view$: Val<View | undefined>;
27
+ };
28
+
29
+ export class ViewSync {
30
+ private sem = new SideEffectManager();
31
+ private synchronizer: CameraSynchronizer;
32
+
33
+ constructor(private context: ViewSyncContext) {
34
+ this.synchronizer = new CameraSynchronizer((camera: ICamera) => {
35
+ this.context.camera$.setValue(camera, true);
36
+ const notStoreCamera =
37
+ this.context.viewMode$ && this.context.viewMode$.value === ViewMode.Freedom;
38
+ if (notStoreCamera) {
39
+ return;
40
+ } else {
41
+ this.context.storeCamera(camera);
42
+ }
43
+ });
44
+ this.bindView(this.context.view$.value);
45
+ this.sem.add(() => [
46
+ this.context.view$.subscribe(view => {
47
+ const currentCamera = this.context.camera$.value;
48
+ if (currentCamera && this.context.size$.value) {
49
+ view?.moveCamera({
50
+ scale: 1,
51
+ animationMode: AnimationMode.Immediately,
52
+ });
53
+ this.synchronizer.onRemoteUpdate(currentCamera, this.context.size$.value);
54
+ }
55
+
56
+ this.bindView(view);
57
+ }),
58
+ this.context.camera$.subscribe((camera, skipUpdate) => {
59
+ const size = this.context.size$.value;
60
+ if (camera && size && !skipUpdate) {
61
+ this.synchronizer.onRemoteUpdate(camera, size);
62
+ }
63
+ }),
64
+ this.context.size$.subscribe(size => {
65
+ if (size) {
66
+ this.synchronizer.onRemoteSizeUpdate(size);
67
+ }
68
+ }),
69
+ this.context.stageRect$.reaction(rect => {
70
+ if (rect) {
71
+ this.synchronizer.setRect(rect);
72
+ }
73
+ })
74
+ ]);
75
+ const camera$size$ = combine([this.context.camera$, this.context.size$]);
76
+ camera$size$.reaction(([camera, size]) => {
77
+ if (camera && size) {
78
+ this.synchronizer.onRemoteUpdate(camera, size);
79
+ camera$size$.destroy();
80
+ }
81
+ });
82
+ }
83
+
84
+ public bindView = (view?: View) => {
85
+ if (!view) return;
86
+ this.synchronizer.setView(view);
87
+ this.sem.flush("view");
88
+ this.sem.add(() => {
89
+ view.callbacks.on("onCameraUpdatedByDevice", this.onCameraUpdatedByDevice);
90
+ return () =>
91
+ view.callbacks.off("onCameraUpdatedByDevice", this.onCameraUpdatedByDevice);
92
+ }, "view");
93
+ };
94
+
95
+ private onCameraUpdatedByDevice = (camera: Camera) => {
96
+ if (!camera) return;
97
+ this.synchronizer.onLocalCameraUpdate({ ...camera, id: this.context.uid });
98
+ const stage = this.context.stageRect$.value;
99
+ if (stage) {
100
+ const size = { width: stage.width, height: stage.height, id: this.context.uid };
101
+ if (!isEqual(size, this.context.size$.value)) {
102
+ this.context.storeSize(size);
103
+ }
104
+ }
105
+ };
106
+
107
+ public destroy() {
108
+ this.sem.flushAll();
109
+ }
110
+ }
package/src/constants.ts CHANGED
@@ -5,7 +5,6 @@ export enum Events {
5
5
  AppBoxStateChange = "AppBoxStateChange",
6
6
  GetAttributes = "GetAttributes",
7
7
  UpdateWindowManagerWrapper = "UpdateWindowManagerWrapper",
8
- InitReplay = "InitReplay",
9
8
  WindowCreated = "WindowCreated",
10
9
  SetMainViewScenePath = "SetMainViewScenePath",
11
10
  SetMainViewSceneIndex = "SetMainViewSceneIndex",
@@ -27,6 +26,11 @@ export enum AppAttributes {
27
26
  Position = "position",
28
27
  SceneIndex = "SceneIndex",
29
28
  ZIndex = "zIndex",
29
+ Visible = "visible",
30
+ Ratio = "ratio",
31
+ StageRatio = "stageRatio",
32
+ Draggable = "draggable",
33
+ Resizable = "resizable",
30
34
  }
31
35
 
32
36
  export enum AppEvents {