@netless/window-manager 0.4.0-canary.25 → 0.4.0-canary.29

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/api.md CHANGED
@@ -11,6 +11,7 @@
11
11
  - [`closeApp`](#closeApp)
12
12
  - [`setMainViewSceneIndex`](#setMainViewSceneIndex)
13
13
  - [`setBoxState`](#setBoxState)
14
+ - [`cleanCurrentScene`](#cleanCurrentScene)
14
15
  - [实例属性](#prototypes)
15
16
  - [事件回调](#events)
16
17
 
@@ -128,6 +129,14 @@ manager.setMainViewSceneIndex(1)
128
129
  manager.setBoxState("normal") // boxState: normal | maximized | minimized
129
130
  ```
130
131
 
132
+ <h3 id="cleanCurrentScene">cleanCurrentScene</h3>
133
+
134
+ > 清除当前 focus 的 view 的笔迹
135
+
136
+ ```ts
137
+ manager.cleanCurrentScene()
138
+ ```
139
+
131
140
  <br>
132
141
 
133
142
  <h2 id="prototypes">实例属性</h2>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netless/window-manager",
3
- "version": "0.4.0-canary.25",
3
+ "version": "0.4.0-canary.29",
4
4
  "description": "",
5
5
  "main": "dist/index.es.js",
6
6
  "module": "dist/index.es.js",
@@ -33,7 +33,7 @@
33
33
  "devDependencies": {
34
34
  "@netless/app-docs-viewer": "^0.2.6",
35
35
  "@netless/app-media-player": "0.1.0-beta.5",
36
- "@netless/telebox-insider": "0.2.21",
36
+ "@netless/telebox-insider": "0.2.22",
37
37
  "@rollup/plugin-commonjs": "^20.0.0",
38
38
  "@rollup/plugin-node-resolve": "^13.0.4",
39
39
  "@rollup/plugin-url": "^6.1.0",
@@ -58,6 +58,6 @@
58
58
  "typescript": "^4.3.5",
59
59
  "video.js": "^7.14.3",
60
60
  "vite": "^2.5.3",
61
- "white-web-sdk": "^2.16.1"
61
+ "white-web-sdk": "^2.16.3"
62
62
  }
63
63
  }
package/src/AppContext.ts CHANGED
@@ -152,7 +152,10 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
152
152
  }
153
153
 
154
154
  /** Dispatch events to other clients (and self). */
155
- public dispatchMagixEvent: MagixEventDispatcher<TMagixEventPayloads> = (this.manager.displayer as Room).dispatchMagixEvent.bind(this.manager.displayer)
155
+ public dispatchMagixEvent: MagixEventDispatcher<TMagixEventPayloads> = (...args) => {
156
+ // can't dispatch events on replay mode
157
+ return this.manager.room?.dispatchMagixEvent(...args);
158
+ }
156
159
 
157
160
  /** Listen to events from others clients (and self messages). */
158
161
  public addMagixEventListener: MagixEventAddListener<TMagixEventPayloads> = (event, handler, options) => {
package/src/AppManager.ts CHANGED
@@ -1,9 +1,15 @@
1
- import pRetry from "p-retry";
2
1
  import { AppAttributes, AppStatus, Events, MagixEventName, ROOT_DIR } from "./constants";
3
2
  import { AppListeners } from "./AppListener";
4
3
  import { AppProxy } from "./AppProxy";
4
+ import { appRegister } from "./Register";
5
5
  import { autorun, isPlayer, isRoom, ScenePathType } from "white-web-sdk";
6
- import { callbacks, emitter, WindowManager, reconnectRefresher } from "./index";
6
+ import { callbacks, emitter, reconnectRefresher, WindowManager } from "./index";
7
+ import { get, isInteger, orderBy } from "lodash";
8
+ import { log } from "./Utils/log";
9
+ import { MainViewProxy } from "./View/MainView";
10
+ import { onObjectRemoved, safeListenPropsUpdated } from "./Utils/Reactive";
11
+ import { store } from "./AttributesDelegate";
12
+ import { ViewManager } from "./View/ViewManager";
7
13
  import {
8
14
  entireScenes,
9
15
  genAppId,
@@ -12,17 +18,11 @@ import {
12
18
  setScenePath,
13
19
  setViewFocusScenePath,
14
20
  } from "./Utils/Common";
15
- import { log } from "./Utils/log";
16
- import { MainViewProxy } from "./View/MainView";
17
- import { onObjectRemoved, safeListenPropsUpdated } from "./Utils/Reactive";
18
- import { get, isInteger, sortBy } from "lodash";
19
- import { store } from "./AttributesDelegate";
20
- import { ViewManager } from "./View/ViewManager";
21
21
  import type { ReconnectRefresher } from "./ReconnectRefresher";
22
22
  import type { BoxManager } from "./BoxManager";
23
23
  import type { Displayer, DisplayerState, Room, ScenesCallbacksNode } from "white-web-sdk";
24
24
  import type { AddAppParams, BaseInsertParams, TeleBoxRect, EmitterEvent } from "./index";
25
- import { appRegister } from "./Register";
25
+ import { AppCreateQueue } from "./Utils/AppCreateQueue";
26
26
 
27
27
  export class AppManager {
28
28
  public displayer: Displayer;
@@ -41,6 +41,7 @@ export class AppManager {
41
41
  private _prevSceneIndex: number | undefined;
42
42
  private _prevFocused: string | undefined;
43
43
  private callbacksNode: ScenesCallbacksNode | null;
44
+ private appCreateQueue = new AppCreateQueue();
44
45
 
45
46
  constructor(public windowManger: WindowManager) {
46
47
  this.displayer = windowManger.displayer;
@@ -185,13 +186,18 @@ export class AppManager {
185
186
  return autorun(() => {
186
187
  const focused = get(this.attributes, "focus");
187
188
  if (this._prevFocused !== focused) {
188
- this.boxManager?.focusBox({ appId: focused });
189
- const appProxy = this.appProxies.get(focused);
190
- if (appProxy) {
191
- appRegister.notifyApp(appProxy.kind, "focus", { appId: focused });
192
- }
193
189
  callbacks.emit("focusedChange", focused);
194
190
  this._prevFocused = focused;
191
+ if (focused !== undefined) {
192
+ this.boxManager?.focusBox({ appId: focused });
193
+ // 确保 focus 修改的时候, appProxy 已经创建
194
+ setTimeout(() => {
195
+ const appProxy = this.appProxies.get(focused);
196
+ if (appProxy) {
197
+ appRegister.notifyApp(appProxy.kind, "focus", { appId: focused });
198
+ }
199
+ }, 0);
200
+ }
195
201
  }
196
202
  });
197
203
  });
@@ -223,19 +229,18 @@ export class AppManager {
223
229
  createdAt: apps[appId].createdAt,
224
230
  };
225
231
  });
226
- for (const { id } of sortBy(appsWithCreatedAt, "createdAt")) {
232
+ for (const { id } of orderBy(appsWithCreatedAt, "createdAt", "asc")) {
227
233
  if (!this.appProxies.has(id) && !this.appStatus.has(id)) {
228
234
  const app = apps[id];
229
235
 
230
- pRetry(
231
- async () => {
232
- this.appStatus.set(id, AppStatus.StartCreate);
233
- // 防御 appAttributes 有可能为 undefined 的情况,这里做一个重试
234
- const appAttributes = this.attributes[id];
235
- if (!appAttributes) {
236
- throw new Error("appAttributes is undefined");
237
- }
238
- await this.baseInsertApp(
236
+ this.appStatus.set(id, AppStatus.StartCreate);
237
+ try {
238
+ const appAttributes = this.attributes[id];
239
+ if (!appAttributes) {
240
+ throw new Error("appAttributes is undefined");
241
+ }
242
+ this.appCreateQueue.push(() => {
243
+ return this.baseInsertApp(
239
244
  {
240
245
  kind: app.kind,
241
246
  options: app.options,
@@ -244,13 +249,11 @@ export class AppManager {
244
249
  id,
245
250
  false
246
251
  );
247
- this.focusByAttributes(apps);
248
- },
249
- { retries: 3 }
250
- ).catch(err => {
251
- console.warn(`[WindowManager]: Insert App Error`, err);
252
- this.appStatus.delete(id);
253
- });
252
+ });
253
+ this.focusByAttributes(apps);
254
+ } catch (error) {
255
+ console.warn(`[WindowManager]: Insert App Error`, error);
256
+ }
254
257
  }
255
258
  }
256
259
  }
@@ -436,7 +439,7 @@ export class AppManager {
436
439
  this.safeSetAttributes({ _mainScenePath: scenePath });
437
440
  this.store.setMainViewFocusPath(this.mainView);
438
441
  this.updateSceneIndex();
439
- this.dispatchInternalEvent(Events.SetMainViewScenePath, { nextScenePath: scenePath });
442
+ this.dispatchSetMainViewScenePath(scenePath);
440
443
  }
441
444
  }
442
445
 
@@ -466,9 +469,7 @@ export class AppManager {
466
469
  if (success) {
467
470
  this.store.setMainViewScenePath(scenePath);
468
471
  this.safeSetAttributes({ _mainSceneIndex: index });
469
- this.dispatchInternalEvent(Events.SetMainViewScenePath, {
470
- nextScenePath: scenePath,
471
- });
472
+ this.dispatchSetMainViewScenePath(scenePath);
472
473
  }
473
474
  } else {
474
475
  throw new Error(`[WindowManager]: ${sceneDir}: ${index} not valid index`);
@@ -477,6 +478,12 @@ export class AppManager {
477
478
  }
478
479
  }
479
480
 
481
+ private dispatchSetMainViewScenePath(scenePath: string): void {
482
+ this.dispatchInternalEvent(Events.SetMainViewScenePath, { nextScenePath: scenePath });
483
+ // 兼容 15 的 SDK, 需要 room 的当前 ScenePath
484
+ setScenePath(this.room, scenePath);
485
+ }
486
+
480
487
  public getAppInitPath(appId: string): string | undefined {
481
488
  const attrs = this.store.getAppAttributes(appId);
482
489
  if (attrs) {
@@ -583,6 +590,7 @@ export class AppManager {
583
590
  this.mainViewProxy.destroy();
584
591
  callbacks.clearListeners();
585
592
  this.callbacksNode?.dispose();
593
+ this.appCreateQueue.destroy();
586
594
  this._prevSceneIndex = undefined;
587
595
  }
588
596
  }
package/src/AppProxy.ts CHANGED
@@ -165,7 +165,7 @@ export class AppProxy {
165
165
  // 延迟执行 setup, 防止初始化的属性没有更新成功
166
166
  const result = await app.setup(context);
167
167
  this.appResult = result;
168
- appRegister.notifyApp(app.kind, "created", { appId, result });
168
+ appRegister.notifyApp(this.kind, "created", { appId, result });
169
169
  this.afterSetupApp(boxInitState);
170
170
  this.fixMobileSize();
171
171
  }, 50);
package/src/Helper.ts CHANGED
@@ -1,3 +1,7 @@
1
+ import { getVersionNumber } from "./Utils/Common";
2
+ import { REQUIRE_VERSION } from "./constants";
3
+ import { WhiteVersion } from "white-web-sdk";
4
+ import { WhiteWebSDKInvalidError } from "./Utils/error";
1
5
  import { WindowManager } from "./index";
2
6
 
3
7
  export const setupWrapper = (
@@ -27,4 +31,11 @@ export const setupWrapper = (
27
31
  WindowManager.wrapper = wrapper;
28
32
 
29
33
  return { playground, wrapper, sizer, mainViewElement };
30
- };
34
+ };
35
+
36
+ export const checkVersion = () => {
37
+ const version = getVersionNumber(WhiteVersion);
38
+ if (version < getVersionNumber(REQUIRE_VERSION)) {
39
+ throw new WhiteWebSDKInvalidError(REQUIRE_VERSION);
40
+ }
41
+ };
@@ -0,0 +1,54 @@
1
+ import type { AppProxy } from "../AppProxy";
2
+
3
+ export type Invoker = () => Promise<AppProxy | undefined>;
4
+
5
+ export class AppCreateQueue {
6
+ private list: Invoker[] = [];
7
+ private currentInvoker: Invoker | undefined;
8
+ private timer: number | undefined;
9
+
10
+ private initInterval() {
11
+ return setInterval(() => {
12
+ this.invoke();
13
+ }, 50);
14
+ }
15
+
16
+ public push(item: Invoker) {
17
+ this.list.push(item);
18
+ this.invoke();
19
+ if (this.timer === undefined && this.list.length > 0) {
20
+ this.timer = this.initInterval();
21
+ }
22
+ }
23
+
24
+ public invoke() {
25
+ if (this.list.length === 0) {
26
+ return;
27
+ }
28
+ if (this.currentInvoker !== undefined) {
29
+ return;
30
+ }
31
+
32
+ const item = this.list.shift();
33
+ if (item) {
34
+ this.currentInvoker = item;
35
+ item()
36
+ .then(() => {
37
+ this.currentInvoker = undefined;
38
+ if (this.list.length === 0) {
39
+ clearInterval(this.timer);
40
+ }
41
+ })
42
+ .catch(error => {
43
+ console.error(`[WindowManager]: create app error: ${error.message}`);
44
+ clearInterval(this.timer);
45
+ });
46
+ }
47
+ }
48
+
49
+ public destroy() {
50
+ if (this.timer) {
51
+ clearInterval(this.timer);
52
+ }
53
+ }
54
+ }
@@ -1,12 +1,11 @@
1
1
  import { appRegister } from "../Register";
2
- import { debounce, memoize } from "lodash";
2
+ import { debounce } from "lodash";
3
3
  import { emitter } from "../index";
4
4
  import { ScenePathType } from "white-web-sdk";
5
5
  import { v4 } from "uuid";
6
6
  import type { PublicEvent } from "../index";
7
7
  import type { Displayer, ViewVisionMode, Room, View } from "white-web-sdk";
8
8
  import type Emittery from "emittery";
9
- import { log } from "./log";
10
9
  import { ROOT_DIR } from "../constants";
11
10
 
12
11
  export const genAppId = async (kind: string) => {
@@ -101,15 +100,9 @@ export const makeValidScenePath = (displayer: Displayer, scenePath: string, inde
101
100
  };
102
101
 
103
102
  export const entireScenes = (displayer: Displayer) => {
104
- // 缓存 entireScenes 结果, 避免频繁调用
105
- const cacheKey = Math.round(Date.now() / 1000);
106
- return invokeEntireScenes(cacheKey, displayer);
103
+ return displayer.entireScenes();
107
104
  };
108
105
 
109
- const invokeEntireScenes = memoize((cacheKey: number, displayer: Displayer) => {
110
- log("invokeEntireScenes", cacheKey);
111
- return displayer.entireScenes();
112
- });
113
106
 
114
107
  export const isValidScenePath = (scenePath: string) => {
115
108
  return scenePath.startsWith("/");
@@ -4,11 +4,12 @@ import type { WindowManager } from "../index";
4
4
  import type { Camera, Room, Player, PlayerSeekingResult } from "white-web-sdk";
5
5
 
6
6
  // 修改多窗口状态下一些失效的方法实现到 manager 的 mainview 上, 降低迁移成本
7
- export const replaceRoomFunction = (room: Room, manager: WindowManager) => {
7
+ export const replaceRoomFunction = (room: Room | Player, manager: WindowManager) => {
8
8
  if (isPlayer(room)) {
9
9
  const player = room as unknown as Player;
10
10
  delegateSeekToProgressTime(player);
11
11
  } else {
12
+ room = room as unknown as Room;
12
13
  const descriptor = Object.getOwnPropertyDescriptor(room, "disableCameraTransform");
13
14
  if (descriptor) return;
14
15
  Object.defineProperty(room, "disableCameraTransform", {
package/src/index.ts CHANGED
@@ -2,25 +2,25 @@ import Emittery from "emittery";
2
2
  import pRetry from "p-retry";
3
3
  import { AppManager } from "./AppManager";
4
4
  import { appRegister } from "./Register";
5
+ import { checkVersion, setupWrapper } from "./Helper";
5
6
  import { ContainerResizeObserver } from "./ContainerResizeObserver";
6
7
  import { createBoxManager } from "./BoxManager";
7
8
  import { CursorManager } from "./Cursor";
8
- import { DEFAULT_CONTAINER_RATIO, Events, REQUIRE_VERSION } from "./constants";
9
+ import { DEFAULT_CONTAINER_RATIO, Events } from "./constants";
9
10
  import { Fields } from "./AttributesDelegate";
10
11
  import { initDb } from "./Register/storage";
12
+ import { InvisiblePlugin, isPlayer, isRoom, RoomPhase, ViewMode } from "white-web-sdk";
11
13
  import { isNull, isObject } from "lodash";
12
14
  import { log } from "./Utils/log";
13
15
  import { ReconnectRefresher } from "./ReconnectRefresher";
14
16
  import { replaceRoomFunction } from "./Utils/RoomHacker";
15
17
  import { setupBuiltin } from "./BuiltinApps";
16
- import { setupWrapper } from "./Helper";
17
18
  import "./style.css";
18
19
  import "@netless/telebox-insider/dist/style.css";
19
20
  import {
20
21
  addEmitterOnceListener,
21
22
  ensureValidScenePath,
22
23
  entireScenes,
23
- getVersionNumber,
24
24
  isValidScenePath,
25
25
  wait,
26
26
  } from "./Utils/Common";
@@ -30,17 +30,8 @@ import {
30
30
  AppManagerNotInitError,
31
31
  InvalidScenePath,
32
32
  ParamsInvalidError,
33
- WhiteWebSDKInvalidError,
34
33
  } from "./Utils/error";
35
34
  import type { Apps, Position } from "./AttributesDelegate";
36
- import {
37
- InvisiblePlugin,
38
- isPlayer,
39
- isRoom,
40
- RoomPhase,
41
- ViewMode,
42
- WhiteVersion,
43
- } from "white-web-sdk";
44
35
  import type {
45
36
  Displayer,
46
37
  SceneDefinition,
@@ -54,6 +45,7 @@ import type {
54
45
  Rectangle,
55
46
  ViewVisionMode,
56
47
  CameraState,
48
+ Player,
57
49
  } from "white-web-sdk";
58
50
  import type { AppListeners } from "./AppListener";
59
51
  import type { NetlessApp, RegisterParams } from "./typings";
@@ -161,7 +153,7 @@ export type PublicEvent = {
161
153
  };
162
154
 
163
155
  export type MountParams = {
164
- room: Room;
156
+ room: Room | Player;
165
157
  container?: HTMLElement;
166
158
  /** 白板高宽比例, 默认为 9 / 16 */
167
159
  containerSizeRatio?: number;
@@ -223,7 +215,8 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
223
215
  const cursor = params.cursor;
224
216
  WindowManager.params = params;
225
217
 
226
- this.checkVersion();
218
+ checkVersion();
219
+ let manager: WindowManager | undefined = undefined;
227
220
  if (isRoom(room)) {
228
221
  if (room.phase !== RoomPhase.Connected) {
229
222
  throw new Error("[WindowManager]: Room only Connected can be mount");
@@ -232,11 +225,12 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
232
225
  // redo undo 需要设置这个属性
233
226
  room.disableSerialization = false;
234
227
  }
228
+ manager = await this.initManager(room);
235
229
  }
236
230
  if (WindowManager.isCreated) {
237
231
  throw new Error("[WindowManager]: Already created cannot be created again");
238
232
  }
239
- let manager = await this.initManager(room);
233
+
240
234
  this.debug = Boolean(debug);
241
235
  log("Already insert room", manager);
242
236
 
@@ -247,7 +241,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
247
241
  } else {
248
242
  await pRetry(
249
243
  async count => {
250
- manager = await this.initManager(room);
244
+ manager = (await room.getInvisiblePlugin(WindowManager.kind)) as WindowManager;
251
245
  if (!manager) {
252
246
  log(`manager is empty. retrying ${count}`);
253
247
  throw new Error();
@@ -257,6 +251,10 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
257
251
  );
258
252
  }
259
253
 
254
+ if (!manager) {
255
+ throw new Error("[WindowManager]: create manager failed");
256
+ }
257
+
260
258
  if (containerSizeRatio) {
261
259
  WindowManager.containerSizeRatio = containerSizeRatio;
262
260
  }
@@ -742,18 +740,20 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
742
740
  this.appManager?.boxManager?.setPrefersColorScheme(scheme);
743
741
  }
744
742
 
743
+ public cleanCurrentScene(): void {
744
+ const focused = this.focused;
745
+ if (focused) {
746
+ this.appManager?.focusApp?.view?.cleanCurrentScene();
747
+ } else {
748
+ this.mainView.cleanCurrentScene();
749
+ }
750
+ }
751
+
745
752
  private isDynamicPPT(scenes: SceneDefinition[]) {
746
753
  const sceneSrc = scenes[0]?.ppt?.src;
747
754
  return sceneSrc?.startsWith("pptx://");
748
755
  }
749
756
 
750
- private static checkVersion() {
751
- const version = getVersionNumber(WhiteVersion);
752
- if (version < getVersionNumber(REQUIRE_VERSION)) {
753
- throw new WhiteWebSDKInvalidError(REQUIRE_VERSION);
754
- }
755
- }
756
-
757
757
  private async ensureAttributes() {
758
758
  if (isNull(this.attributes)) {
759
759
  await wait(50);