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

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 (47) hide show
  1. package/__mocks__/white-web-sdk.ts +10 -1
  2. package/dist/App/AppContext.d.ts +6 -6
  3. package/dist/App/AppProxy.d.ts +30 -5
  4. package/dist/App/{WhiteBoardView.d.ts → WhiteboardView.d.ts} +10 -1
  5. package/dist/App/index.d.ts +1 -1
  6. package/dist/AppManager.d.ts +5 -5
  7. package/dist/AttributesDelegate.d.ts +11 -16
  8. package/dist/BoxManager.d.ts +3 -3
  9. package/dist/InternalEmitter.d.ts +2 -0
  10. package/dist/Page/PageController.d.ts +1 -0
  11. package/dist/ReconnectRefresher.d.ts +1 -1
  12. package/dist/Utils/Common.d.ts +1 -0
  13. package/dist/Utils/Reactive.d.ts +2 -0
  14. package/dist/View/CameraSynchronizer.d.ts +9 -9
  15. package/dist/View/MainView.d.ts +18 -7
  16. package/dist/View/ViewSync.d.ts +24 -0
  17. package/dist/constants.d.ts +6 -1
  18. package/dist/index.cjs.js +12 -12
  19. package/dist/index.d.ts +19 -2
  20. package/dist/index.es.js +791 -380
  21. package/dist/index.umd.js +12 -12
  22. package/dist/style.css +1 -1
  23. package/docs/app-context.md +98 -64
  24. package/docs/develop-app.md +2 -5
  25. package/docs/mirgrate-to-1.0.md +28 -0
  26. package/package.json +3 -3
  27. package/pnpm-lock.yaml +9 -9
  28. package/src/App/AppContext.ts +30 -19
  29. package/src/App/AppProxy.ts +251 -39
  30. package/src/App/{WhiteBoardView.ts → WhiteboardView.ts} +38 -4
  31. package/src/App/index.ts +1 -1
  32. package/src/AppManager.ts +33 -30
  33. package/src/AttributesDelegate.ts +18 -18
  34. package/src/BoxManager.ts +20 -15
  35. package/src/InternalEmitter.ts +2 -0
  36. package/src/Page/PageController.ts +1 -0
  37. package/src/PageState.ts +1 -1
  38. package/src/ReconnectRefresher.ts +2 -1
  39. package/src/Utils/Common.ts +6 -0
  40. package/src/Utils/Reactive.ts +43 -26
  41. package/src/Utils/RoomHacker.ts +3 -0
  42. package/src/View/CameraSynchronizer.ts +43 -30
  43. package/src/View/MainView.ts +106 -81
  44. package/src/View/ViewSync.ts +116 -0
  45. package/src/constants.ts +5 -0
  46. package/src/index.ts +59 -15
  47. package/src/style.css +8 -0
@@ -17,7 +17,6 @@ import type {
17
17
  } from "white-web-sdk";
18
18
  import type { ReadonlyTeleBox } from "@netless/telebox-insider";
19
19
  import type Emittery from "emittery";
20
- import type { BoxManager } from "../BoxManager";
21
20
  import type { AppEmitterEvent, Member } from "../index";
22
21
  import type { AppManager } from "../AppManager";
23
22
  import type { AppProxy } from "./AppProxy";
@@ -26,10 +25,9 @@ import type {
26
25
  MagixEventDispatcher,
27
26
  MagixEventRemoveListener,
28
27
  } from "./MagixEvent";
29
- import { WhiteBoardView } from "./WhiteBoardView";
28
+ import { WhiteBoardView } from "./WhiteboardView";
30
29
  import { findMemberByUid } from "../Helper";
31
30
  import { MAX_PAGE_SIZE } from "../constants";
32
- import { putScenes } from "../Utils/Common";
33
31
  import { isNumber } from "lodash";
34
32
 
35
33
  export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOptions = any> {
@@ -49,11 +47,11 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
49
47
  private store = this.manager.store;
50
48
  public readonly isAddApp: boolean;
51
49
  public readonly isReplay = this.manager.isReplay;
52
- private whiteBoardView?: WhiteBoardView;
50
+ public whiteBoardView?: WhiteBoardView;
51
+ public _viewWrapper?: HTMLElement;
53
52
 
54
53
  constructor(
55
54
  private manager: AppManager,
56
- private boxManager: BoxManager,
57
55
  public appId: string,
58
56
  private appProxy: AppProxy,
59
57
  private appOptions?: TAppOptions | (() => TAppOptions)
@@ -62,9 +60,13 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
62
60
  this.isAddApp = appProxy.isAddApp;
63
61
  }
64
62
 
65
- public get displayer(){
63
+ public get displayer() {
66
64
  return this.manager.displayer;
67
- };
65
+ }
66
+
67
+ public get destroyed() {
68
+ return this.appProxy.status === "destroyed";
69
+ }
68
70
 
69
71
  /** @deprecated Use context.storage.state instead. */
70
72
  public getAttributes = (): TAttributes | undefined => {
@@ -92,13 +94,25 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
92
94
  if (!view) {
93
95
  view = this.appProxy.createAppDir();
94
96
  }
95
- view.divElement = this.box.$content as HTMLDivElement;
96
- this.initPageSize(size);
97
- this.whiteBoardView = new WhiteBoardView(this, this.appProxy);
97
+ const viewWrapper = document.createElement("div");
98
+ this._viewWrapper = viewWrapper;
99
+ viewWrapper.className = "window-manager-view-wrapper";
100
+ this.box.$content.parentElement?.appendChild(viewWrapper);
101
+ view.divElement = viewWrapper;
102
+ this.appProxy.fireMemberStateChange();
103
+ if (this.isAddApp) {
104
+ this.ensurePageSize(size);
105
+ }
106
+ this.whiteBoardView = new WhiteBoardView(view, this, this.appProxy, this.ensurePageSize);
107
+ this.appProxy.sideEffectManager.add(() => {
108
+ return () => {
109
+ this.whiteBoardView = undefined;
110
+ }
111
+ });
98
112
  return this.whiteBoardView;
99
113
  }
100
114
 
101
- private initPageSize = (size?: number) => {
115
+ private ensurePageSize = (size?: number) => {
102
116
  if (!isNumber(size)) return;
103
117
  if (!this.appProxy.scenePath) return;
104
118
  if (this.appProxy.pageState.length >= size) return;
@@ -106,25 +120,22 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
106
120
  throw Error(`[WindowManager]: size ${size} muse be in range [1, ${MAX_PAGE_SIZE}]`);
107
121
  }
108
122
  const needInsert = size - this.appProxy.pageState.length;
109
- const startPageNumber = this.appProxy.pageState.length;
110
- const scenes = new Array(needInsert).fill({}).map((_, index) => {
111
- return { name: `${startPageNumber + index + 1}` };
112
- });
113
- putScenes(this.room, this.appProxy.scenePath, scenes);
123
+ const scenes = new Array(needInsert).fill({});
124
+ this.room?.putScenes(this.appProxy.scenePath, scenes);
114
125
  }
115
126
 
116
127
  public getInitScenePath = () => {
117
- return this.manager.getAppInitPath(this.appId);
128
+ return this.appProxy.scenePath;
118
129
  };
119
130
 
120
131
  /** Get App writable status. */
121
132
  public get isWritable(): boolean {
122
- return this.manager.canOperate;
133
+ return this.manager.canOperate && !this.destroyed;
123
134
  };
124
135
 
125
136
  /** Get the App Window UI box. */
126
137
  public get box(): ReadonlyTeleBox {
127
- const box = this.boxManager.getBox(this.appId);
138
+ const box = this.appProxy.box$.value;
128
139
  if (box) {
129
140
  return box;
130
141
  } else {
@@ -3,12 +3,18 @@ import { AppAttributes, AppEvents, Events, SETUP_APP_DELAY } from "../constants"
3
3
  import { AppContext } from "./AppContext";
4
4
  import { AppPageStateImpl } from "./AppPageStateImpl";
5
5
  import { appRegister } from "../Register";
6
- import { autorun } from "white-web-sdk";
6
+ import { ViewSync } from "../View/ViewSync"
7
+ import { autorun, reaction, toJS } from "white-web-sdk";
8
+ import { boxEmitter } from "../BoxEmitter";
7
9
  import { BoxManagerNotFoundError } from "../Utils/error";
8
- import { debounce, get } from "lodash";
10
+ import { calculateNextIndex } from "../Page";
11
+ import { combine, Val, ValManager } from "value-enhancer";
12
+ import { debounce, get, isEqual } from "lodash";
9
13
  import { emitter } from "../InternalEmitter";
10
14
  import { Fields } from "../AttributesDelegate";
11
15
  import { log } from "../Utils/log";
16
+ import { SideEffectManager } from "side-effect-manager";
17
+ import type { ICamera, ISize } from "../AttributesDelegate";
12
18
  import {
13
19
  entireScenes,
14
20
  getScenePath,
@@ -24,14 +30,12 @@ import type {
24
30
  setAppOptions,
25
31
  AppListenerKeys,
26
32
  } from "../index";
27
- import type { SceneState, View, SceneDefinition } from "white-web-sdk";
33
+ import type { SceneState, View, SceneDefinition, MemberState} from "white-web-sdk";
28
34
  import type { AppManager } from "../AppManager";
29
35
  import type { NetlessApp } from "../typings";
30
- import type { ReadonlyTeleBox } from "@netless/telebox-insider";
36
+ import type { ReadonlyTeleBox, TeleBoxRect } from "@netless/telebox-insider";
31
37
  import type { PageRemoveService, PageState } from "../Page";
32
- import { calculateNextIndex } from "../Page";
33
- import { boxEmitter } from "../BoxEmitter";
34
- import { SideEffectManager } from "side-effect-manager";
38
+ import { createValSync } from "../Utils/Reactive";
35
39
 
36
40
  export type AppEmitter = Emittery<AppEmitterEvent>;
37
41
 
@@ -48,17 +52,26 @@ export class AppProxy implements PageRemoveService {
48
52
  private appProxies = this.manager.appProxies;
49
53
  private viewManager = this.manager.viewManager;
50
54
  private store = this.manager.store;
55
+ public uid = this.manager.uid;
51
56
 
52
57
  public isAddApp: boolean;
53
- private status: "normal" | "destroyed" = "normal";
58
+ public status: "normal" | "destroyed" = "normal";
54
59
  private stateKey: string;
55
60
  public _pageState: AppPageStateImpl;
56
- private _prevFullPath: string | undefined;
57
61
 
58
- public appResult?: NetlessApp<any>;
59
- public appContext?: AppContext<any, any>;
62
+ public appResult?: NetlessApp;
63
+ public appContext?: AppContext;
64
+
65
+ public sideEffectManager = new SideEffectManager();
66
+ private valManager = new ValManager();
60
67
 
61
- private sideEffectManager = new SideEffectManager();
68
+ private fullPath$ = this.valManager.attach(new Val<string | undefined>(undefined));
69
+ private viewSync?: ViewSync;
70
+
71
+ public camera$ = this.valManager.attach(new Val<ICamera | undefined>(undefined));
72
+ public size$ = this.valManager.attach(new Val<ISize | undefined>(undefined));
73
+ public box$ = this.valManager.attach(new Val<ReadonlyTeleBox | undefined>(undefined));
74
+ public view$ = this.valManager.attach(new Val<View | undefined>(undefined));
62
75
 
63
76
  constructor(
64
77
  private params: BaseInsertParams,
@@ -90,16 +103,142 @@ export class AppProxy implements PageRemoveService {
90
103
  view: this.view,
91
104
  notifyPageStateChange: this.notifyPageStateChange,
92
105
  });
93
- this.sideEffectManager.add(() => {
94
- return () => this._pageState.destroy();
95
- });
96
- this.sideEffectManager.add(() => {
97
- return emitter.on("roomMembersChange", members => {
106
+ this.sideEffectManager.add(() => () => this._pageState.destroy());
107
+ this.sideEffectManager.add(() =>
108
+ emitter.on("roomMembersChange", members => {
98
109
  this.appEmitter.emit("roomMembersChange", members);
99
- });
110
+ })
111
+ );
112
+ this.camera$.setValue(toJS(this.appAttributes.camera));
113
+ this.size$.setValue(toJS(this.appAttributes.size));
114
+ this.addCameraReaction();
115
+ this.addSizeReaction();
116
+ this.sideEffectManager.add(() =>
117
+ combine([this.box$, this.view$]).subscribe(([box, view]) => {
118
+ if (box && view) {
119
+ if (!this.camera$.value) {
120
+ this.storeCamera({
121
+ centerX: null,
122
+ centerY: null,
123
+ scale: 1,
124
+ id: this.uid,
125
+ });
126
+ this.camera$.setValue(toJS(this.appAttributes.camera));
127
+ }
128
+ if (!this.size$.value && box.contentStageRect) {
129
+ const initialRect = this.computedInitialRect(box.contentStageRect);
130
+ const width = initialRect?.width || box.contentStageRect.width;
131
+ const height = initialRect?.height || box.contentStageRect.height;
132
+ this.storeSize({
133
+ id: this.uid,
134
+ width,
135
+ height,
136
+ });
137
+ this.size$.setValue(toJS(this.appAttributes.size));
138
+ }
139
+ this.viewSync = new ViewSync({
140
+ uid: this.uid,
141
+ view$: this.view$,
142
+ camera$: this.camera$,
143
+ size$: this.size$,
144
+ stageRect$: box._contentStageRect$,
145
+ storeCamera: this.storeCamera,
146
+ storeSize: this.storeSize
147
+ });
148
+ this.sideEffectManager.add(() => () => this.viewSync?.destroy());
149
+ }
150
+ })
151
+ );
152
+ this.sideEffectManager.add(() =>
153
+ emitter.on("memberStateChange", this.onMemberStateChange)
154
+ );
155
+ this.box$.subscribe(box => {
156
+ if (!box) return;
157
+ this.sideEffectManager.add(() => [
158
+ createValSync(
159
+ () => this.appAttributes?.state.visible,
160
+ box._visible$,
161
+ this.isAddApp,
162
+ ),
163
+ createValSync(
164
+ () => this.appAttributes?.state.ratio,
165
+ box._ratio$,
166
+ this.isAddApp,
167
+ ),
168
+ createValSync(
169
+ () => this.appAttributes?.state.stageRatio,
170
+ box._stageRatio$,
171
+ this.isAddApp,
172
+ ),
173
+ createValSync(
174
+ () => this.appAttributes?.state.draggable,
175
+ box._draggable$,
176
+ this.isAddApp,
177
+ ),
178
+ createValSync(
179
+ () => this.appAttributes?.state.resizable,
180
+ box._resizable$,
181
+ this.isAddApp,
182
+ ),
183
+ box._visible$.subscribe(visible => {
184
+ this.store.updateAppState(this.id, AppAttributes.Visible, visible);
185
+ }),
186
+ box._ratio$.subscribe(ratio => {
187
+ this.store.updateAppState(this.id, AppAttributes.Ratio, ratio);
188
+ }),
189
+ box._stageRatio$.subscribe(stageRatio => {
190
+ this.store.updateAppState(this.id, AppAttributes.StageRatio, stageRatio);
191
+ }),
192
+ box._draggable$.subscribe(draggable => {
193
+ this.store.updateAppState(this.id, AppAttributes.Draggable, draggable);
194
+ }),
195
+ box._resizable$.subscribe(resizable => {
196
+ console.log("resizable change", resizable);
197
+ this.store.updateAppState(this.id, AppAttributes.Resizable, resizable);
198
+ }),
199
+ ])
100
200
  });
101
201
  }
102
202
 
203
+ public fireMemberStateChange = () => {
204
+ if (this.manager.room) {
205
+ this.onMemberStateChange(this.manager.room.state.memberState);
206
+ }
207
+ }
208
+
209
+ private onMemberStateChange = (memberState: MemberState) => {
210
+ // clicker 教具把事件穿透给下层
211
+ const needPointerEventsNone = memberState.currentApplianceName === "clicker";
212
+ if (needPointerEventsNone) {
213
+ if (this.appContext?._viewWrapper) {
214
+ this.appContext._viewWrapper.style.pointerEvents = "none";
215
+ }
216
+ } else {
217
+ if (this.appContext?._viewWrapper) {
218
+ this.appContext._viewWrapper.style.pointerEvents = "auto";
219
+ }
220
+ }
221
+ }
222
+
223
+ private computedInitialRect = (boxRect: TeleBoxRect) => {
224
+ const managerRect = this.manager.boxManager?.stageRect;
225
+ if (managerRect) {
226
+ const { width, height } = managerRect;
227
+ const boxRatio = boxRect.height / boxRect.width;
228
+ if (height < 480) {
229
+ return {
230
+ width: 480 / boxRatio,
231
+ height: 480,
232
+ };
233
+ } else {
234
+ return {
235
+ width: width * 0.65,
236
+ height: height * 0.65,
237
+ };
238
+ }
239
+ }
240
+ }
241
+
103
242
  public createAppDir() {
104
243
  const scenePath = this.scenePath || this.appScenePath;
105
244
  const sceneNode = this._pageState.createSceneNode(scenePath);
@@ -127,7 +266,7 @@ export class AppProxy implements PageRemoveService {
127
266
  }
128
267
 
129
268
  public get view(): View | undefined {
130
- return this.manager.viewManager.getView(this.id);
269
+ return this.view$.value;
131
270
  }
132
271
 
133
272
  public get viewIndex(): number | undefined {
@@ -162,7 +301,7 @@ export class AppProxy implements PageRemoveService {
162
301
  }
163
302
 
164
303
  public setFullPath(path: string) {
165
- this.manager.safeUpdateAttributes(["apps", this.id, Fields.FullPath], path);
304
+ this.store.updateAppAttributes(this.id, Fields.FullPath, path);
166
305
  }
167
306
 
168
307
  public async baseInsertApp(skipUpdate = false): Promise<{ appId: string; app: NetlessApp }> {
@@ -191,7 +330,7 @@ export class AppProxy implements PageRemoveService {
191
330
  }
192
331
 
193
332
  public get box(): ReadonlyTeleBox | undefined {
194
- return this.boxManager?.getBox(this.id);
333
+ return this.box$.value;
195
334
  }
196
335
 
197
336
  private async setupApp(
@@ -205,7 +344,7 @@ export class AppProxy implements PageRemoveService {
205
344
  if (!this.boxManager) {
206
345
  throw new BoxManagerNotFoundError();
207
346
  }
208
- const context = new AppContext(this.manager, this.boxManager, appId, this, appOptions);
347
+ const context = new AppContext(this.manager, appId, this, appOptions);
209
348
  this.appContext = context;
210
349
  try {
211
350
  emitter.once(`${appId}${Events.WindowCreated}` as any).then(async () => {
@@ -225,13 +364,14 @@ export class AppProxy implements PageRemoveService {
225
364
  this.fixMobileSize();
226
365
  }, SETUP_APP_DELAY);
227
366
  });
228
- this.boxManager?.createBox({
367
+ const box = this.boxManager?.createBox({
229
368
  appId: appId,
230
369
  app,
231
370
  options,
232
371
  canOperate: this.manager.canOperate,
233
372
  smartPosition: this.isAddApp,
234
373
  });
374
+ this.box$.setValue(box);
235
375
  if (this.isAddApp && this.box) {
236
376
  this.store.updateAppState(appId, AppAttributes.ZIndex, this.box.zIndex);
237
377
  this.boxManager.focusBox({ appId }, false);
@@ -246,12 +386,14 @@ export class AppProxy implements PageRemoveService {
246
386
  private fixMobileSize() {
247
387
  const box = this.boxManager?.getBox(this.id);
248
388
  if (box) {
249
- this.boxManager?.resizeBox({
250
- appId: this.id,
251
- width: box.intrinsicWidth + 0.001,
252
- height: box.intrinsicHeight + 0.001,
253
- skipUpdate: true,
254
- });
389
+ if (!box.minimized) {
390
+ this.boxManager?.resizeBox({
391
+ appId: this.id,
392
+ width: box.intrinsicWidth + 0.001,
393
+ height: box.intrinsicHeight + 0.001,
394
+ skipUpdate: true,
395
+ });
396
+ }
255
397
  }
256
398
  }
257
399
 
@@ -376,7 +518,7 @@ export class AppProxy implements PageRemoveService {
376
518
  }
377
519
 
378
520
  private appAttributesUpdateListener = (appId: string) => {
379
- this.manager.refresher?.add(appId, () => {
521
+ this.manager.refresher.add(appId, () => {
380
522
  return autorun(() => {
381
523
  const attrs = this.manager.attributes[appId];
382
524
  if (attrs) {
@@ -384,7 +526,7 @@ export class AppProxy implements PageRemoveService {
384
526
  }
385
527
  });
386
528
  });
387
- this.manager.refresher?.add(this.stateKey, () => {
529
+ this.manager.refresher.add(this.stateKey, () => {
388
530
  return autorun(() => {
389
531
  const appState = this.appAttributes?.state;
390
532
  if (appState?.zIndex > 0 && appState.zIndex !== this.box?.zIndex) {
@@ -392,13 +534,13 @@ export class AppProxy implements PageRemoveService {
392
534
  }
393
535
  });
394
536
  });
395
- this.manager.refresher?.add(`${appId}-fullPath`, () => {
537
+ this.manager.refresher.add(`${appId}-fullPath`, () => {
396
538
  return autorun(() => {
397
539
  const fullPath = this.appAttributes?.fullPath;
398
540
  this.setFocusScenePathHandler(fullPath);
399
- if (this._prevFullPath !== fullPath) {
541
+ if (this.fullPath$.value !== fullPath) {
400
542
  this.notifyPageStateChange();
401
- this._prevFullPath = fullPath;
543
+ this.fullPath$.setValue(fullPath);
402
544
  }
403
545
  });
404
546
  });
@@ -428,6 +570,7 @@ export class AppProxy implements PageRemoveService {
428
570
 
429
571
  private createView(): View {
430
572
  const view = this.viewManager.createView(this.id);
573
+ this.view$.setValue(view);
431
574
  this.setViewFocusScenePath();
432
575
  return view;
433
576
  }
@@ -477,10 +620,37 @@ export class AppProxy implements PageRemoveService {
477
620
  const fullPath = this._pageState.getFullPath(index);
478
621
  if (fullPath) {
479
622
  this.setFullPath(fullPath);
623
+ setScenePath(this.manager.room, fullPath);
480
624
  }
481
625
  }
482
626
  }
483
627
 
628
+ public storeCamera = (camera: ICamera) => {
629
+ this.store.updateAppAttributes(this.id, Fields.Camera, camera);
630
+ };
631
+
632
+ public storeSize = (size: ISize) => {
633
+ this.store.updateAppAttributes(this.id, Fields.Size, size);
634
+ };
635
+
636
+ public updateSize = (width: number, height: number) => {
637
+ const iSize = {
638
+ id: this.manager.uid,
639
+ width, height
640
+ }
641
+ this.store.updateAppAttributes(this.id, Fields.Size, iSize);
642
+ this.size$.setValue(iSize);
643
+ }
644
+
645
+ public moveCamera = (camera: Partial<ICamera>) => {
646
+ if (!this.camera$.value) {
647
+ return;
648
+ }
649
+ const nextCamera = { ...this.camera$.value, ...camera, id: this.uid };
650
+ this.storeCamera(nextCamera);
651
+ this.camera$.setValue(nextCamera);
652
+ };
653
+
484
654
  public async destroy(
485
655
  needCloseBox: boolean,
486
656
  cleanAttrs: boolean,
@@ -496,6 +666,7 @@ export class AppProxy implements PageRemoveService {
496
666
  console.error("[WindowManager]: notifyApp error", error.message, error.stack);
497
667
  }
498
668
  this.appEmitter.clearListeners();
669
+ this.sideEffectManager.flushAll();
499
670
  emitter.emit(`destroy-${this.id}` as any, { error });
500
671
  if (needCloseBox) {
501
672
  this.boxManager?.closeBox(this.id, skipUpdate);
@@ -510,11 +681,52 @@ export class AppProxy implements PageRemoveService {
510
681
 
511
682
  this.viewManager.destroyView(this.id);
512
683
  this.manager.appStatus.delete(this.id);
513
- this.manager.refresher?.remove(this.id);
514
- this.manager.refresher?.remove(this.stateKey);
515
- this.manager.refresher?.remove(`${this.id}-fullPath`);
516
- this._prevFullPath = undefined;
517
- this.sideEffectManager.flushAll();
684
+ this.manager.refresher.remove(this.id);
685
+ this.manager.refresher.remove(this.stateKey);
686
+ this.manager.refresher.remove(`${this.id}-fullPath`);
687
+ this.manager.refresher.remove(`${this.id}-camera`);
688
+ this.manager.refresher.remove(`${this.id}-size`);
689
+ this.valManager.destroy();
690
+ }
691
+
692
+ private addCameraReaction = () => {
693
+ this.sideEffectManager.add(() =>
694
+ this.manager.refresher.add(`${this.id}-camera`, () =>
695
+ reaction(
696
+ () => this.appAttributes?.camera,
697
+ camera => {
698
+ if (camera) {
699
+ const rawCamera = toJS(camera);
700
+ if (!isEqual(rawCamera, this.camera$.value)) {
701
+ this.camera$.setValue(rawCamera);
702
+ }
703
+ }
704
+ }
705
+ )
706
+ )
707
+ , "camera");
708
+ }
709
+
710
+ private addSizeReaction = () => {
711
+ this.sideEffectManager.add(() =>
712
+ this.manager.refresher.add(`${this.id}-size`, () =>
713
+ reaction(
714
+ () => this.appAttributes?.size,
715
+ size => {
716
+ if (size) {
717
+ const rawSize = toJS(size);
718
+ if (!isEqual(rawSize, this.size$.value)) {
719
+ this.size$.setValue(rawSize);
720
+ }
721
+ }
722
+ }
723
+ )
724
+ )
725
+ , "size");
726
+ }
727
+
728
+ public onFocus = () => {
729
+ this.setScenePath();
518
730
  }
519
731
 
520
732
  public close(): Promise<void> {
@@ -1,26 +1,56 @@
1
1
  import { putScenes } from "../Utils/Common";
2
2
  import { Val } from "value-enhancer";
3
+ import { pick } from "lodash";
3
4
 
4
5
  import type { ReadonlyVal } from "value-enhancer";
5
6
  import type { AddPageParams, PageController, PageState } from "../Page";
6
7
  import type { AppProxy } from "./AppProxy";
7
8
  import type { AppContext } from "./AppContext";
9
+ import type { View } from "white-web-sdk";
10
+ import type { TeleBoxRect } from "@netless/telebox-insider";
11
+ import type { ICamera } from "../AttributesDelegate";
12
+
13
+ export type WhiteBoardViewCamera = Omit<ICamera, "scale" | "id">;
8
14
 
9
15
  export class WhiteBoardView implements PageController {
10
16
  public readonly pageState$: ReadonlyVal<PageState>;
17
+ public readonly camera$: ReadonlyVal<WhiteBoardViewCamera>;
11
18
 
12
- constructor(protected appContext: AppContext, protected appProxy: AppProxy) {
19
+ constructor(
20
+ public view: View,
21
+ protected appContext: AppContext,
22
+ protected appProxy: AppProxy,
23
+ public ensureSize: (size: number) => void
24
+ ) {
13
25
  const pageState$ = new Val<PageState>(appProxy.pageState);
14
26
  this.pageState$ = pageState$;
15
- appProxy.appEmitter.on("pageStateChange", pageState => {
16
- pageState$.setValue(pageState);
17
- });
27
+ this.appProxy.sideEffectManager.add(() =>
28
+ appProxy.appEmitter.on("pageStateChange", pageState => {
29
+ pageState$.setValue(pageState);
30
+ })
31
+ );
32
+ const camera$ = new Val<WhiteBoardViewCamera>(
33
+ pick(this.view.camera, ["centerX", "centerY"])
34
+ );
35
+ this.camera$ = camera$;
36
+ this.appProxy.sideEffectManager.add(() =>
37
+ appProxy.camera$.subscribe(camera => {
38
+ if (camera) {
39
+ camera$.setValue(pick(camera, ["centerX", "centerY"]));
40
+ }
41
+ })
42
+ );
43
+ view.disableCameraTransform = true;
18
44
  }
19
45
 
20
46
  public get pageState() {
21
47
  return this.pageState$.value;
22
48
  }
23
49
 
50
+ public moveCamera(camera: Partial<WhiteBoardViewCamera>) {
51
+ this.appProxy.moveCamera(camera);
52
+ }
53
+
24
54
  public nextPage = async (): Promise<boolean> => {
25
55
  const nextIndex = this.pageState.index + 1;
26
56
  return this.jumpPage(nextIndex);
@@ -65,4 +95,8 @@ export class WhiteBoardView implements PageController {
65
95
  }
66
96
  return this.appProxy.removeSceneByIndex(needRemoveIndex);
67
97
  };
98
+
99
+ public setRect(rect: Omit<TeleBoxRect, "x" | "y">) {
100
+ this.appProxy.updateSize(rect.width, rect.height);
101
+ }
68
102
  }
package/src/App/index.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export * from "./AppProxy";
2
2
  export * from "./AppContext";
3
- export * from "./WhiteBoardView";
3
+ export * from "./WhiteboardView";