@netless/window-manager 1.0.0-canary.1 → 1.0.0-canary.2

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.
package/dist/typings.d.ts CHANGED
@@ -3,6 +3,7 @@ import type { AnimationMode, ApplianceNames, Displayer, DisplayerState, Player,
3
3
  import type { AppContext } from "./App";
4
4
  import type { ReadonlyTeleBox, TeleBoxRect } from "@netless/telebox-insider";
5
5
  import type { PageState } from "./Page";
6
+ import type { Member } from "./Helper";
6
7
  export interface NetlessApp<Attributes = any, MagixEventPayloads = any, AppOptions = any, SetupResult = any> {
7
8
  kind: string;
8
9
  config?: {
@@ -49,6 +50,7 @@ export declare type AppEmitterEvent<T = any> = {
49
50
  reconnected: void;
50
51
  seek: number;
51
52
  pageStateChange: PageState;
53
+ roomMembersChange: Member[];
52
54
  };
53
55
  export declare type RegisterEventData = {
54
56
  appId: string;
@@ -71,8 +73,10 @@ export declare type RegisterParams<AppOptions = any, SetupResult = any, Attribut
71
73
  export declare type AppListenerKeys = keyof AppEmitterEvent;
72
74
  export declare type ApplianceIcons = Partial<Record<ApplianceNames, string>>;
73
75
  export type { AppContext } from "./App/AppContext";
76
+ export type { WhiteBoardView } from "./App";
74
77
  export type { ReadonlyTeleBox, TeleBoxRect };
75
78
  export type { SceneState, SceneDefinition, View, AnimationMode, Displayer, Room, Player };
76
79
  export type { Storage, StorageStateChangedEvent, StorageStateChangedListener } from "./App/Storage";
77
80
  export * from "./Page";
78
81
  export * from "./Utils/error";
82
+ export type { Member } from "./Helper";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netless/window-manager",
3
- "version": "1.0.0-canary.1",
3
+ "version": "1.0.0-canary.2",
4
4
  "description": "",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.es.js",
@@ -29,6 +29,7 @@
29
29
  "p-retry": "^4.6.1",
30
30
  "side-effect-manager": "^1.1.0",
31
31
  "uuid": "^7.0.3",
32
+ "value-enhancer": "^1.2.1",
32
33
  "video.js": ">=7"
33
34
  },
34
35
  "devDependencies": {
package/pnpm-lock.yaml CHANGED
@@ -35,6 +35,7 @@ specifiers:
35
35
  svelte: ^3.42.4
36
36
  typescript: ^4.5.5
37
37
  uuid: ^7.0.3
38
+ value-enhancer: ^1.2.1
38
39
  video.js: '>=7'
39
40
  vite: ^2.5.3
40
41
  vitest: ^0.14.1
@@ -48,6 +49,7 @@ dependencies:
48
49
  p-retry: registry.npmmirror.com/p-retry/4.6.1
49
50
  side-effect-manager: 1.1.0
50
51
  uuid: registry.npmmirror.com/uuid/7.0.3
52
+ value-enhancer: 1.2.1
51
53
  video.js: registry.npmmirror.com/video.js/7.18.1
52
54
 
53
55
  devDependencies:
@@ -1,5 +1,4 @@
1
1
  import { BoxNotCreatedError } from "../Utils/error";
2
- import { putScenes } from "../Utils/Common";
3
2
  import { Storage } from "./Storage";
4
3
  import {
5
4
  autorun,
@@ -19,7 +18,7 @@ import type {
19
18
  import type { ReadonlyTeleBox } from "@netless/telebox-insider";
20
19
  import type Emittery from "emittery";
21
20
  import type { BoxManager } from "../BoxManager";
22
- import type { AppEmitterEvent } from "../index";
21
+ import type { AppEmitterEvent, Member } from "../index";
23
22
  import type { AppManager } from "../AppManager";
24
23
  import type { AppProxy } from "./AppProxy";
25
24
  import type {
@@ -27,11 +26,13 @@ import type {
27
26
  MagixEventDispatcher,
28
27
  MagixEventRemoveListener,
29
28
  } from "./MagixEvent";
30
- import type { AddPageParams, PageController, PageState } from "../Page";
29
+ import { WhiteBoardView } from "./WhiteBoardView";
30
+ import { findMemberByUid } from "../Helper";
31
+ import { MAX_PAGE_SIZE } from "../constants";
32
+ import { putScenes } from "../Utils/Common";
33
+ import { isNumber } from "lodash";
31
34
 
32
- export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOptions = any>
33
- implements PageController
34
- {
35
+ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOptions = any> {
35
36
  public readonly emitter: Emittery<AppEmitterEvent<TAttributes>>;
36
37
  public readonly mobxUtils = {
37
38
  autorun,
@@ -48,6 +49,7 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
48
49
  private store = this.manager.store;
49
50
  public readonly isAddApp: boolean;
50
51
  public readonly isReplay = this.manager.isReplay;
52
+ private whiteBoardView?: WhiteBoardView;
51
53
 
52
54
  constructor(
53
55
  private manager: AppManager,
@@ -60,7 +62,7 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
60
62
  this.isAddApp = appProxy.isAddApp;
61
63
  }
62
64
 
63
- public getDisplayer = () => {
65
+ public get displayer(){
64
66
  return this.manager.displayer;
65
67
  };
66
68
 
@@ -78,32 +80,50 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
78
80
  }
79
81
  };
80
82
 
81
- public getView = (): View | undefined => {
83
+ public get view(): View | undefined {
82
84
  return this.appProxy.view;
83
85
  };
84
86
 
85
- public mountView = (dom: HTMLElement): void => {
86
- const view = this.getView();
87
- if (view) {
88
- view.divElement = dom as HTMLDivElement;
89
- setTimeout(() => {
90
- // 渲染需要时间,延迟 refresh
91
- this.getRoom()?.refreshViewSize();
92
- }, 1000);
87
+ public createWhiteBoardView = (size?: number): WhiteBoardView => {
88
+ if (this.whiteBoardView) {
89
+ return this.whiteBoardView;
93
90
  }
94
- };
91
+ let view = this.view;
92
+ if (!view) {
93
+ view = this.appProxy.createAppDir();
94
+ }
95
+ view.divElement = this.box.$content as HTMLDivElement;
96
+ this.initPageSize(size);
97
+ this.whiteBoardView = new WhiteBoardView(this, this.appProxy);
98
+ return this.whiteBoardView;
99
+ }
100
+
101
+ private initPageSize = (size?: number) => {
102
+ if (!isNumber(size)) return;
103
+ if (!this.appProxy.scenePath) return;
104
+ if (this.appProxy.pageState.length >= size) return;
105
+ if (size <= 0 || size >= MAX_PAGE_SIZE) {
106
+ throw Error(`[WindowManager]: size ${size} muse be in range [1, ${MAX_PAGE_SIZE}]`);
107
+ }
108
+ const needInsert = size - this.appProxy.pageState.length;
109
+ const startPageNumber = this.appProxy.pageState.length;
110
+ const scenes = new Array(needInsert).fill({}).map((_, index) => {
111
+ return { name: `${startPageNumber + index + 1}` };
112
+ });
113
+ putScenes(this.room, this.appProxy.scenePath, scenes);
114
+ }
95
115
 
96
116
  public getInitScenePath = () => {
97
117
  return this.manager.getAppInitPath(this.appId);
98
118
  };
99
119
 
100
120
  /** Get App writable status. */
101
- public getIsWritable = (): boolean => {
121
+ public get isWritable(): boolean {
102
122
  return this.manager.canOperate;
103
123
  };
104
124
 
105
125
  /** Get the App Window UI box. */
106
- public getBox = (): ReadonlyTeleBox => {
126
+ public get box(): ReadonlyTeleBox {
107
127
  const box = this.boxManager.getBox(this.appId);
108
128
  if (box) {
109
129
  return box;
@@ -112,10 +132,25 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
112
132
  }
113
133
  };
114
134
 
115
- public getRoom = (): Room | undefined => {
135
+ public get room(): Room | undefined {
116
136
  return this.manager.room;
117
137
  };
118
138
 
139
+ public get members() {
140
+ return this.manager.members;
141
+ }
142
+
143
+ public get memberState(): Member {
144
+ const self = findMemberByUid(this.room, this.manager.uid);
145
+ if (!self) {
146
+ throw new Error(`Member ${this.manager.uid} not found.`);
147
+ }
148
+ return {
149
+ uid: this.manager.uid,
150
+ ...self,
151
+ }
152
+ }
153
+
119
154
  /** @deprecated Use context.storage.setState instead. */
120
155
  public setAttributes = (attributes: TAttributes) => {
121
156
  this.manager.safeSetAttributes({ [this.appId]: attributes });
@@ -128,11 +163,12 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
128
163
  }
129
164
  };
130
165
 
166
+ /** @deprecated Use Pages api instead. */
131
167
  public setScenePath = async (scenePath: string): Promise<void> => {
132
168
  if (!this.appProxy.box) return;
133
169
  this.appProxy.setFullPath(scenePath);
134
170
  // 兼容 15 版本 SDK 的切页
135
- this.getRoom()?.setScenePath(scenePath);
171
+ this.room?.setScenePath(scenePath);
136
172
  };
137
173
 
138
174
  /** Get the local App options. */
@@ -196,55 +232,4 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
196
232
  public removeMagixEventListener = this.manager.displayer.removeMagixEventListener.bind(
197
233
  this.manager.displayer
198
234
  ) as MagixEventRemoveListener<TMagixEventPayloads>;
199
-
200
- /** PageController */
201
- public nextPage = async (): Promise<boolean> => {
202
- const nextIndex = this.pageState.index + 1;
203
- if (nextIndex > this.pageState.length - 1) {
204
- console.warn("[WindowManager] nextPage: index out of range");
205
- return false;
206
- }
207
- this.appProxy.setSceneIndex(nextIndex);
208
- return true;
209
- };
210
-
211
- public prevPage = async (): Promise<boolean> => {
212
- const nextIndex = this.pageState.index - 1;
213
- if (nextIndex < 0) {
214
- console.warn("[WindowManager] prevPage: index out of range");
215
- return false;
216
- }
217
- this.appProxy.setSceneIndex(nextIndex);
218
- return true;
219
- };
220
-
221
- public addPage = async (params?: AddPageParams) => {
222
- const after = params?.after;
223
- const scene = params?.scene;
224
- const scenePath = this.appProxy.scenePath;
225
- if (!scenePath) return;
226
- if (after) {
227
- const nextIndex = this.pageState.index + 1;
228
- putScenes(this.manager.room, scenePath, [scene || {}], nextIndex);
229
- } else {
230
- putScenes(this.manager.room, scenePath, [scene || {}]);
231
- }
232
- };
233
-
234
- public removePage = async (index?: number): Promise<boolean> => {
235
- const needRemoveIndex = index === undefined ? this.pageState.index : index;
236
- if (this.pageState.length === 1) {
237
- console.warn(`[WindowManager]: can not remove the last page`);
238
- return false;
239
- }
240
- if (needRemoveIndex < 0 || needRemoveIndex >= this.pageState.length) {
241
- console.warn(`[WindowManager]: page index ${index} out of range`);
242
- return false;
243
- }
244
- return this.appProxy.removeSceneByIndex(needRemoveIndex);;
245
- }
246
-
247
- public get pageState(): PageState {
248
- return this.appProxy.pageState;
249
- }
250
235
  }
@@ -9,11 +9,15 @@ export type AppPageStateParams = {
9
9
  };
10
10
 
11
11
  export class AppPageStateImpl {
12
- private sceneNode: ScenesCallbacksNode | null = null;
12
+ public sceneNode: ScenesCallbacksNode | null = null;
13
+ private scenePath?: string;
14
+ private view?: View;
13
15
 
14
16
  constructor(private params: AppPageStateParams) {
15
17
  const { displayer, scenePath } = this.params;
18
+ this.view = this.params.view;
16
19
  if (scenePath) {
20
+ this.scenePath = scenePath;
17
21
  this.sceneNode = displayer.createScenesCallback(scenePath, {
18
22
  onAddScene: this.onSceneChange,
19
23
  onRemoveScene: this.onSceneChange,
@@ -21,24 +25,39 @@ export class AppPageStateImpl {
21
25
  }
22
26
  }
23
27
 
24
- private onSceneChange = (node: ScenesCallbacksNode) => {
25
- this.sceneNode = node;
28
+ public createSceneNode = (scenePath: string) => {
29
+ this.scenePath = scenePath;
30
+ if (this.sceneNode) {
31
+ this.sceneNode.dispose();
32
+ }
33
+ this.sceneNode = this.params.displayer.createScenesCallback(scenePath, {
34
+ onAddScene: this.onSceneChange,
35
+ onRemoveScene: this.onSceneChange,
36
+ });
37
+ return this.sceneNode;
38
+ }
39
+
40
+ public setView(view: View) {
41
+ this.view = view;
42
+ }
43
+
44
+ private onSceneChange = () => {
26
45
  this.params.notifyPageStateChange();
27
46
  };
28
47
 
29
48
  public getFullPath(index: number) {
30
49
  const scenes = this.sceneNode?.scenes;
31
- if (this.params.scenePath && scenes) {
50
+ if (this.scenePath && scenes) {
32
51
  const name = scenes[index];
33
52
  if (name) {
34
- return `${this.params.scenePath}/${name}`;
53
+ return `${this.scenePath}/${name}`;
35
54
  }
36
55
  }
37
56
  }
38
57
 
39
58
  public toObject(): PageState {
40
59
  return {
41
- index: this.params.view?.focusSceneIndex || 0,
60
+ index: this.view?.focusSceneIndex || 0,
42
61
  length: this.sceneNode?.scenes.length || 0,
43
62
  };
44
63
  }
@@ -12,6 +12,7 @@ import { log } from "../Utils/log";
12
12
  import {
13
13
  entireScenes,
14
14
  getScenePath,
15
+ putScenes,
15
16
  removeScenes,
16
17
  setScenePath,
17
18
  setViewFocusScenePath,
@@ -30,6 +31,7 @@ import type { ReadonlyTeleBox } from "@netless/telebox-insider";
30
31
  import type { PageRemoveService, PageState } from "../Page";
31
32
  import { calculateNextIndex } from "../Page";
32
33
  import { boxEmitter } from "../BoxEmitter";
34
+ import { SideEffectManager } from "side-effect-manager";
33
35
 
34
36
  export type AppEmitter = Emittery<AppEmitterEvent>;
35
37
 
@@ -37,6 +39,7 @@ export class AppProxy implements PageRemoveService {
37
39
  public kind: string;
38
40
  public id: string;
39
41
  public scenePath?: string;
42
+ private appScenePath: string;
40
43
  public appEmitter: AppEmitter;
41
44
  public scenes?: SceneDefinition[];
42
45
 
@@ -49,12 +52,14 @@ export class AppProxy implements PageRemoveService {
49
52
  public isAddApp: boolean;
50
53
  private status: "normal" | "destroyed" = "normal";
51
54
  private stateKey: string;
52
- private _pageState: AppPageStateImpl;
55
+ public _pageState: AppPageStateImpl;
53
56
  private _prevFullPath: string | undefined;
54
57
 
55
58
  public appResult?: NetlessApp<any>;
56
59
  public appContext?: AppContext<any, any>;
57
60
 
61
+ private sideEffectManager = new SideEffectManager();
62
+
58
63
  constructor(
59
64
  private params: BaseInsertParams,
60
65
  private manager: AppManager,
@@ -63,6 +68,7 @@ export class AppProxy implements PageRemoveService {
63
68
  ) {
64
69
  this.kind = params.kind;
65
70
  this.id = appId;
71
+ this.appScenePath = `/${this.id}-app-dir`;
66
72
  this.stateKey = `${this.id}_state`;
67
73
  this.appProxies.set(this.id, this);
68
74
  this.appEmitter = new Emittery();
@@ -75,12 +81,37 @@ export class AppProxy implements PageRemoveService {
75
81
  // 只有传入了 scenePath 的 App 才会创建 View
76
82
  this.createView();
77
83
  }
84
+ if (!this.scenePath) {
85
+ this.scenePath = this.appScenePath;
86
+ }
78
87
  this._pageState = new AppPageStateImpl({
79
88
  displayer: this.manager.displayer,
80
89
  scenePath: this.scenePath,
81
90
  view: this.view,
82
91
  notifyPageStateChange: this.notifyPageStateChange,
83
92
  });
93
+ this.sideEffectManager.add(() => {
94
+ return () => this._pageState.destroy();
95
+ });
96
+ this.sideEffectManager.add(() => {
97
+ return emitter.on("roomMembersChange", members => {
98
+ this.appEmitter.emit("roomMembersChange", members);
99
+ });
100
+ });
101
+ }
102
+
103
+ public createAppDir() {
104
+ const scenePath = this.scenePath || this.appScenePath;
105
+ const sceneNode = this._pageState.createSceneNode(scenePath);
106
+ if (!sceneNode) {
107
+ putScenes(this.manager.room, scenePath, [{ name: "1" }]);
108
+ this._pageState.createSceneNode(scenePath);
109
+ this.setSceneIndex(0);
110
+ }
111
+ this.scenes = entireScenes(this.manager.displayer)[scenePath];
112
+ const view = this.createView();
113
+ this._pageState.setView(view);
114
+ return view;
84
115
  }
85
116
 
86
117
  private initScenes() {
@@ -395,14 +426,16 @@ export class AppProxy implements PageRemoveService {
395
426
  return fullPath;
396
427
  }
397
428
 
398
- private async createView(): Promise<View> {
399
- const view = await this.viewManager.createView(this.id);
429
+ private createView(): View {
430
+ const view = this.viewManager.createView(this.id);
400
431
  this.setViewFocusScenePath();
401
432
  return view;
402
433
  }
403
434
 
404
435
  public notifyPageStateChange = debounce(() => {
405
- this.appEmitter.emit("pageStateChange", this.pageState);
436
+ if (this.pageState) {
437
+ this.appEmitter.emit("pageStateChange", this.pageState);
438
+ }
406
439
  }, 50);
407
440
 
408
441
  public get pageState(): PageState {
@@ -412,7 +445,7 @@ export class AppProxy implements PageRemoveService {
412
445
  // PageRemoveService
413
446
  public async removeSceneByIndex(index: number) {
414
447
  const scenePath = this._pageState.getFullPath(index);
415
- if (scenePath) {
448
+ if (scenePath && this.pageState) {
416
449
  const nextIndex = calculateNextIndex(index, this.pageState);
417
450
  // 只修改 focus path 不修改 FullPath
418
451
  this.setSceneIndexWithoutSync(nextIndex);
@@ -474,7 +507,6 @@ export class AppProxy implements PageRemoveService {
474
507
  }
475
508
  }
476
509
  this.appProxies.delete(this.id);
477
- this._pageState.destroy();
478
510
 
479
511
  this.viewManager.destroyView(this.id);
480
512
  this.manager.appStatus.delete(this.id);
@@ -482,6 +514,7 @@ export class AppProxy implements PageRemoveService {
482
514
  this.manager.refresher?.remove(this.stateKey);
483
515
  this.manager.refresher?.remove(`${this.id}-fullPath`);
484
516
  this._prevFullPath = undefined;
517
+ this.sideEffectManager.flushAll();
485
518
  }
486
519
 
487
520
  public close(): Promise<void> {
@@ -37,7 +37,7 @@ export class Storage<TState extends Record<string, any> = any> implements Storag
37
37
  this._state = {} as TState;
38
38
  const rawState = this._getRawState(this._state);
39
39
 
40
- if (this._context.getIsWritable()) {
40
+ if (this._context.isWritable) {
41
41
  if (this.id === null) {
42
42
  if (context.isAddApp && defaultState) {
43
43
  this.setState(defaultState);
@@ -115,7 +115,7 @@ export class Storage<TState extends Record<string, any> = any> implements Storag
115
115
  return;
116
116
  }
117
117
 
118
- if (!this._context.getIsWritable()) {
118
+ if (!this._context.isWritable) {
119
119
  console.error(new Error(`Cannot setState on Storage "${this.id}" without writable access`), state);
120
120
  return;
121
121
  }
@@ -165,7 +165,7 @@ export class Storage<TState extends Record<string, any> = any> implements Storag
165
165
  return;
166
166
  }
167
167
 
168
- if (!this._context.getIsWritable()) {
168
+ if (!this._context.isWritable) {
169
169
  console.error(new Error(`Cannot empty Storage "${this.id}" without writable access.`));
170
170
  return;
171
171
  }
@@ -181,7 +181,7 @@ export class Storage<TState extends Record<string, any> = any> implements Storag
181
181
  throw new Error(`Cannot delete main Storage`);
182
182
  }
183
183
 
184
- if (!this._context.getIsWritable()) {
184
+ if (!this._context.isWritable) {
185
185
  console.error(new Error(`Cannot delete Storage "${this.id}" without writable access.`));
186
186
  return;
187
187
  }
@@ -0,0 +1,68 @@
1
+ import { putScenes } from "../Utils/Common";
2
+ import { Val } from "value-enhancer";
3
+
4
+ import type { ReadonlyVal } from "value-enhancer";
5
+ import type { AddPageParams, PageController, PageState } from "../Page";
6
+ import type { AppProxy } from "./AppProxy";
7
+ import type { AppContext } from "./AppContext";
8
+
9
+ export class WhiteBoardView implements PageController {
10
+ public readonly pageState$: ReadonlyVal<PageState>;
11
+
12
+ constructor(protected appContext: AppContext, protected appProxy: AppProxy) {
13
+ const pageState$ = new Val<PageState>(appProxy.pageState);
14
+ this.pageState$ = pageState$;
15
+ appProxy.appEmitter.on("pageStateChange", pageState => {
16
+ pageState$.setValue(pageState);
17
+ });
18
+ }
19
+
20
+ public get pageState() {
21
+ return this.pageState$.value;
22
+ }
23
+
24
+ public nextPage = async (): Promise<boolean> => {
25
+ const nextIndex = this.pageState.index + 1;
26
+ return this.jumpPage(nextIndex);
27
+ };
28
+
29
+ public prevPage = async (): Promise<boolean> => {
30
+ const nextIndex = this.pageState.index - 1;
31
+ return this.jumpPage(nextIndex);
32
+ };
33
+
34
+ public jumpPage = async (index: number): Promise<boolean> => {
35
+ if (index < 0 || index >= this.pageState.length) {
36
+ console.warn(`[WindowManager]: index ${index} out of range`);
37
+ return false;
38
+ }
39
+ this.appProxy.setSceneIndex(index);
40
+ return true;
41
+ };
42
+
43
+ public addPage = async (params?: AddPageParams) => {
44
+ const after = params?.after;
45
+ const scene = params?.scene;
46
+ const scenePath = this.appProxy.scenePath;
47
+ if (!scenePath) return;
48
+ if (after) {
49
+ const nextIndex = this.pageState.index + 1;
50
+ putScenes(this.appContext.room, scenePath, [scene || {}], nextIndex);
51
+ } else {
52
+ putScenes(this.appContext.room, scenePath, [scene || {}]);
53
+ }
54
+ };
55
+
56
+ public removePage = async (index?: number): Promise<boolean> => {
57
+ const needRemoveIndex = index === undefined ? this.pageState.index : index;
58
+ if (this.pageState.length === 1) {
59
+ console.warn(`[WindowManager]: can not remove the last page`);
60
+ return false;
61
+ }
62
+ if (needRemoveIndex < 0 || needRemoveIndex >= this.pageState.length) {
63
+ console.warn(`[WindowManager]: page index ${index} out of range`);
64
+ return false;
65
+ }
66
+ return this.appProxy.removeSceneByIndex(needRemoveIndex);
67
+ };
68
+ }
package/src/App/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from "./AppProxy";
2
2
  export * from "./AppContext";
3
+ export * from "./WhiteBoardView";
package/src/AppManager.ts CHANGED
@@ -15,6 +15,7 @@ import { MainViewProxy } from "./View/MainView";
15
15
  import { onObjectRemoved, safeListenPropsUpdated } from "./Utils/Reactive";
16
16
  import { reconnectRefresher, WindowManager } from "./index";
17
17
  import { RedoUndo } from "./RedoUndo";
18
+ import { serializeRoomMembers } from "./Helper";
18
19
  import { SideEffectManager } from "side-effect-manager";
19
20
  import { ViewManager } from "./View/ViewManager";
20
21
  import type { SyncRegisterAppPayload } from "./Register";
@@ -46,7 +47,7 @@ import type {
46
47
  BoxResizePayload,
47
48
  BoxStateChangePayload,
48
49
  } from "./BoxEmitter";
49
-
50
+ import type { Member } from "./Helper";
50
51
 
51
52
  export class AppManager {
52
53
  public displayer: Displayer;
@@ -299,6 +300,10 @@ export class AppManager {
299
300
  return this.room?.uid || "";
300
301
  }
301
302
 
303
+ public get members(): Member[] {
304
+ return serializeRoomMembers(this.displayer.state.roomMembers);
305
+ }
306
+
302
307
  public getMainViewSceneDir() {
303
308
  const scenePath = this.store.getMainViewScenePath();
304
309
  if (scenePath) {
@@ -660,6 +665,9 @@ export class AppManager {
660
665
  this.appProxies.forEach(appProxy => {
661
666
  appProxy.appEmitter.emit("roomStateChange", state);
662
667
  });
668
+ if (state.roomMembers) {
669
+ emitter.emit("roomMembersChange", this.members);
670
+ }
663
671
  emitter.emit("observerIdChange", this.displayer.observerId);
664
672
  };
665
673
 
package/src/Helper.ts CHANGED
@@ -2,7 +2,7 @@ import { getVersionNumber } from "./Utils/Common";
2
2
  import { REQUIRE_VERSION } from "./constants";
3
3
  import { WhiteVersion } from "white-web-sdk";
4
4
  import { WhiteWebSDKInvalidError } from "./Utils/error";
5
- import type { Room } from "white-web-sdk";
5
+ import type { Room , RoomMember} from "white-web-sdk";
6
6
 
7
7
  export const setupWrapper = (
8
8
  root: HTMLElement
@@ -32,3 +32,12 @@ export const findMemberByUid = (room: Room | undefined, uid: string) => {
32
32
  const roomMembers = room?.state.roomMembers;
33
33
  return roomMembers?.find(member => member.payload?.uid === uid);
34
34
  };
35
+
36
+ export type Member = RoomMember & { uid: string };
37
+
38
+ export const serializeRoomMembers = (members: readonly RoomMember[]) => {
39
+ return members.map(member => ({
40
+ uid: member.payload?.uid || "",
41
+ ...member,
42
+ }));
43
+ }
@@ -1,6 +1,7 @@
1
1
  import Emittery from "emittery";
2
2
  import type { TeleBoxRect } from "@netless/telebox-insider";
3
3
  import type { AppInitState, CursorMovePayload } from "./index";
4
+ import type { Member } from "./Helper";
4
5
 
5
6
  export type RemoveSceneParams = {
6
7
  scenePath: string;
@@ -29,6 +30,7 @@ export type EmitterEvent = {
29
30
  changePageState: undefined;
30
31
  writableChange: boolean;
31
32
  containerSizeRatioUpdate: number;
33
+ roomMembersChange: Member[];
32
34
  };
33
35
 
34
36
  export type EmitterType = Emittery<EmitterEvent>;
package/src/constants.ts CHANGED
@@ -57,3 +57,5 @@ export const ROOT_DIR = "/";
57
57
  export const INIT_DIR = "/init";
58
58
 
59
59
  export const SETUP_APP_DELAY = 50;
60
+
61
+ export const MAX_PAGE_SIZE = 500;
package/src/typings.ts CHANGED
@@ -13,6 +13,7 @@ import type {
13
13
  import type { AppContext } from "./App";
14
14
  import type { ReadonlyTeleBox, TeleBoxRect } from "@netless/telebox-insider";
15
15
  import type { PageState } from "./Page";
16
+ import type { Member } from "./Helper";
16
17
 
17
18
  export interface NetlessApp<Attributes = any, MagixEventPayloads = any, AppOptions = any, SetupResult = any> {
18
19
  kind: string;
@@ -53,6 +54,7 @@ export type AppEmitterEvent<T = any> = {
53
54
  reconnected: void;
54
55
  seek: number;
55
56
  pageStateChange: PageState,
57
+ roomMembersChange: Member[];
56
58
  };
57
59
 
58
60
  export type RegisterEventData = {
@@ -79,8 +81,10 @@ export type AppListenerKeys = keyof AppEmitterEvent;
79
81
  export type ApplianceIcons = Partial<Record<ApplianceNames, string>>;
80
82
 
81
83
  export type { AppContext } from "./App/AppContext";
84
+ export type { WhiteBoardView } from "./App";
82
85
  export type { ReadonlyTeleBox, TeleBoxRect };
83
86
  export type { SceneState, SceneDefinition, View, AnimationMode, Displayer, Room, Player };
84
87
  export type { Storage, StorageStateChangedEvent, StorageStateChangedListener } from "./App/Storage";
85
88
  export * from "./Page";
86
89
  export * from "./Utils/error";
90
+ export type { Member } from "./Helper";