@netless/window-manager 0.4.0-canary.9 → 0.4.2
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 +22 -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 +32 -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 +8 -9
- 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 +27 -8
- package/src/AppManager.ts +231 -70
- 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 +33 -18
- 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 +150 -33
- package/src/shim.d.ts +2 -1
- package/src/style.css +6 -1
- package/src/typings.ts +2 -2
- package/vite.config.js +7 -4
- 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/Cursor/index.ts
CHANGED
@@ -1,13 +1,11 @@
|
|
1
|
-
import {
|
2
|
-
import { Base } from "../Base";
|
3
|
-
import { compact, debounce, get, uniq } from "lodash";
|
1
|
+
import { ApplianceNames } from "white-web-sdk";
|
4
2
|
import { Cursor } from "./Cursor";
|
5
|
-
import { CursorState } from "../constants";
|
3
|
+
import { CursorState, Events } from "../constants";
|
6
4
|
import { emitter, WindowManager } from "../index";
|
7
|
-
import { Fields } from "../AttributesDelegate";
|
8
|
-
import { onObjectInserted } from "../Utils/Reactive";
|
9
5
|
import { SideEffectManager } from "side-effect-manager";
|
10
|
-
import
|
6
|
+
import { throttle } from "lodash";
|
7
|
+
import type { CursorMovePayload } from "../index";
|
8
|
+
import type { PositionType } from "../AttributesDelegate";
|
11
9
|
import type { Point, RoomMember, View } from "white-web-sdk";
|
12
10
|
import type { AppManager } from "../AppManager";
|
13
11
|
|
@@ -21,30 +19,47 @@ export type MoveCursorParams = {
|
|
21
19
|
x: number;
|
22
20
|
y: number;
|
23
21
|
};
|
24
|
-
export class CursorManager
|
22
|
+
export class CursorManager {
|
25
23
|
public containerRect?: DOMRect;
|
26
24
|
public wrapperRect?: DOMRect;
|
27
25
|
public cursorInstances: Map<string, Cursor> = new Map();
|
28
26
|
public roomMembers?: readonly RoomMember[];
|
29
27
|
private mainViewElement?: HTMLDivElement;
|
30
28
|
private sideEffectManager = new SideEffectManager();
|
29
|
+
private store = this.manager.store;
|
31
30
|
|
32
|
-
constructor(private
|
33
|
-
|
34
|
-
this.roomMembers = this.appManager.room?.state.roomMembers;
|
31
|
+
constructor(private manager: AppManager, private enableCursor: boolean) {
|
32
|
+
this.roomMembers = this.manager.room?.state.roomMembers;
|
35
33
|
const wrapper = WindowManager.wrapper;
|
36
34
|
if (wrapper) {
|
37
35
|
this.setupWrapper(wrapper);
|
38
36
|
}
|
39
|
-
emitter.on("
|
40
|
-
this.
|
37
|
+
emitter.on("cursorMove", payload => {
|
38
|
+
let cursorInstance = this.cursorInstances.get(payload.uid);
|
39
|
+
if (!cursorInstance) {
|
40
|
+
cursorInstance = new Cursor(this.manager, payload.uid, this, WindowManager.wrapper);
|
41
|
+
this.cursorInstances.set(payload.uid, cursorInstance);
|
42
|
+
}
|
43
|
+
if (payload.state === CursorState.Leave) {
|
44
|
+
cursorInstance.leave();
|
45
|
+
} else {
|
46
|
+
const member = cursorInstance.updateMember();
|
47
|
+
const isLaserPointer =
|
48
|
+
member?.memberState.currentApplianceName === ApplianceNames.laserPointer;
|
49
|
+
if (this.enableCursor || isLaserPointer) {
|
50
|
+
cursorInstance.move(payload.position);
|
51
|
+
}
|
52
|
+
}
|
53
|
+
});
|
54
|
+
this.sideEffectManager.add(() => {
|
55
|
+
const unsubscribe = emitter.on("playgroundSizeChange", () => {
|
56
|
+
this.updateContainerRect();
|
57
|
+
});
|
58
|
+
return unsubscribe;
|
41
59
|
});
|
42
60
|
}
|
43
61
|
|
44
62
|
public setupWrapper(wrapper: HTMLElement) {
|
45
|
-
if (this.manager.refresher?.hasReactor("cursors")) {
|
46
|
-
this.destroy();
|
47
|
-
}
|
48
63
|
this.sideEffectManager.add(() => {
|
49
64
|
wrapper.addEventListener("pointerenter", this.mouseMoveListener);
|
50
65
|
wrapper.addEventListener("pointermove", this.mouseMoveListener);
|
@@ -56,77 +71,38 @@ export class CursorManager extends Base {
|
|
56
71
|
};
|
57
72
|
});
|
58
73
|
|
59
|
-
this.initCursorAttributes();
|
60
74
|
this.wrapperRect = wrapper.getBoundingClientRect();
|
61
|
-
this.startReaction(wrapper);
|
62
75
|
}
|
63
76
|
|
64
77
|
public setMainViewDivElement(div: HTMLDivElement) {
|
65
78
|
this.mainViewElement = div;
|
66
79
|
}
|
67
80
|
|
68
|
-
private startReaction(wrapper: HTMLElement) {
|
69
|
-
this.manager.refresher?.add("cursors", () => {
|
70
|
-
return onObjectInserted(this.cursors, () => {
|
71
|
-
this.handleRoomMembersChange(wrapper);
|
72
|
-
});
|
73
|
-
});
|
74
|
-
}
|
75
|
-
|
76
|
-
private getUids = (members: readonly RoomMember[] | undefined) => {
|
77
|
-
return compact(uniq(members?.map(member => member.payload?.uid)));
|
78
|
-
};
|
79
|
-
|
80
|
-
private handleRoomMembersChange = debounce((wrapper: HTMLElement) => {
|
81
|
-
const uids = this.getUids(this.roomMembers);
|
82
|
-
const cursors = Object.keys(this.cursors);
|
83
|
-
if (uids?.length) {
|
84
|
-
cursors.map(uid => {
|
85
|
-
if (uids.includes(uid) && !this.cursorInstances.has(uid)) {
|
86
|
-
if (uid === this.context.uid) {
|
87
|
-
return;
|
88
|
-
}
|
89
|
-
const component = new Cursor(
|
90
|
-
this.appManager,
|
91
|
-
this.addCursorChangeListener,
|
92
|
-
this.cursors,
|
93
|
-
uid,
|
94
|
-
this,
|
95
|
-
wrapper
|
96
|
-
);
|
97
|
-
this.cursorInstances.set(uid, component);
|
98
|
-
}
|
99
|
-
});
|
100
|
-
}
|
101
|
-
}, 100);
|
102
|
-
|
103
|
-
public get cursors() {
|
104
|
-
return this.manager.attributes?.[Fields.Cursors];
|
105
|
-
}
|
106
|
-
|
107
81
|
public get boxState() {
|
108
82
|
return this.store.getBoxState();
|
109
83
|
}
|
110
84
|
|
111
85
|
public get focusView() {
|
112
|
-
return this.
|
86
|
+
return this.manager.focusApp?.view;
|
113
87
|
}
|
114
88
|
|
115
|
-
private mouseMoveListener =
|
89
|
+
private mouseMoveListener = throttle((event: MouseEvent) => {
|
116
90
|
this.updateCursor(this.getType(event), event.clientX, event.clientY);
|
117
|
-
},
|
91
|
+
}, 16);
|
118
92
|
|
119
93
|
private updateCursor(event: EventType, clientX: number, clientY: number) {
|
120
94
|
if (this.wrapperRect && this.manager.canOperate) {
|
121
|
-
const view = event.type === "main" ? this.
|
95
|
+
const view = event.type === "main" ? this.manager.mainView : this.focusView;
|
122
96
|
const point = this.getPoint(view, clientX, clientY);
|
123
97
|
if (point) {
|
124
|
-
this.
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
98
|
+
this.manager.dispatchInternalEvent(Events.CursorMove, {
|
99
|
+
uid: this.manager.uid,
|
100
|
+
position: {
|
101
|
+
x: point.x,
|
102
|
+
y: point.y,
|
103
|
+
type: event.type,
|
104
|
+
},
|
105
|
+
} as CursorMovePayload);
|
130
106
|
}
|
131
107
|
}
|
132
108
|
}
|
@@ -151,7 +127,7 @@ export class CursorManager extends Base {
|
|
151
127
|
*/
|
152
128
|
private getType = (event: MouseEvent | Touch): EventType => {
|
153
129
|
const target = event.target as HTMLElement;
|
154
|
-
const focusApp = this.
|
130
|
+
const focusApp = this.manager.focusApp;
|
155
131
|
switch (target.parentElement) {
|
156
132
|
case this.mainViewElement: {
|
157
133
|
return { type: "main" };
|
@@ -165,25 +141,8 @@ export class CursorManager extends Base {
|
|
165
141
|
}
|
166
142
|
};
|
167
143
|
|
168
|
-
private initCursorAttributes() {
|
169
|
-
this.store.updateCursor(this.context.uid, {
|
170
|
-
x: 0,
|
171
|
-
y: 0,
|
172
|
-
type: "main",
|
173
|
-
});
|
174
|
-
this.store.updateCursorState(this.context.uid, CursorState.Leave);
|
175
|
-
}
|
176
|
-
|
177
|
-
private setNormalCursorState() {
|
178
|
-
const cursorState = this.store.getCursorState(this.context.uid);
|
179
|
-
if (cursorState !== CursorState.Normal) {
|
180
|
-
this.store.updateCursorState(this.context.uid, CursorState.Normal);
|
181
|
-
}
|
182
|
-
}
|
183
|
-
|
184
144
|
private mouseLeaveListener = () => {
|
185
|
-
this.hideCursor(this.
|
186
|
-
this.store.updateCursorState(this.context.uid, CursorState.Leave);
|
145
|
+
this.hideCursor(this.manager.uid);
|
187
146
|
};
|
188
147
|
|
189
148
|
public updateContainerRect() {
|
@@ -191,16 +150,6 @@ export class CursorManager extends Base {
|
|
191
150
|
this.wrapperRect = WindowManager.wrapper?.getBoundingClientRect();
|
192
151
|
}
|
193
152
|
|
194
|
-
public setRoomMembers(members: readonly RoomMember[]) {
|
195
|
-
this.roomMembers = members;
|
196
|
-
this.cursorInstances.forEach(cursor => {
|
197
|
-
cursor.setMember();
|
198
|
-
});
|
199
|
-
if (WindowManager.wrapper) {
|
200
|
-
this.handleRoomMembersChange(WindowManager.wrapper);
|
201
|
-
}
|
202
|
-
}
|
203
|
-
|
204
153
|
public deleteCursor(uid: string) {
|
205
154
|
this.store.cleanCursor(uid);
|
206
155
|
const cursor = this.cursorInstances.get(uid);
|
@@ -216,48 +165,6 @@ export class CursorManager extends Base {
|
|
216
165
|
}
|
217
166
|
}
|
218
167
|
|
219
|
-
public cleanMemberAttributes(members: readonly RoomMember[]) {
|
220
|
-
const uids = this.getUids(members);
|
221
|
-
const needDeleteIds: string[] = [];
|
222
|
-
const cursors = Object.keys(this.cursors);
|
223
|
-
cursors.map(cursorId => {
|
224
|
-
const index = uids.findIndex(id => id === cursorId);
|
225
|
-
if (index === -1) {
|
226
|
-
needDeleteIds.push(cursorId);
|
227
|
-
}
|
228
|
-
});
|
229
|
-
needDeleteIds.forEach(uid => {
|
230
|
-
this.deleteCursor(uid);
|
231
|
-
});
|
232
|
-
}
|
233
|
-
|
234
|
-
public onReconnect() {
|
235
|
-
if (this.cursorInstances.size) {
|
236
|
-
this.cursorInstances.forEach(cursor => cursor.destroy());
|
237
|
-
this.cursorInstances.clear();
|
238
|
-
}
|
239
|
-
this.roomMembers = this.appManager.room?.state.roomMembers;
|
240
|
-
if (WindowManager.wrapper) {
|
241
|
-
this.handleRoomMembersChange(WindowManager.wrapper);
|
242
|
-
}
|
243
|
-
}
|
244
|
-
|
245
|
-
public addCursorChangeListener = (
|
246
|
-
uid: string,
|
247
|
-
callback: (position: Position, state: CursorState) => void
|
248
|
-
) => {
|
249
|
-
this.manager.refresher?.add(uid, () => {
|
250
|
-
const disposer = autorun(() => {
|
251
|
-
const position = get(this.cursors, [uid, Fields.Position]);
|
252
|
-
const state = get(this.cursors, [uid, Fields.CursorState]);
|
253
|
-
if (position) {
|
254
|
-
callback(position, state);
|
255
|
-
}
|
256
|
-
});
|
257
|
-
return disposer;
|
258
|
-
});
|
259
|
-
};
|
260
|
-
|
261
168
|
public destroy() {
|
262
169
|
this.sideEffectManager.flushAll();
|
263
170
|
if (this.cursorInstances.size) {
|
@@ -266,6 +173,5 @@ export class CursorManager extends Base {
|
|
266
173
|
});
|
267
174
|
this.cursorInstances.clear();
|
268
175
|
}
|
269
|
-
this.manager.refresher?.remove("cursors");
|
270
176
|
}
|
271
177
|
}
|
package/src/Helper.ts
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
import { getVersionNumber } from "./Utils/Common";
|
2
|
+
import { REQUIRE_VERSION } from "./constants";
|
3
|
+
import { WhiteVersion } from "white-web-sdk";
|
4
|
+
import { WhiteWebSDKInvalidError } from "./Utils/error";
|
1
5
|
import { WindowManager } from "./index";
|
2
6
|
|
3
7
|
export const setupWrapper = (
|
@@ -27,4 +31,11 @@ export const setupWrapper = (
|
|
27
31
|
WindowManager.wrapper = wrapper;
|
28
32
|
|
29
33
|
return { playground, wrapper, sizer, mainViewElement };
|
30
|
-
};
|
34
|
+
};
|
35
|
+
|
36
|
+
export const checkVersion = () => {
|
37
|
+
const version = getVersionNumber(WhiteVersion);
|
38
|
+
if (version < getVersionNumber(REQUIRE_VERSION)) {
|
39
|
+
throw new WhiteWebSDKInvalidError(REQUIRE_VERSION);
|
40
|
+
}
|
41
|
+
};
|
package/src/Register/index.ts
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
import Emittery from "emittery";
|
2
|
-
import type { NetlessApp, RegisterEvents, RegisterParams } from "../typings";
|
3
2
|
import { loadApp } from "./loader";
|
3
|
+
import type { NetlessApp, RegisterEvents, RegisterParams } from "../typings";
|
4
|
+
|
5
|
+
export type LoadAppEvent = {
|
6
|
+
kind: string;
|
7
|
+
status: "start" | "success" | "failed";
|
8
|
+
reason?: string;
|
9
|
+
};
|
4
10
|
|
5
11
|
class AppRegister {
|
6
12
|
public kindEmitters: Map<string, Emittery<RegisterEvents>> = new Map();
|
@@ -10,34 +16,39 @@ class AppRegister {
|
|
10
16
|
|
11
17
|
public async register(params: RegisterParams): Promise<void> {
|
12
18
|
this.registered.set(params.kind, params);
|
13
|
-
|
14
|
-
const srcOrAppOrFunction = params.src
|
15
|
-
let downloadApp: () => Promise<NetlessApp
|
16
|
-
|
19
|
+
|
20
|
+
const srcOrAppOrFunction = params.src;
|
21
|
+
let downloadApp: () => Promise<NetlessApp>;
|
22
|
+
|
17
23
|
if (typeof srcOrAppOrFunction === "string") {
|
18
24
|
downloadApp = async () => {
|
19
|
-
|
25
|
+
let appClass = (await loadApp(srcOrAppOrFunction, params.kind)) as any;
|
20
26
|
if (appClass) {
|
21
|
-
|
27
|
+
if (appClass.__esModule) {
|
28
|
+
appClass = appClass.default;
|
29
|
+
}
|
30
|
+
return appClass;
|
22
31
|
} else {
|
23
|
-
throw new Error(
|
32
|
+
throw new Error(
|
33
|
+
`[WindowManager]: load remote script failed, ${srcOrAppOrFunction}`
|
34
|
+
);
|
24
35
|
}
|
25
|
-
}
|
36
|
+
};
|
26
37
|
} else if (typeof srcOrAppOrFunction === "function") {
|
27
|
-
downloadApp = srcOrAppOrFunction
|
38
|
+
downloadApp = srcOrAppOrFunction;
|
28
39
|
} else {
|
29
|
-
downloadApp = async () => srcOrAppOrFunction
|
40
|
+
downloadApp = async () => srcOrAppOrFunction;
|
30
41
|
}
|
31
42
|
|
32
43
|
this.appClasses.set(params.kind, async () => {
|
33
|
-
let app = this.appClassesCache.get(params.kind)
|
44
|
+
let app = this.appClassesCache.get(params.kind);
|
34
45
|
if (!app) {
|
35
|
-
app = downloadApp()
|
36
|
-
this.appClassesCache.set(params.kind, app)
|
46
|
+
app = downloadApp();
|
47
|
+
this.appClassesCache.set(params.kind, app);
|
37
48
|
}
|
38
|
-
return app
|
49
|
+
return app;
|
39
50
|
});
|
40
|
-
|
51
|
+
|
41
52
|
if (params.addHooks) {
|
42
53
|
const emitter = this.createKindEmitter(params.kind);
|
43
54
|
if (emitter) {
|
@@ -46,7 +57,11 @@ class AppRegister {
|
|
46
57
|
}
|
47
58
|
}
|
48
59
|
|
49
|
-
public async notifyApp<T extends keyof RegisterEvents>(
|
60
|
+
public async notifyApp<T extends keyof RegisterEvents>(
|
61
|
+
kind: string,
|
62
|
+
event: T,
|
63
|
+
payload: RegisterEvents[T]
|
64
|
+
) {
|
50
65
|
const emitter = this.kindEmitters.get(kind);
|
51
66
|
await emitter?.emit(event, payload);
|
52
67
|
}
|
package/src/Register/loader.ts
CHANGED
@@ -1,14 +1,15 @@
|
|
1
|
+
import { callbacks } from "../index";
|
1
2
|
import { getItem, setItem } from "./storage";
|
2
3
|
import type { NetlessApp } from "../typings";
|
3
4
|
|
4
5
|
const Prefix = "NetlessApp";
|
5
6
|
|
6
|
-
const TIMEOUT = 10000; // 10 秒超时
|
7
|
+
const TIMEOUT = 10000; // 下载 script 10 秒超时
|
7
8
|
|
8
9
|
export const getScript = async (url: string): Promise<string> => {
|
9
10
|
const item = await getItem(url);
|
10
11
|
if (item) {
|
11
|
-
return item;
|
12
|
+
return item.sourceCode;
|
12
13
|
} else {
|
13
14
|
const result = await fetchWithTimeout(url, { timeout: TIMEOUT });
|
14
15
|
const text = await result.text();
|
@@ -18,7 +19,7 @@ export const getScript = async (url: string): Promise<string> => {
|
|
18
19
|
};
|
19
20
|
|
20
21
|
export const executeScript = (text: string, appName: string): NetlessApp => {
|
21
|
-
let result = Function(text +
|
22
|
+
let result = Function(text + `\n;return ${appName}`)();
|
22
23
|
if (typeof result === "undefined") {
|
23
24
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
24
25
|
// @ts-ignore
|
@@ -33,19 +34,33 @@ export const loadApp = async (
|
|
33
34
|
name?: string
|
34
35
|
): Promise<NetlessApp | undefined> => {
|
35
36
|
const appName = name || Prefix + key;
|
36
|
-
|
37
|
+
callbacks.emit("loadApp", { kind: key, status: "start" });
|
37
38
|
try {
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
39
|
+
const text = await getScript(url);
|
40
|
+
if (!text || text.length === 0) {
|
41
|
+
callbacks.emit("loadApp", { kind: key, status: "failed", reason: "script is empty." });
|
42
|
+
return;
|
43
|
+
}
|
44
|
+
try {
|
45
|
+
const result = executeScript(text, appName);
|
46
|
+
callbacks.emit("loadApp", { kind: key, status: "success" });
|
47
|
+
return result;
|
48
|
+
} catch (error: any) {
|
49
|
+
if (error.message.includes("Can only have one anonymous define call per script file")) {
|
50
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
51
|
+
// @ts-ignore
|
52
|
+
const define = window.define;
|
53
|
+
if ("function" == typeof define && define.amd) {
|
54
|
+
delete define.amd;
|
55
|
+
}
|
56
|
+
const result = executeScript(text, appName);
|
57
|
+
callbacks.emit("loadApp", { kind: key, status: "success" });
|
58
|
+
return result;
|
46
59
|
}
|
47
|
-
|
60
|
+
callbacks.emit("loadApp", { kind: key, status: "failed", reason: error.message });
|
48
61
|
}
|
62
|
+
} catch (error: any) {
|
63
|
+
callbacks.emit("loadApp", { kind: key, status: "failed", reason: error.message });
|
49
64
|
}
|
50
65
|
};
|
51
66
|
|
package/src/Register/storage.ts
CHANGED
@@ -3,6 +3,11 @@ const DatabaseName = "__WindowManagerAppCache";
|
|
3
3
|
let db: IDBDatabase;
|
4
4
|
let store: IDBObjectStore;
|
5
5
|
|
6
|
+
export type Item = {
|
7
|
+
kind: string;
|
8
|
+
sourceCode: string;
|
9
|
+
}
|
10
|
+
|
6
11
|
export const initDb = async () => {
|
7
12
|
db = await createDb();
|
8
13
|
}
|
@@ -12,7 +17,7 @@ export const setItem = (key: string, val: any) => {
|
|
12
17
|
return addRecord(db, { kind: key, sourceCode: val })
|
13
18
|
};
|
14
19
|
|
15
|
-
export const getItem = async (key: string): Promise<
|
20
|
+
export const getItem = async (key: string): Promise<Item | null> => {
|
16
21
|
if (!db) return null;
|
17
22
|
return await query(db, key);
|
18
23
|
};
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import type { AppProxy } from "../AppProxy";
|
2
|
+
|
3
|
+
export type Invoker = () => Promise<AppProxy | undefined>;
|
4
|
+
|
5
|
+
export class AppCreateQueue {
|
6
|
+
private list: Invoker[] = [];
|
7
|
+
private currentInvoker: Invoker | undefined;
|
8
|
+
private timer: number | undefined;
|
9
|
+
|
10
|
+
private initInterval() {
|
11
|
+
return setInterval(() => {
|
12
|
+
this.invoke();
|
13
|
+
}, 50);
|
14
|
+
}
|
15
|
+
|
16
|
+
public push(item: Invoker) {
|
17
|
+
this.list.push(item);
|
18
|
+
this.invoke();
|
19
|
+
if (this.timer === undefined && this.list.length > 0) {
|
20
|
+
this.timer = this.initInterval();
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
public invoke() {
|
25
|
+
if (this.list.length === 0) {
|
26
|
+
return;
|
27
|
+
}
|
28
|
+
if (this.currentInvoker !== undefined) {
|
29
|
+
return;
|
30
|
+
}
|
31
|
+
|
32
|
+
const item = this.list.shift();
|
33
|
+
if (item) {
|
34
|
+
this.currentInvoker = item;
|
35
|
+
item()
|
36
|
+
.then(() => {
|
37
|
+
this.currentInvoker = undefined;
|
38
|
+
if (this.list.length === 0) {
|
39
|
+
clearInterval(this.timer);
|
40
|
+
}
|
41
|
+
})
|
42
|
+
.catch(error => {
|
43
|
+
console.error(`[WindowManager]: create app error: ${error.message}`);
|
44
|
+
clearInterval(this.timer);
|
45
|
+
});
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
public destroy() {
|
50
|
+
if (this.timer) {
|
51
|
+
clearInterval(this.timer);
|
52
|
+
}
|
53
|
+
}
|
54
|
+
}
|
package/src/Utils/Common.ts
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
import { appRegister } from "../Register";
|
2
2
|
import { debounce } from "lodash";
|
3
3
|
import { emitter } from "../index";
|
4
|
+
import { ScenePathType } from "white-web-sdk";
|
4
5
|
import { v4 } from "uuid";
|
5
6
|
import type { PublicEvent } from "../index";
|
6
7
|
import type { Displayer, ViewVisionMode, Room, View } from "white-web-sdk";
|
7
8
|
import type Emittery from "emittery";
|
9
|
+
import { ROOT_DIR } from "../constants";
|
8
10
|
|
9
11
|
export const genAppId = async (kind: string) => {
|
10
12
|
const impl = await appRegister.appClasses.get(kind)?.();
|
@@ -17,6 +19,14 @@ export const genAppId = async (kind: string) => {
|
|
17
19
|
export const setViewFocusScenePath = (view: View, focusScenePath: string) => {
|
18
20
|
if (view.focusScenePath !== focusScenePath) {
|
19
21
|
view.focusScenePath = focusScenePath;
|
22
|
+
return view;
|
23
|
+
}
|
24
|
+
};
|
25
|
+
|
26
|
+
export const setViewSceneIndex = (view: View, index: number) => {
|
27
|
+
if (view.focusSceneIndex !== index) {
|
28
|
+
view.focusSceneIndex = index;
|
29
|
+
return view;
|
20
30
|
}
|
21
31
|
};
|
22
32
|
|
@@ -42,6 +52,15 @@ export const getScenePath = (
|
|
42
52
|
}
|
43
53
|
};
|
44
54
|
|
55
|
+
export const removeScenes = (room: Room | undefined, scenePath: string) => {
|
56
|
+
if (room) {
|
57
|
+
const type = room.scenePathType(scenePath);
|
58
|
+
if (type !== ScenePathType.None) {
|
59
|
+
room.removeScenes(scenePath);
|
60
|
+
}
|
61
|
+
}
|
62
|
+
};
|
63
|
+
|
45
64
|
export const setViewMode = (view: View, mode: ViewVisionMode) => {
|
46
65
|
if (!(view as any).didRelease && view.mode !== mode) {
|
47
66
|
view.mode = mode;
|
@@ -70,8 +89,10 @@ export const notifyMainViewModeChange = debounce(
|
|
70
89
|
export const makeValidScenePath = (displayer: Displayer, scenePath: string, index = 0) => {
|
71
90
|
const scenes = entireScenes(displayer)[scenePath];
|
72
91
|
if (!scenes) return;
|
73
|
-
const
|
74
|
-
if (
|
92
|
+
const scene = scenes[index];
|
93
|
+
if (!scene) return;
|
94
|
+
const firstSceneName = scene.name;
|
95
|
+
if (scenePath === ROOT_DIR) {
|
75
96
|
return `/${firstSceneName}`;
|
76
97
|
} else {
|
77
98
|
return `${scenePath}/${firstSceneName}`;
|
@@ -82,10 +103,22 @@ export const entireScenes = (displayer: Displayer) => {
|
|
82
103
|
return displayer.entireScenes();
|
83
104
|
};
|
84
105
|
|
106
|
+
|
85
107
|
export const isValidScenePath = (scenePath: string) => {
|
86
108
|
return scenePath.startsWith("/");
|
87
109
|
};
|
88
110
|
|
111
|
+
export const parseSceneDir = (scenePath: string) => {
|
112
|
+
const sceneList = scenePath.split("/");
|
113
|
+
sceneList.pop();
|
114
|
+
let sceneDir = sceneList.join("/");
|
115
|
+
// "/page1" 的 dir 为 "/"
|
116
|
+
if (sceneDir === "") {
|
117
|
+
sceneDir = "/";
|
118
|
+
}
|
119
|
+
return sceneDir;
|
120
|
+
};
|
121
|
+
|
89
122
|
export const ensureValidScenePath = (scenePath: string) => {
|
90
123
|
if (scenePath.endsWith("/")) {
|
91
124
|
return scenePath.slice(0, -1);
|
package/src/Utils/RoomHacker.ts
CHANGED
@@ -1,23 +1,15 @@
|
|
1
1
|
import { emitter } from "../index";
|
2
2
|
import { isPlayer } from "white-web-sdk";
|
3
|
-
import type { WindowManager } from
|
4
|
-
import type { Camera, Room
|
3
|
+
import type { WindowManager } from "../index";
|
4
|
+
import type { Camera, Room, Player, PlayerSeekingResult } from "white-web-sdk";
|
5
5
|
|
6
6
|
// 修改多窗口状态下一些失效的方法实现到 manager 的 mainview 上, 降低迁移成本
|
7
|
-
export const replaceRoomFunction = (room: Room, manager: WindowManager) => {
|
7
|
+
export const replaceRoomFunction = (room: Room | Player, manager: WindowManager) => {
|
8
8
|
if (isPlayer(room)) {
|
9
9
|
const player = room as unknown as Player;
|
10
|
-
|
11
|
-
// eslint-disable-next-line no-inner-declarations
|
12
|
-
async function newSeek(time: number): Promise<PlayerSeekingResult> {
|
13
|
-
const seekResult = await originSeek.call(player, time);
|
14
|
-
if (seekResult === "success") {
|
15
|
-
emitter.emit("seek", time);
|
16
|
-
}
|
17
|
-
return seekResult;
|
18
|
-
}
|
19
|
-
player.seekToProgressTime = newSeek;
|
10
|
+
delegateSeekToProgressTime(player);
|
20
11
|
} else {
|
12
|
+
room = room as unknown as Room;
|
21
13
|
const descriptor = Object.getOwnPropertyDescriptor(room, "disableCameraTransform");
|
22
14
|
if (descriptor) return;
|
23
15
|
Object.defineProperty(room, "disableCameraTransform", {
|
@@ -32,13 +24,13 @@ export const replaceRoomFunction = (room: Room, manager: WindowManager) => {
|
|
32
24
|
Object.defineProperty(room, "canUndoSteps", {
|
33
25
|
get() {
|
34
26
|
return manager.mainView.canUndoSteps;
|
35
|
-
}
|
27
|
+
},
|
36
28
|
});
|
37
29
|
|
38
30
|
Object.defineProperty(room, "canRedoSteps", {
|
39
31
|
get() {
|
40
32
|
return manager.mainView.canRedoSteps;
|
41
|
-
}
|
33
|
+
},
|
42
34
|
});
|
43
35
|
|
44
36
|
room.moveCamera = (camera: Camera) => manager.mainView.moveCamera(camera);
|
@@ -49,8 +41,31 @@ export const replaceRoomFunction = (room: Room, manager: WindowManager) => {
|
|
49
41
|
room.fillSceneSnapshot = (...args) => manager.mainView.fillSceneSnapshot(...args);
|
50
42
|
room.generateScreenshot = (...args) => manager.mainView.generateScreenshot(...args);
|
51
43
|
room.setMemberState = (...args) => manager.mainView.setMemberState(...args);
|
52
|
-
room.redo = () => manager.
|
53
|
-
room.undo = () => manager.
|
44
|
+
room.redo = () => manager.redo();
|
45
|
+
room.undo = () => manager.undo();
|
46
|
+
room.cleanCurrentScene = () => manager.cleanCurrentScene();
|
47
|
+
delegateRemoveScenes(room);
|
54
48
|
}
|
49
|
+
};
|
55
50
|
|
56
|
-
|
51
|
+
const delegateRemoveScenes = (room: Room) => {
|
52
|
+
const originRemoveScenes = room.removeScenes;
|
53
|
+
room.removeScenes = (scenePath: string) => {
|
54
|
+
const result = originRemoveScenes.call(room, scenePath);
|
55
|
+
emitter.emit("removeScenes", scenePath);
|
56
|
+
return result;
|
57
|
+
};
|
58
|
+
};
|
59
|
+
|
60
|
+
const delegateSeekToProgressTime = (player: Player) => {
|
61
|
+
const originSeek = player.seekToProgressTime;
|
62
|
+
// eslint-disable-next-line no-inner-declarations
|
63
|
+
async function newSeek(time: number): Promise<PlayerSeekingResult> {
|
64
|
+
const seekResult = await originSeek.call(player, time);
|
65
|
+
if (seekResult === "success") {
|
66
|
+
emitter.emit("seek", time);
|
67
|
+
}
|
68
|
+
return seekResult;
|
69
|
+
}
|
70
|
+
player.seekToProgressTime = newSeek;
|
71
|
+
};
|