@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
package/src/BoxManager.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import { AppAttributes, Events, MIN_HEIGHT, MIN_WIDTH } from "./constants";
2
2
  import { debounce } from "lodash";
3
+ import { SideEffectManager } from "side-effect-manager";
3
4
  import { TELE_BOX_STATE, TeleBoxManager } from "@netless/telebox-insider";
4
5
  import { WindowManager } from "./index";
6
+ import type { Writeable } from "./typings";
5
7
  import type { BoxEmitterType } from "./BoxEmitter";
6
- import type { AddAppOptions, AppInitState } from "./index";
8
+ import type { AddAppOptions } from "./index";
7
9
  import type {
8
10
  TeleBoxManagerUpdateConfig,
9
11
  TeleBoxManagerCreateConfig,
@@ -18,7 +20,7 @@ import type { NetlessApp } from "./typings";
18
20
  import type { View } from "white-web-sdk";
19
21
  import type { CallbacksType } from "./callback";
20
22
  import type { EmitterType } from "./InternalEmitter";
21
- import { SideEffectManager } from "side-effect-manager";
23
+ import type { AppState } from "./App/type";
22
24
 
23
25
  export { TELE_BOX_STATE };
24
26
 
@@ -47,11 +49,12 @@ export type CreateTeleBoxManagerConfig = {
47
49
  collectorStyles?: Partial<CSSStyleDeclaration>;
48
50
  prefersColorScheme?: TeleBoxColorScheme;
49
51
  stageRatio?: number;
52
+ containerStyle?: string;
53
+ stageStyle?: string;
50
54
  };
51
55
 
52
56
  export type BoxManagerContext = {
53
57
  safeSetAttributes: (attributes: any) => void;
54
- getMainView: () => View;
55
58
  updateAppState: (appId: string, field: AppAttributes, value: any) => void;
56
59
  emitter: EmitterType;
57
60
  boxEmitter: BoxEmitterType;
@@ -72,7 +75,6 @@ export const createBoxManager = (
72
75
  return new BoxManager(
73
76
  {
74
77
  safeSetAttributes: (attributes: any) => manager.safeSetAttributes(attributes),
75
- getMainView: () => manager.mainView,
76
78
  updateAppState: (...args) => manager.appManager?.store.updateAppState(...args),
77
79
  canOperate: () => manager.canOperate,
78
80
  notifyContainerRectUpdate: (rect: TeleBoxRect) =>
@@ -81,7 +83,7 @@ export const createBoxManager = (
81
83
  setAppFocus: (appId: string) => manager.appManager?.store.setAppFocus(appId, true),
82
84
  callbacks,
83
85
  emitter,
84
- boxEmitter
86
+ boxEmitter,
85
87
  },
86
88
  options
87
89
  );
@@ -100,17 +102,17 @@ export class BoxManager {
100
102
  this.teleBoxManager = this.setupBoxManager(createTeleBoxManagerConfig);
101
103
  this.sideEffectManager.add(() => [
102
104
  // 使用 _xxx$.reaction 订阅修改的值, 不管有没有 skipUpdate, 修改值都会触发回调
103
- this.teleBoxManager._state$.reaction(state => {
105
+ this.teleBoxManager.onValChanged("state", state => {
104
106
  callbacks.emit("boxStateChange", state);
105
107
  emitter.emit("boxStateChange", state);
106
108
  }),
107
- this.teleBoxManager._darkMode$.reaction(darkMode => {
109
+ this.teleBoxManager.onValChanged("darkMode", darkMode => {
108
110
  callbacks.emit("darkModeChange", darkMode);
109
111
  }),
110
- this.teleBoxManager._prefersColorScheme$.reaction(colorScheme => {
112
+ this.teleBoxManager.onValChanged("prefersColorScheme", colorScheme => {
111
113
  callbacks.emit("prefersColorSchemeChange", colorScheme);
112
114
  }),
113
- this.teleBoxManager._minimized$.reaction((minimized, skipUpdate) => {
115
+ this.teleBoxManager.onValChanged("minimized", (minimized, skipUpdate) => {
114
116
  if (skipUpdate) {
115
117
  return;
116
118
  }
@@ -126,7 +128,7 @@ export class BoxManager {
126
128
  }
127
129
  }
128
130
  }),
129
- this.teleBoxManager._maximized$.reaction((maximized, skipUpdate) => {
131
+ this.teleBoxManager.onValChanged("maximized", (maximized, skipUpdate) => {
130
132
  if (skipUpdate) {
131
133
  return;
132
134
  }
@@ -140,7 +142,11 @@ export class BoxManager {
140
142
  this.teleBoxManager.events.on(
141
143
  "intrinsic_move",
142
144
  debounce((box: ReadonlyTeleBox): void => {
143
- boxEmitter.emit("move", { appId: box.id, x: box.intrinsicX, y: box.intrinsicY });
145
+ boxEmitter.emit("move", {
146
+ appId: box.id,
147
+ x: box.intrinsicX,
148
+ y: box.intrinsicY,
149
+ });
144
150
  }, 50)
145
151
  ),
146
152
  this.teleBoxManager.events.on(
@@ -165,23 +171,12 @@ export class BoxManager {
165
171
  this.teleBoxManager.events.on("z_index", box => {
166
172
  this.context.updateAppState(box.id, AppAttributes.ZIndex, box.zIndex);
167
173
  }),
168
- this.teleBoxManager._stageRect$.subscribe(stage => {
169
- emitter.emit("playgroundSizeChange", stage);
170
- this.context.notifyContainerRectUpdate(stage);
171
- }),
172
- emitter.on("writableChange", isWritable => {
173
- this.teleBoxManager.setHighlightStage(isWritable);
174
- }),
175
174
  emitter.on("containerSizeRatioUpdate", ratio => {
176
175
  this.teleBoxManager._stageRatio$.setValue(ratio);
177
176
  }),
178
177
  ]);
179
178
  }
180
179
 
181
- private get mainView() {
182
- return this.context.getMainView();
183
- }
184
-
185
180
  private get canOperate() {
186
181
  return this.context.canOperate();
187
182
  }
@@ -214,9 +209,14 @@ export class BoxManager {
214
209
  return this.teleBoxManager.stageRect;
215
210
  }
216
211
 
217
- public createBox(params: CreateBoxParams): void {
212
+ public get stageRect$() {
213
+ return this.teleBoxManager._stageRect$;
214
+ }
215
+
216
+ public createBox(params: CreateBoxParams): ReadonlyTeleBox | undefined {
218
217
  if (!this.teleBoxManager) return;
219
- let { minwidth = MIN_WIDTH, minheight = MIN_HEIGHT } = params.app.config ?? {};
218
+ // eslint-disable-next-line prefer-const
219
+ let { minwidth = MIN_WIDTH, minheight = MIN_HEIGHT, enableShadowDOM = true } = params.app.config ?? {};
220
220
  const { width, height } = params.app.config ?? {};
221
221
  const title = params.options?.title || params.appId;
222
222
  const rect = this.teleBoxManager.rootRect;
@@ -236,22 +236,32 @@ export class BoxManager {
236
236
  width,
237
237
  height,
238
238
  id: params.appId,
239
+ enableShadowDOM,
239
240
  };
240
- this.teleBoxManager.create(createBoxConfig, params.smartPosition);
241
+ const box = this.teleBoxManager.create(createBoxConfig, params.smartPosition);
241
242
  this.context.emitter.emit(`${params.appId}${Events.WindowCreated}` as any);
243
+ return box;
242
244
  }
243
245
 
244
246
  public setupBoxManager(
245
247
  createTeleBoxManagerConfig?: CreateTeleBoxManagerConfig
246
248
  ): TeleBoxManager {
247
249
  const root = WindowManager.playground;
248
- const initManagerState: TeleBoxManagerConfig = {
250
+ const initManagerState: Writeable<TeleBoxManagerConfig> = {
249
251
  stageRatio: createTeleBoxManagerConfig?.stageRatio,
250
252
  root: root,
251
253
  fence: false,
252
254
  prefersColorScheme: createTeleBoxManagerConfig?.prefersColorScheme,
253
255
  };
254
256
 
257
+ if (createTeleBoxManagerConfig?.containerStyle) {
258
+ initManagerState.containerStyle = createTeleBoxManagerConfig.containerStyle;
259
+ }
260
+
261
+ if (createTeleBoxManagerConfig?.stageStyle) {
262
+ initManagerState.stageStyle = createTeleBoxManagerConfig.stageStyle;
263
+ }
264
+
255
265
  const manager = new TeleBoxManager(initManagerState);
256
266
  if (this.teleBoxManager) {
257
267
  this.teleBoxManager.destroy();
@@ -289,21 +299,19 @@ export class BoxManager {
289
299
  return this.teleBoxManager.topBox;
290
300
  }
291
301
 
292
- public updateBoxState(state?: AppInitState): void {
302
+ public updateBoxState(state?: AppState): void {
293
303
  if (!state) return;
294
304
  const box = this.getBox(state.id);
295
305
  if (box) {
296
- this.teleBoxManager.update(
297
- box.id,
298
- {
299
- x: state.x,
300
- y: state.y,
301
- width: state.width || 0.5,
302
- height: state.height || 0.5,
303
- zIndex: state.zIndex,
304
- },
305
- true
306
- );
306
+ if (state.size) {
307
+ box._intrinsicSize$.setValue(state.size, true);
308
+ }
309
+ if (state.position) {
310
+ box._intrinsicCoord$.setValue(state.position, true);
311
+ }
312
+ if (state.zIndex) {
313
+ box._zIndex$.setValue(state.zIndex, true);
314
+ }
307
315
  setTimeout(() => {
308
316
  if (state.focus) {
309
317
  this.teleBoxManager.focusBox(box.id, true);
@@ -320,7 +328,10 @@ export class BoxManager {
320
328
  }
321
329
 
322
330
  public moveBox({ appId, x, y }: MoveBoxParams): void {
323
- this.teleBoxManager.update(appId, { x, y }, true);
331
+ const box = this.getBox(appId);
332
+ if (box) {
333
+ box._intrinsicCoord$.setValue({ x, y}, true);
334
+ }
324
335
  }
325
336
 
326
337
  public focusBox({ appId }: AppId, skipUpdate = true): void {
@@ -328,7 +339,10 @@ export class BoxManager {
328
339
  }
329
340
 
330
341
  public resizeBox({ appId, width, height, skipUpdate }: ResizeBoxParams): void {
331
- this.teleBoxManager.update(appId, { width, height }, skipUpdate);
342
+ const box = this.getBox(appId);
343
+ if (box) {
344
+ box._intrinsicSize$.setValue({ width, height }, skipUpdate);
345
+ }
332
346
  }
333
347
 
334
348
  public setBoxMinSize(params: SetBoxMinSizeParams): void {
@@ -21,3 +21,8 @@ export const BuiltinApps = {
21
21
  DocsViewer: AppDocsViewer.kind as string,
22
22
  MediaPlayer: AppMediaPlayer.kind as string,
23
23
  };
24
+
25
+ export const BuiltinAppsMap = {
26
+ [BuiltinApps.DocsViewer]: AppDocsViewer,
27
+ [BuiltinApps.MediaPlayer]: AppMediaPlayer,
28
+ }
@@ -7,6 +7,7 @@ import type { RoomMember } from "white-web-sdk";
7
7
  import type { CursorManager } from "./index";
8
8
  import type { SvelteComponent } from "svelte";
9
9
  import type { AppManager } from "../AppManager";
10
+ import type { TeleBoxRect } from "@netless/telebox-insider";
10
11
 
11
12
  export type Payload = {
12
13
  [key: string]: any;
@@ -50,18 +51,21 @@ export class Cursor {
50
51
  this.hide();
51
52
  };
52
53
 
53
- private moveCursor(cursor: Position, rect: DOMRect, view: any) {
54
+ private moveCursor(cursor: Position, rect: TeleBoxRect, view: any) {
54
55
  const { x, y, type } = cursor;
55
56
  const point = view?.screen.convertPointToScreen(x, y);
56
57
  if (point) {
57
58
  let translateX = point.x - 2;
58
59
  let translateY = point.y - 18;
59
60
  if (type === "app") {
60
- const wrapperRect = this.cursorManager.wrapperRect;
61
+ const wrapperRect = this.cursorManager.playgroundRect;
61
62
  if (wrapperRect) {
62
63
  translateX = translateX + rect.x - wrapperRect.x;
63
64
  translateY = translateY + rect.y - wrapperRect.y;
64
65
  }
66
+ } else {
67
+ translateX = translateX + rect.x;
68
+ translateY = translateY + rect.y;
65
69
  }
66
70
  if (point.x < 0 || point.x > rect.width || point.y < 0 || point.y > rect.height) {
67
71
  this.component?.$set({ visible: false, x: translateX, y: translateY });
@@ -5,7 +5,7 @@ import { emitter } from "../InternalEmitter";
5
5
  import { SideEffectManager } from "side-effect-manager";
6
6
  import { throttle } from "lodash";
7
7
  import { WindowManager } from "../index";
8
- import type { CursorMovePayload , ApplianceIcons} from "../index";
8
+ import type { CursorMovePayload , ApplianceIcons, TeleBoxRect } from "../index";
9
9
  import type { PositionType } from "../AttributesDelegate";
10
10
  import type { Point, RoomMember, View } from "white-web-sdk";
11
11
  import type { AppManager } from "../AppManager";
@@ -23,8 +23,8 @@ export type MoveCursorParams = {
23
23
  };
24
24
 
25
25
  export class CursorManager {
26
- public containerRect?: DOMRect;
27
- public wrapperRect?: DOMRect;
26
+ public wrapperRect?: TeleBoxRect;
27
+ public playgroundRect?: DOMRect;
28
28
  public cursorInstances: Map<string, Cursor> = new Map();
29
29
  public roomMembers?: readonly RoomMember[];
30
30
  private mainViewElement?: HTMLDivElement;
@@ -89,8 +89,6 @@ export class CursorManager {
89
89
  wrapper.removeEventListener("pointerleave", this.mouseLeaveListener);
90
90
  };
91
91
  });
92
-
93
- this.wrapperRect = wrapper.getBoundingClientRect();
94
92
  }
95
93
 
96
94
  public setMainViewDivElement(div: HTMLDivElement) {
@@ -168,8 +166,8 @@ export class CursorManager {
168
166
  };
169
167
 
170
168
  public updateContainerRect() {
171
- this.containerRect = WindowManager.container?.getBoundingClientRect();
172
- this.wrapperRect = WindowManager.playground?.getBoundingClientRect();
169
+ this.wrapperRect = this.manager.boxManager?.teleBoxManager.stageRect;
170
+ this.playgroundRect = WindowManager.playground?.getBoundingClientRect();
173
171
  }
174
172
 
175
173
  public deleteCursor(uid: string) {
package/src/Helper.ts CHANGED
@@ -1,11 +1,14 @@
1
- import { getVersionNumber } from "./Utils/Common";
1
+ import { getVersionNumber, wait } from "./Utils/Common";
2
+ import { log } from "./Utils/log";
2
3
  import { REQUIRE_VERSION } from "./constants";
3
- import { WhiteVersion } from "white-web-sdk";
4
+ import { toJS, WhiteVersion } from "white-web-sdk";
4
5
  import { WhiteWebSDKInvalidError } from "./Utils/error";
5
- import type { Room , RoomMember} from "white-web-sdk";
6
+ import { WindowManager } from "./index";
7
+ import type { Room, RoomMember } from "white-web-sdk";
6
8
 
7
9
  export const setupWrapper = (
8
- root: HTMLElement
10
+ root: HTMLElement,
11
+ target: HTMLElement
9
12
  ): {
10
13
  playground: HTMLDivElement;
11
14
  mainViewElement: HTMLDivElement;
@@ -15,7 +18,7 @@ export const setupWrapper = (
15
18
 
16
19
  const mainViewElement = document.createElement("div");
17
20
  mainViewElement.className = "netless-window-manager-main-view";
18
- playground.appendChild(mainViewElement);
21
+ target.appendChild(mainViewElement);
19
22
  root.appendChild(playground);
20
23
 
21
24
  return { playground, mainViewElement };
@@ -38,6 +41,21 @@ export type Member = RoomMember & { uid: string };
38
41
  export const serializeRoomMembers = (members: readonly RoomMember[]) => {
39
42
  return members.map(member => ({
40
43
  uid: member.payload?.uid || "",
41
- ...member,
44
+ ...toJS(member),
42
45
  }));
43
- }
46
+ };
47
+
48
+ export const createInvisiblePlugin = async (room: Room) => {
49
+ try {
50
+ const manager = (await room.createInvisiblePlugin(WindowManager, {})) as WindowManager;
51
+ return manager;
52
+ } catch (error) {
53
+ // 如果有两个用户同时调用 WindowManager.mount 有概率出现这个错误
54
+ if (error.message === `invisible plugin "WindowManager" exits`) {
55
+ await wait(200);
56
+ return room.getInvisiblePlugin(WindowManager.kind) as WindowManager;
57
+ } else {
58
+ log("createInvisiblePlugin failed", error);
59
+ }
60
+ }
61
+ };
@@ -1,7 +1,7 @@
1
1
  import Emittery from "emittery";
2
2
  import type { TeleBoxRect } from "@netless/telebox-insider";
3
- import type { AppInitState, CursorMovePayload } from "./index";
4
- import type { Member } from "./Helper";
3
+ import type { CursorMovePayload } from "./index";
4
+ import type { MemberState } from "white-web-sdk";
5
5
 
6
6
  export type RemoveSceneParams = {
7
7
  scenePath: string;
@@ -10,7 +10,6 @@ export type RemoveSceneParams = {
10
10
 
11
11
  export type EmitterEvent = {
12
12
  onCreated: undefined;
13
- InitReplay: AppInitState;
14
13
  error: Error;
15
14
  seekStart: undefined;
16
15
  seek: number;
@@ -30,7 +29,7 @@ export type EmitterEvent = {
30
29
  changePageState: undefined;
31
30
  writableChange: boolean;
32
31
  containerSizeRatioUpdate: number;
33
- roomMembersChange: Member[];
32
+ memberStateChange: MemberState;
34
33
  };
35
34
 
36
35
  export type EmitterType = Emittery<EmitterEvent>;
@@ -13,6 +13,7 @@ export type PageState = {
13
13
  export interface PageController {
14
14
  nextPage: () => Promise<boolean>;
15
15
  prevPage: () => Promise<boolean>;
16
+ jumpPage: (index: number) => Promise<boolean>;
16
17
  addPage: (params?: AddPageParams) => Promise<void>;
17
18
  removePage: (index: number) => Promise<boolean>;
18
19
  pageState: PageState;
package/src/PageState.ts CHANGED
@@ -6,7 +6,7 @@ import type { PageState } from "./Page";
6
6
  export class PageStateImpl {
7
7
  constructor(private manager: AppManager) {
8
8
  emitter.on("changePageState", () => {
9
- callbacks.emit("pageStateChange", this.toObject());
9
+ callbacks.emit("pageStateChange", this.toObject());
10
10
  });
11
11
  }
12
12
 
@@ -46,14 +46,18 @@ export class ReconnectRefresher {
46
46
  this.ctx.emitter.emit("startReconnect");
47
47
  }
48
48
  if (phase === RoomPhase.Connected && this.phase === RoomPhase.Reconnecting) {
49
- this.room?.dispatchMagixEvent(EnsureReconnectEvent, {});
49
+ if (this.room?.isWritable) {
50
+ this.room?.dispatchMagixEvent(EnsureReconnectEvent, {});
51
+ } else {
52
+ this.onReconnected();
53
+ }
50
54
  }
51
55
  this.phase = phase;
52
56
  };
53
57
 
54
58
  private onReconnected = debounce(() => {
55
59
  this._onReconnected();
56
- }, 3000);
60
+ }, 1000);
57
61
 
58
62
  private _onReconnected = () => {
59
63
  log("onReconnected refresh reactors");
@@ -88,6 +92,7 @@ export class ReconnectRefresher {
88
92
  this.reactors.set(id, func);
89
93
  this.disposers.set(id, func());
90
94
  }
95
+ return () => this.remove(id);
91
96
  }
92
97
 
93
98
  public remove(id: string) {
@@ -17,6 +17,9 @@ export const genAppId = async (kind: string) => {
17
17
  };
18
18
 
19
19
  export const setViewFocusScenePath = (view: View, focusScenePath: string) => {
20
+ if ((view as any).didRelease) {
21
+ return;
22
+ }
20
23
  if (view.focusScenePath !== focusScenePath) {
21
24
  view.focusScenePath = focusScenePath;
22
25
  return view;
@@ -30,6 +33,12 @@ export const setViewSceneIndex = (view: View, index: number) => {
30
33
  }
31
34
  };
32
35
 
36
+ export const releaseView = (view: View) => {
37
+ if (!(view as any).didRelease) {
38
+ view.release();
39
+ }
40
+ }
41
+
33
42
  export const setScenePath = (room: Room | undefined, scenePath: string) => {
34
43
  if (room && room.isWritable) {
35
44
  if (room.state.sceneState.scenePath !== scenePath) {
@@ -1,6 +1,6 @@
1
- import { listenUpdated, unlistenUpdated, reaction, UpdateEventKind } from "white-web-sdk";
2
- import type { AkkoObjectUpdatedProperty , AkkoObjectUpdatedListener } from "white-web-sdk";
3
1
  import { isObject } from "lodash";
2
+ import { listenUpdated, reaction, unlistenUpdated, UpdateEventKind } from "white-web-sdk";
3
+ import type { AkkoObjectUpdatedProperty, AkkoObjectUpdatedListener } from "white-web-sdk";
4
4
 
5
5
  // 兼容 13 和 14 版本 SDK
6
6
  export const onObjectByEvent = (event: UpdateEventKind) => {
@@ -12,7 +12,7 @@ export const onObjectByEvent = (event: UpdateEventKind) => {
12
12
  if (kinds.includes(event)) {
13
13
  func();
14
14
  }
15
- }
15
+ };
16
16
  listenUpdated(object, listener);
17
17
  func();
18
18
  return () => unlistenUpdated(object, listener);
@@ -21,43 +21,44 @@ export const onObjectByEvent = (event: UpdateEventKind) => {
21
21
  () => object,
22
22
  () => {
23
23
  func();
24
- }, {
24
+ },
25
+ {
25
26
  fireImmediately: true,
26
27
  }
27
- )
28
+ );
28
29
  }
29
- }
30
- }
30
+ };
31
+ };
31
32
 
32
33
  export const safeListenPropsUpdated = <T>(
33
34
  getProps: () => T,
34
35
  callback: AkkoObjectUpdatedListener<T>,
35
36
  onDestroyed?: (props: unknown) => void
36
- ) => {
37
+ ) => {
37
38
  let disposeListenUpdated: (() => void) | null = null;
38
39
  const disposeReaction = reaction(
39
- getProps,
40
- () => {
41
- if (disposeListenUpdated) {
42
- disposeListenUpdated();
43
- disposeListenUpdated = null;
44
- }
45
- const props = getProps();
46
- if (isObject(props)) {
47
- disposeListenUpdated = () => unlistenUpdated(props, callback);
48
- listenUpdated(props, callback);
49
- } else {
50
- onDestroyed?.(props);
51
- }
52
- },
53
- { fireImmediately: true }
40
+ getProps,
41
+ () => {
42
+ if (disposeListenUpdated) {
43
+ disposeListenUpdated();
44
+ disposeListenUpdated = null;
45
+ }
46
+ const props = getProps();
47
+ if (isObject(props)) {
48
+ disposeListenUpdated = () => unlistenUpdated(props, callback);
49
+ listenUpdated(props, callback);
50
+ } else {
51
+ onDestroyed?.(props);
52
+ }
53
+ },
54
+ { fireImmediately: true }
54
55
  );
55
56
 
56
57
  return () => {
57
- disposeListenUpdated?.();
58
- disposeReaction();
58
+ disposeListenUpdated?.();
59
+ disposeReaction();
59
60
  };
60
- }
61
+ };
61
62
 
62
63
  export const onObjectRemoved = onObjectByEvent(UpdateEventKind.Removed);
63
64
  export const onObjectInserted = onObjectByEvent(UpdateEventKind.Inserted);
@@ -56,6 +56,9 @@ export const replaceRoomFunction = (room: Room | Player, manager: WindowManager)
56
56
  room.lockImages = (...args) => manager.lockImages(...args);
57
57
 
58
58
  delegateRemoveScenes(room, manager);
59
+ if (!(room as any).dynamicPpt.slideStateAdapter.pptHandler) {
60
+ (room as any).dynamicPpt.slideStateAdapter.pptHandler = manager.createPPTHandler();
61
+ }
59
62
  }
60
63
  };
61
64
 
@@ -1,20 +1,20 @@
1
1
  import { AnimationMode } from "white-web-sdk";
2
- import { delay, throttle } from "lodash";
2
+ import { isEqual, pick, throttle } from "lodash";
3
3
  import type { TeleBoxRect } from "@netless/telebox-insider";
4
- import type { Camera, View, Size } from "white-web-sdk";
5
- import type { MainViewSize } from "../AttributesDelegate";
4
+ import type { Camera, View } from "white-web-sdk";
5
+ import type { ICamera, ISize } from "../AttributesDelegate";
6
6
 
7
- export type SaveCamera = (camera: Camera) => void;
7
+ export type SaveCamera = (camera: ICamera) => void;
8
8
 
9
9
  export class CameraSynchronizer {
10
- protected remoteCamera?: Camera;
11
- protected remoteSize?: MainViewSize;
10
+ public remoteCamera?: ICamera;
11
+ public remoteSize?: ISize;
12
12
  protected rect?: TeleBoxRect;
13
13
  protected view?: View;
14
14
 
15
15
  constructor(protected saveCamera: SaveCamera) {}
16
16
 
17
- public setRect(rect: TeleBoxRect) {
17
+ public setRect = (rect: TeleBoxRect) => {
18
18
  this.rect = rect;
19
19
  if (this.remoteCamera && this.remoteSize) {
20
20
  this.onRemoteUpdate(this.remoteCamera, this.remoteSize);
@@ -26,42 +26,45 @@ export class CameraSynchronizer {
26
26
  }
27
27
 
28
28
  // 远端 Camera 或者 size 更新
29
- public onRemoteUpdate = throttle((camera: Camera, size: MainViewSize) => {
29
+ public onRemoteUpdate = throttle((camera: ICamera, size: ISize) => {
30
30
  this.remoteCamera = camera;
31
31
  this.remoteSize = size;
32
32
  if (this.remoteSize && this.rect) {
33
- let scale: number;
34
- if (size.width < size.height) {
35
- scale = this.rect.width / size.width;
36
- } else {
37
- scale = this.rect.height / size.height;
38
- }
39
- const nextScale = camera.scale * scale;
40
- const moveCamera = () => this.view?.moveCamera({
41
- centerX: camera.centerX,
42
- centerY: camera.centerY,
33
+ const wScale = this.rect.width / size.width;
34
+ const hScale = this.rect.height / size.height;
35
+ const nextScale = camera.scale * Math.min(wScale, hScale);
36
+ const config: Partial<Camera> & { animationMode: AnimationMode } = {
43
37
  scale: nextScale,
44
- animationMode: AnimationMode.Immediately,
45
- });
46
- // TODO 直接调用 moveCamera 依然会出现 camera 错误的情况,这里暂时加一个 delay 保证 camera 是对的, 后续需要 SDK 进行修改
47
- delay(moveCamera, 10);
38
+ animationMode: AnimationMode.Continuous,
39
+ }
40
+ if (camera.centerX !== null) {
41
+ config.centerX = camera.centerX;
42
+ }
43
+ if (camera.centerY !== null) {
44
+ config.centerY = camera.centerY;
45
+ }
46
+ this.moveCamera(config);
48
47
  }
49
- }, 50);
48
+ }, 10);
50
49
 
50
+ public onRemoteSizeUpdate(size: ISize) {
51
+ this.remoteSize = size;
52
+ const needMoveCamera = !isEqual(pick(this.rect, ["width", "height"]), pick(size, ["width", "height"]));
53
+ if (this.rect && this.remoteCamera && needMoveCamera) {
54
+ const scale = this.rect.width / size.width;
55
+ const nextScale = this.remoteCamera.scale * scale;
56
+ this.moveCamera({
57
+ scale: nextScale,
58
+ })
59
+ }
60
+ }
51
61
 
52
- public onLocalCameraUpdate(camera: Camera) {
62
+ public onLocalCameraUpdate(camera: ICamera) {
53
63
  this.saveCamera(camera);
64
+ this.remoteCamera = camera;
54
65
  }
55
66
 
56
- // 本地 Size 更新, 先匹配 camera 到新的 size 然后再发送 camera 数据到远端
57
- public onLocalSizeUpdate(size: Size) {
58
- if (this.rect && this.view) {
59
- const scale = this.rect.width / size.width;
60
- const nextScale = this.view.camera.scale * scale;
61
- this.view.moveCamera({
62
- scale: nextScale,
63
- animationMode: AnimationMode.Immediately
64
- });
65
- }
67
+ private moveCamera(camera: Partial<Camera>) {
68
+ this.view?.moveCamera({ ...camera, animationMode: AnimationMode.Continuous });
66
69
  }
67
70
  }