@netless/window-manager 1.0.0-canary.1 → 1.0.0-canary.4
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 +14 -15
- package/dist/App/AppPageStateImpl.d.ts +6 -2
- package/dist/App/AppProxy.d.ts +17 -2
- package/dist/App/AppViewSync.d.ts +11 -0
- package/dist/App/WhiteboardView.d.ts +21 -0
- package/dist/App/index.d.ts +1 -0
- package/dist/AppManager.d.ts +3 -1
- package/dist/AttributesDelegate.d.ts +6 -14
- package/dist/BoxManager.d.ts +1 -1
- package/dist/Helper.d.ts +12 -2
- package/dist/InternalEmitter.d.ts +2 -0
- package/dist/ReconnectRefresher.d.ts +1 -1
- package/dist/Utils/Common.d.ts +1 -0
- package/dist/View/CameraSynchronizer.d.ts +4 -4
- package/dist/View/ViewSync.d.ts +7 -0
- package/dist/constants.d.ts +1 -0
- package/dist/index.cjs.js +12 -12
- package/dist/index.d.ts +3 -1
- package/dist/index.es.js +563 -576
- package/dist/index.umd.js +12 -12
- 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 +3 -2
- package/pnpm-lock.yaml +11 -5
- package/src/App/AppContext.ts +65 -72
- package/src/App/AppPageStateImpl.ts +25 -6
- package/src/App/AppProxy.ts +110 -13
- package/src/App/AppViewSync.ts +69 -0
- package/src/App/Storage/index.ts +4 -4
- package/src/App/WhiteboardView.ts +85 -0
- package/src/App/index.ts +1 -0
- package/src/AppManager.ts +10 -2
- package/src/AttributesDelegate.ts +14 -17
- package/src/BoxManager.ts +3 -2
- package/src/Helper.ts +10 -1
- package/src/InternalEmitter.ts +2 -0
- package/src/ReconnectRefresher.ts +1 -0
- package/src/Utils/Common.ts +6 -0
- package/src/View/CameraSynchronizer.ts +15 -8
- package/src/View/MainView.ts +9 -13
- package/src/View/ViewSync.ts +10 -0
- package/src/constants.ts +2 -0
- package/src/index.ts +3 -1
- package/src/style.css +9 -0
- package/src/typings.ts +4 -0
package/src/App/AppContext.ts
CHANGED
@@ -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
|
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
|
65
|
+
public get displayer(){
|
64
66
|
return this.manager.displayer;
|
65
67
|
};
|
66
68
|
|
@@ -78,32 +80,58 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
|
|
78
80
|
}
|
79
81
|
};
|
80
82
|
|
81
|
-
public
|
83
|
+
public get view(): View | undefined {
|
82
84
|
return this.appProxy.view;
|
83
85
|
};
|
84
86
|
|
85
|
-
public
|
86
|
-
|
87
|
-
|
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
|
+
const viewWrapper = document.createElement("div");
|
96
|
+
viewWrapper.className = "window-manager-view-wrapper";
|
97
|
+
this.box.$content.parentElement?.appendChild(viewWrapper);
|
98
|
+
const removeViewWrapper = () => {
|
99
|
+
this.box.$content.parentElement?.removeChild(viewWrapper);
|
100
|
+
}
|
101
|
+
view.divElement = viewWrapper;
|
102
|
+
if (this.isAddApp) {
|
103
|
+
this.initPageSize(size);
|
104
|
+
}
|
105
|
+
this.whiteBoardView = new WhiteBoardView(this, this.appProxy, removeViewWrapper);
|
106
|
+
return this.whiteBoardView;
|
107
|
+
}
|
108
|
+
|
109
|
+
private initPageSize = (size?: number) => {
|
110
|
+
if (!isNumber(size)) return;
|
111
|
+
if (!this.appProxy.scenePath) return;
|
112
|
+
if (this.appProxy.pageState.length >= size) return;
|
113
|
+
if (size <= 0 || size >= MAX_PAGE_SIZE) {
|
114
|
+
throw Error(`[WindowManager]: size ${size} muse be in range [1, ${MAX_PAGE_SIZE}]`);
|
115
|
+
}
|
116
|
+
const needInsert = size - this.appProxy.pageState.length;
|
117
|
+
const startPageNumber = this.appProxy.pageState.length;
|
118
|
+
const scenes = new Array(needInsert).fill({}).map((_, index) => {
|
119
|
+
return { name: `${startPageNumber + index + 1}` };
|
120
|
+
});
|
121
|
+
putScenes(this.room, this.appProxy.scenePath, scenes);
|
122
|
+
}
|
95
123
|
|
96
124
|
public getInitScenePath = () => {
|
97
125
|
return this.manager.getAppInitPath(this.appId);
|
98
126
|
};
|
99
127
|
|
100
128
|
/** Get App writable status. */
|
101
|
-
public
|
129
|
+
public get isWritable(): boolean {
|
102
130
|
return this.manager.canOperate;
|
103
131
|
};
|
104
132
|
|
105
133
|
/** Get the App Window UI box. */
|
106
|
-
public
|
134
|
+
public get box(): ReadonlyTeleBox {
|
107
135
|
const box = this.boxManager.getBox(this.appId);
|
108
136
|
if (box) {
|
109
137
|
return box;
|
@@ -112,10 +140,25 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
|
|
112
140
|
}
|
113
141
|
};
|
114
142
|
|
115
|
-
public
|
143
|
+
public get room(): Room | undefined {
|
116
144
|
return this.manager.room;
|
117
145
|
};
|
118
146
|
|
147
|
+
public get members() {
|
148
|
+
return this.manager.members;
|
149
|
+
}
|
150
|
+
|
151
|
+
public get memberState(): Member {
|
152
|
+
const self = findMemberByUid(this.room, this.manager.uid);
|
153
|
+
if (!self) {
|
154
|
+
throw new Error(`Member ${this.manager.uid} not found.`);
|
155
|
+
}
|
156
|
+
return {
|
157
|
+
uid: this.manager.uid,
|
158
|
+
...self,
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
119
162
|
/** @deprecated Use context.storage.setState instead. */
|
120
163
|
public setAttributes = (attributes: TAttributes) => {
|
121
164
|
this.manager.safeSetAttributes({ [this.appId]: attributes });
|
@@ -128,11 +171,12 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
|
|
128
171
|
}
|
129
172
|
};
|
130
173
|
|
174
|
+
/** @deprecated Use Pages api instead. */
|
131
175
|
public setScenePath = async (scenePath: string): Promise<void> => {
|
132
176
|
if (!this.appProxy.box) return;
|
133
177
|
this.appProxy.setFullPath(scenePath);
|
134
178
|
// 兼容 15 版本 SDK 的切页
|
135
|
-
this.
|
179
|
+
this.room?.setScenePath(scenePath);
|
136
180
|
};
|
137
181
|
|
138
182
|
/** Get the local App options. */
|
@@ -196,55 +240,4 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
|
|
196
240
|
public removeMagixEventListener = this.manager.displayer.removeMagixEventListener.bind(
|
197
241
|
this.manager.displayer
|
198
242
|
) 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
243
|
}
|
@@ -9,11 +9,15 @@ export type AppPageStateParams = {
|
|
9
9
|
};
|
10
10
|
|
11
11
|
export class AppPageStateImpl {
|
12
|
-
|
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
|
-
|
25
|
-
this.
|
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.
|
50
|
+
if (this.scenePath && scenes) {
|
32
51
|
const name = scenes[index];
|
33
52
|
if (name) {
|
34
|
-
return `${this.
|
53
|
+
return `${this.scenePath}/${name}`;
|
35
54
|
}
|
36
55
|
}
|
37
56
|
}
|
38
57
|
|
39
58
|
public toObject(): PageState {
|
40
59
|
return {
|
41
|
-
index: this.
|
60
|
+
index: this.view?.focusSceneIndex || 0,
|
42
61
|
length: this.sceneNode?.scenes.length || 0,
|
43
62
|
};
|
44
63
|
}
|
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 } 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 } from "white-web-sdk";
|
27
34
|
import type { AppManager } from "../AppManager";
|
28
35
|
import type { NetlessApp } from "../typings";
|
29
36
|
import type { ReadonlyTeleBox } 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
|
private status: "normal" | "destroyed" = "normal";
|
51
58
|
private stateKey: string;
|
52
|
-
|
59
|
+
public _pageState: AppPageStateImpl;
|
53
60
|
private _prevFullPath: string | undefined;
|
54
61
|
|
55
62
|
public appResult?: NetlessApp<any>;
|
56
63
|
public appContext?: AppContext<any, any>;
|
57
64
|
|
65
|
+
private sideEffectManager = new SideEffectManager();
|
66
|
+
|
67
|
+
public camera$ = new Val<ICamera | undefined>(undefined);
|
68
|
+
public size$ = new Val<ISize | undefined>(undefined);
|
69
|
+
|
70
|
+
private appViewSync?: AppViewSync;
|
71
|
+
|
72
|
+
public box$ = new Val<ReadonlyTeleBox | undefined>(undefined);
|
73
|
+
public view$ = 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,68 @@ 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.sideEffectManager.add(() => {
|
114
|
+
return this.manager.refresher.add(`${this.id}-camera`, () => {
|
115
|
+
return reaction(
|
116
|
+
() => this.appAttributes?.camera,
|
117
|
+
camera => {
|
118
|
+
if (camera && camera.id !== this.uid) {
|
119
|
+
this.camera$.setValue(toJS(camera));
|
120
|
+
}
|
121
|
+
}
|
122
|
+
);
|
123
|
+
});
|
124
|
+
});
|
125
|
+
this.sideEffectManager.add(() => {
|
126
|
+
return this.manager.refresher.add(`${this.id}-size`, () => {
|
127
|
+
return reaction(
|
128
|
+
() => this.appAttributes?.size,
|
129
|
+
size => {
|
130
|
+
if (size && size.id !== this.uid) {
|
131
|
+
this.size$.setValue(toJS(size));
|
132
|
+
}
|
133
|
+
}
|
134
|
+
);
|
135
|
+
});
|
136
|
+
});
|
137
|
+
combine([this.box$, this.view$]).subscribe(([box, view]) => {
|
138
|
+
if (box && view) {
|
139
|
+
const appViewSync = new AppViewSync(this);
|
140
|
+
this.appViewSync = appViewSync;
|
141
|
+
this.sideEffectManager.add(() => () => appViewSync.destroy());
|
142
|
+
}
|
143
|
+
});
|
144
|
+
}
|
145
|
+
|
146
|
+
public createAppDir() {
|
147
|
+
const scenePath = this.scenePath || this.appScenePath;
|
148
|
+
const sceneNode = this._pageState.createSceneNode(scenePath);
|
149
|
+
if (!sceneNode) {
|
150
|
+
putScenes(this.manager.room, scenePath, [{ name: "1" }]);
|
151
|
+
this._pageState.createSceneNode(scenePath);
|
152
|
+
this.setSceneIndex(0);
|
153
|
+
}
|
154
|
+
this.scenes = entireScenes(this.manager.displayer)[scenePath];
|
155
|
+
const view = this.createView();
|
156
|
+
this._pageState.setView(view);
|
157
|
+
return view;
|
84
158
|
}
|
85
159
|
|
86
160
|
private initScenes() {
|
@@ -131,7 +205,7 @@ export class AppProxy implements PageRemoveService {
|
|
131
205
|
}
|
132
206
|
|
133
207
|
public setFullPath(path: string) {
|
134
|
-
this.
|
208
|
+
this.store.updateAppAttributes(this.id, Fields.FullPath, path);
|
135
209
|
}
|
136
210
|
|
137
211
|
public async baseInsertApp(skipUpdate = false): Promise<{ appId: string; app: NetlessApp }> {
|
@@ -160,7 +234,7 @@ export class AppProxy implements PageRemoveService {
|
|
160
234
|
}
|
161
235
|
|
162
236
|
public get box(): ReadonlyTeleBox | undefined {
|
163
|
-
return this.
|
237
|
+
return this.box$.value;
|
164
238
|
}
|
165
239
|
|
166
240
|
private async setupApp(
|
@@ -194,13 +268,14 @@ export class AppProxy implements PageRemoveService {
|
|
194
268
|
this.fixMobileSize();
|
195
269
|
}, SETUP_APP_DELAY);
|
196
270
|
});
|
197
|
-
this.boxManager?.createBox({
|
271
|
+
const box = this.boxManager?.createBox({
|
198
272
|
appId: appId,
|
199
273
|
app,
|
200
274
|
options,
|
201
275
|
canOperate: this.manager.canOperate,
|
202
276
|
smartPosition: this.isAddApp,
|
203
277
|
});
|
278
|
+
this.box$.setValue(box);
|
204
279
|
if (this.isAddApp && this.box) {
|
205
280
|
this.store.updateAppState(appId, AppAttributes.ZIndex, this.box.zIndex);
|
206
281
|
this.boxManager.focusBox({ appId }, false);
|
@@ -395,14 +470,17 @@ export class AppProxy implements PageRemoveService {
|
|
395
470
|
return fullPath;
|
396
471
|
}
|
397
472
|
|
398
|
-
private
|
399
|
-
const view =
|
473
|
+
private createView(): View {
|
474
|
+
const view = this.viewManager.createView(this.id);
|
475
|
+
this.view$.setValue(view);
|
400
476
|
this.setViewFocusScenePath();
|
401
477
|
return view;
|
402
478
|
}
|
403
479
|
|
404
480
|
public notifyPageStateChange = debounce(() => {
|
405
|
-
|
481
|
+
if (this.pageState) {
|
482
|
+
this.appEmitter.emit("pageStateChange", this.pageState);
|
483
|
+
}
|
406
484
|
}, 50);
|
407
485
|
|
408
486
|
public get pageState(): PageState {
|
@@ -412,7 +490,7 @@ export class AppProxy implements PageRemoveService {
|
|
412
490
|
// PageRemoveService
|
413
491
|
public async removeSceneByIndex(index: number) {
|
414
492
|
const scenePath = this._pageState.getFullPath(index);
|
415
|
-
if (scenePath) {
|
493
|
+
if (scenePath && this.pageState) {
|
416
494
|
const nextIndex = calculateNextIndex(index, this.pageState);
|
417
495
|
// 只修改 focus path 不修改 FullPath
|
418
496
|
this.setSceneIndexWithoutSync(nextIndex);
|
@@ -448,6 +526,22 @@ export class AppProxy implements PageRemoveService {
|
|
448
526
|
}
|
449
527
|
}
|
450
528
|
|
529
|
+
public storeCamera = (camera: ICamera) => {
|
530
|
+
this.store.updateAppAttributes(this.id, Fields.Camera, camera);
|
531
|
+
};
|
532
|
+
|
533
|
+
public storeSize = (size: ISize) => {
|
534
|
+
this.store.updateAppAttributes(this.id, Fields.Size, size);
|
535
|
+
};
|
536
|
+
|
537
|
+
public moveCamera = (camera: Camera) => {
|
538
|
+
if (!this.camera$.value) {
|
539
|
+
return;
|
540
|
+
}
|
541
|
+
const nextCamera = { ...this.camera$.value, ...camera };
|
542
|
+
this.storeCamera(nextCamera);
|
543
|
+
};
|
544
|
+
|
451
545
|
public async destroy(
|
452
546
|
needCloseBox: boolean,
|
453
547
|
cleanAttrs: boolean,
|
@@ -463,6 +557,7 @@ export class AppProxy implements PageRemoveService {
|
|
463
557
|
console.error("[WindowManager]: notifyApp error", error.message, error.stack);
|
464
558
|
}
|
465
559
|
this.appEmitter.clearListeners();
|
560
|
+
this.sideEffectManager.flushAll();
|
466
561
|
emitter.emit(`destroy-${this.id}` as any, { error });
|
467
562
|
if (needCloseBox) {
|
468
563
|
this.boxManager?.closeBox(this.id, skipUpdate);
|
@@ -474,7 +569,6 @@ export class AppProxy implements PageRemoveService {
|
|
474
569
|
}
|
475
570
|
}
|
476
571
|
this.appProxies.delete(this.id);
|
477
|
-
this._pageState.destroy();
|
478
572
|
|
479
573
|
this.viewManager.destroyView(this.id);
|
480
574
|
this.manager.appStatus.delete(this.id);
|
@@ -482,6 +576,9 @@ export class AppProxy implements PageRemoveService {
|
|
482
576
|
this.manager.refresher?.remove(this.stateKey);
|
483
577
|
this.manager.refresher?.remove(`${this.id}-fullPath`);
|
484
578
|
this._prevFullPath = undefined;
|
579
|
+
this.camera$.destroy();
|
580
|
+
this.size$.destroy();
|
581
|
+
this.box$.destroy();
|
485
582
|
}
|
486
583
|
|
487
584
|
public close(): Promise<void> {
|
@@ -0,0 +1,69 @@
|
|
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
|
+
|
7
|
+
export class AppViewSync {
|
8
|
+
private sem = new SideEffectManager();
|
9
|
+
private synchronizer: CameraSynchronizer;
|
10
|
+
|
11
|
+
constructor(private appProxy: AppProxy) {
|
12
|
+
this.synchronizer = new CameraSynchronizer((camera: Camera) => {
|
13
|
+
this.appProxy.storeCamera({
|
14
|
+
id: this.appProxy.uid,
|
15
|
+
...camera,
|
16
|
+
});
|
17
|
+
});
|
18
|
+
this.bindView(appProxy.view);
|
19
|
+
this.sem.add(() => this.appProxy.camera$.subscribe(camera => {
|
20
|
+
const size = this.appProxy.size$.value;
|
21
|
+
if (camera && size) {
|
22
|
+
this.synchronizer.onRemoteUpdate(camera, size);
|
23
|
+
}
|
24
|
+
}));
|
25
|
+
const box = this.appProxy.box;
|
26
|
+
if (box && box.contentStageRect) {
|
27
|
+
this.synchronizer.setRect(box.contentStageRect);
|
28
|
+
this.sem.add(() =>
|
29
|
+
box._contentStageRect$.subscribe(rect => {
|
30
|
+
if (rect) {
|
31
|
+
this.synchronizer.setRect(rect);
|
32
|
+
}
|
33
|
+
}),
|
34
|
+
);
|
35
|
+
if (!this.appProxy.size$.value) {
|
36
|
+
this.appProxy.storeSize({
|
37
|
+
id: this.appProxy.uid,
|
38
|
+
width: box.contentStageRect.width,
|
39
|
+
height: box.contentStageRect.height,
|
40
|
+
});
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
public bindView = (view?: View) => {
|
46
|
+
if (!view) return;
|
47
|
+
this.synchronizer.setView(view);
|
48
|
+
this.sem.add(() => {
|
49
|
+
view.callbacks.on("onCameraUpdatedByDevice", this.onCameraUpdatedByDevice);
|
50
|
+
return () =>
|
51
|
+
view.callbacks.off("onCameraUpdatedByDevice", this.onCameraUpdatedByDevice);
|
52
|
+
});
|
53
|
+
};
|
54
|
+
|
55
|
+
private onCameraUpdatedByDevice = (camera: Camera) => {
|
56
|
+
this.synchronizer.onLocalCameraUpdate(camera);
|
57
|
+
const stage = this.appProxy.box?.contentStageRect;
|
58
|
+
if (stage) {
|
59
|
+
const size = { width: stage.width, height: stage.height, id: this.appProxy.uid };
|
60
|
+
if (!isEqual(size, this.appProxy.size$.value)) {
|
61
|
+
this.appProxy.storeSize(size);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
};
|
65
|
+
|
66
|
+
public destroy() {
|
67
|
+
this.sem.flushAll();
|
68
|
+
}
|
69
|
+
}
|
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
|
}
|