@netless/window-manager 0.4.9-canary.2 → 0.4.11-canary.0
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/CHANGELOG.md +13 -0
- package/dist/AppManager.d.ts +5 -0
- package/dist/AppProxy.d.ts +1 -1
- package/dist/AttributesDelegate.d.ts +2 -1
- package/dist/InternalEmitter.d.ts +1 -0
- package/dist/Register/index.d.ts +10 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.es.js +5 -5
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +5 -5
- package/dist/index.umd.js.map +1 -1
- package/docs/develop-app.md +60 -22
- package/package.json +2 -2
- package/src/AppContext.ts +12 -3
- package/src/AppManager.ts +74 -31
- package/src/AppProxy.ts +2 -1
- package/src/AttributesDelegate.ts +1 -0
- package/src/InternalEmitter.ts +1 -0
- package/src/Register/index.ts +28 -1
- package/src/index.ts +12 -0
package/docs/develop-app.md
CHANGED
@@ -11,28 +11,6 @@ const HelloWorld: NetlessApp = {
|
|
11
11
|
kind: "HelloWorld",
|
12
12
|
setup: (context: AppContext) => {
|
13
13
|
context.mountView(context.getBox().$content); // 可选: 挂载 View 到 box 上
|
14
|
-
|
15
|
-
const storage = context.createStorage<{ a: number }>("HelloWorld", { a: 1 });
|
16
|
-
console.log(storage.state === { a: 1 });
|
17
|
-
|
18
|
-
storage.addStateChangedListener(diff => {
|
19
|
-
if (diff.a) {
|
20
|
-
console.log(diff.a.oldValue === 1);
|
21
|
-
console.log(diff.a.newValue === 2);
|
22
|
-
}
|
23
|
-
});
|
24
|
-
|
25
|
-
if (context.getIsWritable()) {
|
26
|
-
// 只有在可写状态才可以调用 setState
|
27
|
-
storage.setState({ a: 2 });
|
28
|
-
}
|
29
|
-
|
30
|
-
// magixEvent 事件是房间内范围的, 建议 app 内使用需要添加自己的 prefix
|
31
|
-
context.addMagixEventListener(`${context.appId}_event1`, message => {
|
32
|
-
console.log("MagixEvent", message);
|
33
|
-
});
|
34
|
-
|
35
|
-
context.dispatchMagixEvent(`${context.appId}_event1`, { count: 1 });
|
36
14
|
},
|
37
15
|
};
|
38
16
|
|
@@ -48,3 +26,63 @@ manager.addApp({
|
|
48
26
|
},
|
49
27
|
});
|
50
28
|
```
|
29
|
+
|
30
|
+
## Counter
|
31
|
+
|
32
|
+
```ts
|
33
|
+
const Counter: NetlessApp<{ count: number }> = {
|
34
|
+
kind: "Counter",
|
35
|
+
setup: (context) => {
|
36
|
+
const storage = context.storage;
|
37
|
+
storage.ensureState({ count: 0 });
|
38
|
+
|
39
|
+
const box = context.getBox(); // box 为这个应用打开的窗口
|
40
|
+
const $content = box.$content // 获取窗口的 content
|
41
|
+
|
42
|
+
const countDom = document.createElement("div");
|
43
|
+
countDom.innerText = storage.state.count.toString();
|
44
|
+
$content.appendChild(countDom);
|
45
|
+
|
46
|
+
// state 变化回调
|
47
|
+
storage.addStateChangedListener(diff => {
|
48
|
+
if (diff.count) {
|
49
|
+
// diff 会给出 newValue 和 oldValue
|
50
|
+
console.log(diff.count.newValue);
|
51
|
+
console.log(diff.count.oldValue);
|
52
|
+
countDom.innerText = diff.count.newValue.toString();
|
53
|
+
}
|
54
|
+
});
|
55
|
+
|
56
|
+
const incButton = document.createElement("button");
|
57
|
+
incButton.innerText = "Inc";
|
58
|
+
const incButtonOnClick = () => {
|
59
|
+
storage.setState({ count: storage.state.count + 1 });
|
60
|
+
}
|
61
|
+
incButton.addEventListener("click", incButtonOnClick);
|
62
|
+
$content.appendChild(incButton);
|
63
|
+
|
64
|
+
const decButton = document.createElement("button");
|
65
|
+
decButton.innerText = "Dec";
|
66
|
+
const decButtonOnClick = () => {
|
67
|
+
storage.setState({ count: storage.state.count - 1 });
|
68
|
+
}
|
69
|
+
decButton.addEventListener("click", decButtonOnClick);
|
70
|
+
$content.appendChild(decButton);
|
71
|
+
|
72
|
+
// 监听事件
|
73
|
+
const event1Disposer = context.addMagixEventListener("event1", msg => {
|
74
|
+
console.log("event1", msg);
|
75
|
+
});
|
76
|
+
|
77
|
+
// 向打开 app 的其他人发送消息
|
78
|
+
context.dispatchMagixEvent("event1", { count: 10 });
|
79
|
+
|
80
|
+
// 应用销毁时, 注意清理掉监听器
|
81
|
+
context.emitter.on("destroy", () => {
|
82
|
+
incButton.removeEventListener("click", incButtonOnClick);
|
83
|
+
decButton.removeEventListener("click", decButtonOnClick);
|
84
|
+
event1Disposer();
|
85
|
+
});
|
86
|
+
}
|
87
|
+
}
|
88
|
+
```
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@netless/window-manager",
|
3
|
-
"version": "0.4.
|
3
|
+
"version": "0.4.11-canary.0",
|
4
4
|
"description": "",
|
5
5
|
"main": "dist/index.es.js",
|
6
6
|
"module": "dist/index.es.js",
|
@@ -31,7 +31,7 @@
|
|
31
31
|
"video.js": ">=7"
|
32
32
|
},
|
33
33
|
"devDependencies": {
|
34
|
-
"@netless/app-docs-viewer": "^0.2.
|
34
|
+
"@netless/app-docs-viewer": "^0.2.8",
|
35
35
|
"@netless/app-media-player": "0.1.0-beta.5",
|
36
36
|
"@rollup/plugin-commonjs": "^20.0.0",
|
37
37
|
"@rollup/plugin-node-resolve": "^13.0.4",
|
package/src/AppContext.ts
CHANGED
@@ -165,7 +165,8 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
|
|
165
165
|
/** Dispatch events to other clients (and self). */
|
166
166
|
public dispatchMagixEvent: MagixEventDispatcher<TMagixEventPayloads> = (...args) => {
|
167
167
|
// can't dispatch events on replay mode
|
168
|
-
|
168
|
+
const appScopeEvent = `${this.appId}:${args[0]}`;
|
169
|
+
return this.manager.room?.dispatchMagixEvent(appScopeEvent, args[1]);
|
169
170
|
};
|
170
171
|
|
171
172
|
/** Listen to events from others clients (and self messages). */
|
@@ -174,9 +175,17 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
|
|
174
175
|
handler,
|
175
176
|
options
|
176
177
|
) => {
|
177
|
-
this.
|
178
|
+
const appScopeEvent = `${this.appId}:${event}`;
|
179
|
+
this.manager.displayer.addMagixEventListener(
|
180
|
+
appScopeEvent,
|
181
|
+
handler as WhiteEventListener,
|
182
|
+
options
|
183
|
+
);
|
178
184
|
return () =>
|
179
|
-
this.manager.displayer.removeMagixEventListener(
|
185
|
+
this.manager.displayer.removeMagixEventListener(
|
186
|
+
appScopeEvent,
|
187
|
+
handler as WhiteEventListener
|
188
|
+
);
|
180
189
|
};
|
181
190
|
|
182
191
|
/** Remove a Magix event listener. */
|
package/src/AppManager.ts
CHANGED
@@ -6,6 +6,7 @@ import { appRegister } from "./Register";
|
|
6
6
|
import { autorun, isPlayer, isRoom, ScenePathType } from "white-web-sdk";
|
7
7
|
import { callbacks } from "./callback";
|
8
8
|
import { emitter } from "./InternalEmitter";
|
9
|
+
import { Fields, store } from "./AttributesDelegate";
|
9
10
|
import { get, isInteger, orderBy } from "lodash";
|
10
11
|
import { log } from "./Utils/log";
|
11
12
|
import { MainViewProxy } from "./View/MainView";
|
@@ -13,8 +14,8 @@ import { onObjectRemoved, safeListenPropsUpdated } from "./Utils/Reactive";
|
|
13
14
|
import { reconnectRefresher, WindowManager } from "./index";
|
14
15
|
import { RedoUndo } from "./RedoUndo";
|
15
16
|
import { SideEffectManager } from "side-effect-manager";
|
16
|
-
import { store } from "./AttributesDelegate";
|
17
17
|
import { ViewManager } from "./View/ViewManager";
|
18
|
+
import type { SyncRegisterAppPayload } from "./Register";
|
18
19
|
import type { EmitterEvent } from "./InternalEmitter";
|
19
20
|
import {
|
20
21
|
entireScenes,
|
@@ -34,6 +35,7 @@ import type {
|
|
34
35
|
SceneState,
|
35
36
|
} from "white-web-sdk";
|
36
37
|
import type { AddAppParams, BaseInsertParams, TeleBoxRect } from "./index";
|
38
|
+
|
37
39
|
export class AppManager {
|
38
40
|
public displayer: Displayer;
|
39
41
|
public viewManager: ViewManager;
|
@@ -88,33 +90,37 @@ export class AppManager {
|
|
88
90
|
|
89
91
|
emitter.once("onCreated").then(() => this.onCreated());
|
90
92
|
emitter.on("onReconnected", () => this.onReconnected());
|
93
|
+
|
91
94
|
if (isPlayer(this.displayer)) {
|
92
|
-
emitter.on("seek",
|
93
|
-
this.appProxies.forEach(appProxy => {
|
94
|
-
appProxy.onSeek(time);
|
95
|
-
});
|
96
|
-
this.attributesUpdateCallback(this.attributes.apps);
|
97
|
-
this.onAppDelete(this.attributes.apps);
|
98
|
-
});
|
95
|
+
emitter.on("seek", this.onPlayerSeek);
|
99
96
|
}
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
this.onRootDirRemoved();
|
105
|
-
emitter.emit("rootDirRemoved");
|
106
|
-
return;
|
107
|
-
}
|
108
|
-
const mainViewScenePath = this.store.getMainViewScenePath();
|
109
|
-
if (this.room && mainViewScenePath) {
|
110
|
-
if (mainViewScenePath === scenePath) {
|
111
|
-
this.setMainViewScenePath(ROOT_DIR);
|
112
|
-
}
|
113
|
-
}
|
114
|
-
});
|
97
|
+
|
98
|
+
emitter.on("removeScenes", this.onRemoveScenes);
|
99
|
+
emitter.on("setReadonly", this.onReadonlyChanged);
|
100
|
+
|
115
101
|
this.createRootDirScenesCallback();
|
102
|
+
|
103
|
+
appRegister.setSyncRegisterApp(payload => {
|
104
|
+
this.safeUpdateAttributes([Fields.Registered, payload.kind], payload);
|
105
|
+
});
|
116
106
|
}
|
117
107
|
|
108
|
+
private onRemoveScenes = (scenePath: string) => {
|
109
|
+
if (scenePath === ROOT_DIR) {
|
110
|
+
this.setMainViewScenePath(ROOT_DIR);
|
111
|
+
this.createRootDirScenesCallback();
|
112
|
+
this.onRootDirRemoved();
|
113
|
+
emitter.emit("rootDirRemoved");
|
114
|
+
return;
|
115
|
+
}
|
116
|
+
const mainViewScenePath = this.store.getMainViewScenePath();
|
117
|
+
if (this.room && mainViewScenePath) {
|
118
|
+
if (mainViewScenePath === scenePath) {
|
119
|
+
this.setMainViewScenePath(ROOT_DIR);
|
120
|
+
}
|
121
|
+
}
|
122
|
+
};
|
123
|
+
|
118
124
|
/**
|
119
125
|
* 根目录被删除时所有的 scene 都会被删除.
|
120
126
|
* 所以需要关掉所有开启了 view 的 app
|
@@ -129,6 +135,20 @@ export class AppManager {
|
|
129
135
|
this.mainViewProxy.rebind();
|
130
136
|
}
|
131
137
|
|
138
|
+
private onReadonlyChanged = () => {
|
139
|
+
this.appProxies.forEach(appProxy => {
|
140
|
+
appProxy.emitAppIsWritableChange();
|
141
|
+
});
|
142
|
+
};
|
143
|
+
|
144
|
+
private onPlayerSeek = (time: number) => {
|
145
|
+
this.appProxies.forEach(appProxy => {
|
146
|
+
appProxy.onSeek(time);
|
147
|
+
});
|
148
|
+
this.attributesUpdateCallback(this.attributes.apps);
|
149
|
+
this.onAppDelete(this.attributes.apps);
|
150
|
+
};
|
151
|
+
|
132
152
|
private createRootDirScenesCallback = () => {
|
133
153
|
let isRecreate = false;
|
134
154
|
if (this.callbacksNode) {
|
@@ -236,14 +256,7 @@ export class AppManager {
|
|
236
256
|
this.refresher?.add("minimized", () => {
|
237
257
|
return autorun(() => {
|
238
258
|
const minimized = this.attributes.minimized;
|
239
|
-
|
240
|
-
if (minimized === true) {
|
241
|
-
this.boxManager?.blurAllBox();
|
242
|
-
}
|
243
|
-
setTimeout(() => {
|
244
|
-
this.boxManager?.setMinimized(Boolean(minimized));
|
245
|
-
}, 0);
|
246
|
-
}
|
259
|
+
this.onMinimized(minimized);
|
247
260
|
});
|
248
261
|
});
|
249
262
|
this.refresher?.add("mainViewIndex", () => {
|
@@ -278,6 +291,12 @@ export class AppManager {
|
|
278
291
|
}
|
279
292
|
});
|
280
293
|
});
|
294
|
+
this.refresher?.add("registeredChange", () => {
|
295
|
+
return autorun(() => {
|
296
|
+
const registered = get(this.attributes, Fields.Registered);
|
297
|
+
this.onRegisteredChange(registered);
|
298
|
+
});
|
299
|
+
});
|
281
300
|
if (!this.attributes.apps || Object.keys(this.attributes.apps).length === 0) {
|
282
301
|
const mainScenePath = this.store.getMainViewScenePath();
|
283
302
|
if (!mainScenePath) return;
|
@@ -348,6 +367,30 @@ export class AppManager {
|
|
348
367
|
}
|
349
368
|
}
|
350
369
|
|
370
|
+
private onRegisteredChange = (registered: Record<string, SyncRegisterAppPayload>) => {
|
371
|
+
if (!registered) return;
|
372
|
+
Object.entries(registered).forEach(([kind, payload]) => {
|
373
|
+
if (!appRegister.appClasses.has(kind)) {
|
374
|
+
appRegister.register({
|
375
|
+
kind,
|
376
|
+
src: payload.src,
|
377
|
+
name: payload.name,
|
378
|
+
});
|
379
|
+
}
|
380
|
+
});
|
381
|
+
};
|
382
|
+
|
383
|
+
private onMinimized = (minimized: boolean | undefined) => {
|
384
|
+
if (this.boxManager?.minimized !== minimized) {
|
385
|
+
if (minimized === true) {
|
386
|
+
this.boxManager?.blurAllBox();
|
387
|
+
}
|
388
|
+
setTimeout(() => {
|
389
|
+
this.boxManager?.setMinimized(Boolean(minimized));
|
390
|
+
}, 0);
|
391
|
+
}
|
392
|
+
}
|
393
|
+
|
351
394
|
public refresh() {
|
352
395
|
this.attributesUpdateCallback(this.attributes.apps);
|
353
396
|
}
|
package/src/AppProxy.ts
CHANGED
@@ -92,7 +92,7 @@ export class AppProxy {
|
|
92
92
|
|
93
93
|
public getFullScenePath(): string | undefined {
|
94
94
|
if (this.scenePath) {
|
95
|
-
return get(this.appAttributes, [Fields.FullPath]
|
95
|
+
return get(this.appAttributes, [Fields.FullPath]) || this.getFullScenePathFromScenes();
|
96
96
|
}
|
97
97
|
}
|
98
98
|
|
@@ -347,6 +347,7 @@ export class AppProxy {
|
|
347
347
|
if (fullPath && this.view) {
|
348
348
|
setViewFocusScenePath(this.view, fullPath);
|
349
349
|
}
|
350
|
+
return fullPath;
|
350
351
|
}
|
351
352
|
|
352
353
|
private async createView(): Promise<View> {
|
package/src/InternalEmitter.ts
CHANGED
package/src/Register/index.ts
CHANGED
@@ -8,12 +8,25 @@ export type LoadAppEvent = {
|
|
8
8
|
reason?: string;
|
9
9
|
};
|
10
10
|
|
11
|
+
export type SyncRegisterAppPayload = { kind: string, src: string, name: string | undefined };
|
12
|
+
export type SyncRegisterApp = (payload: SyncRegisterAppPayload) => void;
|
13
|
+
|
11
14
|
class AppRegister {
|
12
15
|
public kindEmitters: Map<string, Emittery<RegisterEvents>> = new Map();
|
13
16
|
public registered: Map<string, RegisterParams> = new Map();
|
14
17
|
public appClassesCache: Map<string, Promise<NetlessApp>> = new Map();
|
15
18
|
public appClasses: Map<string, () => Promise<NetlessApp>> = new Map();
|
16
19
|
|
20
|
+
private syncRegisterApp: SyncRegisterApp | null = null;
|
21
|
+
|
22
|
+
public setSyncRegisterApp(fn: SyncRegisterApp) {
|
23
|
+
this.syncRegisterApp = fn;
|
24
|
+
}
|
25
|
+
|
26
|
+
public onSyncRegisterAppChange = (payload: SyncRegisterAppPayload) => {
|
27
|
+
this.register({ kind: payload.kind, src: payload.src });
|
28
|
+
}
|
29
|
+
|
17
30
|
public async register(params: RegisterParams): Promise<void> {
|
18
31
|
this.appClassesCache.delete(params.kind);
|
19
32
|
this.registered.set(params.kind, params);
|
@@ -23,7 +36,7 @@ class AppRegister {
|
|
23
36
|
|
24
37
|
if (typeof srcOrAppOrFunction === "string") {
|
25
38
|
downloadApp = async () => {
|
26
|
-
let appClass = (await loadApp(srcOrAppOrFunction, params.kind)) as any;
|
39
|
+
let appClass = (await loadApp(srcOrAppOrFunction, params.kind, params.name)) as any;
|
27
40
|
if (appClass) {
|
28
41
|
if (appClass.__esModule) {
|
29
42
|
appClass = appClass.default;
|
@@ -35,6 +48,9 @@ class AppRegister {
|
|
35
48
|
);
|
36
49
|
}
|
37
50
|
};
|
51
|
+
if (this.syncRegisterApp) {
|
52
|
+
this.syncRegisterApp({ kind: params.kind, src: srcOrAppOrFunction, name: params.name });
|
53
|
+
}
|
38
54
|
} else if (typeof srcOrAppOrFunction === "function") {
|
39
55
|
downloadApp = srcOrAppOrFunction;
|
40
56
|
} else {
|
@@ -58,6 +74,17 @@ class AppRegister {
|
|
58
74
|
}
|
59
75
|
}
|
60
76
|
|
77
|
+
public unregister(kind: string) {
|
78
|
+
this.appClasses.delete(kind);
|
79
|
+
this.appClassesCache.delete(kind);
|
80
|
+
this.registered.delete(kind);
|
81
|
+
const kindEmitter = this.kindEmitters.get(kind);
|
82
|
+
if (kindEmitter) {
|
83
|
+
kindEmitter.clearListeners();
|
84
|
+
this.kindEmitters.delete(kind);
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
61
88
|
public async notifyApp<T extends keyof RegisterEvents>(
|
62
89
|
kind: string,
|
63
90
|
event: T,
|
package/src/index.ts
CHANGED
@@ -368,6 +368,13 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
|
|
368
368
|
return appRegister.register(params);
|
369
369
|
}
|
370
370
|
|
371
|
+
/**
|
372
|
+
* 注销插件
|
373
|
+
*/
|
374
|
+
public static unregister(kind: string) {
|
375
|
+
return appRegister.unregister(kind);
|
376
|
+
}
|
377
|
+
|
371
378
|
/**
|
372
379
|
* 创建一个 app 至白板
|
373
380
|
*/
|
@@ -517,6 +524,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
|
|
517
524
|
public setReadonly(readonly: boolean): void {
|
518
525
|
this.readonly = readonly;
|
519
526
|
this.boxManager?.setReadonly(readonly);
|
527
|
+
emitter.emit("setReadonly", readonly);
|
520
528
|
}
|
521
529
|
|
522
530
|
/**
|
@@ -855,6 +863,9 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
|
|
855
863
|
if (!this.attributes["_mainSceneIndex"]) {
|
856
864
|
this.safeSetAttributes({ _mainSceneIndex: 0 });
|
857
865
|
}
|
866
|
+
if (!this.attributes[Fields.Registered]) {
|
867
|
+
this.safeSetAttributes({ [Fields.Registered]: {} });
|
868
|
+
}
|
858
869
|
}
|
859
870
|
}
|
860
871
|
}
|
@@ -864,3 +875,4 @@ setupBuiltin();
|
|
864
875
|
export * from "./typings";
|
865
876
|
|
866
877
|
export { BuiltinApps } from "./BuiltinApps";
|
878
|
+
export type { PublicEvent } from "./callback";
|