@netless/window-manager 0.4.12 → 0.4.15

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/docs/advanced.md CHANGED
@@ -5,6 +5,7 @@
5
5
  - [清屏](#clean-current-scene)
6
6
  - [判断是否打开某种 APP](#has-kind)
7
7
  - [页面控制器](#page-control)
8
+ - [视角](#view-mode)
8
9
 
9
10
 
10
11
  <h3 id="redo-undo">撤销重做</h3>
@@ -87,4 +88,16 @@ manager.emitter.on("pageStateChange", state => {
87
88
  manager.nextPage()
88
89
  manager.prevPage()
89
90
  manager.addPage()
90
- ```
91
+ ```
92
+
93
+ <br>
94
+
95
+ <h3 id="view-mode">视角跟随</h3>
96
+
97
+ `ViewMode` 有 `broadcaster` `freedom` 两种模式
98
+
99
+ 可写权限默认进去为 `broadcaster` 并且互相操作并跟随视角
100
+
101
+ 当 `room` 设置 `Writable` 为 `false` 时此时只能跟随视角并不能广播视角
102
+
103
+ 在 `room` 的 `isWritable` 设置为 `false` 后想重新跟随视角可以通过设置为 `broadcaster` 跟随
@@ -0,0 +1,111 @@
1
+ ## AppContext
2
+
3
+ - [api](#api)
4
+ - [events](#events)
5
+
6
+ <h2 id="api">API</h2>
7
+
8
+ ### appId
9
+
10
+ 插入 `app` 时生成的唯一 ID
11
+
12
+ ```ts
13
+ const appId = context.appId
14
+ ```
15
+
16
+ ### getDisplayer
17
+
18
+ 在默认情况下 `Displayer` 为白板的 `room` 实例
19
+
20
+ 回放时则为 `Player` 实例
21
+
22
+ ```ts
23
+ const displayer = context.getDisplayer()
24
+
25
+ assert(displayer, room) // 互动房间
26
+ assert(displayer, player) // 回放房间
27
+ ```
28
+
29
+ ### getScenes
30
+
31
+ `scenes` 在 `addApp` 时传入 `scenePath` 会由 `WindowManager` 创建
32
+
33
+ ```ts
34
+ const scenes = context.getScenes()
35
+ ```
36
+
37
+ ### getView
38
+
39
+ `View` 为白板中一块可标注部分
40
+
41
+ ```ts
42
+ const view = context.getView()
43
+ ```
44
+
45
+ ### getIsWritable
46
+
47
+ 获取当前状态是否可写
48
+
49
+ ```ts
50
+ // isWritable === (room.isWritable && box.readonly)
51
+ const isWritable = context.getIsWritable()
52
+ ```
53
+
54
+ ### getBox
55
+
56
+ 获取当前 app 的 box
57
+
58
+ ```ts
59
+ const box = context.getBox()
60
+
61
+ box.$content // box 的 main element
62
+ box.$footer
63
+ ```
64
+
65
+ ### setScenePath
66
+
67
+ 切换当前 `view` 的 `scenePath`
68
+
69
+ ```ts
70
+ context.setScenePath("/page/2")
71
+ ```
72
+
73
+ ### mountView
74
+
75
+ 挂载 view 到指定 dom
76
+
77
+ ```ts
78
+ context.mountView(ref)
79
+ ```
80
+
81
+ <h2 id="events">events</h2>
82
+
83
+ ### destroy
84
+
85
+ app 被关闭时发送的事件
86
+
87
+ ```ts
88
+ context.emitter.on("destroy", () => {
89
+ // release your listeners
90
+ })
91
+ ```
92
+
93
+ ### writableChange
94
+
95
+ 白板可写状态切换时触发
96
+
97
+ ```ts
98
+ context.emitter.on("writableChange", isWritable => {
99
+ //
100
+ })
101
+ ```
102
+
103
+ ### focus
104
+
105
+ 当前 app 获得焦点或者失去焦点时触发
106
+
107
+ ```ts
108
+ context.emitter.on("focus", focus => {
109
+ //
110
+ })
111
+ ```
@@ -1,5 +1,7 @@
1
1
  # 开发自定义 APP
2
2
 
3
+ - [AppContext](./app-context.md)
4
+
3
5
  ## official apps https://github.com/netless-io/netless-app
4
6
 
5
7
  <br>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netless/window-manager",
3
- "version": "0.4.12",
3
+ "version": "0.4.15",
4
4
  "description": "",
5
5
  "main": "dist/index.es.js",
6
6
  "module": "dist/index.es.js",
@@ -55,6 +55,10 @@ export class AppListeners {
55
55
  this.cursorMoveHandler(data.payload);
56
56
  break;
57
57
  }
58
+ case Events.RootDirRemoved: {
59
+ this.rootDirRemovedHandler();
60
+ break;
61
+ }
58
62
  default:
59
63
  break;
60
64
  }
@@ -93,4 +97,8 @@ export class AppListeners {
93
97
  private cursorMoveHandler = (payload: any) => {
94
98
  emitter.emit("cursorMove", payload);
95
99
  };
100
+
101
+ private rootDirRemovedHandler = () => {
102
+ this.manager.onRootDirRemoved();
103
+ };
96
104
  }
package/src/AppManager.ts CHANGED
@@ -109,10 +109,8 @@ export class AppManager {
109
109
  private onRemoveScenes = (scenePath: string) => {
110
110
  // 如果移除根目录就把 scenePath 设置为初始值
111
111
  if (scenePath === ROOT_DIR) {
112
- this.setMainViewScenePath(INIT_DIR);
113
- this.createRootDirScenesCallback();
114
112
  this.onRootDirRemoved();
115
- emitter.emit("rootDirRemoved");
113
+ this.dispatchInternalEvent(Events.RootDirRemoved);
116
114
  return;
117
115
  }
118
116
  // 如果移除的 path 跟 MainViewScenePath 相同就取当前目录的当前 index
@@ -129,7 +127,10 @@ export class AppManager {
129
127
  * 根目录被删除时所有的 scene 都会被删除.
130
128
  * 所以需要关掉所有开启了 view 的 app
131
129
  */
132
- private onRootDirRemoved() {
130
+ public onRootDirRemoved() {
131
+ this.setMainViewScenePath(INIT_DIR);
132
+ this.createRootDirScenesCallback();
133
+
133
134
  this.appProxies.forEach(appProxy => {
134
135
  if (appProxy.view) {
135
136
  this.closeApp(appProxy.id);
@@ -137,6 +138,8 @@ export class AppManager {
137
138
  });
138
139
  // 删除了根目录的 scenes 之后 mainview 需要重新绑定, 否则主白板会不能渲染
139
140
  this.mainViewProxy.rebind();
141
+
142
+ emitter.emit("rootDirRemoved");
140
143
  }
141
144
 
142
145
  private onReadonlyChanged = () => {
@@ -274,34 +277,13 @@ export class AppManager {
274
277
  this.refresher?.add("mainViewIndex", () => {
275
278
  return autorun(() => {
276
279
  const mainSceneIndex = get(this.attributes, "_mainSceneIndex");
277
- if (mainSceneIndex !== undefined && this._prevSceneIndex !== mainSceneIndex) {
278
- callbacks.emit("mainViewSceneIndexChange", mainSceneIndex);
279
- emitter.emit("changePageState");
280
- if (this.callbacksNode) {
281
- this.updateSceneState(this.callbacksNode);
282
- }
283
- this._prevSceneIndex = mainSceneIndex;
284
- }
280
+ this.onMainViewIndexChange(mainSceneIndex);
285
281
  });
286
282
  });
287
283
  this.refresher?.add("focusedChange", () => {
288
284
  return autorun(() => {
289
285
  const focused = get(this.attributes, "focus");
290
- if (this._prevFocused !== focused) {
291
- callbacks.emit("focusedChange", focused);
292
- emitter.emit("focusedChange", { focused, prev: this._prevFocused });
293
- this._prevFocused = focused;
294
- if (focused !== undefined) {
295
- this.boxManager?.focusBox({ appId: focused });
296
- // 确保 focus 修改的时候, appProxy 已经创建
297
- setTimeout(() => {
298
- const appProxy = this.appProxies.get(focused);
299
- if (appProxy) {
300
- appRegister.notifyApp(appProxy.kind, "focus", { appId: focused });
301
- }
302
- }, 0);
303
- }
304
- }
286
+ this.onFocusChange(focused);
305
287
  });
306
288
  });
307
289
  this.refresher?.add("registeredChange", () => {
@@ -329,6 +311,35 @@ export class AppManager {
329
311
  });
330
312
  }
331
313
 
314
+ private onMainViewIndexChange = (index: number) => {
315
+ if (index !== undefined && this._prevSceneIndex !== index) {
316
+ callbacks.emit("mainViewSceneIndexChange", index);
317
+ emitter.emit("changePageState");
318
+ if (this.callbacksNode) {
319
+ this.updateSceneState(this.callbacksNode);
320
+ }
321
+ this._prevSceneIndex = index;
322
+ }
323
+ }
324
+
325
+ private onFocusChange = (focused: string | undefined) => {
326
+ if (this._prevFocused !== focused) {
327
+ callbacks.emit("focusedChange", focused);
328
+ emitter.emit("focusedChange", { focused, prev: this._prevFocused });
329
+ this._prevFocused = focused;
330
+ if (focused !== undefined) {
331
+ this.boxManager?.focusBox({ appId: focused });
332
+ // 确保 focus 修改的时候, appProxy 已经创建
333
+ setTimeout(() => {
334
+ const appProxy = this.appProxies.get(focused);
335
+ if (appProxy) {
336
+ appRegister.notifyApp(appProxy.kind, "focus", { appId: focused });
337
+ }
338
+ }, 0);
339
+ }
340
+ }
341
+ }
342
+
332
343
  /**
333
344
  * 插件更新 attributes 时的回调
334
345
  *
@@ -717,7 +728,7 @@ export class AppManager {
717
728
  });
718
729
  }
719
730
 
720
- public dispatchInternalEvent(event: Events, payload: any) {
731
+ public dispatchInternalEvent(event: Events, payload?: any) {
721
732
  this.safeDispatchMagixEvent(MagixEventName, {
722
733
  eventName: event,
723
734
  payload: payload,
@@ -0,0 +1,47 @@
1
+ import type { ScenesCallbacks, ScenesCallbacksNode } from "white-web-sdk";
2
+ import type { AppManager } from "../AppManager";
3
+
4
+ export class ScenesCallbackManager {
5
+
6
+ private nodes: Map<string, ScenesCallbacksNode> = new Map();
7
+
8
+ constructor(private manager: AppManager) {}
9
+
10
+ public createNode(path: string, callbacks?: Partial<ScenesCallbacks>): ScenesCallbacksNode | null {
11
+ const node = this.manager.displayer.createScenesCallback(path, callbacks);
12
+ if (node) {
13
+ this.nodes.set(path, node);
14
+ }
15
+ return node;
16
+ }
17
+
18
+ public getScenes(path: string) {
19
+ const node = this.nodes.get(path);
20
+ return node?.scenes;
21
+ }
22
+
23
+ public getScenesOnce(path: string) {
24
+ let node = this.nodes.get(path);
25
+ if (!node) {
26
+ const created = this.createNode(path);
27
+ if (created) {
28
+ node = created;
29
+ }
30
+ }
31
+ const scenes = node?.scenes;
32
+ this.removeNode(path);
33
+ return scenes;
34
+ }
35
+
36
+ public removeNode(path: string) {
37
+ const node = this.nodes.get(path);
38
+ if (node) {
39
+ node.dispose();
40
+ this.nodes.delete(path);
41
+ }
42
+ }
43
+
44
+ public destroy(): void {
45
+ this.nodes.forEach(node => node.dispose());
46
+ }
47
+ }
@@ -22,7 +22,6 @@ export class MainViewProxy {
22
22
  this.mainView = this.createMainView();
23
23
  this.moveCameraSizeByAttributes();
24
24
  emitter.once("mainViewMounted").then(() => {
25
- this.addMainViewListener();
26
25
  setTimeout(() => {
27
26
  this.start();
28
27
  if (!this.mainViewCamera || !this.mainViewSize) {
@@ -55,6 +54,7 @@ export class MainViewProxy {
55
54
  public start() {
56
55
  if (this.started) return;
57
56
  this.sizeChangeHandler(this.mainViewSize);
57
+ this.addMainViewListener();
58
58
  this.addCameraListener();
59
59
  this.manager.refresher?.add(Fields.MainViewCamera, this.cameraReaction);
60
60
  this.started = true;
@@ -143,6 +143,7 @@ export class MainViewProxy {
143
143
  this.view.divElement.removeEventListener("click", this.mainViewClickListener);
144
144
  this.view.divElement.removeEventListener("touchend", this.mainViewClickListener);
145
145
  }
146
+ this.mainViewIsAddListener = false;
146
147
  }
147
148
 
148
149
  private mainViewClickListener = () => {
package/src/constants.ts CHANGED
@@ -13,6 +13,7 @@ export enum Events {
13
13
  MoveCamera = "MoveCamera",
14
14
  MoveCameraToContain = "MoveCameraToContain",
15
15
  CursorMove = "CursorMove",
16
+ RootDirRemoved = "RootDirRemoved",
16
17
  }
17
18
 
18
19
  export const MagixEventName = "__WindowManger";
package/src/index.ts CHANGED
@@ -553,9 +553,10 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
553
553
  * 设置 ViewMode
554
554
  */
555
555
  public setViewMode(mode: ViewMode): void {
556
- if (!this.canOperate) return;
557
556
  if (mode === ViewMode.Broadcaster) {
558
- this.appManager?.mainViewProxy.setCameraAndSize();
557
+ if (this.canOperate) {
558
+ this.appManager?.mainViewProxy.setCameraAndSize();
559
+ }
559
560
  this.appManager?.mainViewProxy.start();
560
561
  }
561
562
  if (mode === ViewMode.Freedom) {