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

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 +2284 -954
  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} +10 -7
  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} +40 -14
  49. package/dist/src/shim.d.ts +11 -0
  50. package/dist/{typings.d.ts → src/typings.d.ts} +8 -2
  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 +29 -0
  56. package/pnpm-lock.yaml +517 -35
  57. package/src/App/AppContext.ts +50 -28
  58. package/src/App/AppProxy.ts +266 -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 +60 -40
  66. package/src/BuiltinApps.ts +5 -0
  67. package/src/Cursor/Cursor.ts +7 -3
  68. package/src/Cursor/index.ts +7 -8
  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 +141 -57
  83. package/src/style.css +3 -46
  84. package/src/typings.ts +8 -2
  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,29 @@ export class AppProxy implements PageRemoveService {
223
332
  this.appResult = result;
224
333
  appRegister.notifyApp(this.kind, "created", { appId, result });
225
334
  this.fixMobileSize();
335
+ if (this.isAddApp) {
336
+ this.setupDone();
337
+ }
226
338
  }, SETUP_APP_DELAY);
227
339
  });
228
- this.boxManager?.createBox({
340
+ const box = this.boxManager?.createBox({
229
341
  appId: appId,
230
342
  app,
231
343
  options,
232
344
  canOperate: this.manager.canOperate,
233
345
  smartPosition: this.isAddApp,
234
- });
346
+ }) as TeleBox;
347
+ const registerParams = appRegister.registered.get(this.kind);
348
+ if (registerParams?.contentStyles) {
349
+ box?.mountUserStyles(registerParams.contentStyles);
350
+ }
351
+ this.box$.setValue(box);
235
352
  if (this.isAddApp && this.box) {
236
353
  this.store.updateAppState(appId, AppAttributes.ZIndex, this.box.zIndex);
354
+ this.store.updateAppState(appId, AppAttributes.Size, {
355
+ width: this.box.intrinsicWidth,
356
+ height: this.box.intrinsicHeight,
357
+ });
237
358
  this.boxManager.focusBox({ appId }, false);
238
359
  }
239
360
  } catch (error: any) {
@@ -246,12 +367,14 @@ export class AppProxy implements PageRemoveService {
246
367
  private fixMobileSize() {
247
368
  const box = this.boxManager?.getBox(this.id);
248
369
  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
- });
370
+ if (!box.minimized) {
371
+ this.boxManager?.resizeBox({
372
+ appId: this.id,
373
+ width: box.intrinsicWidth + 0.001,
374
+ height: box.intrinsicHeight + 0.001,
375
+ skipUpdate: true,
376
+ });
377
+ }
255
378
  }
256
379
  }
257
380
 
@@ -296,30 +419,18 @@ export class AppProxy implements PageRemoveService {
296
419
  }
297
420
  }
298
421
 
299
- public getAppInitState = (id: string) => {
422
+ public getAppInitState = (id: string): AppState | undefined => {
300
423
  const attrs = this.store.getAppState(id);
301
424
  if (!attrs) return;
302
- const position = attrs?.[AppAttributes.Position];
303
425
  const focus = this.store.focus;
304
- const size = attrs?.[AppAttributes.Size];
305
- const sceneIndex = attrs?.[AppAttributes.SceneIndex];
306
426
  const maximized = this.attributes?.["maximized"];
307
427
  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
- }
428
+ let payload = { maximized, minimized, id } as AppState;
429
+ const state = omitBy(attrs, isUndefined);
313
430
  if (focus === id) {
314
431
  payload = { ...payload, focus: true };
315
432
  }
316
- if (size) {
317
- payload = { ...payload, width: size.width, height: size.height };
318
- }
319
- if (sceneIndex) {
320
- payload = { ...payload, sceneIndex };
321
- }
322
- return payload;
433
+ return Object.assign(payload, state);;
323
434
  };
324
435
 
325
436
  public emitAppSceneStateChange(sceneState: SceneState) {
@@ -376,32 +487,34 @@ export class AppProxy implements PageRemoveService {
376
487
  }
377
488
 
378
489
  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
- });
490
+ this.sideEffectManager.add(() => [
491
+ this.manager.refresher.add(appId, () => {
492
+ return autorun(() => {
493
+ const attrs = this.manager.attributes[appId];
494
+ if (attrs) {
495
+ this.appEmitter.emit("attributesUpdate", attrs);
496
+ }
497
+ });
498
+ }),
499
+ this.manager.refresher.add(this.stateKey, () => {
500
+ return autorun(() => {
501
+ const appState = this.appAttributes?.state;
502
+ if (appState?.zIndex > 0 && appState.zIndex !== this.box?.zIndex) {
503
+ this.boxManager?.setZIndex(appId, appState.zIndex);
504
+ }
505
+ });
506
+ }),
507
+ this.manager.refresher.add(`${appId}-fullPath`, () => {
508
+ return autorun(() => {
509
+ const fullPath = this.appAttributes?.fullPath;
510
+ this.setFocusScenePathHandler(fullPath);
511
+ if (this.fullPath$.value !== fullPath) {
512
+ this.notifyPageStateChange();
513
+ this.fullPath$.setValue(fullPath);
514
+ }
515
+ });
516
+ }),
517
+ ]);
405
518
  };
406
519
 
407
520
  private setFocusScenePathHandler = debounce((fullPath: string | undefined) => {
@@ -419,6 +532,7 @@ export class AppProxy implements PageRemoveService {
419
532
  }
420
533
 
421
534
  public setViewFocusScenePath() {
535
+ if (this.status === "destroyed") return;
422
536
  const fullPath = this.getFullScenePath();
423
537
  if (fullPath && this.view) {
424
538
  setViewFocusScenePath(this.view, fullPath);
@@ -428,6 +542,7 @@ export class AppProxy implements PageRemoveService {
428
542
 
429
543
  private createView(): View {
430
544
  const view = this.viewManager.createView(this.id);
545
+ this.view$.setValue(view);
431
546
  this.setViewFocusScenePath();
432
547
  return view;
433
548
  }
@@ -477,10 +592,37 @@ export class AppProxy implements PageRemoveService {
477
592
  const fullPath = this._pageState.getFullPath(index);
478
593
  if (fullPath) {
479
594
  this.setFullPath(fullPath);
595
+ setScenePath(this.manager.room, fullPath);
480
596
  }
481
597
  }
482
598
  }
483
599
 
600
+ public storeCamera = (camera: ICamera) => {
601
+ this.store.updateAppAttributes(this.id, Fields.Camera, camera);
602
+ };
603
+
604
+ public storeSize = (size: ISize) => {
605
+ this.store.updateAppAttributes(this.id, Fields.Size, size);
606
+ };
607
+
608
+ public updateSize = (width: number, height: number) => {
609
+ const iSize = {
610
+ id: this.manager.uid,
611
+ width, height
612
+ }
613
+ this.store.updateAppAttributes(this.id, Fields.Size, iSize);
614
+ this.size$.setValue(iSize);
615
+ }
616
+
617
+ public moveCamera = (camera: Partial<ICamera>) => {
618
+ if (!this.camera$.value) {
619
+ return;
620
+ }
621
+ const nextCamera = { ...this.camera$.value, ...camera, id: this.uid };
622
+ this.storeCamera(nextCamera);
623
+ this.camera$.setValue(nextCamera);
624
+ };
625
+
484
626
  public async destroy(
485
627
  needCloseBox: boolean,
486
628
  cleanAttrs: boolean,
@@ -491,11 +633,13 @@ export class AppProxy implements PageRemoveService {
491
633
  this.status = "destroyed";
492
634
  try {
493
635
  await appRegister.notifyApp(this.kind, "destroy", { appId: this.id });
636
+ callbacks.emit("appClose", { appId: this.id, kind: this.kind, error });
494
637
  await this.appEmitter.emit("destroy", { error });
495
638
  } catch (error) {
496
639
  console.error("[WindowManager]: notifyApp error", error.message, error.stack);
497
640
  }
498
641
  this.appEmitter.clearListeners();
642
+ this.sideEffectManager.flushAll();
499
643
  emitter.emit(`destroy-${this.id}` as any, { error });
500
644
  if (needCloseBox) {
501
645
  this.boxManager?.closeBox(this.id, skipUpdate);
@@ -510,11 +654,53 @@ export class AppProxy implements PageRemoveService {
510
654
 
511
655
  this.viewManager.destroyView(this.id);
512
656
  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();
657
+ this.valManager.destroy();
658
+ }
659
+
660
+ private addCameraReaction = () => {
661
+ this.sideEffectManager.add(() =>
662
+ this.manager.refresher.add(`${this.id}-camera`, () =>
663
+ reaction(
664
+ () => this.appAttributes?.camera,
665
+ camera => {
666
+ if (camera) {
667
+ const rawCamera = toJS(camera);
668
+ if (!isEqual(rawCamera, this.camera$.value)) {
669
+ this.camera$.setValue(rawCamera);
670
+ }
671
+ }
672
+ }
673
+ )
674
+ )
675
+ , "camera");
676
+ }
677
+
678
+ private addSizeReaction = () => {
679
+ this.sideEffectManager.add(() =>
680
+ this.manager.refresher.add(`${this.id}-size`, () =>
681
+ reaction(
682
+ () => this.appAttributes?.size,
683
+ size => {
684
+ if (size) {
685
+ const rawSize = toJS(size);
686
+ if (!isEqual(rawSize, this.size$.value)) {
687
+ this.size$.setValue(rawSize);
688
+ }
689
+ }
690
+ }
691
+ )
692
+ )
693
+ , "size");
694
+ }
695
+
696
+ public onFocus = () => {
697
+ this.setScenePath();
698
+ }
699
+
700
+ // 异步值设置完成通知其他端创建 app
701
+ private setupDone = () => {
702
+ this.store.updateAppAttributes(this.id, "setup", true);
703
+ this.manager.dispatchInternalEvent(Events.InvokeAttributesUpdateCallback);
518
704
  }
519
705
 
520
706
  public close(): Promise<void> {