@netless/window-manager 0.4.0-canary.8 → 0.4.1
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/.idea/inspectionProfiles/Project_Default.xml +7 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/.idea/window-manager.iml +12 -0
- package/.vscode/settings.json +1 -0
- package/CHANGELOG.md +43 -2
- package/README.md +3 -0
- package/dist/App/MagixEvent/index.d.ts +29 -0
- package/dist/App/Storage/index.d.ts +19 -6
- package/dist/App/Storage/typings.d.ts +1 -0
- package/dist/AppContext.d.ts +39 -17
- package/dist/AppListener.d.ts +2 -0
- package/dist/AppManager.d.ts +23 -8
- package/dist/AppProxy.d.ts +5 -5
- package/dist/AttributesDelegate.d.ts +2 -2
- package/dist/BoxManager.d.ts +6 -4
- package/dist/BuiltinApps.d.ts +0 -1
- package/dist/Cursor/Cursor.d.ts +10 -12
- package/dist/Cursor/index.d.ts +6 -16
- package/dist/Helper.d.ts +1 -0
- package/dist/Register/index.d.ts +5 -0
- package/dist/Register/storage.d.ts +5 -1
- package/dist/Utils/AppCreateQueue.d.ts +11 -0
- package/dist/Utils/Common.d.ts +4 -1
- package/dist/Utils/RoomHacker.d.ts +3 -3
- package/dist/View/MainView.d.ts +4 -3
- package/dist/constants.d.ts +5 -2
- package/dist/index.d.ts +34 -6
- package/dist/index.es.js +41 -1
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +41 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/typings.d.ts +2 -2
- package/docs/advanced.md +53 -0
- package/docs/api.md +79 -6
- package/docs/concept.md +9 -0
- package/docs/replay.md +40 -0
- package/package.json +7 -8
- package/src/App/MagixEvent/index.ts +68 -0
- package/src/App/Storage/index.ts +89 -43
- package/src/App/Storage/typings.ts +4 -2
- package/src/AppContext.ts +61 -24
- package/src/AppListener.ts +28 -8
- package/src/AppManager.ts +244 -71
- package/src/AppProxy.ts +40 -29
- package/src/AttributesDelegate.ts +2 -2
- package/src/BoxManager.ts +33 -19
- package/src/BuiltinApps.ts +0 -1
- package/src/ContainerResizeObserver.ts +3 -3
- package/src/Cursor/Cursor.svelte +25 -21
- package/src/Cursor/Cursor.ts +25 -38
- package/src/Cursor/icons.ts +2 -0
- package/src/Cursor/index.ts +45 -139
- package/src/Helper.ts +12 -1
- package/src/Register/index.ts +32 -17
- package/src/Register/loader.ts +28 -13
- package/src/Register/storage.ts +6 -1
- package/src/Utils/AppCreateQueue.ts +54 -0
- package/src/Utils/Common.ts +35 -2
- package/src/Utils/RoomHacker.ts +44 -14
- package/src/View/MainView.ts +19 -12
- package/src/View/ViewManager.ts +1 -2
- package/src/constants.ts +6 -2
- package/src/image/laser-pointer-cursor.svg +17 -0
- package/src/index.ts +152 -33
- package/src/shim.d.ts +2 -1
- package/src/style.css +6 -1
- package/src/typings.ts +2 -2
- package/vite.config.js +5 -2
- package/dist/Base/Context.d.ts +0 -12
- package/dist/Base/index.d.ts +0 -7
- package/src/Base/Context.ts +0 -45
- package/src/Base/index.ts +0 -10
package/src/AppContext.ts
CHANGED
@@ -6,9 +6,9 @@ import {
|
|
6
6
|
unlistenDisposed,
|
7
7
|
unlistenUpdated,
|
8
8
|
toJS
|
9
|
-
|
9
|
+
} from 'white-web-sdk';
|
10
10
|
import { BoxNotCreatedError } from './Utils/error';
|
11
|
-
import type { Room, SceneDefinition, View } from "white-web-sdk";
|
11
|
+
import type { Room, SceneDefinition, View, EventListener as WhiteEventListener } from "white-web-sdk";
|
12
12
|
import type { ReadonlyTeleBox } from "@netless/telebox-insider";
|
13
13
|
import type Emittery from "emittery";
|
14
14
|
import type { BoxManager } from "./BoxManager";
|
@@ -16,9 +16,10 @@ import type { AppEmitterEvent } from "./index";
|
|
16
16
|
import type { AppManager } from "./AppManager";
|
17
17
|
import type { AppProxy } from "./AppProxy";
|
18
18
|
import { Storage } from './App/Storage';
|
19
|
+
import type { MagixEventAddListener, MagixEventDispatcher, MagixEventRemoveListener } from './App/MagixEvent';
|
19
20
|
|
20
|
-
export class AppContext<
|
21
|
-
public readonly emitter: Emittery<AppEmitterEvent<
|
21
|
+
export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOptions = any> {
|
22
|
+
public readonly emitter: Emittery<AppEmitterEvent<TAttributes>>;
|
22
23
|
public readonly mobxUtils = {
|
23
24
|
autorun,
|
24
25
|
reaction,
|
@@ -40,45 +41,45 @@ export class AppContext<TAttrs extends Record<string, any>, AppOptions = any> {
|
|
40
41
|
private boxManager: BoxManager,
|
41
42
|
public appId: string,
|
42
43
|
private appProxy: AppProxy,
|
43
|
-
private appOptions?:
|
44
|
+
private appOptions?: TAppOptions | (() => TAppOptions),
|
44
45
|
) {
|
45
46
|
this.emitter = appProxy.appEmitter;
|
46
47
|
this.isAddApp = appProxy.isAddApp;
|
47
48
|
}
|
48
49
|
|
49
|
-
public getDisplayer() {
|
50
|
+
public getDisplayer = () => {
|
50
51
|
return this.manager.displayer;
|
51
52
|
}
|
52
53
|
|
53
|
-
|
54
|
+
/** @deprecated Use context.storage.state instead. */
|
55
|
+
public getAttributes = (): TAttributes | undefined => {
|
54
56
|
return this.appProxy.attributes;
|
55
57
|
}
|
56
58
|
|
57
|
-
public getScenes(): SceneDefinition[] | undefined {
|
59
|
+
public getScenes = (): SceneDefinition[] | undefined => {
|
58
60
|
const appAttr = this.store.getAppAttributes(this.appId);
|
59
61
|
if (appAttr?.isDynamicPPT) {
|
60
|
-
|
61
|
-
if (appProxy) {
|
62
|
-
return appProxy.scenes;
|
63
|
-
}
|
62
|
+
return this.appProxy.scenes;
|
64
63
|
} else {
|
65
64
|
return appAttr?.options["scenes"];
|
66
65
|
}
|
67
66
|
}
|
68
67
|
|
69
|
-
public getView(): View | undefined {
|
68
|
+
public getView = (): View | undefined => {
|
70
69
|
return this.appProxy.view;
|
71
70
|
}
|
72
71
|
|
73
|
-
public getInitScenePath() {
|
72
|
+
public getInitScenePath = () => {
|
74
73
|
return this.manager.getAppInitPath(this.appId);
|
75
74
|
}
|
76
75
|
|
77
|
-
|
76
|
+
/** Get App writable status. */
|
77
|
+
public getIsWritable = (): boolean => {
|
78
78
|
return this.manager.canOperate;
|
79
79
|
}
|
80
80
|
|
81
|
-
|
81
|
+
/** Get the App Window UI box. */
|
82
|
+
public getBox = (): ReadonlyTeleBox => {
|
82
83
|
const box = this.boxManager.getBox(this.appId);
|
83
84
|
if (box) {
|
84
85
|
return box;
|
@@ -87,26 +88,30 @@ export class AppContext<TAttrs extends Record<string, any>, AppOptions = any> {
|
|
87
88
|
}
|
88
89
|
}
|
89
90
|
|
90
|
-
public getRoom(): Room | undefined {
|
91
|
+
public getRoom = (): Room | undefined => {
|
91
92
|
return this.manager.room;
|
92
93
|
}
|
93
94
|
|
94
|
-
|
95
|
+
/** @deprecated Use context.storage.setState instead. */
|
96
|
+
public setAttributes = (attributes: TAttributes) => {
|
95
97
|
this.manager.safeSetAttributes({ [this.appId]: attributes });
|
96
98
|
}
|
97
99
|
|
98
|
-
|
100
|
+
/** @deprecated Use context.storage.setState instead. */
|
101
|
+
public updateAttributes = (keys: string[], value: any) => {
|
99
102
|
if (this.manager.attributes[this.appId]) {
|
100
103
|
this.manager.safeUpdateAttributes([this.appId, ...keys], value);
|
101
104
|
}
|
102
105
|
}
|
103
106
|
|
104
|
-
public async
|
107
|
+
public setScenePath = async (scenePath: string): Promise<void> => {
|
105
108
|
if (!this.appProxy.box) return;
|
106
109
|
this.appProxy.setFullPath(scenePath);
|
110
|
+
// 兼容 15 版本 SDK 的切页
|
111
|
+
this.getRoom()?.setScenePath(scenePath);
|
107
112
|
}
|
108
113
|
|
109
|
-
public mountView(dom: HTMLDivElement): void {
|
114
|
+
public mountView = (dom: HTMLDivElement): void => {
|
110
115
|
const view = this.getView();
|
111
116
|
if (view) {
|
112
117
|
view.divElement = dom;
|
@@ -117,15 +122,47 @@ export class AppContext<TAttrs extends Record<string, any>, AppOptions = any> {
|
|
117
122
|
}
|
118
123
|
}
|
119
124
|
|
120
|
-
|
121
|
-
|
125
|
+
/** Get the local App options. */
|
126
|
+
public getAppOptions = (): TAppOptions | undefined => {
|
127
|
+
return typeof this.appOptions === 'function' ? (this.appOptions as () => TAppOptions)() : this.appOptions
|
122
128
|
}
|
123
129
|
|
124
|
-
|
130
|
+
private _storage?: Storage<TAttributes>
|
131
|
+
|
132
|
+
/** Main Storage for attributes. */
|
133
|
+
public get storage(): Storage<TAttributes> {
|
134
|
+
if (!this._storage) {
|
135
|
+
this._storage = new Storage(this);
|
136
|
+
}
|
137
|
+
return this._storage;
|
138
|
+
}
|
139
|
+
|
140
|
+
/**
|
141
|
+
* Create separated storages for flexible state management.
|
142
|
+
* @param storeId Namespace for the storage. Storages of the same namespace share the same data.
|
143
|
+
* @param defaultState Default state for initial storage creation.
|
144
|
+
* @returns
|
145
|
+
*/
|
146
|
+
public createStorage = <TState>(storeId: string, defaultState?: TState): Storage<TState> => {
|
125
147
|
const storage = new Storage(this, storeId, defaultState);
|
126
148
|
this.emitter.on("destroy", () => {
|
127
149
|
storage.destroy();
|
128
150
|
});
|
129
151
|
return storage;
|
130
152
|
}
|
153
|
+
|
154
|
+
/** Dispatch events to other clients (and self). */
|
155
|
+
public dispatchMagixEvent: MagixEventDispatcher<TMagixEventPayloads> = (...args) => {
|
156
|
+
// can't dispatch events on replay mode
|
157
|
+
return this.manager.room?.dispatchMagixEvent(...args);
|
158
|
+
}
|
159
|
+
|
160
|
+
/** Listen to events from others clients (and self messages). */
|
161
|
+
public addMagixEventListener: MagixEventAddListener<TMagixEventPayloads> = (event, handler, options) => {
|
162
|
+
this.manager.displayer.addMagixEventListener(event, handler as WhiteEventListener, options);
|
163
|
+
return () => this.manager.displayer.removeMagixEventListener(event, handler as WhiteEventListener);
|
164
|
+
}
|
165
|
+
|
166
|
+
/** Remove a Magix event listener. */
|
167
|
+
public removeMagixEventListener = this.manager.displayer.removeMagixEventListener.bind(this.manager.displayer) as MagixEventRemoveListener<TMagixEventPayloads>
|
131
168
|
}
|
package/src/AppListener.ts
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
import { callbacks } from
|
2
|
-
import { Events, MagixEventName } from
|
3
|
-
import
|
1
|
+
import { callbacks, emitter } from "./index";
|
2
|
+
import { Events, MagixEventName } from "./constants";
|
3
|
+
import { isEqual, omit } from "lodash";
|
4
|
+
import { setViewFocusScenePath } from "./Utils/Common";
|
5
|
+
import type { AnimationMode, Camera, Event } from "white-web-sdk";
|
4
6
|
import type { AppManager } from "./AppManager";
|
5
7
|
import type { TeleBoxState } from "@netless/telebox-insider";
|
6
|
-
import { setViewFocusScenePath } from './Utils/Common';
|
7
|
-
|
8
8
|
export class AppListeners {
|
9
9
|
private displayer = this.manager.displayer;
|
10
10
|
|
@@ -42,10 +42,18 @@ export class AppListeners {
|
|
42
42
|
this.setMainViewScenePathHandler(data.payload);
|
43
43
|
break;
|
44
44
|
}
|
45
|
+
case Events.MoveCamera: {
|
46
|
+
this.moveCameraHandler(data.payload);
|
47
|
+
break;
|
48
|
+
}
|
45
49
|
case Events.MoveCameraToContain: {
|
46
50
|
this.moveCameraToContainHandler(data.payload);
|
47
51
|
break;
|
48
52
|
}
|
53
|
+
case Events.CursorMove: {
|
54
|
+
this.cursorMoveHandler(data.payload);
|
55
|
+
break;
|
56
|
+
}
|
49
57
|
default:
|
50
58
|
break;
|
51
59
|
}
|
@@ -63,13 +71,25 @@ export class AppListeners {
|
|
63
71
|
|
64
72
|
private boxStateChangeHandler = (state: TeleBoxState) => {
|
65
73
|
callbacks.emit("boxStateChange", state);
|
66
|
-
}
|
74
|
+
};
|
67
75
|
|
68
76
|
private setMainViewScenePathHandler = ({ nextScenePath }: { nextScenePath: string }) => {
|
69
77
|
setViewFocusScenePath(this.manager.mainView, nextScenePath);
|
70
|
-
|
78
|
+
callbacks.emit("mainViewScenePathChange", nextScenePath);
|
79
|
+
};
|
80
|
+
|
81
|
+
private moveCameraHandler = (
|
82
|
+
payload: Camera & { animationMode?: AnimationMode | undefined }
|
83
|
+
) => {
|
84
|
+
if (isEqual(omit(payload, ["animationMode"]), { ...this.manager.mainView.camera })) return;
|
85
|
+
this.manager.mainView.moveCamera(payload);
|
86
|
+
};
|
71
87
|
|
72
88
|
private moveCameraToContainHandler = (payload: any) => {
|
73
89
|
this.manager.mainView.moveCameraToContain(payload);
|
74
|
-
}
|
90
|
+
};
|
91
|
+
|
92
|
+
private cursorMoveHandler = (payload: any) => {
|
93
|
+
emitter.emit("cursorMove", payload);
|
94
|
+
};
|
75
95
|
}
|
package/src/AppManager.ts
CHANGED
@@ -1,19 +1,27 @@
|
|
1
|
-
import
|
2
|
-
import {
|
1
|
+
import { AppAttributes, AppStatus, Events, MagixEventName, ROOT_DIR } from "./constants";
|
2
|
+
import { AppCreateQueue } from "./Utils/AppCreateQueue";
|
3
3
|
import { AppListeners } from "./AppListener";
|
4
4
|
import { AppProxy } from "./AppProxy";
|
5
|
+
import { appRegister } from "./Register";
|
5
6
|
import { autorun, isPlayer, isRoom, ScenePathType } from "white-web-sdk";
|
6
|
-
import { callbacks, emitter,
|
7
|
-
import {
|
7
|
+
import { callbacks, emitter, reconnectRefresher, WindowManager } from "./index";
|
8
|
+
import { get, isInteger, orderBy } from "lodash";
|
8
9
|
import { log } from "./Utils/log";
|
9
10
|
import { MainViewProxy } from "./View/MainView";
|
10
11
|
import { onObjectRemoved, safeListenPropsUpdated } from "./Utils/Reactive";
|
11
|
-
import { sortBy } from "lodash";
|
12
12
|
import { store } from "./AttributesDelegate";
|
13
13
|
import { ViewManager } from "./View/ViewManager";
|
14
|
+
import {
|
15
|
+
entireScenes,
|
16
|
+
genAppId,
|
17
|
+
makeValidScenePath,
|
18
|
+
parseSceneDir,
|
19
|
+
setScenePath,
|
20
|
+
setViewFocusScenePath,
|
21
|
+
} from "./Utils/Common";
|
14
22
|
import type { ReconnectRefresher } from "./ReconnectRefresher";
|
15
23
|
import type { BoxManager } from "./BoxManager";
|
16
|
-
import type { Displayer, DisplayerState, Room } from "white-web-sdk";
|
24
|
+
import type { Displayer, DisplayerState, Room, ScenesCallbacksNode, View } from "white-web-sdk";
|
17
25
|
import type { AddAppParams, BaseInsertParams, TeleBoxRect, EmitterEvent } from "./index";
|
18
26
|
|
19
27
|
export class AppManager {
|
@@ -25,10 +33,16 @@ export class AppManager {
|
|
25
33
|
public mainViewProxy: MainViewProxy;
|
26
34
|
public refresher?: ReconnectRefresher;
|
27
35
|
public isReplay = this.windowManger.isReplay;
|
36
|
+
public mainViewScenesLength = 0;
|
28
37
|
|
29
38
|
private appListeners: AppListeners;
|
30
39
|
public boxManager?: BoxManager;
|
31
40
|
|
41
|
+
private _prevSceneIndex: number | undefined;
|
42
|
+
private _prevFocused: string | undefined;
|
43
|
+
private callbacksNode: ScenesCallbacksNode | null;
|
44
|
+
private appCreateQueue = new AppCreateQueue();
|
45
|
+
|
32
46
|
constructor(public windowManger: WindowManager) {
|
33
47
|
this.displayer = windowManger.displayer;
|
34
48
|
this.store.setContext({
|
@@ -57,6 +71,70 @@ export class AppManager {
|
|
57
71
|
this.onAppDelete(this.attributes.apps);
|
58
72
|
});
|
59
73
|
}
|
74
|
+
emitter.on("removeScenes", scenePath => {
|
75
|
+
if (scenePath === ROOT_DIR) {
|
76
|
+
this.setMainViewScenePath(ROOT_DIR);
|
77
|
+
return;
|
78
|
+
}
|
79
|
+
const mainViewScenePath = this.store.getMainViewScenePath();
|
80
|
+
if (this.room && mainViewScenePath) {
|
81
|
+
if (mainViewScenePath === scenePath) {
|
82
|
+
this.setMainViewScenePath(ROOT_DIR);
|
83
|
+
}
|
84
|
+
}
|
85
|
+
});
|
86
|
+
this.callbacksNode = this.displayer.createScenesCallback(ROOT_DIR, {
|
87
|
+
onAddScene: scenesCallback => {
|
88
|
+
this.mainViewScenesLength = scenesCallback.scenes.length;
|
89
|
+
callbacks.emit("mainViewScenesLengthChange", this.mainViewScenesLength);
|
90
|
+
},
|
91
|
+
onRemoveScene: scenesCallback => {
|
92
|
+
this.mainViewScenesLength = scenesCallback.scenes.length;
|
93
|
+
callbacks.emit("mainViewScenesLengthChange", this.mainViewScenesLength);
|
94
|
+
},
|
95
|
+
});
|
96
|
+
if (this.callbacksNode) {
|
97
|
+
this.mainViewScenesLength = this.callbacksNode.scenes.length;
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
private get eventName() {
|
102
|
+
return isRoom(this.displayer) ? "onRoomStateChanged" : "onPlayerStateChanged";
|
103
|
+
}
|
104
|
+
|
105
|
+
public get attributes() {
|
106
|
+
return this.windowManger.attributes;
|
107
|
+
}
|
108
|
+
|
109
|
+
public get canOperate() {
|
110
|
+
return this.windowManger.canOperate;
|
111
|
+
}
|
112
|
+
|
113
|
+
public get room() {
|
114
|
+
return isRoom(this.displayer) ? (this.displayer as Room) : undefined;
|
115
|
+
}
|
116
|
+
|
117
|
+
public get mainView() {
|
118
|
+
return this.mainViewProxy.view;
|
119
|
+
}
|
120
|
+
|
121
|
+
public get focusApp() {
|
122
|
+
if (this.store.focus) {
|
123
|
+
return this.appProxies.get(this.store.focus);
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
public get uid() {
|
128
|
+
return this.room?.uid || "";
|
129
|
+
}
|
130
|
+
|
131
|
+
public getMainViewSceneDir() {
|
132
|
+
const scenePath = this.store.getMainViewScenePath();
|
133
|
+
if (scenePath) {
|
134
|
+
return parseSceneDir(scenePath);
|
135
|
+
} else {
|
136
|
+
throw new Error("[WindowManager]: mainViewSceneDir not found");
|
137
|
+
}
|
60
138
|
}
|
61
139
|
|
62
140
|
private async onCreated() {
|
@@ -95,6 +173,38 @@ export class AppManager {
|
|
95
173
|
}
|
96
174
|
});
|
97
175
|
});
|
176
|
+
this.refresher?.add("mainViewIndex", () => {
|
177
|
+
return autorun(() => {
|
178
|
+
const mainSceneIndex = get(this.attributes, "_mainSceneIndex");
|
179
|
+
if (mainSceneIndex !== undefined && this._prevSceneIndex !== mainSceneIndex) {
|
180
|
+
callbacks.emit("mainViewSceneIndexChange", mainSceneIndex);
|
181
|
+
this._prevSceneIndex = mainSceneIndex;
|
182
|
+
}
|
183
|
+
});
|
184
|
+
});
|
185
|
+
this.refresher?.add("focusedChange", () => {
|
186
|
+
return autorun(() => {
|
187
|
+
const focused = get(this.attributes, "focus");
|
188
|
+
if (this._prevFocused !== focused) {
|
189
|
+
callbacks.emit("focusedChange", focused);
|
190
|
+
this.disposePrevFocusViewRedoUndoListeners(this._prevFocused);
|
191
|
+
setTimeout(() => {
|
192
|
+
this.addRedoUndoListeners(focused);
|
193
|
+
}, 0);
|
194
|
+
this._prevFocused = focused;
|
195
|
+
if (focused !== undefined) {
|
196
|
+
this.boxManager?.focusBox({ appId: focused });
|
197
|
+
// 确保 focus 修改的时候, appProxy 已经创建
|
198
|
+
setTimeout(() => {
|
199
|
+
const appProxy = this.appProxies.get(focused);
|
200
|
+
if (appProxy) {
|
201
|
+
appRegister.notifyApp(appProxy.kind, "focus", { appId: focused });
|
202
|
+
}
|
203
|
+
}, 0);
|
204
|
+
}
|
205
|
+
}
|
206
|
+
});
|
207
|
+
});
|
98
208
|
if (!this.attributes.apps || Object.keys(this.attributes.apps).length === 0) {
|
99
209
|
const mainScenePath = this.store.getMainViewScenePath();
|
100
210
|
if (!mainScenePath) return;
|
@@ -105,8 +215,61 @@ export class AppManager {
|
|
105
215
|
}
|
106
216
|
this.displayerWritableListener(!this.room?.isWritable);
|
107
217
|
this.displayer.callbacks.on("onEnableWriteNowChanged", this.displayerWritableListener);
|
218
|
+
this._prevFocused = this.attributes.focus;
|
219
|
+
this.addRedoUndoListeners(this.attributes.focus);
|
108
220
|
}
|
109
221
|
|
222
|
+
private disposePrevFocusViewRedoUndoListeners = (prevFocused: string | undefined) => {
|
223
|
+
if (prevFocused === undefined) {
|
224
|
+
this.mainView.callbacks.off("onCanRedoStepsUpdate", this.onCanRedoStepsUpdate);
|
225
|
+
this.mainView.callbacks.off("onCanUndoStepsUpdate", this.onCanRedoStepsUpdate);
|
226
|
+
} else {
|
227
|
+
const appProxy = this.appProxies.get(prevFocused);
|
228
|
+
if (appProxy) {
|
229
|
+
appProxy.view?.callbacks.off("onCanRedoStepsUpdate", this.onCanRedoStepsUpdate);
|
230
|
+
appProxy.view?.callbacks.off("onCanUndoStepsUpdate", this.onCanUndoStepsUpdate);
|
231
|
+
}
|
232
|
+
}
|
233
|
+
};
|
234
|
+
|
235
|
+
private addRedoUndoListeners = (focused: string | undefined) => {
|
236
|
+
if (focused === undefined) {
|
237
|
+
this.addViewCallbacks(
|
238
|
+
this.mainView,
|
239
|
+
this.onCanRedoStepsUpdate,
|
240
|
+
this.onCanUndoStepsUpdate
|
241
|
+
);
|
242
|
+
} else {
|
243
|
+
const focusApp = this.appProxies.get(focused);
|
244
|
+
if (focusApp && focusApp.view) {
|
245
|
+
this.addViewCallbacks(
|
246
|
+
focusApp.view,
|
247
|
+
this.onCanRedoStepsUpdate,
|
248
|
+
this.onCanUndoStepsUpdate
|
249
|
+
);
|
250
|
+
}
|
251
|
+
}
|
252
|
+
};
|
253
|
+
|
254
|
+
private addViewCallbacks = (
|
255
|
+
view: View,
|
256
|
+
redoListener: (steps: number) => void,
|
257
|
+
undoListener: (steps: number) => void
|
258
|
+
) => {
|
259
|
+
redoListener(view.canRedoSteps);
|
260
|
+
undoListener(view.canUndoSteps);
|
261
|
+
view.callbacks.on("onCanRedoStepsUpdate", redoListener);
|
262
|
+
view.callbacks.on("onCanUndoStepsUpdate", undoListener);
|
263
|
+
};
|
264
|
+
|
265
|
+
private onCanRedoStepsUpdate = (steps: number) => {
|
266
|
+
callbacks.emit("canRedoStepsChange", steps);
|
267
|
+
};
|
268
|
+
|
269
|
+
private onCanUndoStepsUpdate = (steps: number) => {
|
270
|
+
callbacks.emit("canUndoStepsChange", steps);
|
271
|
+
};
|
272
|
+
|
110
273
|
/**
|
111
274
|
* 插件更新 attributes 时的回调
|
112
275
|
*
|
@@ -122,19 +285,18 @@ export class AppManager {
|
|
122
285
|
createdAt: apps[appId].createdAt,
|
123
286
|
};
|
124
287
|
});
|
125
|
-
for (const { id } of
|
288
|
+
for (const { id } of orderBy(appsWithCreatedAt, "createdAt", "asc")) {
|
126
289
|
if (!this.appProxies.has(id) && !this.appStatus.has(id)) {
|
127
290
|
const app = apps[id];
|
128
291
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
await this.baseInsertApp(
|
292
|
+
this.appStatus.set(id, AppStatus.StartCreate);
|
293
|
+
try {
|
294
|
+
const appAttributes = this.attributes[id];
|
295
|
+
if (!appAttributes) {
|
296
|
+
throw new Error("appAttributes is undefined");
|
297
|
+
}
|
298
|
+
this.appCreateQueue.push(() => {
|
299
|
+
return this.baseInsertApp(
|
138
300
|
{
|
139
301
|
kind: app.kind,
|
140
302
|
options: app.options,
|
@@ -143,13 +305,11 @@ export class AppManager {
|
|
143
305
|
id,
|
144
306
|
false
|
145
307
|
);
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
this.appStatus.delete(id);
|
152
|
-
});
|
308
|
+
});
|
309
|
+
this.focusByAttributes(apps);
|
310
|
+
} catch (error) {
|
311
|
+
console.warn(`[WindowManager]: Insert App Error`, error);
|
312
|
+
}
|
153
313
|
}
|
154
314
|
}
|
155
315
|
}
|
@@ -190,10 +350,11 @@ export class AppManager {
|
|
190
350
|
emitter.emit("mainViewMounted");
|
191
351
|
}
|
192
352
|
|
193
|
-
public setMainViewFocusPath() {
|
194
|
-
const
|
195
|
-
if (
|
196
|
-
setViewFocusScenePath(this.mainView,
|
353
|
+
public setMainViewFocusPath(scenePath?: string) {
|
354
|
+
const focusScenePath = scenePath || this.store.getMainViewScenePath();
|
355
|
+
if (focusScenePath) {
|
356
|
+
const view = setViewFocusScenePath(this.mainView, focusScenePath);
|
357
|
+
return view?.focusScenePath === focusScenePath;
|
197
358
|
}
|
198
359
|
}
|
199
360
|
|
@@ -272,10 +433,6 @@ export class AppManager {
|
|
272
433
|
}
|
273
434
|
});
|
274
435
|
}
|
275
|
-
if (state.roomMembers) {
|
276
|
-
this.windowManger.cursorManager?.setRoomMembers(state.roomMembers);
|
277
|
-
this.windowManger.cursorManager?.cleanMemberAttributes(state.roomMembers);
|
278
|
-
}
|
279
436
|
this.appProxies.forEach(appProxy => {
|
280
437
|
appProxy.appEmitter.emit("roomStateChange", state);
|
281
438
|
});
|
@@ -296,37 +453,14 @@ export class AppManager {
|
|
296
453
|
});
|
297
454
|
if (isWritable === true) {
|
298
455
|
this.mainView.disableCameraTransform = false;
|
456
|
+
if (this.room && this.room.disableSerialization === true) {
|
457
|
+
this.room.disableSerialization = false;
|
458
|
+
}
|
299
459
|
} else {
|
300
460
|
this.mainView.disableCameraTransform = true;
|
301
461
|
}
|
302
462
|
};
|
303
463
|
|
304
|
-
private get eventName() {
|
305
|
-
return isRoom(this.displayer) ? "onRoomStateChanged" : "onPlayerStateChanged";
|
306
|
-
}
|
307
|
-
|
308
|
-
public get attributes() {
|
309
|
-
return this.windowManger.attributes;
|
310
|
-
}
|
311
|
-
|
312
|
-
public get canOperate() {
|
313
|
-
return this.windowManger.canOperate;
|
314
|
-
}
|
315
|
-
|
316
|
-
public get room() {
|
317
|
-
return isRoom(this.displayer) ? (this.displayer as Room) : undefined;
|
318
|
-
}
|
319
|
-
|
320
|
-
public get mainView() {
|
321
|
-
return this.mainViewProxy.view;
|
322
|
-
}
|
323
|
-
|
324
|
-
public get focusApp() {
|
325
|
-
if (this.store.focus) {
|
326
|
-
return this.appProxies.get(this.store.focus);
|
327
|
-
}
|
328
|
-
}
|
329
|
-
|
330
464
|
public safeSetAttributes(attributes: any) {
|
331
465
|
this.windowManger.safeSetAttributes(attributes);
|
332
466
|
}
|
@@ -338,6 +472,10 @@ export class AppManager {
|
|
338
472
|
public async setMainViewScenePath(scenePath: string) {
|
339
473
|
if (this.room) {
|
340
474
|
const scenePathType = this.displayer.scenePathType(scenePath);
|
475
|
+
const sceneDir = parseSceneDir(scenePath);
|
476
|
+
if (sceneDir !== ROOT_DIR) {
|
477
|
+
throw new Error(`[WindowManager]: main view scenePath must in root dir "/"`);
|
478
|
+
}
|
341
479
|
if (scenePathType === ScenePathType.None) {
|
342
480
|
throw new Error(`[WindowManager]: ${scenePath} not valid scene`);
|
343
481
|
} else if (scenePathType === ScenePathType.Page) {
|
@@ -352,32 +490,56 @@ export class AppManager {
|
|
352
490
|
}
|
353
491
|
|
354
492
|
private async _setMainViewScenePath(scenePath: string) {
|
355
|
-
this.
|
356
|
-
|
357
|
-
|
358
|
-
|
493
|
+
const success = this.setMainViewFocusPath(scenePath);
|
494
|
+
if (success) {
|
495
|
+
this.safeSetAttributes({ _mainScenePath: scenePath });
|
496
|
+
this.store.setMainViewFocusPath(this.mainView);
|
497
|
+
this.updateSceneIndex();
|
498
|
+
this.dispatchSetMainViewScenePath(scenePath);
|
499
|
+
}
|
359
500
|
}
|
360
501
|
|
502
|
+
private updateSceneIndex = () => {
|
503
|
+
const scenePath = this.store.getMainViewScenePath() as string;
|
504
|
+
const sceneDir = parseSceneDir(scenePath);
|
505
|
+
const scenes = entireScenes(this.displayer)[sceneDir];
|
506
|
+
if (scenes.length) {
|
507
|
+
// "/ppt3/1" -> "1"
|
508
|
+
const pageName = scenePath.replace(sceneDir, "").replace("/", "");
|
509
|
+
const index = scenes.findIndex(scene => scene.name === pageName);
|
510
|
+
if (isInteger(index) && index >= 0) {
|
511
|
+
this.safeSetAttributes({ _mainSceneIndex: index });
|
512
|
+
}
|
513
|
+
}
|
514
|
+
};
|
515
|
+
|
361
516
|
public async setMainViewSceneIndex(index: number) {
|
362
517
|
if (this.room) {
|
363
|
-
this.
|
518
|
+
if (this.store.getMainViewSceneIndex() === index) return;
|
364
519
|
const mainViewScenePath = this.store.getMainViewScenePath() as string;
|
365
520
|
if (mainViewScenePath) {
|
366
|
-
const
|
367
|
-
sceneList.pop();
|
368
|
-
let sceneDir = sceneList.join("/");
|
369
|
-
if (sceneDir === "") {
|
370
|
-
sceneDir = "/";
|
371
|
-
}
|
521
|
+
const sceneDir = parseSceneDir(mainViewScenePath);
|
372
522
|
const scenePath = makeValidScenePath(this.displayer, sceneDir, index);
|
373
523
|
if (scenePath) {
|
374
|
-
this.
|
375
|
-
|
524
|
+
const success = this.setMainViewFocusPath(scenePath);
|
525
|
+
if (success) {
|
526
|
+
this.store.setMainViewScenePath(scenePath);
|
527
|
+
this.safeSetAttributes({ _mainSceneIndex: index });
|
528
|
+
this.dispatchSetMainViewScenePath(scenePath);
|
529
|
+
}
|
530
|
+
} else {
|
531
|
+
throw new Error(`[WindowManager]: ${sceneDir}: ${index} not valid index`);
|
376
532
|
}
|
377
|
-
}
|
533
|
+
}
|
378
534
|
}
|
379
535
|
}
|
380
536
|
|
537
|
+
private dispatchSetMainViewScenePath(scenePath: string): void {
|
538
|
+
this.dispatchInternalEvent(Events.SetMainViewScenePath, { nextScenePath: scenePath });
|
539
|
+
// 兼容 15 的 SDK, 需要 room 的当前 ScenePath
|
540
|
+
setScenePath(this.room, scenePath);
|
541
|
+
}
|
542
|
+
|
381
543
|
public getAppInitPath(appId: string): string | undefined {
|
382
544
|
const attrs = this.store.getAppAttributes(appId);
|
383
545
|
if (attrs) {
|
@@ -445,6 +607,7 @@ export class AppManager {
|
|
445
607
|
const reconnected = appProxies.map(appProxy => {
|
446
608
|
return appProxy.onReconnected();
|
447
609
|
});
|
610
|
+
this.mainViewProxy.onReconnect();
|
448
611
|
await Promise.all(reconnected);
|
449
612
|
}
|
450
613
|
|
@@ -461,6 +624,11 @@ export class AppManager {
|
|
461
624
|
});
|
462
625
|
}
|
463
626
|
|
627
|
+
public findMemberByUid = (uid: string) => {
|
628
|
+
const roomMembers = this.room?.state.roomMembers;
|
629
|
+
return roomMembers?.find(member => member.payload?.uid === uid);
|
630
|
+
};
|
631
|
+
|
464
632
|
public destroy() {
|
465
633
|
this.displayer.callbacks.off(this.eventName, this.displayerStateListener);
|
466
634
|
this.displayer.callbacks.off("onEnableWriteNowChanged", this.displayerWritableListener);
|
@@ -477,5 +645,10 @@ export class AppManager {
|
|
477
645
|
this.refresher?.destroy();
|
478
646
|
this.mainViewProxy.destroy();
|
479
647
|
callbacks.clearListeners();
|
648
|
+
this.callbacksNode?.dispose();
|
649
|
+
this.appCreateQueue.destroy();
|
650
|
+
this.disposePrevFocusViewRedoUndoListeners(this._prevFocused);
|
651
|
+
this._prevFocused = undefined;
|
652
|
+
this._prevSceneIndex = undefined;
|
480
653
|
}
|
481
654
|
}
|