@netless/window-manager 0.4.0-canary.12 → 0.4.0-canary.16

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.
@@ -50,13 +50,15 @@ export type MagixEventHandler<
50
50
  TEvent extends MagixEventTypes<TPayloads> = MagixEventTypes<TPayloads>
51
51
  > = (message: MagixEventMessage<TPayloads, TEvent>) => void;
52
52
 
53
+ export type MagixEventListenerDisposer = () => void
54
+
53
55
  export type MagixEventAddListener<TPayloads = any> = <
54
56
  TEvent extends MagixEventTypes<TPayloads> = MagixEventTypes<TPayloads>
55
57
  >(
56
58
  event: TEvent,
57
59
  handler: MagixEventHandler<TPayloads, TEvent>,
58
60
  options?: MagixEventListenerOptions | undefined
59
- ) => void;
61
+ ) => MagixEventListenerDisposer;
60
62
 
61
63
  export type MagixEventRemoveListener<TPayloads = any> = <
62
64
  TEvent extends MagixEventTypes<TPayloads> = MagixEventTypes<TPayloads>
@@ -4,7 +4,7 @@ import { SideEffectManager } from "side-effect-manager";
4
4
  import type { AppContext } from "../../AppContext";
5
5
  import { safeListenPropsUpdated } from "../../Utils/Reactive";
6
6
  import { isRef, makeRef, plainObjectKeys } from "./utils";
7
- import type { Diff, MaybeRefValue, RefValue, StorageStateChangedEvent } from "./typings";
7
+ import type { Diff, MaybeRefValue, RefValue, StorageStateChangedEvent, StorageStateChangedListener, StorageStateChangedListenerDisposer } from "./typings";
8
8
  import { StorageEvent } from "./StorageEvent";
9
9
 
10
10
  export * from './typings';
@@ -86,6 +86,11 @@ export class Storage<TState extends Record<string, any> = any> implements Storag
86
86
  }
87
87
 
88
88
  readonly onStateChanged = new StorageEvent<StorageStateChangedEvent<TState>>();
89
+
90
+ addStateChangedListener(handler: StorageStateChangedListener<TState>): StorageStateChangedListenerDisposer {
91
+ this.onStateChanged.addListener(handler);
92
+ return () => this.onStateChanged.removeListener(handler);
93
+ }
89
94
 
90
95
  ensureState(state: Partial<TState>): void {
91
96
  return this.setState(
@@ -16,6 +16,8 @@ export type StorageOnSetStatePayload<TState = unknown> = {
16
16
  [K in keyof TState]?: MaybeRefValue<TState[K]>;
17
17
  };
18
18
 
19
- export type StorageStateChangedEvent<TState = any> = Diff<TState>
19
+ export type StorageStateChangedEvent<TState = any> = Diff<TState>;
20
20
 
21
- export type StorageStateChangedListener<TState = any> = StorageEventListener<StorageStateChangedEvent<TState>>
21
+ export type StorageStateChangedListener<TState = any> = StorageEventListener<StorageStateChangedEvent<TState>>;
22
+
23
+ export type StorageStateChangedListenerDisposer = () => void;
package/src/AppContext.ts CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  toJS
9
9
  } from 'white-web-sdk';
10
10
  import { BoxNotCreatedError } from './Utils/error';
11
- import type { Room, SceneDefinition, View } from "white-web-sdk";
11
+ import type { Room, SceneDefinition, View, EventListener as WhiteEventListener } from "white-web-sdk";
12
12
  import type { ReadonlyTeleBox } from "@netless/telebox-insider";
13
13
  import type Emittery from "emittery";
14
14
  import type { BoxManager } from "./BoxManager";
@@ -59,10 +59,7 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
59
59
  public getScenes = (): SceneDefinition[] | undefined => {
60
60
  const appAttr = this.store.getAppAttributes(this.appId);
61
61
  if (appAttr?.isDynamicPPT) {
62
- const appProxy = this.manager.appProxies.get(this.appId);
63
- if (appProxy) {
64
- return appProxy.scenes;
65
- }
62
+ return this.appProxy.scenes;
66
63
  } else {
67
64
  return appAttr?.options["scenes"];
68
65
  }
@@ -158,7 +155,10 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
158
155
  public dispatchMagixEvent: MagixEventDispatcher<TMagixEventPayloads> = (this.manager.displayer as Room).dispatchMagixEvent.bind(this.manager.displayer)
159
156
 
160
157
  /** Listen to events from others clients (and self messages). */
161
- public addMagixEventListener: MagixEventAddListener<TMagixEventPayloads> = this.manager.displayer.addMagixEventListener.bind(this.manager.displayer)
158
+ public addMagixEventListener: MagixEventAddListener<TMagixEventPayloads> = (event, handler, options) => {
159
+ this.manager.displayer.addMagixEventListener(event, handler as WhiteEventListener, options);
160
+ return () => this.manager.displayer.removeMagixEventListener(event, handler as WhiteEventListener);
161
+ }
162
162
 
163
163
  /** Remove a Magix event listener. */
164
164
  public removeMagixEventListener = this.manager.displayer.removeMagixEventListener.bind(this.manager.displayer) as MagixEventRemoveListener<TMagixEventPayloads>
package/src/AppManager.ts CHANGED
@@ -4,11 +4,11 @@ import { AppListeners } from "./AppListener";
4
4
  import { AppProxy } from "./AppProxy";
5
5
  import { autorun, isPlayer, isRoom, ScenePathType } from "white-web-sdk";
6
6
  import { callbacks, emitter, WindowManager, reconnectRefresher } from "./index";
7
- import { genAppId, makeValidScenePath, setScenePath, setViewFocusScenePath } from "./Utils/Common";
7
+ import { entireScenes, genAppId, makeValidScenePath, parseSceneDir, setScenePath, setViewFocusScenePath } from "./Utils/Common";
8
8
  import { log } from "./Utils/log";
9
9
  import { MainViewProxy } from "./View/MainView";
10
10
  import { onObjectRemoved, safeListenPropsUpdated } from "./Utils/Reactive";
11
- import { get, sortBy } from "lodash";
11
+ import { get, isInteger, sortBy } from "lodash";
12
12
  import { store } from "./AttributesDelegate";
13
13
  import { ViewManager } from "./View/ViewManager";
14
14
  import type { ReconnectRefresher } from "./ReconnectRefresher";
@@ -212,10 +212,11 @@ export class AppManager {
212
212
  emitter.emit("mainViewMounted");
213
213
  }
214
214
 
215
- public setMainViewFocusPath() {
216
- const scenePath = this.store.getMainViewScenePath();
217
- if (scenePath) {
218
- setViewFocusScenePath(this.mainView, scenePath);
215
+ public setMainViewFocusPath(scenePath?: string) {
216
+ const focusScenePath = scenePath || this.store.getMainViewScenePath();
217
+ if (focusScenePath) {
218
+ const view = setViewFocusScenePath(this.mainView, focusScenePath);
219
+ return view?.focusScenePath === focusScenePath;
219
220
  }
220
221
  }
221
222
 
@@ -377,27 +378,44 @@ export class AppManager {
377
378
  }
378
379
 
379
380
  private async _setMainViewScenePath(scenePath: string) {
380
- this.safeSetAttributes({ _mainScenePath: scenePath });
381
- this.setMainViewFocusPath();
382
- this.store.setMainViewFocusPath(this.mainView);
383
- this.dispatchInternalEvent(Events.SetMainViewScenePath, { nextScenePath: scenePath });
381
+ const success = this.setMainViewFocusPath(scenePath);
382
+ if (success) {
383
+ this.safeSetAttributes({ _mainScenePath: scenePath });
384
+ this.store.setMainViewFocusPath(this.mainView);
385
+ this.updateSceneIndex();
386
+ this.dispatchInternalEvent(Events.SetMainViewScenePath, { nextScenePath: scenePath });
387
+ }
388
+ }
389
+
390
+ private updateSceneIndex = () => {
391
+ const scenePath = this.store.getMainViewScenePath() as string;
392
+ const sceneDir = parseSceneDir(scenePath);
393
+ const scenes = entireScenes(this.displayer)[sceneDir];
394
+ if (scenes.length) {
395
+ // "/ppt3/1" -> "1"
396
+ const pageName = scenePath.replace(sceneDir, "").replace("/", "");
397
+ const index = scenes.findIndex(scene => scene.name === pageName);
398
+ if (isInteger(index) && index >= 0) {
399
+ this.safeSetAttributes({ _mainSceneIndex: index });
400
+ }
401
+ }
384
402
  }
385
403
 
386
404
  public async setMainViewSceneIndex(index: number) {
387
405
  if (this.room) {
388
- this.safeSetAttributes({ _mainSceneIndex: index });
406
+ if (this.store.getMainViewSceneIndex() === index) return;
389
407
  const mainViewScenePath = this.store.getMainViewScenePath() as string;
390
408
  if (mainViewScenePath) {
391
- const sceneList = mainViewScenePath.split("/");
392
- sceneList.pop();
393
- let sceneDir = sceneList.join("/");
394
- if (sceneDir === "") {
395
- sceneDir = "/";
396
- }
409
+ const sceneDir = parseSceneDir(mainViewScenePath);
397
410
  const scenePath = makeValidScenePath(this.displayer, sceneDir, index);
398
411
  if (scenePath) {
399
- this.store.setMainViewScenePath(scenePath);
400
- this.setMainViewFocusPath();
412
+ const success = this.setMainViewFocusPath(scenePath);
413
+ if (success) {
414
+ this.store.setMainViewScenePath(scenePath);
415
+ this.safeSetAttributes({ _mainSceneIndex: index });
416
+ }
417
+ } else {
418
+ throw new Error(`[WindowManager]: ${sceneDir}: ${index} not valid index`);
401
419
  }
402
420
  }
403
421
  }
package/src/AppProxy.ts CHANGED
@@ -7,7 +7,7 @@ import { emitter } from "./index";
7
7
  import { Fields } from "./AttributesDelegate";
8
8
  import { debounce, get } from "lodash";
9
9
  import { log } from "./Utils/log";
10
- import { setScenePath, setViewFocusScenePath, getScenePath } from "./Utils/Common";
10
+ import { setScenePath, setViewFocusScenePath, getScenePath, removeScenes } from "./Utils/Common";
11
11
  import type {
12
12
  AppEmitterEvent,
13
13
  AppInitState,
@@ -327,7 +327,7 @@ export class AppProxy extends Base {
327
327
  });
328
328
  this.manager.refresher?.add(`${appId}-fullPath`, () => {
329
329
  return autorun(() => {
330
- const fullPath = this.appAttributes.fullPath;
330
+ const fullPath = this.appAttributes?.fullPath;
331
331
  this.setFocusScenePathHandler(fullPath);
332
332
  });
333
333
  });
@@ -377,6 +377,9 @@ export class AppProxy extends Base {
377
377
  }
378
378
  if (cleanAttrs) {
379
379
  this.store.cleanAppAttributes(this.id);
380
+ if (this.scenePath) {
381
+ removeScenes(this.manager.room, this.scenePath);
382
+ }
380
383
  }
381
384
  this.appProxies.delete(this.id);
382
385
 
package/src/BoxManager.ts CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  TELE_BOX_STATE,
6
6
  TeleBoxCollector,
7
7
  TeleBoxManager,
8
+ TeleBoxConfig,
8
9
  } from "@netless/telebox-insider";
9
10
  import { emitter, WindowManager } from "./index";
10
11
  import type { AddAppOptions, AppInitState, EmitterType, CallbacksType } from "./index";
@@ -379,6 +380,10 @@ export class BoxManager {
379
380
  }
380
381
  }
381
382
 
383
+ public updateBox(id: string, payload: TeleBoxConfig, skipUpdate = true): void {
384
+ this.teleBoxManager.update(id, payload, skipUpdate);
385
+ }
386
+
382
387
  public setReadonly(readonly: boolean) {
383
388
  this.teleBoxManager.setReadonly(readonly);
384
389
  }
@@ -10,34 +10,39 @@ class AppRegister {
10
10
 
11
11
  public async register(params: RegisterParams): Promise<void> {
12
12
  this.registered.set(params.kind, params);
13
-
14
- const srcOrAppOrFunction = params.src
15
- let downloadApp: () => Promise<NetlessApp>
16
-
13
+
14
+ const srcOrAppOrFunction = params.src;
15
+ let downloadApp: () => Promise<NetlessApp>;
16
+
17
17
  if (typeof srcOrAppOrFunction === "string") {
18
18
  downloadApp = async () => {
19
- const appClass = await loadApp(srcOrAppOrFunction, params.kind);
19
+ let appClass = (await loadApp(srcOrAppOrFunction, params.kind)) as any;
20
20
  if (appClass) {
21
- return appClass
21
+ if (appClass.__esModule) {
22
+ appClass = appClass.default;
23
+ }
24
+ return appClass;
22
25
  } else {
23
- throw new Error(`[WindowManager]: load remote script failed, ${srcOrAppOrFunction}`);
26
+ throw new Error(
27
+ `[WindowManager]: load remote script failed, ${srcOrAppOrFunction}`
28
+ );
24
29
  }
25
- }
30
+ };
26
31
  } else if (typeof srcOrAppOrFunction === "function") {
27
- downloadApp = srcOrAppOrFunction
32
+ downloadApp = srcOrAppOrFunction;
28
33
  } else {
29
- downloadApp = async () => srcOrAppOrFunction
34
+ downloadApp = async () => srcOrAppOrFunction;
30
35
  }
31
36
 
32
37
  this.appClasses.set(params.kind, async () => {
33
- let app = this.appClassesCache.get(params.kind)
38
+ let app = this.appClassesCache.get(params.kind);
34
39
  if (!app) {
35
- app = downloadApp()
36
- this.appClassesCache.set(params.kind, app)
40
+ app = downloadApp();
41
+ this.appClassesCache.set(params.kind, app);
37
42
  }
38
- return app
43
+ return app;
39
44
  });
40
-
45
+
41
46
  if (params.addHooks) {
42
47
  const emitter = this.createKindEmitter(params.kind);
43
48
  if (emitter) {
@@ -46,7 +51,11 @@ class AppRegister {
46
51
  }
47
52
  }
48
53
 
49
- public async notifyApp<T extends keyof RegisterEvents>(kind: string, event: T, payload: RegisterEvents[T]) {
54
+ public async notifyApp<T extends keyof RegisterEvents>(
55
+ kind: string,
56
+ event: T,
57
+ payload: RegisterEvents[T]
58
+ ) {
50
59
  const emitter = this.kindEmitters.get(kind);
51
60
  await emitter?.emit(event, payload);
52
61
  }
@@ -8,7 +8,7 @@ const TIMEOUT = 10000; // 10 秒超时
8
8
  export const getScript = async (url: string): Promise<string> => {
9
9
  const item = await getItem(url);
10
10
  if (item) {
11
- return item;
11
+ return item.sourceCode;
12
12
  } else {
13
13
  const result = await fetchWithTimeout(url, { timeout: TIMEOUT });
14
14
  const text = await result.text();
@@ -3,6 +3,11 @@ const DatabaseName = "__WindowManagerAppCache";
3
3
  let db: IDBDatabase;
4
4
  let store: IDBObjectStore;
5
5
 
6
+ export type Item = {
7
+ kind: string;
8
+ sourceCode: string;
9
+ }
10
+
6
11
  export const initDb = async () => {
7
12
  db = await createDb();
8
13
  }
@@ -12,7 +17,7 @@ export const setItem = (key: string, val: any) => {
12
17
  return addRecord(db, { kind: key, sourceCode: val })
13
18
  };
14
19
 
15
- export const getItem = async (key: string): Promise<string | null> => {
20
+ export const getItem = async (key: string): Promise<Item | null> => {
16
21
  if (!db) return null;
17
22
  return await query(db, key);
18
23
  };
@@ -1,6 +1,7 @@
1
1
  import { appRegister } from "../Register";
2
2
  import { debounce } from "lodash";
3
3
  import { emitter } from "../index";
4
+ import { ScenePathType } from "white-web-sdk";
4
5
  import { v4 } from "uuid";
5
6
  import type { PublicEvent } from "../index";
6
7
  import type { Displayer, ViewVisionMode, Room, View } from "white-web-sdk";
@@ -17,6 +18,14 @@ export const genAppId = async (kind: string) => {
17
18
  export const setViewFocusScenePath = (view: View, focusScenePath: string) => {
18
19
  if (view.focusScenePath !== focusScenePath) {
19
20
  view.focusScenePath = focusScenePath;
21
+ return view;
22
+ }
23
+ };
24
+
25
+ export const setViewSceneIndex = (view: View, index: number) => {
26
+ if (view.focusSceneIndex !== index) {
27
+ view.focusSceneIndex = index;
28
+ return view;
20
29
  }
21
30
  };
22
31
 
@@ -42,6 +51,15 @@ export const getScenePath = (
42
51
  }
43
52
  };
44
53
 
54
+ export const removeScenes = (room: Room | undefined, scenePath: string) => {
55
+ if (room) {
56
+ const type = room.scenePathType(scenePath);
57
+ if (type !== ScenePathType.None) {
58
+ room.removeScenes(scenePath);
59
+ }
60
+ }
61
+ };
62
+
45
63
  export const setViewMode = (view: View, mode: ViewVisionMode) => {
46
64
  if (!(view as any).didRelease && view.mode !== mode) {
47
65
  view.mode = mode;
@@ -70,7 +88,9 @@ export const notifyMainViewModeChange = debounce(
70
88
  export const makeValidScenePath = (displayer: Displayer, scenePath: string, index = 0) => {
71
89
  const scenes = entireScenes(displayer)[scenePath];
72
90
  if (!scenes) return;
73
- const firstSceneName = scenes[index].name;
91
+ const scene = scenes[index];
92
+ if (!scene) return;
93
+ const firstSceneName = scene.name;
74
94
  if (scenePath === "/") {
75
95
  return `/${firstSceneName}`;
76
96
  } else {
@@ -86,6 +106,17 @@ export const isValidScenePath = (scenePath: string) => {
86
106
  return scenePath.startsWith("/");
87
107
  };
88
108
 
109
+ export const parseSceneDir = (scenePath: string) => {
110
+ const sceneList = scenePath.split("/");
111
+ sceneList.pop();
112
+ let sceneDir = sceneList.join("/");
113
+ // "/page1" 的 dir 为 "/"
114
+ if (sceneDir === "") {
115
+ sceneDir = "/";
116
+ }
117
+ return sceneDir;
118
+ };
119
+
89
120
  export const ensureValidScenePath = (scenePath: string) => {
90
121
  if (scenePath.endsWith("/")) {
91
122
  return scenePath.slice(0, -1);
@@ -51,6 +51,7 @@ export const replaceRoomFunction = (room: Room, manager: WindowManager) => {
51
51
  room.setMemberState = (...args) => manager.mainView.setMemberState(...args);
52
52
  room.redo = () => manager.mainView.redo();
53
53
  room.undo = () => manager.mainView.undo();
54
+ room.cleanCurrentScene = () => manager.mainView.cleanCurrentScene();
54
55
  }
55
56
 
56
57
  };
@@ -1,4 +1,4 @@
1
- import type { View , Displayer} from "white-web-sdk";
1
+ import type { View, Displayer } from "white-web-sdk";
2
2
 
3
3
  export class ViewManager {
4
4
  public views: Map<string, View> = new Map();
@@ -38,7 +38,6 @@ export class ViewManager {
38
38
  }
39
39
  }
40
40
 
41
-
42
41
  export const createView = (displayer: Displayer): View => {
43
42
  const view = displayer.views.createView();
44
43
  setDefaultCameraBound(view);
package/src/index.ts CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  ensureValidScenePath,
22
22
  getVersionNumber,
23
23
  isValidScenePath,
24
+ parseSceneDir,
24
25
  wait,
25
26
  } from "./Utils/Common";
26
27
  import type { TELE_BOX_STATE, BoxManager } from "./BoxManager";
@@ -427,6 +428,12 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
427
428
  const appScenePath = appManager.store.getAppScenePath(appId);
428
429
  if (appScenePath && appScenePath === scenePath) {
429
430
  console.warn(`[WindowManager]: ScenePath ${scenePath} Already opened`);
431
+ if (this.boxManager) {
432
+ const topBox = this.boxManager.getTopBox();
433
+ if (topBox) {
434
+ this.boxManager.setZIndex(appId, topBox.zIndex + 1, false);
435
+ }
436
+ }
430
437
  return;
431
438
  }
432
439
  }
@@ -571,6 +578,19 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
571
578
  return this.attributes.focus;
572
579
  }
573
580
 
581
+ public get mainViewSceneIndex(): number {
582
+ return this.appManager?.store.getMainViewSceneIndex();
583
+ }
584
+
585
+ public get mainViewSceneDir(): string {
586
+ const scenePath = this.appManager?.store.getMainViewScenePath();
587
+ if (scenePath) {
588
+ return parseSceneDir(scenePath);
589
+ } else {
590
+ throw new Error("[WindowManager]: mainViewSceneDir not found");
591
+ }
592
+ }
593
+
574
594
  /**
575
595
  * 查询所有的 App
576
596
  */