@netless/window-manager 1.0.0-canary.3 → 1.0.0-canary.30

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 (87) hide show
  1. package/__mocks__/white-web-sdk.ts +10 -1
  2. package/dist/index.cjs.js +120 -12
  3. package/dist/index.es.js +2243 -950
  4. package/dist/index.umd.js +120 -12
  5. package/dist/{App → src/App}/AppContext.d.ts +12 -8
  6. package/dist/{App → src/App}/AppPageStateImpl.d.ts +0 -0
  7. package/dist/{App → src/App}/AppProxy.d.ts +36 -7
  8. package/dist/{App → src/App}/MagixEvent/index.d.ts +0 -0
  9. package/dist/{App → src/App}/Storage/StorageEvent.d.ts +0 -0
  10. package/dist/{App → src/App}/Storage/index.d.ts +0 -0
  11. package/dist/{App → src/App}/Storage/typings.d.ts +0 -0
  12. package/dist/{App → src/App}/Storage/utils.d.ts +0 -0
  13. package/dist/src/App/WhiteboardView.d.ts +27 -0
  14. package/dist/{App → src/App}/index.d.ts +2 -1
  15. package/dist/src/App/type.d.ts +21 -0
  16. package/dist/{AppListener.d.ts → src/AppListener.d.ts} +0 -2
  17. package/dist/{AppManager.d.ts → src/AppManager.d.ts} +7 -6
  18. package/dist/{AttributesDelegate.d.ts → src/AttributesDelegate.d.ts} +11 -16
  19. package/dist/{BoxEmitter.d.ts → src/BoxEmitter.d.ts} +0 -0
  20. package/dist/{BoxManager.d.ts → src/BoxManager.d.ts} +8 -6
  21. package/dist/{BuiltinApps.d.ts → src/BuiltinApps.d.ts} +3 -0
  22. package/dist/{Cursor → src/Cursor}/Cursor.d.ts +0 -0
  23. package/dist/{Cursor → src/Cursor}/icons.d.ts +0 -0
  24. package/dist/{Cursor → src/Cursor}/index.d.ts +3 -3
  25. package/dist/{Helper.d.ts → src/Helper.d.ts} +4 -8
  26. package/dist/{InternalEmitter.d.ts → src/InternalEmitter.d.ts} +3 -4
  27. package/dist/{Page → src/Page}/PageController.d.ts +1 -0
  28. package/dist/{Page → src/Page}/index.d.ts +0 -0
  29. package/dist/{PageState.d.ts → src/PageState.d.ts} +0 -0
  30. package/dist/{ReconnectRefresher.d.ts → src/ReconnectRefresher.d.ts} +1 -1
  31. package/dist/{RedoUndo.d.ts → src/RedoUndo.d.ts} +0 -0
  32. package/dist/{Register → src/Register}/index.d.ts +0 -0
  33. package/dist/{Register → src/Register}/loader.d.ts +0 -0
  34. package/dist/{Register → src/Register}/storage.d.ts +0 -0
  35. package/dist/{Utils → src/Utils}/AppCreateQueue.d.ts +0 -0
  36. package/dist/{Utils → src/Utils}/Common.d.ts +1 -0
  37. package/dist/{Utils → src/Utils}/Reactive.d.ts +0 -0
  38. package/dist/{Utils → src/Utils}/RoomHacker.d.ts +0 -0
  39. package/dist/{Utils → src/Utils}/error.d.ts +0 -0
  40. package/dist/{Utils → src/Utils}/log.d.ts +0 -0
  41. package/dist/src/View/CameraSynchronizer.d.ts +18 -0
  42. package/dist/{View → src/View}/MainView.d.ts +18 -7
  43. package/dist/{View → src/View}/ViewManager.d.ts +0 -0
  44. package/dist/src/View/ViewSync.d.ts +24 -0
  45. package/dist/{callback.d.ts → src/callback.d.ts} +5 -0
  46. package/dist/{constants.d.ts → src/constants.d.ts} +8 -5
  47. package/dist/src/image.d.ts +19 -0
  48. package/dist/{index.d.ts → src/index.d.ts} +36 -13
  49. package/dist/src/shim.d.ts +11 -0
  50. package/dist/{typings.d.ts → src/typings.d.ts} +6 -0
  51. package/dist/style.css +1 -1
  52. package/docs/app-context.md +157 -25
  53. package/docs/mirgrate-to-1.0.md +28 -0
  54. package/package.json +12 -7
  55. package/playwright.config.ts +28 -0
  56. package/pnpm-lock.yaml +517 -35
  57. package/src/App/AppContext.ts +50 -28
  58. package/src/App/AppProxy.ts +264 -80
  59. package/src/App/{WhiteBoardView.ts → WhiteboardView.ts} +38 -7
  60. package/src/App/index.ts +2 -1
  61. package/src/App/type.ts +22 -0
  62. package/src/AppListener.ts +5 -21
  63. package/src/AppManager.ts +56 -43
  64. package/src/AttributesDelegate.ts +19 -19
  65. package/src/BoxManager.ts +54 -40
  66. package/src/BuiltinApps.ts +5 -0
  67. package/src/Cursor/Cursor.ts +6 -2
  68. package/src/Cursor/index.ts +5 -7
  69. package/src/Helper.ts +25 -7
  70. package/src/InternalEmitter.ts +3 -4
  71. package/src/Page/PageController.ts +1 -0
  72. package/src/PageState.ts +1 -1
  73. package/src/ReconnectRefresher.ts +7 -2
  74. package/src/Utils/Common.ts +9 -0
  75. package/src/Utils/Reactive.ts +27 -26
  76. package/src/Utils/RoomHacker.ts +3 -0
  77. package/src/View/CameraSynchronizer.ts +37 -34
  78. package/src/View/MainView.ts +108 -81
  79. package/src/View/ViewSync.ts +110 -0
  80. package/src/callback.ts +1 -0
  81. package/src/constants.ts +6 -3
  82. package/src/index.ts +126 -56
  83. package/src/style.css +2 -45
  84. package/src/typings.ts +6 -0
  85. package/vite.config.js +5 -3
  86. package/dist/App/WhiteBoardView.d.ts +0 -18
  87. package/dist/View/CameraSynchronizer.d.ts +0 -17
@@ -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,21 +99,39 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
92
99
  if (!view) {
93
100
  view = this.appProxy.createAppDir();
94
101
  }
102
+ if (params) {
103
+ if (isBoolean(params.syncCamera)) {
104
+ this.appProxy.syncCamera$.setValue(params.syncCamera);
105
+ }
106
+ }
95
107
  const viewWrapper = document.createElement("div");
108
+ this._viewWrapper = viewWrapper;
96
109
  viewWrapper.className = "window-manager-view-wrapper";
97
- this.box.$content.parentElement?.appendChild(viewWrapper);
98
- const removeViewWrapper = () => {
99
- this.box.$content.parentElement?.removeChild(viewWrapper);
100
- }
101
- view.divElement = viewWrapper
110
+ this.box.$main.appendChild(viewWrapper);
111
+ view.divElement = viewWrapper;
112
+ this.appProxy.fireMemberStateChange();
102
113
  if (this.isAddApp) {
103
- this.initPageSize(size);
114
+ this.ensurePageSize(params?.size);
104
115
  }
105
- this.whiteBoardView = new WhiteBoardView(this, this.appProxy, removeViewWrapper);
116
+ this.whiteBoardView = new WhiteBoardView(view, this, this.appProxy, this.ensurePageSize);
117
+ this.appProxy.sideEffectManager.add(() => [
118
+ this.box._stageRect$.subscribe(rect => {
119
+ viewWrapper.style.left = `${rect.x}px`;
120
+ viewWrapper.style.top = `${rect.y}px`;
121
+ viewWrapper.style.width = `${rect.width}px`;
122
+ viewWrapper.style.height = `${rect.height}px`;
123
+ }),
124
+ () => {
125
+ return () => {
126
+ this.whiteBoardView = undefined;
127
+ }
128
+ }
129
+ ]);
130
+ this.appProxy.whiteBoardViewCreated$.setValue(true);
106
131
  return this.whiteBoardView;
107
132
  }
108
133
 
109
- private initPageSize = (size?: number) => {
134
+ private ensurePageSize = (size?: number) => {
110
135
  if (!isNumber(size)) return;
111
136
  if (!this.appProxy.scenePath) return;
112
137
  if (this.appProxy.pageState.length >= size) return;
@@ -114,25 +139,22 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
114
139
  throw Error(`[WindowManager]: size ${size} muse be in range [1, ${MAX_PAGE_SIZE}]`);
115
140
  }
116
141
  const needInsert = size - this.appProxy.pageState.length;
117
- const startPageNumber = this.appProxy.pageState.length;
118
- const scenes = new Array(needInsert).fill({}).map((_, index) => {
119
- return { name: `${startPageNumber + index + 1}` };
120
- });
121
- putScenes(this.room, this.appProxy.scenePath, scenes);
142
+ const scenes = new Array(needInsert).fill({});
143
+ this.room?.putScenes(this.appProxy.scenePath, scenes);
122
144
  }
123
145
 
124
146
  public getInitScenePath = () => {
125
- return this.manager.getAppInitPath(this.appId);
147
+ return this.appProxy.scenePath;
126
148
  };
127
149
 
128
150
  /** Get App writable status. */
129
151
  public get isWritable(): boolean {
130
- return this.manager.canOperate;
152
+ return this.manager.canOperate && !this.destroyed;
131
153
  };
132
154
 
133
155
  /** Get the App Window UI box. */
134
156
  public get box(): ReadonlyTeleBox {
135
- const box = this.boxManager.getBox(this.appId);
157
+ const box = this.appProxy.box$.value;
136
158
  if (box) {
137
159
  return box;
138
160
  } else {
@@ -144,11 +166,11 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
144
166
  return this.manager.room;
145
167
  };
146
168
 
147
- public get members() {
148
- return this.manager.members;
169
+ public get members(): Member[] {
170
+ return this.manager.members$.value;
149
171
  }
150
172
 
151
- public get memberState(): Member {
173
+ public get currentMember(): Member {
152
174
  const self = findMemberByUid(this.room, this.manager.uid);
153
175
  if (!self) {
154
176
  throw new Error(`Member ${this.manager.uid} not found.`);
@@ -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,17 @@ 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, TeleBox, 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";
38
+ import { callbacks } from "../callback";
35
39
 
36
40
  export type AppEmitter = Emittery<AppEmitterEvent>;
37
41
 
@@ -48,17 +52,28 @@ 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));
75
+ public syncCamera$ = this.valManager.attach(new Val<boolean>(true));
76
+ public whiteBoardViewCreated$ = this.valManager.attach(new Val<boolean>(false));
62
77
 
63
78
  constructor(
64
79
  private params: BaseInsertParams,
@@ -90,14 +105,108 @@ export class AppProxy implements PageRemoveService {
90
105
  view: this.view,
91
106
  notifyPageStateChange: this.notifyPageStateChange,
92
107
  });
93
- this.sideEffectManager.add(() => {
94
- return () => this._pageState.destroy();
95
- });
96
- this.sideEffectManager.add(() => {
97
- return emitter.on("roomMembersChange", members => {
108
+ this.sideEffectManager.add(() => () => this._pageState.destroy());
109
+ this.camera$.setValue(toJS(this.appAttributes.camera));
110
+ this.size$.setValue(toJS(this.appAttributes.size));
111
+ this.addCameraReaction();
112
+ this.addSizeReaction();
113
+ this.sideEffectManager.add(() =>
114
+ emitter.on("memberStateChange", this.onMemberStateChange)
115
+ );
116
+ this.sideEffectManager.add(() => [
117
+ this.syncCamera$.reaction(syncCamera => {
118
+ if (!syncCamera) {
119
+ if (this.viewSync) {
120
+ this.viewSync.destroy();
121
+ this.viewSync = undefined;
122
+ this.sideEffectManager.flush("camera");
123
+ this.sideEffectManager.flush("size");
124
+ }
125
+ }
126
+ }),
127
+ this.whiteBoardViewCreated$.reaction(created => {
128
+ if (created && this.box) {
129
+ if (!this.syncCamera$.value) return;
130
+ combine([this.box$, this.view$]).subscribe(([box, view]) => {
131
+ if (box && view) {
132
+ if (!this.camera$.value) {
133
+ this.storeCamera({
134
+ centerX: null,
135
+ centerY: null,
136
+ scale: 1,
137
+ id: this.uid,
138
+ });
139
+ this.camera$.setValue(toJS(this.appAttributes.camera));
140
+ }
141
+ if (!this.size$.value && box.stageRect) {
142
+ const initialRect = this.computedInitialRect(box.stageRect);
143
+ const width = initialRect?.width || box.stageRect.width;
144
+ const height = initialRect?.height || box.stageRect.height;
145
+ this.storeSize({
146
+ id: this.uid,
147
+ width,
148
+ height,
149
+ });
150
+ this.size$.setValue(toJS(this.appAttributes.size));
151
+ }
152
+ this.viewSync = new ViewSync({
153
+ uid: this.uid,
154
+ view$: this.view$,
155
+ camera$: this.camera$,
156
+ size$: this.size$,
157
+ stageRect$: box._stageRect$,
158
+ storeCamera: this.storeCamera,
159
+ storeSize: this.storeSize
160
+ });
161
+ this.sideEffectManager.add(() => () => this.viewSync?.destroy());
162
+ this.whiteBoardViewCreated$.destroy();
163
+ }
164
+ })
165
+ }
166
+ }),
167
+ this.manager.members$.reaction(members => {
98
168
  this.appEmitter.emit("roomMembersChange", members);
99
- });
100
- });
169
+ }),
170
+ ]);
171
+ }
172
+
173
+ public fireMemberStateChange = () => {
174
+ if (this.manager.room) {
175
+ this.onMemberStateChange(this.manager.room.state.memberState);
176
+ }
177
+ }
178
+
179
+ private onMemberStateChange = (memberState: MemberState) => {
180
+ // clicker 教具把事件穿透给下层
181
+ const needPointerEventsNone = memberState.currentApplianceName === "clicker";
182
+ if (needPointerEventsNone) {
183
+ if (this.appContext?._viewWrapper) {
184
+ this.appContext._viewWrapper.style.pointerEvents = "none";
185
+ }
186
+ } else {
187
+ if (this.appContext?._viewWrapper) {
188
+ this.appContext._viewWrapper.style.pointerEvents = "auto";
189
+ }
190
+ }
191
+ }
192
+
193
+ private computedInitialRect = (boxRect: TeleBoxRect) => {
194
+ const managerRect = this.manager.boxManager?.stageRect;
195
+ if (managerRect) {
196
+ const { width, height } = managerRect;
197
+ const boxRatio = boxRect.height / boxRect.width;
198
+ if (height < 480) {
199
+ return {
200
+ width: 480 / boxRatio,
201
+ height: 480,
202
+ };
203
+ } else {
204
+ return {
205
+ width: width * 0.65,
206
+ height: height * 0.65,
207
+ };
208
+ }
209
+ }
101
210
  }
102
211
 
103
212
  public createAppDir() {
@@ -127,7 +236,7 @@ export class AppProxy implements PageRemoveService {
127
236
  }
128
237
 
129
238
  public get view(): View | undefined {
130
- return this.manager.viewManager.getView(this.id);
239
+ return this.view$.value;
131
240
  }
132
241
 
133
242
  public get viewIndex(): number | undefined {
@@ -162,7 +271,7 @@ export class AppProxy implements PageRemoveService {
162
271
  }
163
272
 
164
273
  public setFullPath(path: string) {
165
- this.manager.safeUpdateAttributes(["apps", this.id, Fields.FullPath], path);
274
+ this.store.updateAppAttributes(this.id, Fields.FullPath, path);
166
275
  }
167
276
 
168
277
  public async baseInsertApp(skipUpdate = false): Promise<{ appId: string; app: NetlessApp }> {
@@ -191,7 +300,7 @@ export class AppProxy implements PageRemoveService {
191
300
  }
192
301
 
193
302
  public get box(): ReadonlyTeleBox | undefined {
194
- return this.boxManager?.getBox(this.id);
303
+ return this.box$.value;
195
304
  }
196
305
 
197
306
  private async setupApp(
@@ -205,11 +314,11 @@ export class AppProxy implements PageRemoveService {
205
314
  if (!this.boxManager) {
206
315
  throw new BoxManagerNotFoundError();
207
316
  }
208
- const context = new AppContext(this.manager, this.boxManager, appId, this, appOptions);
317
+ const context = new AppContext(this.manager, appId, this, appOptions);
209
318
  this.appContext = context;
210
319
  try {
211
320
  emitter.once(`${appId}${Events.WindowCreated}` as any).then(async () => {
212
- let boxInitState: AppInitState | undefined;
321
+ let boxInitState: AppState | undefined;
213
322
  if (!skipUpdate) {
214
323
  boxInitState = this.getAppInitState(appId);
215
324
  this.boxManager?.updateBoxState(boxInitState);
@@ -223,17 +332,27 @@ export class AppProxy implements PageRemoveService {
223
332
  this.appResult = result;
224
333
  appRegister.notifyApp(this.kind, "created", { appId, result });
225
334
  this.fixMobileSize();
335
+ this.setupDone();
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
+ }) as TeleBox;
345
+ const registerParams = appRegister.registered.get(this.kind);
346
+ if (registerParams?.contentStyles) {
347
+ box?.mountUserStyles(registerParams.contentStyles);
348
+ }
349
+ this.box$.setValue(box);
235
350
  if (this.isAddApp && this.box) {
236
351
  this.store.updateAppState(appId, AppAttributes.ZIndex, this.box.zIndex);
352
+ this.store.updateAppState(appId, AppAttributes.Size, {
353
+ width: this.box.intrinsicWidth,
354
+ height: this.box.intrinsicHeight,
355
+ });
237
356
  this.boxManager.focusBox({ appId }, false);
238
357
  }
239
358
  } catch (error: any) {
@@ -246,12 +365,14 @@ export class AppProxy implements PageRemoveService {
246
365
  private fixMobileSize() {
247
366
  const box = this.boxManager?.getBox(this.id);
248
367
  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
- });
368
+ if (!box.minimized) {
369
+ this.boxManager?.resizeBox({
370
+ appId: this.id,
371
+ width: box.intrinsicWidth + 0.001,
372
+ height: box.intrinsicHeight + 0.001,
373
+ skipUpdate: true,
374
+ });
375
+ }
255
376
  }
256
377
  }
257
378
 
@@ -296,30 +417,18 @@ export class AppProxy implements PageRemoveService {
296
417
  }
297
418
  }
298
419
 
299
- public getAppInitState = (id: string) => {
420
+ public getAppInitState = (id: string): AppState | undefined => {
300
421
  const attrs = this.store.getAppState(id);
301
422
  if (!attrs) return;
302
- const position = attrs?.[AppAttributes.Position];
303
423
  const focus = this.store.focus;
304
- const size = attrs?.[AppAttributes.Size];
305
- const sceneIndex = attrs?.[AppAttributes.SceneIndex];
306
424
  const maximized = this.attributes?.["maximized"];
307
425
  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
- }
426
+ let payload = { maximized, minimized, id } as AppState;
427
+ const state = omitBy(attrs, isUndefined);
313
428
  if (focus === id) {
314
429
  payload = { ...payload, focus: true };
315
430
  }
316
- if (size) {
317
- payload = { ...payload, width: size.width, height: size.height };
318
- }
319
- if (sceneIndex) {
320
- payload = { ...payload, sceneIndex };
321
- }
322
- return payload;
431
+ return Object.assign(payload, state);;
323
432
  };
324
433
 
325
434
  public emitAppSceneStateChange(sceneState: SceneState) {
@@ -376,32 +485,34 @@ export class AppProxy implements PageRemoveService {
376
485
  }
377
486
 
378
487
  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
- });
488
+ this.sideEffectManager.add(() => [
489
+ this.manager.refresher.add(appId, () => {
490
+ return autorun(() => {
491
+ const attrs = this.manager.attributes[appId];
492
+ if (attrs) {
493
+ this.appEmitter.emit("attributesUpdate", attrs);
494
+ }
495
+ });
496
+ }),
497
+ this.manager.refresher.add(this.stateKey, () => {
498
+ return autorun(() => {
499
+ const appState = this.appAttributes?.state;
500
+ if (appState?.zIndex > 0 && appState.zIndex !== this.box?.zIndex) {
501
+ this.boxManager?.setZIndex(appId, appState.zIndex);
502
+ }
503
+ });
504
+ }),
505
+ this.manager.refresher.add(`${appId}-fullPath`, () => {
506
+ return autorun(() => {
507
+ const fullPath = this.appAttributes?.fullPath;
508
+ this.setFocusScenePathHandler(fullPath);
509
+ if (this.fullPath$.value !== fullPath) {
510
+ this.notifyPageStateChange();
511
+ this.fullPath$.setValue(fullPath);
512
+ }
513
+ });
514
+ }),
515
+ ]);
405
516
  };
406
517
 
407
518
  private setFocusScenePathHandler = debounce((fullPath: string | undefined) => {
@@ -419,6 +530,7 @@ export class AppProxy implements PageRemoveService {
419
530
  }
420
531
 
421
532
  public setViewFocusScenePath() {
533
+ if (this.status === "destroyed") return;
422
534
  const fullPath = this.getFullScenePath();
423
535
  if (fullPath && this.view) {
424
536
  setViewFocusScenePath(this.view, fullPath);
@@ -428,6 +540,7 @@ export class AppProxy implements PageRemoveService {
428
540
 
429
541
  private createView(): View {
430
542
  const view = this.viewManager.createView(this.id);
543
+ this.view$.setValue(view);
431
544
  this.setViewFocusScenePath();
432
545
  return view;
433
546
  }
@@ -477,10 +590,37 @@ export class AppProxy implements PageRemoveService {
477
590
  const fullPath = this._pageState.getFullPath(index);
478
591
  if (fullPath) {
479
592
  this.setFullPath(fullPath);
593
+ setScenePath(this.manager.room, fullPath);
480
594
  }
481
595
  }
482
596
  }
483
597
 
598
+ public storeCamera = (camera: ICamera) => {
599
+ this.store.updateAppAttributes(this.id, Fields.Camera, camera);
600
+ };
601
+
602
+ public storeSize = (size: ISize) => {
603
+ this.store.updateAppAttributes(this.id, Fields.Size, size);
604
+ };
605
+
606
+ public updateSize = (width: number, height: number) => {
607
+ const iSize = {
608
+ id: this.manager.uid,
609
+ width, height
610
+ }
611
+ this.store.updateAppAttributes(this.id, Fields.Size, iSize);
612
+ this.size$.setValue(iSize);
613
+ }
614
+
615
+ public moveCamera = (camera: Partial<ICamera>) => {
616
+ if (!this.camera$.value) {
617
+ return;
618
+ }
619
+ const nextCamera = { ...this.camera$.value, ...camera, id: this.uid };
620
+ this.storeCamera(nextCamera);
621
+ this.camera$.setValue(nextCamera);
622
+ };
623
+
484
624
  public async destroy(
485
625
  needCloseBox: boolean,
486
626
  cleanAttrs: boolean,
@@ -491,11 +631,13 @@ export class AppProxy implements PageRemoveService {
491
631
  this.status = "destroyed";
492
632
  try {
493
633
  await appRegister.notifyApp(this.kind, "destroy", { appId: this.id });
634
+ callbacks.emit("appClose", { appId: this.id, kind: this.kind, error });
494
635
  await this.appEmitter.emit("destroy", { error });
495
636
  } catch (error) {
496
637
  console.error("[WindowManager]: notifyApp error", error.message, error.stack);
497
638
  }
498
639
  this.appEmitter.clearListeners();
640
+ this.sideEffectManager.flushAll();
499
641
  emitter.emit(`destroy-${this.id}` as any, { error });
500
642
  if (needCloseBox) {
501
643
  this.boxManager?.closeBox(this.id, skipUpdate);
@@ -510,11 +652,53 @@ export class AppProxy implements PageRemoveService {
510
652
 
511
653
  this.viewManager.destroyView(this.id);
512
654
  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();
655
+ this.valManager.destroy();
656
+ }
657
+
658
+ private addCameraReaction = () => {
659
+ this.sideEffectManager.add(() =>
660
+ this.manager.refresher.add(`${this.id}-camera`, () =>
661
+ reaction(
662
+ () => this.appAttributes?.camera,
663
+ camera => {
664
+ if (camera) {
665
+ const rawCamera = toJS(camera);
666
+ if (!isEqual(rawCamera, this.camera$.value)) {
667
+ this.camera$.setValue(rawCamera);
668
+ }
669
+ }
670
+ }
671
+ )
672
+ )
673
+ , "camera");
674
+ }
675
+
676
+ private addSizeReaction = () => {
677
+ this.sideEffectManager.add(() =>
678
+ this.manager.refresher.add(`${this.id}-size`, () =>
679
+ reaction(
680
+ () => this.appAttributes?.size,
681
+ size => {
682
+ if (size) {
683
+ const rawSize = toJS(size);
684
+ if (!isEqual(rawSize, this.size$.value)) {
685
+ this.size$.setValue(rawSize);
686
+ }
687
+ }
688
+ }
689
+ )
690
+ )
691
+ , "size");
692
+ }
693
+
694
+ public onFocus = () => {
695
+ this.setScenePath();
696
+ }
697
+
698
+ // 异步值设置完成通知其他端创建 app
699
+ private setupDone = () => {
700
+ this.store.updateAppAttributes(this.id, "setup", true);
701
+ this.manager.dispatchInternalEvent(Events.InvokeAttributesUpdateCallback);
518
702
  }
519
703
 
520
704
  public close(): Promise<void> {