@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
@@ -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,11 +25,15 @@ 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
- import { isNumber } from "lodash";
31
+ import { isBoolean, isNumber } from "lodash";
32
+
33
+ export type CreateWhiteBoardViewParams = {
34
+ size?: number;
35
+ syncCamera?: boolean;
36
+ }
34
37
 
35
38
  export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOptions = any> {
36
39
  public readonly emitter: Emittery<AppEmitterEvent<TAttributes>>;
@@ -49,11 +52,11 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
49
52
  private store = this.manager.store;
50
53
  public readonly isAddApp: boolean;
51
54
  public readonly isReplay = this.manager.isReplay;
52
- private whiteBoardView?: WhiteBoardView;
55
+ public whiteBoardView?: WhiteBoardView;
56
+ public _viewWrapper?: HTMLElement;
53
57
 
54
58
  constructor(
55
59
  private manager: AppManager,
56
- private boxManager: BoxManager,
57
60
  public appId: string,
58
61
  private appProxy: AppProxy,
59
62
  private appOptions?: TAppOptions | (() => TAppOptions)
@@ -62,9 +65,13 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
62
65
  this.isAddApp = appProxy.isAddApp;
63
66
  }
64
67
 
65
- public get displayer(){
68
+ public get displayer() {
66
69
  return this.manager.displayer;
67
- };
70
+ }
71
+
72
+ public get destroyed() {
73
+ return this.appProxy.status === "destroyed";
74
+ }
68
75
 
69
76
  /** @deprecated Use context.storage.state instead. */
70
77
  public getAttributes = (): TAttributes | undefined => {
@@ -84,7 +91,7 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
84
91
  return this.appProxy.view;
85
92
  };
86
93
 
87
- public createWhiteBoardView = (size?: number): WhiteBoardView => {
94
+ public createWhiteBoardView = (params?: CreateWhiteBoardViewParams): WhiteBoardView => {
88
95
  if (this.whiteBoardView) {
89
96
  return this.whiteBoardView;
90
97
  }
@@ -92,13 +99,31 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
92
99
  if (!view) {
93
100
  view = this.appProxy.createAppDir();
94
101
  }
95
- view.divElement = this.box.$content as HTMLDivElement;
96
- this.initPageSize(size);
97
- this.whiteBoardView = new WhiteBoardView(this, this.appProxy);
102
+ if (params) {
103
+ if (isBoolean(params.syncCamera)) {
104
+ this.appProxy.syncCamera$.setValue(params.syncCamera);
105
+ }
106
+ }
107
+ const viewWrapper = document.createElement("div");
108
+ this._viewWrapper = viewWrapper;
109
+ viewWrapper.className = "window-manager-view-wrapper";
110
+ this.box.$content.parentElement?.appendChild(viewWrapper);
111
+ view.divElement = viewWrapper;
112
+ this.appProxy.fireMemberStateChange();
113
+ if (this.isAddApp) {
114
+ this.ensurePageSize(params?.size);
115
+ }
116
+ this.whiteBoardView = new WhiteBoardView(view, this, this.appProxy, this.ensurePageSize);
117
+ this.appProxy.sideEffectManager.add(() => {
118
+ return () => {
119
+ this.whiteBoardView = undefined;
120
+ }
121
+ });
122
+ this.appProxy.whiteBoardViewCreated$.setValue(true);
98
123
  return this.whiteBoardView;
99
124
  }
100
125
 
101
- private initPageSize = (size?: number) => {
126
+ private ensurePageSize = (size?: number) => {
102
127
  if (!isNumber(size)) return;
103
128
  if (!this.appProxy.scenePath) return;
104
129
  if (this.appProxy.pageState.length >= size) return;
@@ -106,25 +131,22 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
106
131
  throw Error(`[WindowManager]: size ${size} muse be in range [1, ${MAX_PAGE_SIZE}]`);
107
132
  }
108
133
  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);
134
+ const scenes = new Array(needInsert).fill({});
135
+ this.room?.putScenes(this.appProxy.scenePath, scenes);
114
136
  }
115
137
 
116
138
  public getInitScenePath = () => {
117
- return this.manager.getAppInitPath(this.appId);
139
+ return this.appProxy.scenePath;
118
140
  };
119
141
 
120
142
  /** Get App writable status. */
121
143
  public get isWritable(): boolean {
122
- return this.manager.canOperate;
144
+ return this.manager.canOperate && !this.destroyed;
123
145
  };
124
146
 
125
147
  /** Get the App Window UI box. */
126
148
  public get box(): ReadonlyTeleBox {
127
- const box = this.boxManager.getBox(this.appId);
149
+ const box = this.appProxy.box$.value;
128
150
  if (box) {
129
151
  return box;
130
152
  } 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, isUndefined, omitBy } 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,
@@ -19,19 +25,16 @@ import {
19
25
  } from "../Utils/Common";
20
26
  import type {
21
27
  AppEmitterEvent,
22
- AppInitState,
23
28
  BaseInsertParams,
24
29
  setAppOptions,
25
30
  AppListenerKeys,
26
31
  } from "../index";
27
- import type { SceneState, View, SceneDefinition } from "white-web-sdk";
32
+ import type { SceneState, View, SceneDefinition, MemberState} from "white-web-sdk";
28
33
  import type { AppManager } from "../AppManager";
29
34
  import type { NetlessApp } from "../typings";
30
- import type { ReadonlyTeleBox } from "@netless/telebox-insider";
35
+ import type { ReadonlyTeleBox, TeleBoxRect } from "@netless/telebox-insider";
31
36
  import type { PageRemoveService, PageState } from "../Page";
32
- import { calculateNextIndex } from "../Page";
33
- import { boxEmitter } from "../BoxEmitter";
34
- import { SideEffectManager } from "side-effect-manager";
37
+ import type { AppState } from "./type";
35
38
 
36
39
  export type AppEmitter = Emittery<AppEmitterEvent>;
37
40
 
@@ -48,17 +51,28 @@ export class AppProxy implements PageRemoveService {
48
51
  private appProxies = this.manager.appProxies;
49
52
  private viewManager = this.manager.viewManager;
50
53
  private store = this.manager.store;
54
+ public uid = this.manager.uid;
51
55
 
52
56
  public isAddApp: boolean;
53
- private status: "normal" | "destroyed" = "normal";
57
+ public status: "normal" | "destroyed" = "normal";
54
58
  private stateKey: string;
55
59
  public _pageState: AppPageStateImpl;
56
- private _prevFullPath: string | undefined;
57
60
 
58
- public appResult?: NetlessApp<any>;
59
- public appContext?: AppContext<any, any>;
61
+ public appResult?: NetlessApp;
62
+ public appContext?: AppContext;
63
+
64
+ public sideEffectManager = new SideEffectManager();
65
+ private valManager = new ValManager();
60
66
 
61
- private sideEffectManager = new SideEffectManager();
67
+ private fullPath$ = this.valManager.attach(new Val<string | undefined>(undefined));
68
+ private viewSync?: ViewSync;
69
+
70
+ public camera$ = this.valManager.attach(new Val<ICamera | undefined>(undefined));
71
+ public size$ = this.valManager.attach(new Val<ISize | undefined>(undefined));
72
+ public box$ = this.valManager.attach(new Val<ReadonlyTeleBox | undefined>(undefined));
73
+ public view$ = this.valManager.attach(new Val<View | undefined>(undefined));
74
+ public syncCamera$ = this.valManager.attach(new Val<boolean>(true));
75
+ public whiteBoardViewCreated$ = this.valManager.attach(new Val<boolean>(false));
62
76
 
63
77
  constructor(
64
78
  private params: BaseInsertParams,
@@ -90,14 +104,110 @@ export class AppProxy implements PageRemoveService {
90
104
  view: this.view,
91
105
  notifyPageStateChange: this.notifyPageStateChange,
92
106
  });
93
- this.sideEffectManager.add(() => {
94
- return () => this._pageState.destroy();
95
- });
96
- this.sideEffectManager.add(() => {
97
- return emitter.on("roomMembersChange", members => {
107
+ this.sideEffectManager.add(() => () => this._pageState.destroy());
108
+ this.sideEffectManager.add(() =>
109
+ emitter.on("roomMembersChange", members => {
98
110
  this.appEmitter.emit("roomMembersChange", members);
99
- });
100
- });
111
+ })
112
+ );
113
+ this.camera$.setValue(toJS(this.appAttributes.camera));
114
+ this.size$.setValue(toJS(this.appAttributes.size));
115
+ this.addCameraReaction();
116
+ this.addSizeReaction();
117
+ this.sideEffectManager.add(() =>
118
+ emitter.on("memberStateChange", this.onMemberStateChange)
119
+ );
120
+ this.sideEffectManager.add(() => [
121
+ this.syncCamera$.reaction(syncCamera => {
122
+ if (!syncCamera) {
123
+ if (this.viewSync) {
124
+ this.viewSync.destroy();
125
+ this.viewSync = undefined;
126
+ this.sideEffectManager.flush("camera");
127
+ this.sideEffectManager.flush("size");
128
+ }
129
+ }
130
+ }),
131
+ this.whiteBoardViewCreated$.reaction(created => {
132
+ if (created && this.box) {
133
+ if (!this.syncCamera$.value) return;
134
+ combine([this.box$, this.view$]).subscribe(([box, view]) => {
135
+ if (box && view) {
136
+ if (!this.camera$.value) {
137
+ this.storeCamera({
138
+ centerX: null,
139
+ centerY: null,
140
+ scale: 1,
141
+ id: this.uid,
142
+ });
143
+ this.camera$.setValue(toJS(this.appAttributes.camera));
144
+ }
145
+ if (!this.size$.value && box.contentStageRect) {
146
+ const initialRect = this.computedInitialRect(box.contentStageRect);
147
+ const width = initialRect?.width || box.contentStageRect.width;
148
+ const height = initialRect?.height || box.contentStageRect.height;
149
+ this.storeSize({
150
+ id: this.uid,
151
+ width,
152
+ height,
153
+ });
154
+ this.size$.setValue(toJS(this.appAttributes.size));
155
+ }
156
+ this.viewSync = new ViewSync({
157
+ uid: this.uid,
158
+ view$: this.view$,
159
+ camera$: this.camera$,
160
+ size$: this.size$,
161
+ stageRect$: box._contentStageRect$,
162
+ storeCamera: this.storeCamera,
163
+ storeSize: this.storeSize
164
+ });
165
+ this.sideEffectManager.add(() => () => this.viewSync?.destroy());
166
+ this.whiteBoardViewCreated$.destroy();
167
+ }
168
+ })
169
+ }
170
+ })
171
+ ]);
172
+ }
173
+
174
+ public fireMemberStateChange = () => {
175
+ if (this.manager.room) {
176
+ this.onMemberStateChange(this.manager.room.state.memberState);
177
+ }
178
+ }
179
+
180
+ private onMemberStateChange = (memberState: MemberState) => {
181
+ // clicker 教具把事件穿透给下层
182
+ const needPointerEventsNone = memberState.currentApplianceName === "clicker";
183
+ if (needPointerEventsNone) {
184
+ if (this.appContext?._viewWrapper) {
185
+ this.appContext._viewWrapper.style.pointerEvents = "none";
186
+ }
187
+ } else {
188
+ if (this.appContext?._viewWrapper) {
189
+ this.appContext._viewWrapper.style.pointerEvents = "auto";
190
+ }
191
+ }
192
+ }
193
+
194
+ private computedInitialRect = (boxRect: TeleBoxRect) => {
195
+ const managerRect = this.manager.boxManager?.stageRect;
196
+ if (managerRect) {
197
+ const { width, height } = managerRect;
198
+ const boxRatio = boxRect.height / boxRect.width;
199
+ if (height < 480) {
200
+ return {
201
+ width: 480 / boxRatio,
202
+ height: 480,
203
+ };
204
+ } else {
205
+ return {
206
+ width: width * 0.65,
207
+ height: height * 0.65,
208
+ };
209
+ }
210
+ }
101
211
  }
102
212
 
103
213
  public createAppDir() {
@@ -127,7 +237,7 @@ export class AppProxy implements PageRemoveService {
127
237
  }
128
238
 
129
239
  public get view(): View | undefined {
130
- return this.manager.viewManager.getView(this.id);
240
+ return this.view$.value;
131
241
  }
132
242
 
133
243
  public get viewIndex(): number | undefined {
@@ -162,7 +272,7 @@ export class AppProxy implements PageRemoveService {
162
272
  }
163
273
 
164
274
  public setFullPath(path: string) {
165
- this.manager.safeUpdateAttributes(["apps", this.id, Fields.FullPath], path);
275
+ this.store.updateAppAttributes(this.id, Fields.FullPath, path);
166
276
  }
167
277
 
168
278
  public async baseInsertApp(skipUpdate = false): Promise<{ appId: string; app: NetlessApp }> {
@@ -191,7 +301,7 @@ export class AppProxy implements PageRemoveService {
191
301
  }
192
302
 
193
303
  public get box(): ReadonlyTeleBox | undefined {
194
- return this.boxManager?.getBox(this.id);
304
+ return this.box$.value;
195
305
  }
196
306
 
197
307
  private async setupApp(
@@ -205,11 +315,11 @@ export class AppProxy implements PageRemoveService {
205
315
  if (!this.boxManager) {
206
316
  throw new BoxManagerNotFoundError();
207
317
  }
208
- const context = new AppContext(this.manager, this.boxManager, appId, this, appOptions);
318
+ const context = new AppContext(this.manager, appId, this, appOptions);
209
319
  this.appContext = context;
210
320
  try {
211
321
  emitter.once(`${appId}${Events.WindowCreated}` as any).then(async () => {
212
- let boxInitState: AppInitState | undefined;
322
+ let boxInitState: AppState | undefined;
213
323
  if (!skipUpdate) {
214
324
  boxInitState = this.getAppInitState(appId);
215
325
  this.boxManager?.updateBoxState(boxInitState);
@@ -225,13 +335,14 @@ export class AppProxy implements PageRemoveService {
225
335
  this.fixMobileSize();
226
336
  }, SETUP_APP_DELAY);
227
337
  });
228
- this.boxManager?.createBox({
338
+ const box = this.boxManager?.createBox({
229
339
  appId: appId,
230
340
  app,
231
341
  options,
232
342
  canOperate: this.manager.canOperate,
233
343
  smartPosition: this.isAddApp,
234
344
  });
345
+ this.box$.setValue(box);
235
346
  if (this.isAddApp && this.box) {
236
347
  this.store.updateAppState(appId, AppAttributes.ZIndex, this.box.zIndex);
237
348
  this.boxManager.focusBox({ appId }, false);
@@ -246,12 +357,14 @@ export class AppProxy implements PageRemoveService {
246
357
  private fixMobileSize() {
247
358
  const box = this.boxManager?.getBox(this.id);
248
359
  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
- });
360
+ if (!box.minimized) {
361
+ this.boxManager?.resizeBox({
362
+ appId: this.id,
363
+ width: box.intrinsicWidth + 0.001,
364
+ height: box.intrinsicHeight + 0.001,
365
+ skipUpdate: true,
366
+ });
367
+ }
255
368
  }
256
369
  }
257
370
 
@@ -296,30 +409,18 @@ export class AppProxy implements PageRemoveService {
296
409
  }
297
410
  }
298
411
 
299
- public getAppInitState = (id: string) => {
412
+ public getAppInitState = (id: string): AppState | undefined => {
300
413
  const attrs = this.store.getAppState(id);
301
414
  if (!attrs) return;
302
- const position = attrs?.[AppAttributes.Position];
303
415
  const focus = this.store.focus;
304
- const size = attrs?.[AppAttributes.Size];
305
- const sceneIndex = attrs?.[AppAttributes.SceneIndex];
306
416
  const maximized = this.attributes?.["maximized"];
307
417
  const minimized = this.attributes?.["minimized"];
308
- const zIndex = attrs?.zIndex;
309
- let payload = { maximized, minimized, zIndex } as AppInitState;
310
- if (position) {
311
- payload = { ...payload, id: id, x: position.x, y: position.y };
312
- }
418
+ let payload = { maximized, minimized, id } as AppState;
419
+ const state = omitBy(attrs, isUndefined);
313
420
  if (focus === id) {
314
421
  payload = { ...payload, focus: true };
315
422
  }
316
- if (size) {
317
- payload = { ...payload, width: size.width, height: size.height };
318
- }
319
- if (sceneIndex) {
320
- payload = { ...payload, sceneIndex };
321
- }
322
- return payload;
423
+ return Object.assign(payload, state);;
323
424
  };
324
425
 
325
426
  public emitAppSceneStateChange(sceneState: SceneState) {
@@ -376,32 +477,34 @@ export class AppProxy implements PageRemoveService {
376
477
  }
377
478
 
378
479
  private appAttributesUpdateListener = (appId: string) => {
379
- this.manager.refresher?.add(appId, () => {
380
- return autorun(() => {
381
- const attrs = this.manager.attributes[appId];
382
- if (attrs) {
383
- this.appEmitter.emit("attributesUpdate", attrs);
384
- }
385
- });
386
- });
387
- this.manager.refresher?.add(this.stateKey, () => {
388
- return autorun(() => {
389
- const appState = this.appAttributes?.state;
390
- if (appState?.zIndex > 0 && appState.zIndex !== this.box?.zIndex) {
391
- this.boxManager?.setZIndex(appId, appState.zIndex);
392
- }
393
- });
394
- });
395
- this.manager.refresher?.add(`${appId}-fullPath`, () => {
396
- return autorun(() => {
397
- const fullPath = this.appAttributes?.fullPath;
398
- this.setFocusScenePathHandler(fullPath);
399
- if (this._prevFullPath !== fullPath) {
400
- this.notifyPageStateChange();
401
- this._prevFullPath = fullPath;
402
- }
403
- });
404
- });
480
+ this.sideEffectManager.add(() => [
481
+ this.manager.refresher.add(appId, () => {
482
+ return autorun(() => {
483
+ const attrs = this.manager.attributes[appId];
484
+ if (attrs) {
485
+ this.appEmitter.emit("attributesUpdate", attrs);
486
+ }
487
+ });
488
+ }),
489
+ this.manager.refresher.add(this.stateKey, () => {
490
+ return autorun(() => {
491
+ const appState = this.appAttributes?.state;
492
+ if (appState?.zIndex > 0 && appState.zIndex !== this.box?.zIndex) {
493
+ this.boxManager?.setZIndex(appId, appState.zIndex);
494
+ }
495
+ });
496
+ }),
497
+ this.manager.refresher.add(`${appId}-fullPath`, () => {
498
+ return autorun(() => {
499
+ const fullPath = this.appAttributes?.fullPath;
500
+ this.setFocusScenePathHandler(fullPath);
501
+ if (this.fullPath$.value !== fullPath) {
502
+ this.notifyPageStateChange();
503
+ this.fullPath$.setValue(fullPath);
504
+ }
505
+ });
506
+ }),
507
+ ]);
405
508
  };
406
509
 
407
510
  private setFocusScenePathHandler = debounce((fullPath: string | undefined) => {
@@ -428,6 +531,7 @@ export class AppProxy implements PageRemoveService {
428
531
 
429
532
  private createView(): View {
430
533
  const view = this.viewManager.createView(this.id);
534
+ this.view$.setValue(view);
431
535
  this.setViewFocusScenePath();
432
536
  return view;
433
537
  }
@@ -477,10 +581,37 @@ export class AppProxy implements PageRemoveService {
477
581
  const fullPath = this._pageState.getFullPath(index);
478
582
  if (fullPath) {
479
583
  this.setFullPath(fullPath);
584
+ setScenePath(this.manager.room, fullPath);
480
585
  }
481
586
  }
482
587
  }
483
588
 
589
+ public storeCamera = (camera: ICamera) => {
590
+ this.store.updateAppAttributes(this.id, Fields.Camera, camera);
591
+ };
592
+
593
+ public storeSize = (size: ISize) => {
594
+ this.store.updateAppAttributes(this.id, Fields.Size, size);
595
+ };
596
+
597
+ public updateSize = (width: number, height: number) => {
598
+ const iSize = {
599
+ id: this.manager.uid,
600
+ width, height
601
+ }
602
+ this.store.updateAppAttributes(this.id, Fields.Size, iSize);
603
+ this.size$.setValue(iSize);
604
+ }
605
+
606
+ public moveCamera = (camera: Partial<ICamera>) => {
607
+ if (!this.camera$.value) {
608
+ return;
609
+ }
610
+ const nextCamera = { ...this.camera$.value, ...camera, id: this.uid };
611
+ this.storeCamera(nextCamera);
612
+ this.camera$.setValue(nextCamera);
613
+ };
614
+
484
615
  public async destroy(
485
616
  needCloseBox: boolean,
486
617
  cleanAttrs: boolean,
@@ -496,6 +627,7 @@ export class AppProxy implements PageRemoveService {
496
627
  console.error("[WindowManager]: notifyApp error", error.message, error.stack);
497
628
  }
498
629
  this.appEmitter.clearListeners();
630
+ this.sideEffectManager.flushAll();
499
631
  emitter.emit(`destroy-${this.id}` as any, { error });
500
632
  if (needCloseBox) {
501
633
  this.boxManager?.closeBox(this.id, skipUpdate);
@@ -510,11 +642,47 @@ export class AppProxy implements PageRemoveService {
510
642
 
511
643
  this.viewManager.destroyView(this.id);
512
644
  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();
645
+ this.valManager.destroy();
646
+ }
647
+
648
+ private addCameraReaction = () => {
649
+ this.sideEffectManager.add(() =>
650
+ this.manager.refresher.add(`${this.id}-camera`, () =>
651
+ reaction(
652
+ () => this.appAttributes?.camera,
653
+ camera => {
654
+ if (camera) {
655
+ const rawCamera = toJS(camera);
656
+ if (!isEqual(rawCamera, this.camera$.value)) {
657
+ this.camera$.setValue(rawCamera);
658
+ }
659
+ }
660
+ }
661
+ )
662
+ )
663
+ , "camera");
664
+ }
665
+
666
+ private addSizeReaction = () => {
667
+ this.sideEffectManager.add(() =>
668
+ this.manager.refresher.add(`${this.id}-size`, () =>
669
+ reaction(
670
+ () => this.appAttributes?.size,
671
+ size => {
672
+ if (size) {
673
+ const rawSize = toJS(size);
674
+ if (!isEqual(rawSize, this.size$.value)) {
675
+ this.size$.setValue(rawSize);
676
+ }
677
+ }
678
+ }
679
+ )
680
+ )
681
+ , "size");
682
+ }
683
+
684
+ public onFocus = () => {
685
+ this.setScenePath();
518
686
  }
519
687
 
520
688
  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,4 @@
1
1
  export * from "./AppProxy";
2
2
  export * from "./AppContext";
3
- export * from "./WhiteBoardView";
3
+ export * from "./WhiteboardView";
4
+ export * from "./type";