@netless/window-manager 1.0.0-canary.9 → 1.0.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/LICENSE.txt +21 -0
- package/README.md +90 -64
- package/README.zh-cn.md +224 -0
- package/dist/index.d.ts +1133 -40
- package/dist/index.js +62 -0
- package/dist/index.js.map +1 -0
- package/dist/{index.es.js → index.mjs} +7954 -5445
- package/dist/index.mjs.map +1 -0
- package/dist/style.css +1 -1
- package/docs/advanced.md +55 -55
- package/docs/api.md +126 -113
- package/docs/app-context.md +248 -209
- package/docs/basic.md +25 -26
- package/docs/camera.md +21 -21
- package/docs/cn/advanced.md +137 -0
- package/docs/cn/api.md +311 -0
- package/docs/cn/app-context.md +369 -0
- package/docs/cn/basic.md +64 -0
- package/docs/cn/camera.md +53 -0
- package/docs/cn/concept.md +9 -0
- package/docs/cn/custom-max-bar.md +31 -0
- package/docs/cn/develop-app.md +94 -0
- package/docs/cn/export-pdf.md +48 -0
- package/docs/cn/migrate.md +60 -0
- package/docs/cn/replay.md +40 -0
- package/docs/concept.md +6 -5
- package/docs/custom-max-bar.md +31 -0
- package/docs/develop-app.md +22 -19
- package/docs/export-pdf.md +48 -0
- package/docs/migrate.md +25 -27
- package/docs/quickstart.md +50 -0
- package/docs/replay.md +20 -20
- package/package.json +32 -22
- package/src/App/AppContext.ts +105 -73
- package/src/App/AppPageStateImpl.ts +6 -25
- package/src/App/AppProxy.ts +41 -166
- package/src/App/MagixEvent/index.ts +38 -38
- package/src/App/Storage/StorageEvent.ts +13 -13
- package/src/App/Storage/index.ts +269 -245
- package/src/App/Storage/typings.ts +4 -2
- package/src/App/Storage/utils.ts +3 -3
- package/src/App/index.ts +0 -1
- package/src/AppListener.ts +8 -8
- package/src/AppManager.ts +88 -77
- package/src/AttributesDelegate.ts +42 -22
- package/src/BoxEmitter.ts +12 -6
- package/src/BoxManager.ts +128 -108
- package/src/ContainerResizeObserver.ts +75 -0
- package/src/Cursor/Cursor.svelte +16 -5
- package/src/Cursor/Cursor.svelte.d.ts +21 -0
- package/src/Cursor/Cursor.ts +77 -13
- package/src/Cursor/icons.ts +6 -0
- package/src/Cursor/icons2.ts +66 -0
- package/src/Cursor/index.ts +127 -26
- package/src/Helper.ts +94 -14
- package/src/InternalEmitter.ts +2 -7
- package/src/Page/index.ts +1 -1
- package/src/PageState.ts +6 -5
- package/src/ReconnectRefresher.ts +9 -4
- package/src/RedoUndo.ts +3 -3
- package/src/Register/index.ts +22 -17
- package/src/Register/loader.ts +26 -22
- package/src/Register/storage.ts +13 -13
- package/src/Utils/Common.ts +18 -14
- package/src/Utils/Reactive.ts +26 -25
- package/src/Utils/RoomHacker.ts +4 -4
- package/src/Utils/error.ts +0 -1
- package/src/View/IframeBridge.ts +680 -0
- package/src/View/MainView.ts +127 -53
- package/src/callback.ts +21 -1
- package/src/constants.ts +0 -2
- package/src/image/pencil-eraser-1.svg +3 -0
- package/src/image/pencil-eraser-2.svg +3 -0
- package/src/image/pencil-eraser-3.svg +3 -0
- package/src/index.ts +220 -83
- package/src/style.css +27 -10
- package/src/typings.ts +20 -10
- package/.prettierignore +0 -7
- package/.prettierrc.json +0 -9
- package/CHANGELOG.md +0 -196
- package/__mocks__/white-web-sdk.ts +0 -50
- package/dist/App/AppContext.d.ts +0 -76
- package/dist/App/AppPageStateImpl.d.ts +0 -21
- package/dist/App/AppProxy.d.ts +0 -86
- package/dist/App/AppViewSync.d.ts +0 -11
- package/dist/App/MagixEvent/index.d.ts +0 -29
- package/dist/App/Storage/StorageEvent.d.ts +0 -8
- package/dist/App/Storage/index.d.ts +0 -39
- package/dist/App/Storage/typings.d.ts +0 -22
- package/dist/App/Storage/utils.d.ts +0 -5
- package/dist/App/WhiteboardView.d.ts +0 -22
- package/dist/App/index.d.ts +0 -3
- package/dist/AppListener.d.ts +0 -21
- package/dist/AppManager.d.ts +0 -107
- package/dist/AttributesDelegate.d.ts +0 -80
- package/dist/BoxEmitter.d.ts +0 -34
- package/dist/BoxManager.d.ts +0 -99
- package/dist/BuiltinApps.d.ts +0 -5
- package/dist/Cursor/Cursor.d.ts +0 -39
- package/dist/Cursor/icons.d.ts +0 -3
- package/dist/Cursor/index.d.ts +0 -46
- package/dist/Helper.d.ts +0 -17
- package/dist/InternalEmitter.d.ts +0 -39
- package/dist/Page/PageController.d.ts +0 -21
- package/dist/Page/index.d.ts +0 -3
- package/dist/PageState.d.ts +0 -9
- package/dist/ReconnectRefresher.d.ts +0 -24
- package/dist/RedoUndo.d.ts +0 -18
- package/dist/Register/index.d.ts +0 -28
- package/dist/Register/loader.d.ts +0 -4
- package/dist/Register/storage.d.ts +0 -8
- package/dist/Utils/AppCreateQueue.d.ts +0 -15
- package/dist/Utils/Common.d.ts +0 -23
- package/dist/Utils/Reactive.d.ts +0 -6
- package/dist/Utils/RoomHacker.d.ts +0 -3
- package/dist/Utils/error.d.ts +0 -27
- package/dist/Utils/log.d.ts +0 -1
- package/dist/View/CameraSynchronizer.d.ts +0 -16
- package/dist/View/MainView.d.ts +0 -47
- package/dist/View/ViewManager.d.ts +0 -13
- package/dist/callback.d.ts +0 -24
- package/dist/constants.d.ts +0 -49
- package/dist/index.cjs.js +0 -46
- package/dist/index.umd.js +0 -46
- package/dist/typings.d.ts +0 -82
- package/jest.config.js +0 -27
- package/pnpm-lock.yaml +0 -6302
- package/src/App/AppViewSync.ts +0 -68
- package/src/App/WhiteboardView.ts +0 -83
- package/src/View/CameraSynchronizer.ts +0 -56
- package/vite.config.js +0 -51
- /package/docs/{qickstart.md → cn/quickstart.md} +0 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
import type { MemberState } from "white-web-sdk";
|
2
|
+
import { ApplianceNames } from "white-web-sdk";
|
3
|
+
|
4
|
+
type Color = string;
|
5
|
+
|
6
|
+
const staticCircle = `data:image/svg+xml,%3Csvg width='24' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Ccircle cx='12' cy='12' r='2.5' stroke='%23000' stroke-linejoin='square'/%3E%3Ccircle cx='12' cy='12' r='3.5' stroke='%23FFF'/%3E%3C/g%3E%3C/svg%3E`;
|
7
|
+
|
8
|
+
function circleUrl(color: Color): string {
|
9
|
+
return `data:image/svg+xml,%3Csvg width='24' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Ccircle cx='12' cy='12' r='2.5' stroke='%23${color}' stroke-linejoin='square'/%3E%3Ccircle cx='12' cy='12' r='3.5' stroke='%23${color}'/%3E%3C/g%3E%3C/svg%3E`;
|
10
|
+
}
|
11
|
+
|
12
|
+
function crossUrl(color: Color): string {
|
13
|
+
return `data:image/svg+xml,%3Csvg width='24' height='24' xmlns='http://www.w3.org/2000/svg' fill='none'%3E%3Cpath d='M5 12H19' stroke='%23${color}' stroke-linejoin='round'/%3E%3Cpath d='M12 5V19' stroke='%23${color}' stroke-linejoin='round'/%3E%3C/svg%3E`;
|
14
|
+
}
|
15
|
+
|
16
|
+
function cssCursor(url: string): string {
|
17
|
+
return `url("${url}") 12 12, auto`;
|
18
|
+
}
|
19
|
+
|
20
|
+
function makeStyleContent(config: { [cursor: string]: string }): string {
|
21
|
+
let result = "";
|
22
|
+
for (const cursor in config) {
|
23
|
+
result += `.netless-whiteboard.${cursor} {cursor: ${config[cursor]}}\n`;
|
24
|
+
}
|
25
|
+
return result;
|
26
|
+
}
|
27
|
+
|
28
|
+
const $style = document.createElement("style");
|
29
|
+
|
30
|
+
export function enableLocal(memberState: MemberState): () => void {
|
31
|
+
const [r, g, b] = memberState.strokeColor;
|
32
|
+
const hex = ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
33
|
+
$style.textContent = makeStyleContent({
|
34
|
+
"cursor-pencil": cssCursor(circleUrl(hex)),
|
35
|
+
"cursor-eraser": cssCursor(staticCircle),
|
36
|
+
"cursor-rectangle": cssCursor(crossUrl(hex)),
|
37
|
+
"cursor-ellipse": cssCursor(crossUrl(hex)),
|
38
|
+
"cursor-straight": cssCursor(crossUrl(hex)),
|
39
|
+
"cursor-arrow": cssCursor(crossUrl(hex)),
|
40
|
+
"cursor-shape": cssCursor(crossUrl(hex)),
|
41
|
+
});
|
42
|
+
document.head.appendChild($style);
|
43
|
+
|
44
|
+
return () => {
|
45
|
+
if ($style.parentNode == null) return;
|
46
|
+
document.head.removeChild($style);
|
47
|
+
};
|
48
|
+
}
|
49
|
+
|
50
|
+
const shapeAppliances: Set<ApplianceNames> = new Set([
|
51
|
+
ApplianceNames.rectangle,
|
52
|
+
ApplianceNames.ellipse,
|
53
|
+
ApplianceNames.straight,
|
54
|
+
ApplianceNames.arrow,
|
55
|
+
ApplianceNames.shape,
|
56
|
+
]);
|
57
|
+
|
58
|
+
export function remoteIcon(applianceName: ApplianceNames, hex: string): string | undefined {
|
59
|
+
if (applianceName === ApplianceNames.pencil) {
|
60
|
+
return circleUrl(hex);
|
61
|
+
} else if (applianceName === ApplianceNames.eraser) {
|
62
|
+
return staticCircle;
|
63
|
+
} else if (shapeAppliances.has(applianceName)) {
|
64
|
+
return crossUrl(hex);
|
65
|
+
}
|
66
|
+
}
|
package/src/Cursor/index.ts
CHANGED
@@ -1,15 +1,16 @@
|
|
1
|
-
import { ApplianceNames } from "white-web-sdk";
|
1
|
+
import { ApplianceNames, isRoom } from "white-web-sdk";
|
2
2
|
import { Cursor } from "./Cursor";
|
3
3
|
import { CursorState, Events } from "../constants";
|
4
|
-
import {
|
4
|
+
import { internalEmitter } from "../InternalEmitter";
|
5
5
|
import { SideEffectManager } from "side-effect-manager";
|
6
|
-
import { throttle } from "lodash";
|
7
6
|
import { WindowManager } from "../index";
|
8
|
-
import type { CursorMovePayload ,
|
7
|
+
import type { CursorMovePayload, ApplianceIcons, CursorOptions } from "../index";
|
9
8
|
import type { PositionType } from "../AttributesDelegate";
|
10
|
-
import type { Point, RoomMember, View } from "white-web-sdk";
|
9
|
+
import type { Point, Room, RoomMember, RoomState, View } from "white-web-sdk";
|
11
10
|
import type { AppManager } from "../AppManager";
|
12
11
|
import { ApplianceMap } from "./icons";
|
12
|
+
import { findMemberByUid } from "../Helper";
|
13
|
+
import { enableLocal } from "./icons2";
|
13
14
|
|
14
15
|
export type EventType = {
|
15
16
|
type: PositionType;
|
@@ -22,31 +23,73 @@ export type MoveCursorParams = {
|
|
22
23
|
y: number;
|
23
24
|
};
|
24
25
|
|
26
|
+
const LocalCursorSideEffectId = "local-cursor";
|
27
|
+
|
25
28
|
export class CursorManager {
|
26
29
|
public containerRect?: DOMRect;
|
27
30
|
public wrapperRect?: DOMRect;
|
28
31
|
public cursorInstances: Map<string, Cursor> = new Map();
|
29
32
|
public roomMembers?: readonly RoomMember[];
|
33
|
+
public userApplianceIcons: ApplianceIcons = {};
|
34
|
+
|
30
35
|
private mainViewElement?: HTMLDivElement;
|
31
36
|
private sideEffectManager = new SideEffectManager();
|
32
37
|
private store = this.manager.store;
|
33
|
-
|
38
|
+
private leaveFlag = true;
|
39
|
+
private _style: CursorOptions["style"] & string = "default";
|
34
40
|
|
35
|
-
constructor(
|
41
|
+
constructor(
|
42
|
+
private manager: AppManager,
|
43
|
+
private enableCursor: boolean,
|
44
|
+
cursorOptions?: CursorOptions,
|
45
|
+
applianceIcons?: ApplianceIcons
|
46
|
+
) {
|
36
47
|
this.roomMembers = this.manager.room?.state.roomMembers;
|
37
|
-
const
|
38
|
-
if (
|
39
|
-
this.setupWrapper(
|
48
|
+
const wrapper = WindowManager.wrapper;
|
49
|
+
if (wrapper) {
|
50
|
+
this.setupWrapper(wrapper);
|
40
51
|
}
|
41
52
|
this.sideEffectManager.add(() => {
|
42
|
-
return
|
53
|
+
return internalEmitter.on("cursorMove", this.onCursorMove);
|
43
54
|
});
|
44
|
-
|
45
55
|
this.sideEffectManager.add(() => {
|
46
|
-
return
|
56
|
+
return internalEmitter.on("playgroundSizeChange", () => this.updateContainerRect());
|
47
57
|
});
|
58
|
+
const room = this.manager.room;
|
59
|
+
if (room) {
|
60
|
+
this.sideEffectManager.add(() => {
|
61
|
+
const update = (state: RoomState) => {
|
62
|
+
if (this.style === "custom" && state.memberState) this.enableCustomCursor();
|
63
|
+
};
|
64
|
+
room.callbacks.on("onRoomStateChanged", update);
|
65
|
+
return () => room.callbacks.off("onRoomStateChanged", update);
|
66
|
+
});
|
67
|
+
}
|
48
68
|
if (applianceIcons) {
|
49
|
-
this.
|
69
|
+
this.userApplianceIcons = applianceIcons;
|
70
|
+
}
|
71
|
+
this.style = cursorOptions?.style || "default";
|
72
|
+
}
|
73
|
+
|
74
|
+
public get applianceIcons(): ApplianceIcons {
|
75
|
+
return { ...ApplianceMap, ...this.userApplianceIcons };
|
76
|
+
}
|
77
|
+
|
78
|
+
public get style() {
|
79
|
+
return this._style;
|
80
|
+
}
|
81
|
+
|
82
|
+
public set style(value) {
|
83
|
+
if (this._style !== value) {
|
84
|
+
this._style = value;
|
85
|
+
this.cursorInstances.forEach(cursor => {
|
86
|
+
cursor.setStyle(value);
|
87
|
+
});
|
88
|
+
if (value === "custom") {
|
89
|
+
this.enableCustomCursor();
|
90
|
+
} else {
|
91
|
+
this.sideEffectManager.flush(LocalCursorSideEffectId);
|
92
|
+
}
|
50
93
|
}
|
51
94
|
}
|
52
95
|
|
@@ -65,12 +108,19 @@ export class CursorManager {
|
|
65
108
|
private initCursorInstance = (uid: string) => {
|
66
109
|
let cursorInstance = this.cursorInstances.get(uid);
|
67
110
|
if (!cursorInstance) {
|
68
|
-
cursorInstance = new Cursor(this.manager, uid, this, WindowManager.
|
111
|
+
cursorInstance = new Cursor(this.manager, uid, this, WindowManager.wrapper);
|
69
112
|
this.cursorInstances.set(uid, cursorInstance);
|
70
113
|
}
|
71
114
|
return cursorInstance;
|
72
115
|
};
|
73
116
|
|
117
|
+
private enableCustomCursor() {
|
118
|
+
this.sideEffectManager.add(
|
119
|
+
() => enableLocal(this.manager.getMemberState()),
|
120
|
+
LocalCursorSideEffectId
|
121
|
+
);
|
122
|
+
}
|
123
|
+
|
74
124
|
private canMoveCursor(member: RoomMember | undefined) {
|
75
125
|
const isLaserPointer =
|
76
126
|
member?.memberState.currentApplianceName === ApplianceNames.laserPointer;
|
@@ -105,15 +155,70 @@ export class CursorManager {
|
|
105
155
|
return this.manager.focusApp?.view;
|
106
156
|
}
|
107
157
|
|
108
|
-
private
|
109
|
-
|
110
|
-
|
158
|
+
private mouseMoveListener_ = (event: PointerEvent, isTouch: boolean) => {
|
159
|
+
const type = this.getType(event);
|
160
|
+
this.updateCursor(type, event.clientX, event.clientY);
|
161
|
+
isTouch && this.showPencilEraserIfNeeded(type, event.clientX, event.clientY);
|
162
|
+
};
|
163
|
+
|
164
|
+
private mouseMoveTimer = 0;
|
165
|
+
private mouseMoveListener = (event: PointerEvent) => {
|
166
|
+
const isTouch = event.pointerType === "touch";
|
167
|
+
if (isTouch && !event.isPrimary) return;
|
168
|
+
const now = Date.now();
|
169
|
+
if (now - this.mouseMoveTimer > 48) {
|
170
|
+
this.mouseMoveTimer = now;
|
171
|
+
if (
|
172
|
+
WindowManager.supportAppliancePlugin &&
|
173
|
+
isRoom(WindowManager.displayer) &&
|
174
|
+
(WindowManager.displayer as Room).disableDeviceInputs
|
175
|
+
) {
|
176
|
+
if (this.leaveFlag) {
|
177
|
+
this.manager.dispatchInternalEvent(Events.CursorMove, {
|
178
|
+
uid: this.manager.uid,
|
179
|
+
state: CursorState.Leave,
|
180
|
+
} as CursorMovePayload);
|
181
|
+
this.leaveFlag = false;
|
182
|
+
}
|
183
|
+
return;
|
184
|
+
}
|
185
|
+
this.mouseMoveListener_(event, isTouch);
|
186
|
+
this.leaveFlag = true;
|
187
|
+
}
|
188
|
+
};
|
189
|
+
|
190
|
+
private mouseLeaveListener = () => {
|
191
|
+
this.hideCursor(this.manager.uid);
|
192
|
+
};
|
193
|
+
|
194
|
+
private showPencilEraserIfNeeded(event: EventType, clientX: number, clientY: number) {
|
195
|
+
const self = findMemberByUid(this.manager.room, this.manager.uid);
|
196
|
+
const isPencilEraser =
|
197
|
+
self?.memberState.currentApplianceName === ApplianceNames.pencilEraser;
|
198
|
+
if (
|
199
|
+
this.wrapperRect &&
|
200
|
+
this.manager.canOperate &&
|
201
|
+
this.canMoveCursor(self) &&
|
202
|
+
isPencilEraser
|
203
|
+
) {
|
204
|
+
const view = event.type === "main" ? this.manager.mainView : this.focusView;
|
205
|
+
const point = this.getPoint(view, clientX, clientY);
|
206
|
+
if (point) {
|
207
|
+
this.onCursorMove({
|
208
|
+
uid: this.manager.uid,
|
209
|
+
position: {
|
210
|
+
x: point.x,
|
211
|
+
y: point.y,
|
212
|
+
type: event.type,
|
213
|
+
},
|
214
|
+
});
|
215
|
+
}
|
111
216
|
}
|
112
|
-
|
113
|
-
}, 16);
|
217
|
+
}
|
114
218
|
|
115
219
|
private updateCursor(event: EventType, clientX: number, clientY: number) {
|
116
|
-
|
220
|
+
const self = findMemberByUid(this.manager.room, this.manager.uid);
|
221
|
+
if (this.wrapperRect && this.manager.canOperate && this.canMoveCursor(self)) {
|
117
222
|
const view = event.type === "main" ? this.manager.mainView : this.focusView;
|
118
223
|
const point = this.getPoint(view, clientX, clientY);
|
119
224
|
if (point) {
|
@@ -163,13 +268,9 @@ export class CursorManager {
|
|
163
268
|
}
|
164
269
|
};
|
165
270
|
|
166
|
-
private mouseLeaveListener = () => {
|
167
|
-
this.hideCursor(this.manager.uid);
|
168
|
-
};
|
169
|
-
|
170
271
|
public updateContainerRect() {
|
171
272
|
this.containerRect = WindowManager.container?.getBoundingClientRect();
|
172
|
-
this.wrapperRect = WindowManager.
|
273
|
+
this.wrapperRect = WindowManager.wrapper?.getBoundingClientRect();
|
173
274
|
}
|
174
275
|
|
175
276
|
public deleteCursor(uid: string) {
|
package/src/Helper.ts
CHANGED
@@ -1,24 +1,39 @@
|
|
1
|
-
import
|
2
|
-
import {
|
1
|
+
import pRetry from "p-retry";
|
2
|
+
import type { Room, RoomMember } from "white-web-sdk";
|
3
3
|
import { WhiteVersion } from "white-web-sdk";
|
4
|
+
import { REQUIRE_VERSION } from "./constants";
|
5
|
+
import { WindowManager } from "./index";
|
6
|
+
import { getVersionNumber } from "./Utils/Common";
|
4
7
|
import { WhiteWebSDKInvalidError } from "./Utils/error";
|
5
|
-
import
|
8
|
+
import { log } from "./Utils/log";
|
6
9
|
|
7
10
|
export const setupWrapper = (
|
8
11
|
root: HTMLElement
|
9
12
|
): {
|
10
13
|
playground: HTMLDivElement;
|
14
|
+
wrapper: HTMLDivElement;
|
15
|
+
sizer: HTMLDivElement;
|
11
16
|
mainViewElement: HTMLDivElement;
|
12
17
|
} => {
|
13
18
|
const playground = document.createElement("div");
|
14
19
|
playground.className = "netless-window-manager-playground";
|
15
20
|
|
21
|
+
const sizer = document.createElement("div");
|
22
|
+
sizer.className = "netless-window-manager-sizer";
|
23
|
+
|
24
|
+
const wrapper = document.createElement("div");
|
25
|
+
wrapper.className = "netless-window-manager-wrapper";
|
26
|
+
|
16
27
|
const mainViewElement = document.createElement("div");
|
17
28
|
mainViewElement.className = "netless-window-manager-main-view";
|
18
|
-
|
29
|
+
|
30
|
+
playground.appendChild(sizer);
|
31
|
+
sizer.appendChild(wrapper);
|
32
|
+
wrapper.appendChild(mainViewElement);
|
19
33
|
root.appendChild(playground);
|
34
|
+
WindowManager.wrapper = wrapper;
|
20
35
|
|
21
|
-
return { playground, mainViewElement };
|
36
|
+
return { playground, wrapper, sizer, mainViewElement };
|
22
37
|
};
|
23
38
|
|
24
39
|
export const checkVersion = () => {
|
@@ -29,15 +44,80 @@ export const checkVersion = () => {
|
|
29
44
|
};
|
30
45
|
|
31
46
|
export const findMemberByUid = (room: Room | undefined, uid: string) => {
|
32
|
-
const roomMembers = room?.state.roomMembers;
|
33
|
-
|
47
|
+
const roomMembers = room?.state.roomMembers || [];
|
48
|
+
let maxMemberId = -1; // 第一个进入房间的用户 memberId 是 0
|
49
|
+
let result: RoomMember | undefined = undefined;
|
50
|
+
for (const member of roomMembers) {
|
51
|
+
if (member.payload?.uid === uid && maxMemberId < member.memberId) {
|
52
|
+
maxMemberId = member.memberId;
|
53
|
+
result = member;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
return result;
|
34
57
|
};
|
35
58
|
|
36
|
-
export
|
59
|
+
export const createInvisiblePlugin = async (room: Room): Promise<WindowManager> => {
|
60
|
+
let manager = room.getInvisiblePlugin(WindowManager.kind) as WindowManager;
|
61
|
+
if (manager) return manager;
|
37
62
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
})
|
43
|
-
|
63
|
+
let resolve!: (manager: WindowManager) => void;
|
64
|
+
const promise = new Promise<WindowManager>(r => {
|
65
|
+
// @ts-expect-error Set private property.
|
66
|
+
WindowManager._resolve = resolve = r;
|
67
|
+
});
|
68
|
+
|
69
|
+
let wasReadonly = false;
|
70
|
+
const canOperate = isRoomTokenWritable(room);
|
71
|
+
if (!room.isWritable && canOperate) {
|
72
|
+
wasReadonly = true;
|
73
|
+
await pRetry(
|
74
|
+
async count => {
|
75
|
+
log(`switching to writable (x${count})`);
|
76
|
+
await room.setWritable(true);
|
77
|
+
},
|
78
|
+
{ retries: 10, maxTimeout: 5000 }
|
79
|
+
);
|
80
|
+
}
|
81
|
+
if (room.isWritable) {
|
82
|
+
log("creating InvisiblePlugin...");
|
83
|
+
room.createInvisiblePlugin(WindowManager, {}).catch(console.warn);
|
84
|
+
} else {
|
85
|
+
if (canOperate) console.warn("[WindowManager]: failed to switch to writable");
|
86
|
+
console.warn("[WindowManager]: waiting for others to create the plugin...");
|
87
|
+
}
|
88
|
+
|
89
|
+
const timeout = setTimeout(() => {
|
90
|
+
console.warn("[WindowManager]: no one called createInvisiblePlugin() after 20 seconds");
|
91
|
+
}, 20_000);
|
92
|
+
|
93
|
+
const abort = setTimeout(() => {
|
94
|
+
throw new Error("[WindowManager]: no one called createInvisiblePlugin() after 60 seconds");
|
95
|
+
}, 60_000);
|
96
|
+
|
97
|
+
const interval = setInterval(() => {
|
98
|
+
manager = room.getInvisiblePlugin(WindowManager.kind) as WindowManager;
|
99
|
+
if (manager) {
|
100
|
+
clearTimeout(abort);
|
101
|
+
clearTimeout(timeout);
|
102
|
+
clearInterval(interval);
|
103
|
+
resolve(manager);
|
104
|
+
if (wasReadonly && room.isWritable) {
|
105
|
+
setTimeout(() => room.setWritable(false).catch(console.warn), 500);
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}, 200);
|
109
|
+
|
110
|
+
return promise;
|
111
|
+
};
|
112
|
+
|
113
|
+
const isRoomTokenWritable = (room: Room) => {
|
114
|
+
try {
|
115
|
+
const str = atob(room.roomToken.slice("NETLESSROOM_".length));
|
116
|
+
const index = str.indexOf("&role=");
|
117
|
+
const role = +str[index + "&role=".length];
|
118
|
+
return role < 2;
|
119
|
+
} catch (error) {
|
120
|
+
console.error(error);
|
121
|
+
return false;
|
122
|
+
}
|
123
|
+
};
|
package/src/InternalEmitter.ts
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
import Emittery from "emittery";
|
2
|
-
import type { TeleBoxRect } from "@netless/telebox-insider";
|
3
2
|
import type { AppInitState, CursorMovePayload } from "./index";
|
4
|
-
import type { Member } from "./Helper";
|
5
|
-
import type { MemberState } from "white-web-sdk";
|
6
3
|
|
7
4
|
export type RemoveSceneParams = {
|
8
5
|
scenePath: string;
|
@@ -18,7 +15,7 @@ export type EmitterEvent = {
|
|
18
15
|
mainViewMounted: undefined;
|
19
16
|
observerIdChange: number;
|
20
17
|
boxStateChange: string;
|
21
|
-
playgroundSizeChange:
|
18
|
+
playgroundSizeChange: DOMRect;
|
22
19
|
startReconnect: undefined;
|
23
20
|
onReconnected: undefined;
|
24
21
|
removeScenes: RemoveSceneParams;
|
@@ -31,9 +28,7 @@ export type EmitterEvent = {
|
|
31
28
|
changePageState: undefined;
|
32
29
|
writableChange: boolean;
|
33
30
|
containerSizeRatioUpdate: number;
|
34
|
-
roomMembersChange: Member[];
|
35
|
-
memberStateChange: MemberState;
|
36
31
|
};
|
37
32
|
|
38
33
|
export type EmitterType = Emittery<EmitterEvent>;
|
39
|
-
export const
|
34
|
+
export const internalEmitter: EmitterType = new Emittery();
|
package/src/Page/index.ts
CHANGED
package/src/PageState.ts
CHANGED
@@ -1,21 +1,22 @@
|
|
1
|
-
import { callbacks } from "./callback";
|
2
|
-
import { emitter } from "./InternalEmitter";
|
3
1
|
import type { AppManager } from "./AppManager";
|
4
2
|
import type { PageState } from "./Page";
|
5
3
|
|
4
|
+
import { internalEmitter } from "./InternalEmitter";
|
5
|
+
import { callbacks } from "./callback";
|
6
|
+
|
6
7
|
export class PageStateImpl {
|
7
8
|
constructor(private manager: AppManager) {
|
8
|
-
|
9
|
+
internalEmitter.on("changePageState", () => {
|
9
10
|
callbacks.emit("pageStateChange", this.toObject());
|
10
11
|
});
|
11
12
|
}
|
12
13
|
|
13
14
|
public get index(): number {
|
14
|
-
return this.manager
|
15
|
+
return this.manager.store.getMainViewSceneIndex() || 0;
|
15
16
|
}
|
16
17
|
|
17
18
|
public get length(): number {
|
18
|
-
return this.manager
|
19
|
+
return this.manager.mainViewScenesLength || 0;
|
19
20
|
}
|
20
21
|
|
21
22
|
public toObject(): PageState {
|
@@ -4,6 +4,7 @@ import { RoomPhase } from "white-web-sdk";
|
|
4
4
|
import type { Room } from "white-web-sdk";
|
5
5
|
import type { EmitterType } from "./InternalEmitter";
|
6
6
|
import { EnsureReconnectEvent } from "./constants";
|
7
|
+
import { wait } from "./Utils/Common";
|
7
8
|
|
8
9
|
export type ReconnectRefresherContext = {
|
9
10
|
emitter: EmitterType;
|
@@ -41,19 +42,24 @@ export class ReconnectRefresher {
|
|
41
42
|
this.ctx = ctx;
|
42
43
|
}
|
43
44
|
|
44
|
-
private onPhaseChanged = (phase: RoomPhase) => {
|
45
|
+
private onPhaseChanged = async (phase: RoomPhase) => {
|
45
46
|
if (phase === RoomPhase.Reconnecting) {
|
46
47
|
this.ctx.emitter.emit("startReconnect");
|
47
48
|
}
|
48
49
|
if (phase === RoomPhase.Connected && this.phase === RoomPhase.Reconnecting) {
|
49
|
-
this.room?.
|
50
|
+
if (this.room?.isWritable) {
|
51
|
+
this.room?.dispatchMagixEvent(EnsureReconnectEvent, {});
|
52
|
+
} else {
|
53
|
+
await wait(500);
|
54
|
+
this.onReconnected();
|
55
|
+
}
|
50
56
|
}
|
51
57
|
this.phase = phase;
|
52
58
|
};
|
53
59
|
|
54
60
|
private onReconnected = debounce(() => {
|
55
61
|
this._onReconnected();
|
56
|
-
},
|
62
|
+
}, 1000);
|
57
63
|
|
58
64
|
private _onReconnected = () => {
|
59
65
|
log("onReconnected refresh reactors");
|
@@ -88,7 +94,6 @@ export class ReconnectRefresher {
|
|
88
94
|
this.reactors.set(id, func);
|
89
95
|
this.disposers.set(id, func());
|
90
96
|
}
|
91
|
-
return () => this.remove(id);
|
92
97
|
}
|
93
98
|
|
94
99
|
public remove(id: string) {
|
package/src/RedoUndo.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { callbacks } from "./callback";
|
2
|
-
import {
|
2
|
+
import { internalEmitter } from "./InternalEmitter";
|
3
3
|
import type { View } from "white-web-sdk";
|
4
4
|
import type { AppProxy } from "./App";
|
5
5
|
|
@@ -11,13 +11,13 @@ export type RedoUndoContext = {
|
|
11
11
|
|
12
12
|
export class RedoUndo {
|
13
13
|
constructor(private context: RedoUndoContext) {
|
14
|
-
|
14
|
+
internalEmitter.on("focusedChange", changed => {
|
15
15
|
this.disposePrevFocusViewRedoUndoListeners(changed.prev);
|
16
16
|
setTimeout(() => {
|
17
17
|
this.addRedoUndoListeners(changed.focused);
|
18
18
|
}, 0);
|
19
19
|
});
|
20
|
-
|
20
|
+
internalEmitter.on("rootDirRemoved", () => {
|
21
21
|
this.disposePrevFocusViewRedoUndoListeners(context.focus());
|
22
22
|
this.addRedoUndoListeners(context.focus());
|
23
23
|
});
|
package/src/Register/index.ts
CHANGED
@@ -8,7 +8,7 @@ export type LoadAppEvent = {
|
|
8
8
|
reason?: string;
|
9
9
|
};
|
10
10
|
|
11
|
-
export type SyncRegisterAppPayload =
|
11
|
+
export type SyncRegisterAppPayload = { kind: string; src: string; name: string | undefined };
|
12
12
|
export type SyncRegisterApp = (payload: SyncRegisterAppPayload) => void;
|
13
13
|
|
14
14
|
class AppRegister {
|
@@ -25,38 +25,43 @@ class AppRegister {
|
|
25
25
|
|
26
26
|
public onSyncRegisterAppChange = (payload: SyncRegisterAppPayload) => {
|
27
27
|
this.register({ kind: payload.kind, src: payload.src });
|
28
|
-
}
|
28
|
+
};
|
29
29
|
|
30
30
|
public async register(params: RegisterParams): Promise<void> {
|
31
31
|
this.appClassesCache.delete(params.kind);
|
32
32
|
this.registered.set(params.kind, params);
|
33
33
|
|
34
|
-
const
|
34
|
+
const paramSrc = params.src;
|
35
35
|
let downloadApp: () => Promise<NetlessApp>;
|
36
36
|
|
37
|
-
if (typeof
|
37
|
+
if (typeof paramSrc === "string") {
|
38
38
|
downloadApp = async () => {
|
39
|
-
|
39
|
+
const result = (await loadApp(paramSrc, params.kind, params.name)) as any;
|
40
|
+
if (result.__esModule) {
|
41
|
+
return result.default;
|
42
|
+
}
|
43
|
+
return result;
|
44
|
+
};
|
45
|
+
if (this.syncRegisterApp) {
|
46
|
+
this.syncRegisterApp({ kind: params.kind, src: paramSrc, name: params.name });
|
47
|
+
}
|
48
|
+
}
|
49
|
+
if (typeof paramSrc === "function") {
|
50
|
+
downloadApp = async () => {
|
51
|
+
let appClass = (await paramSrc()) as any;
|
40
52
|
if (appClass) {
|
41
|
-
if (appClass.__esModule) {
|
53
|
+
if (appClass.__esModule || appClass.default) {
|
42
54
|
appClass = appClass.default;
|
43
55
|
}
|
44
56
|
return appClass;
|
45
57
|
} else {
|
46
|
-
throw new Error(
|
47
|
-
`[WindowManager]: load remote script failed, ${srcOrAppOrFunction}`
|
48
|
-
);
|
58
|
+
throw new Error(`[WindowManager]: load remote script failed, ${paramSrc}`);
|
49
59
|
}
|
50
60
|
};
|
51
|
-
if (this.syncRegisterApp) {
|
52
|
-
this.syncRegisterApp({ kind: params.kind, src: srcOrAppOrFunction, name: params.name });
|
53
|
-
}
|
54
|
-
} else if (typeof srcOrAppOrFunction === "function") {
|
55
|
-
downloadApp = srcOrAppOrFunction;
|
56
|
-
} else {
|
57
|
-
downloadApp = async () => srcOrAppOrFunction;
|
58
61
|
}
|
59
|
-
|
62
|
+
if (typeof paramSrc === "object") {
|
63
|
+
downloadApp = async () => paramSrc;
|
64
|
+
}
|
60
65
|
this.appClasses.set(params.kind, async () => {
|
61
66
|
let app = this.appClassesCache.get(params.kind);
|
62
67
|
if (!app) {
|