@netless/window-manager 0.4.32 → 1.0.0-canary.10
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/__mocks__/white-web-sdk.ts +10 -1
- package/dist/App/AppContext.d.ts +16 -15
- package/dist/App/AppPageStateImpl.d.ts +6 -2
- package/dist/App/AppProxy.d.ts +26 -5
- package/dist/App/AppViewSync.d.ts +11 -0
- package/dist/App/WhiteboardView.d.ts +24 -0
- package/dist/App/index.d.ts +1 -0
- package/dist/AppManager.d.ts +5 -3
- package/dist/AttributesDelegate.d.ts +6 -14
- package/dist/BoxManager.d.ts +9 -8
- package/dist/Helper.d.ts +12 -4
- package/dist/InternalEmitter.d.ts +6 -1
- package/dist/Page/PageController.d.ts +1 -0
- package/dist/ReconnectRefresher.d.ts +1 -1
- package/dist/Utils/Common.d.ts +1 -0
- package/dist/View/CameraSynchronizer.d.ts +17 -0
- package/dist/View/MainView.d.ts +4 -6
- package/dist/constants.d.ts +1 -0
- package/dist/index.cjs.js +21 -22
- package/dist/index.d.ts +6 -5
- package/dist/index.es.js +2512 -2059
- package/dist/index.umd.js +21 -22
- package/dist/style.css +1 -1
- package/dist/typings.d.ts +4 -0
- package/docs/app-context.md +98 -64
- package/docs/develop-app.md +2 -5
- package/package.json +4 -3
- package/pnpm-lock.yaml +90 -97
- package/src/App/AppContext.ts +72 -75
- package/src/App/AppPageStateImpl.ts +25 -6
- package/src/App/AppProxy.ts +206 -35
- package/src/App/AppViewSync.ts +73 -0
- package/src/App/Storage/index.ts +4 -4
- package/src/App/WhiteboardView.ts +89 -0
- package/src/App/index.ts +1 -0
- package/src/AppManager.ts +32 -23
- package/src/AttributesDelegate.ts +14 -17
- package/src/BoxManager.ts +107 -115
- package/src/Cursor/index.ts +5 -5
- package/src/Helper.ts +12 -16
- package/src/InternalEmitter.ts +10 -4
- package/src/Page/PageController.ts +1 -0
- package/src/ReconnectRefresher.ts +1 -0
- package/src/Utils/Common.ts +6 -0
- package/src/View/CameraSynchronizer.ts +72 -0
- package/src/View/MainView.ts +53 -78
- package/src/constants.ts +2 -0
- package/src/index.ts +31 -36
- package/src/style.css +9 -0
- package/src/typings.ts +4 -0
- package/vite.config.js +0 -1
- package/dist/ContainerResizeObserver.d.ts +0 -11
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.es.js.map +0 -1
- package/dist/index.umd.js.map +0 -1
- package/src/ContainerResizeObserver.ts +0 -73
package/src/App/AppProxy.ts
CHANGED
@@ -3,15 +3,22 @@ import { AppAttributes, AppEvents, Events, SETUP_APP_DELAY } from "../constants"
|
|
3
3
|
import { AppContext } from "./AppContext";
|
4
4
|
import { AppPageStateImpl } from "./AppPageStateImpl";
|
5
5
|
import { appRegister } from "../Register";
|
6
|
-
import {
|
6
|
+
import { AppViewSync } from "./AppViewSync";
|
7
|
+
import { autorun, reaction, toJS } from "white-web-sdk";
|
8
|
+
import { boxEmitter } from "../BoxEmitter";
|
7
9
|
import { BoxManagerNotFoundError } from "../Utils/error";
|
10
|
+
import { calculateNextIndex } from "../Page";
|
11
|
+
import { combine, Val, ValManager } from "value-enhancer";
|
8
12
|
import { debounce, get } from "lodash";
|
9
13
|
import { emitter } from "../InternalEmitter";
|
10
14
|
import { Fields } from "../AttributesDelegate";
|
11
15
|
import { log } from "../Utils/log";
|
16
|
+
import { SideEffectManager } from "side-effect-manager";
|
17
|
+
import type { ICamera, ISize } from "../AttributesDelegate";
|
12
18
|
import {
|
13
19
|
entireScenes,
|
14
20
|
getScenePath,
|
21
|
+
putScenes,
|
15
22
|
removeScenes,
|
16
23
|
setScenePath,
|
17
24
|
setViewFocusScenePath,
|
@@ -23,13 +30,11 @@ import type {
|
|
23
30
|
setAppOptions,
|
24
31
|
AppListenerKeys,
|
25
32
|
} from "../index";
|
26
|
-
import type { SceneState, View, SceneDefinition } from "white-web-sdk";
|
33
|
+
import type { SceneState, View, SceneDefinition, Camera , MemberState} from "white-web-sdk";
|
27
34
|
import type { AppManager } from "../AppManager";
|
28
35
|
import type { NetlessApp } from "../typings";
|
29
|
-
import type { ReadonlyTeleBox } from "@netless/telebox-insider";
|
36
|
+
import type { ReadonlyTeleBox, TeleBoxRect } from "@netless/telebox-insider";
|
30
37
|
import type { PageRemoveService, PageState } from "../Page";
|
31
|
-
import { calculateNextIndex } from "../Page";
|
32
|
-
import { boxEmitter } from "../BoxEmitter";
|
33
38
|
|
34
39
|
export type AppEmitter = Emittery<AppEmitterEvent>;
|
35
40
|
|
@@ -37,6 +42,7 @@ export class AppProxy implements PageRemoveService {
|
|
37
42
|
public kind: string;
|
38
43
|
public id: string;
|
39
44
|
public scenePath?: string;
|
45
|
+
private appScenePath: string;
|
40
46
|
public appEmitter: AppEmitter;
|
41
47
|
public scenes?: SceneDefinition[];
|
42
48
|
|
@@ -45,16 +51,27 @@ export class AppProxy implements PageRemoveService {
|
|
45
51
|
private appProxies = this.manager.appProxies;
|
46
52
|
private viewManager = this.manager.viewManager;
|
47
53
|
private store = this.manager.store;
|
54
|
+
public uid = this.manager.uid;
|
48
55
|
|
49
56
|
public isAddApp: boolean;
|
50
|
-
|
57
|
+
public status: "normal" | "destroyed" = "normal";
|
51
58
|
private stateKey: string;
|
52
|
-
|
53
|
-
private _prevFullPath: string | undefined;
|
59
|
+
public _pageState: AppPageStateImpl;
|
54
60
|
|
55
61
|
public appResult?: NetlessApp<any>;
|
56
62
|
public appContext?: AppContext<any, any>;
|
57
63
|
|
64
|
+
private sideEffectManager = new SideEffectManager();
|
65
|
+
private valManager = new ValManager();
|
66
|
+
|
67
|
+
private fullPath$ = this.valManager.attach(new Val<string | undefined>(undefined));
|
68
|
+
private appViewSync?: AppViewSync;
|
69
|
+
|
70
|
+
public camera$ = this.valManager.attach(new Val<ICamera | undefined>(undefined));
|
71
|
+
public size$ = this.valManager.attach(new Val<ISize | undefined>(undefined));
|
72
|
+
public box$ = this.valManager.attach(new Val<ReadonlyTeleBox | undefined>(undefined));
|
73
|
+
public view$ = this.valManager.attach(new Val<View | undefined>(undefined));
|
74
|
+
|
58
75
|
constructor(
|
59
76
|
private params: BaseInsertParams,
|
60
77
|
private manager: AppManager,
|
@@ -63,6 +80,7 @@ export class AppProxy implements PageRemoveService {
|
|
63
80
|
) {
|
64
81
|
this.kind = params.kind;
|
65
82
|
this.id = appId;
|
83
|
+
this.appScenePath = `/${this.id}-app-dir`;
|
66
84
|
this.stateKey = `${this.id}_state`;
|
67
85
|
this.appProxies.set(this.id, this);
|
68
86
|
this.appEmitter = new Emittery();
|
@@ -75,12 +93,109 @@ export class AppProxy implements PageRemoveService {
|
|
75
93
|
// 只有传入了 scenePath 的 App 才会创建 View
|
76
94
|
this.createView();
|
77
95
|
}
|
96
|
+
if (!this.scenePath) {
|
97
|
+
this.scenePath = this.appScenePath;
|
98
|
+
}
|
78
99
|
this._pageState = new AppPageStateImpl({
|
79
100
|
displayer: this.manager.displayer,
|
80
101
|
scenePath: this.scenePath,
|
81
102
|
view: this.view,
|
82
103
|
notifyPageStateChange: this.notifyPageStateChange,
|
83
104
|
});
|
105
|
+
this.sideEffectManager.add(() => () => this._pageState.destroy());
|
106
|
+
this.sideEffectManager.add(() =>
|
107
|
+
emitter.on("roomMembersChange", members => {
|
108
|
+
this.appEmitter.emit("roomMembersChange", members);
|
109
|
+
})
|
110
|
+
);
|
111
|
+
this.camera$.setValue(toJS(this.appAttributes.camera));
|
112
|
+
this.size$.setValue(toJS(this.appAttributes.size));
|
113
|
+
this.addCameraReaction();
|
114
|
+
this.addSizeReaction();
|
115
|
+
this.sideEffectManager.add(() =>
|
116
|
+
combine([this.box$, this.view$]).subscribe(([box, view]) => {
|
117
|
+
if (box && view) {
|
118
|
+
if (!this.camera$.value) {
|
119
|
+
this.storeCamera({
|
120
|
+
centerX: 0,
|
121
|
+
centerY: 0,
|
122
|
+
scale: 1,
|
123
|
+
id: this.uid,
|
124
|
+
});
|
125
|
+
this.camera$.setValue(toJS(this.appAttributes.camera));
|
126
|
+
}
|
127
|
+
if (!this.size$.value && box.contentStageRect) {
|
128
|
+
const initialRect = this.computedInitialRect(box.contentStageRect);
|
129
|
+
const width = initialRect?.width || box.contentStageRect.width;
|
130
|
+
const height = initialRect?.height || box.contentStageRect.height;
|
131
|
+
this.storeSize({
|
132
|
+
id: this.uid,
|
133
|
+
width,
|
134
|
+
height,
|
135
|
+
});
|
136
|
+
this.size$.setValue(toJS(this.appAttributes.size));
|
137
|
+
}
|
138
|
+
this.appViewSync = new AppViewSync(this);
|
139
|
+
this.sideEffectManager.add(() => () => this.appViewSync?.destroy());
|
140
|
+
}
|
141
|
+
})
|
142
|
+
);
|
143
|
+
this.sideEffectManager.add(() =>
|
144
|
+
emitter.on("memberStateChange", this.onMemberStateChange)
|
145
|
+
);
|
146
|
+
}
|
147
|
+
|
148
|
+
public fireMemberStateChange = () => {
|
149
|
+
if (this.manager.room) {
|
150
|
+
this.onMemberStateChange(this.manager.room.state.memberState);
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
private onMemberStateChange = (memberState: MemberState) => {
|
155
|
+
// clicker 教具把事件穿透给下层
|
156
|
+
const needPointerEventsNone = memberState.currentApplianceName === "clicker";
|
157
|
+
if (needPointerEventsNone) {
|
158
|
+
if (this.appContext?._viewWrapper) {
|
159
|
+
this.appContext._viewWrapper.style.pointerEvents = "none";
|
160
|
+
}
|
161
|
+
} else {
|
162
|
+
if (this.appContext?._viewWrapper) {
|
163
|
+
this.appContext._viewWrapper.style.pointerEvents = "auto";
|
164
|
+
}
|
165
|
+
}
|
166
|
+
}
|
167
|
+
|
168
|
+
private computedInitialRect = (boxRect: TeleBoxRect) => {
|
169
|
+
const managerRect = this.manager.boxManager?.stageRect;
|
170
|
+
if (managerRect) {
|
171
|
+
const { width, height } = managerRect;
|
172
|
+
const boxRatio = boxRect.height / boxRect.width;
|
173
|
+
if (height < 480) {
|
174
|
+
return {
|
175
|
+
width: 480 / boxRatio,
|
176
|
+
height: 480,
|
177
|
+
};
|
178
|
+
} else {
|
179
|
+
return {
|
180
|
+
width: width * 0.65,
|
181
|
+
height: height * 0.65,
|
182
|
+
};
|
183
|
+
}
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
public createAppDir() {
|
188
|
+
const scenePath = this.scenePath || this.appScenePath;
|
189
|
+
const sceneNode = this._pageState.createSceneNode(scenePath);
|
190
|
+
if (!sceneNode) {
|
191
|
+
putScenes(this.manager.room, scenePath, [{ name: "1" }]);
|
192
|
+
this._pageState.createSceneNode(scenePath);
|
193
|
+
this.setSceneIndex(0);
|
194
|
+
}
|
195
|
+
this.scenes = entireScenes(this.manager.displayer)[scenePath];
|
196
|
+
const view = this.createView();
|
197
|
+
this._pageState.setView(view);
|
198
|
+
return view;
|
84
199
|
}
|
85
200
|
|
86
201
|
private initScenes() {
|
@@ -96,7 +211,7 @@ export class AppProxy implements PageRemoveService {
|
|
96
211
|
}
|
97
212
|
|
98
213
|
public get view(): View | undefined {
|
99
|
-
return this.
|
214
|
+
return this.view$.value;
|
100
215
|
}
|
101
216
|
|
102
217
|
public get viewIndex(): number | undefined {
|
@@ -131,7 +246,7 @@ export class AppProxy implements PageRemoveService {
|
|
131
246
|
}
|
132
247
|
|
133
248
|
public setFullPath(path: string) {
|
134
|
-
this.
|
249
|
+
this.store.updateAppAttributes(this.id, Fields.FullPath, path);
|
135
250
|
}
|
136
251
|
|
137
252
|
public async baseInsertApp(skipUpdate = false): Promise<{ appId: string; app: NetlessApp }> {
|
@@ -160,7 +275,7 @@ export class AppProxy implements PageRemoveService {
|
|
160
275
|
}
|
161
276
|
|
162
277
|
public get box(): ReadonlyTeleBox | undefined {
|
163
|
-
return this.
|
278
|
+
return this.box$.value;
|
164
279
|
}
|
165
280
|
|
166
281
|
private async setupApp(
|
@@ -191,17 +306,17 @@ export class AppProxy implements PageRemoveService {
|
|
191
306
|
const result = await app.setup(context);
|
192
307
|
this.appResult = result;
|
193
308
|
appRegister.notifyApp(this.kind, "created", { appId, result });
|
194
|
-
this.afterSetupApp(boxInitState);
|
195
309
|
this.fixMobileSize();
|
196
310
|
}, SETUP_APP_DELAY);
|
197
311
|
});
|
198
|
-
this.boxManager?.createBox({
|
312
|
+
const box = this.boxManager?.createBox({
|
199
313
|
appId: appId,
|
200
314
|
app,
|
201
315
|
options,
|
202
316
|
canOperate: this.manager.canOperate,
|
203
317
|
smartPosition: this.isAddApp,
|
204
318
|
});
|
319
|
+
this.box$.setValue(box);
|
205
320
|
if (this.isAddApp && this.box) {
|
206
321
|
this.store.updateAppState(appId, AppAttributes.ZIndex, this.box.zIndex);
|
207
322
|
this.boxManager.focusBox({ appId }, false);
|
@@ -225,14 +340,6 @@ export class AppProxy implements PageRemoveService {
|
|
225
340
|
}
|
226
341
|
}
|
227
342
|
|
228
|
-
private afterSetupApp(boxInitState: AppInitState | undefined): void {
|
229
|
-
if (boxInitState) {
|
230
|
-
if (!boxInitState?.x || !boxInitState.y) {
|
231
|
-
this.boxManager?.setBoxInitState(this.id);
|
232
|
-
}
|
233
|
-
}
|
234
|
-
}
|
235
|
-
|
236
343
|
public async onSeek(time: number) {
|
237
344
|
this.appEmitter.emit("seek", time).catch(err => {
|
238
345
|
console.log(`[WindowManager]: emit seek error: ${err.message}`);
|
@@ -354,7 +461,7 @@ export class AppProxy implements PageRemoveService {
|
|
354
461
|
}
|
355
462
|
|
356
463
|
private appAttributesUpdateListener = (appId: string) => {
|
357
|
-
this.manager.refresher
|
464
|
+
this.manager.refresher.add(appId, () => {
|
358
465
|
return autorun(() => {
|
359
466
|
const attrs = this.manager.attributes[appId];
|
360
467
|
if (attrs) {
|
@@ -362,7 +469,7 @@ export class AppProxy implements PageRemoveService {
|
|
362
469
|
}
|
363
470
|
});
|
364
471
|
});
|
365
|
-
this.manager.refresher
|
472
|
+
this.manager.refresher.add(this.stateKey, () => {
|
366
473
|
return autorun(() => {
|
367
474
|
const appState = this.appAttributes?.state;
|
368
475
|
if (appState?.zIndex > 0 && appState.zIndex !== this.box?.zIndex) {
|
@@ -370,13 +477,13 @@ export class AppProxy implements PageRemoveService {
|
|
370
477
|
}
|
371
478
|
});
|
372
479
|
});
|
373
|
-
this.manager.refresher
|
480
|
+
this.manager.refresher.add(`${appId}-fullPath`, () => {
|
374
481
|
return autorun(() => {
|
375
482
|
const fullPath = this.appAttributes?.fullPath;
|
376
483
|
this.setFocusScenePathHandler(fullPath);
|
377
|
-
if (this.
|
484
|
+
if (this.fullPath$.value !== fullPath) {
|
378
485
|
this.notifyPageStateChange();
|
379
|
-
this.
|
486
|
+
this.fullPath$.setValue(fullPath);
|
380
487
|
}
|
381
488
|
});
|
382
489
|
});
|
@@ -404,14 +511,17 @@ export class AppProxy implements PageRemoveService {
|
|
404
511
|
return fullPath;
|
405
512
|
}
|
406
513
|
|
407
|
-
private
|
408
|
-
const view =
|
514
|
+
private createView(): View {
|
515
|
+
const view = this.viewManager.createView(this.id);
|
516
|
+
this.view$.setValue(view);
|
409
517
|
this.setViewFocusScenePath();
|
410
518
|
return view;
|
411
519
|
}
|
412
520
|
|
413
521
|
public notifyPageStateChange = debounce(() => {
|
414
|
-
|
522
|
+
if (this.pageState) {
|
523
|
+
this.appEmitter.emit("pageStateChange", this.pageState);
|
524
|
+
}
|
415
525
|
}, 50);
|
416
526
|
|
417
527
|
public get pageState(): PageState {
|
@@ -421,7 +531,7 @@ export class AppProxy implements PageRemoveService {
|
|
421
531
|
// PageRemoveService
|
422
532
|
public async removeSceneByIndex(index: number) {
|
423
533
|
const scenePath = this._pageState.getFullPath(index);
|
424
|
-
if (scenePath) {
|
534
|
+
if (scenePath && this.pageState) {
|
425
535
|
const nextIndex = calculateNextIndex(index, this.pageState);
|
426
536
|
// 只修改 focus path 不修改 FullPath
|
427
537
|
this.setSceneIndexWithoutSync(nextIndex);
|
@@ -457,6 +567,31 @@ export class AppProxy implements PageRemoveService {
|
|
457
567
|
}
|
458
568
|
}
|
459
569
|
|
570
|
+
public storeCamera = (camera: ICamera) => {
|
571
|
+
this.store.updateAppAttributes(this.id, Fields.Camera, camera);
|
572
|
+
};
|
573
|
+
|
574
|
+
public storeSize = (size: ISize) => {
|
575
|
+
this.store.updateAppAttributes(this.id, Fields.Size, size);
|
576
|
+
};
|
577
|
+
|
578
|
+
public updateSize = (width: number, height: number) => {
|
579
|
+
const iSize = {
|
580
|
+
id: this.manager.uid,
|
581
|
+
width, height
|
582
|
+
}
|
583
|
+
this.store.updateAppAttributes(this.id, Fields.Size, iSize);
|
584
|
+
this.size$.setValue(iSize);
|
585
|
+
}
|
586
|
+
|
587
|
+
public moveCamera = (camera: Camera) => {
|
588
|
+
if (!this.camera$.value) {
|
589
|
+
return;
|
590
|
+
}
|
591
|
+
const nextCamera = { ...this.camera$.value, ...camera };
|
592
|
+
this.storeCamera(nextCamera);
|
593
|
+
};
|
594
|
+
|
460
595
|
public async destroy(
|
461
596
|
needCloseBox: boolean,
|
462
597
|
cleanAttrs: boolean,
|
@@ -472,6 +607,7 @@ export class AppProxy implements PageRemoveService {
|
|
472
607
|
console.error("[WindowManager]: notifyApp error", error.message, error.stack);
|
473
608
|
}
|
474
609
|
this.appEmitter.clearListeners();
|
610
|
+
this.sideEffectManager.flushAll();
|
475
611
|
emitter.emit(`destroy-${this.id}` as any, { error });
|
476
612
|
if (needCloseBox) {
|
477
613
|
this.boxManager?.closeBox(this.id, skipUpdate);
|
@@ -483,14 +619,49 @@ export class AppProxy implements PageRemoveService {
|
|
483
619
|
}
|
484
620
|
}
|
485
621
|
this.appProxies.delete(this.id);
|
486
|
-
this._pageState.destroy();
|
487
622
|
|
488
623
|
this.viewManager.destroyView(this.id);
|
489
624
|
this.manager.appStatus.delete(this.id);
|
490
|
-
this.manager.refresher
|
491
|
-
this.manager.refresher
|
492
|
-
this.manager.refresher
|
493
|
-
this.
|
625
|
+
this.manager.refresher.remove(this.id);
|
626
|
+
this.manager.refresher.remove(this.stateKey);
|
627
|
+
this.manager.refresher.remove(`${this.id}-fullPath`);
|
628
|
+
this.valManager.destroy();
|
629
|
+
}
|
630
|
+
|
631
|
+
private addCameraReaction = () => {
|
632
|
+
this.sideEffectManager.add(() =>
|
633
|
+
this.manager.refresher.add(`${this.id}-camera`, () =>
|
634
|
+
reaction(
|
635
|
+
() => this.appAttributes?.camera,
|
636
|
+
camera => {
|
637
|
+
if (camera && camera.id !== this.uid) {
|
638
|
+
const rawCamera = toJS(camera);
|
639
|
+
if (rawCamera !== this.camera$.value) {
|
640
|
+
this.camera$.setValue(rawCamera);
|
641
|
+
}
|
642
|
+
}
|
643
|
+
}
|
644
|
+
)
|
645
|
+
)
|
646
|
+
, "camera");
|
647
|
+
}
|
648
|
+
|
649
|
+
private addSizeReaction = () => {
|
650
|
+
this.sideEffectManager.add(() =>
|
651
|
+
this.manager.refresher.add(`${this.id}-size`, () =>
|
652
|
+
reaction(
|
653
|
+
() => this.appAttributes?.size,
|
654
|
+
size => {
|
655
|
+
if (size && size.id !== this.uid) {
|
656
|
+
const rawSize = toJS(size);
|
657
|
+
if (this.size$.value !== rawSize) {
|
658
|
+
this.size$.setValue(rawSize);
|
659
|
+
}
|
660
|
+
}
|
661
|
+
}
|
662
|
+
)
|
663
|
+
)
|
664
|
+
, "size");
|
494
665
|
}
|
495
666
|
|
496
667
|
public close(): Promise<void> {
|
@@ -0,0 +1,73 @@
|
|
1
|
+
import { CameraSynchronizer } from "../View/CameraSynchronizer";
|
2
|
+
import { SideEffectManager } from "side-effect-manager";
|
3
|
+
import type { Camera, View } from "white-web-sdk";
|
4
|
+
import type { AppProxy } from "./AppProxy";
|
5
|
+
import { isEqual } from "lodash";
|
6
|
+
import { combine } from "value-enhancer";
|
7
|
+
|
8
|
+
export class AppViewSync {
|
9
|
+
private sem = new SideEffectManager();
|
10
|
+
private synchronizer: CameraSynchronizer;
|
11
|
+
|
12
|
+
constructor(private appProxy: AppProxy) {
|
13
|
+
this.synchronizer = new CameraSynchronizer((camera: Camera) => {
|
14
|
+
this.appProxy.storeCamera({
|
15
|
+
id: this.appProxy.uid,
|
16
|
+
...camera,
|
17
|
+
});
|
18
|
+
});
|
19
|
+
this.bindView(appProxy.view);
|
20
|
+
this.sem.add(() => this.appProxy.camera$.subscribe(camera => {
|
21
|
+
const size = this.appProxy.size$.value;
|
22
|
+
if (camera && size) {
|
23
|
+
this.synchronizer.onRemoteUpdate(camera, size);
|
24
|
+
}
|
25
|
+
}));
|
26
|
+
this.sem.add(() => this.appProxy.size$.subscribe(size => {
|
27
|
+
if (size) {
|
28
|
+
this.synchronizer.onRemoteSizeUpdate(size);
|
29
|
+
}
|
30
|
+
}));
|
31
|
+
const box = this.appProxy.box;
|
32
|
+
if (box && box.contentStageRect) {
|
33
|
+
this.synchronizer.setRect(box.contentStageRect);
|
34
|
+
this.sem.add(() =>
|
35
|
+
box._contentStageRect$.subscribe(rect => {
|
36
|
+
if (rect) {
|
37
|
+
this.synchronizer.setRect(rect);
|
38
|
+
}
|
39
|
+
}),
|
40
|
+
);
|
41
|
+
}
|
42
|
+
this.sem.add(() => combine([this.appProxy.camera$, this.appProxy.size$]).subscribe(([camera, size]) => {
|
43
|
+
if (camera && size) {
|
44
|
+
this.synchronizer.onRemoteUpdate(camera, size);
|
45
|
+
}
|
46
|
+
}));
|
47
|
+
}
|
48
|
+
|
49
|
+
public bindView = (view?: View) => {
|
50
|
+
if (!view) return;
|
51
|
+
this.synchronizer.setView(view);
|
52
|
+
this.sem.add(() => {
|
53
|
+
view.callbacks.on("onCameraUpdatedByDevice", this.onCameraUpdatedByDevice);
|
54
|
+
return () =>
|
55
|
+
view.callbacks.off("onCameraUpdatedByDevice", this.onCameraUpdatedByDevice);
|
56
|
+
});
|
57
|
+
};
|
58
|
+
|
59
|
+
private onCameraUpdatedByDevice = (camera: Camera) => {
|
60
|
+
this.synchronizer.onLocalCameraUpdate(camera);
|
61
|
+
const stage = this.appProxy.box?.contentStageRect;
|
62
|
+
if (stage) {
|
63
|
+
const size = { width: stage.width, height: stage.height, id: this.appProxy.uid };
|
64
|
+
if (!isEqual(size, this.appProxy.size$.value)) {
|
65
|
+
this.appProxy.storeSize(size);
|
66
|
+
}
|
67
|
+
}
|
68
|
+
};
|
69
|
+
|
70
|
+
public destroy() {
|
71
|
+
this.sem.flushAll();
|
72
|
+
}
|
73
|
+
}
|
package/src/App/Storage/index.ts
CHANGED
@@ -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.
|
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.
|
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.
|
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.
|
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,89 @@
|
|
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
|
+
import type { Camera, View } from "white-web-sdk";
|
9
|
+
import type { TeleBoxRect } from "@netless/telebox-insider";
|
10
|
+
|
11
|
+
export class WhiteBoardView implements PageController {
|
12
|
+
public readonly pageState$: ReadonlyVal<PageState>;
|
13
|
+
|
14
|
+
constructor(
|
15
|
+
public view: View,
|
16
|
+
protected appContext: AppContext,
|
17
|
+
protected appProxy: AppProxy,
|
18
|
+
private removeViewWrapper: () => void,
|
19
|
+
public ensureSize: (size: number) => void
|
20
|
+
) {
|
21
|
+
const pageState$ = new Val<PageState>(appProxy.pageState);
|
22
|
+
this.pageState$ = pageState$;
|
23
|
+
appProxy.appEmitter.on("pageStateChange", pageState => {
|
24
|
+
pageState$.setValue(pageState);
|
25
|
+
});
|
26
|
+
}
|
27
|
+
|
28
|
+
public get pageState() {
|
29
|
+
return this.pageState$.value;
|
30
|
+
}
|
31
|
+
|
32
|
+
public moveCamera(camera: Camera) {
|
33
|
+
this.appProxy.moveCamera(camera);
|
34
|
+
}
|
35
|
+
|
36
|
+
public nextPage = async (): Promise<boolean> => {
|
37
|
+
const nextIndex = this.pageState.index + 1;
|
38
|
+
return this.jumpPage(nextIndex);
|
39
|
+
};
|
40
|
+
|
41
|
+
public prevPage = async (): Promise<boolean> => {
|
42
|
+
const nextIndex = this.pageState.index - 1;
|
43
|
+
return this.jumpPage(nextIndex);
|
44
|
+
};
|
45
|
+
|
46
|
+
public jumpPage = async (index: number): Promise<boolean> => {
|
47
|
+
if (index < 0 || index >= this.pageState.length) {
|
48
|
+
console.warn(`[WindowManager]: index ${index} out of range`);
|
49
|
+
return false;
|
50
|
+
}
|
51
|
+
this.appProxy.setSceneIndex(index);
|
52
|
+
return true;
|
53
|
+
};
|
54
|
+
|
55
|
+
public addPage = async (params?: AddPageParams) => {
|
56
|
+
const after = params?.after;
|
57
|
+
const scene = params?.scene;
|
58
|
+
const scenePath = this.appProxy.scenePath;
|
59
|
+
if (!scenePath) return;
|
60
|
+
if (after) {
|
61
|
+
const nextIndex = this.pageState.index + 1;
|
62
|
+
putScenes(this.appContext.room, scenePath, [scene || {}], nextIndex);
|
63
|
+
} else {
|
64
|
+
putScenes(this.appContext.room, scenePath, [scene || {}]);
|
65
|
+
}
|
66
|
+
};
|
67
|
+
|
68
|
+
public removePage = async (index?: number): Promise<boolean> => {
|
69
|
+
const needRemoveIndex = index === undefined ? this.pageState.index : index;
|
70
|
+
if (this.pageState.length === 1) {
|
71
|
+
console.warn(`[WindowManager]: can not remove the last page`);
|
72
|
+
return false;
|
73
|
+
}
|
74
|
+
if (needRemoveIndex < 0 || needRemoveIndex >= this.pageState.length) {
|
75
|
+
console.warn(`[WindowManager]: page index ${index} out of range`);
|
76
|
+
return false;
|
77
|
+
}
|
78
|
+
return this.appProxy.removeSceneByIndex(needRemoveIndex);
|
79
|
+
};
|
80
|
+
|
81
|
+
public setRect(rect: Omit<TeleBoxRect, "x" | "y">) {
|
82
|
+
this.appProxy.updateSize(rect.width, rect.height);
|
83
|
+
}
|
84
|
+
|
85
|
+
public destroy() {
|
86
|
+
this.pageState$.destroy();
|
87
|
+
this.removeViewWrapper();
|
88
|
+
}
|
89
|
+
}
|
package/src/App/index.ts
CHANGED