@netless/window-manager 0.4.0-canary.1 → 0.4.0-canary.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/App/Storage/StorageEvent.d.ts +8 -0
- package/dist/App/Storage/index.d.ts +26 -0
- package/dist/App/Storage/typings.d.ts +21 -0
- package/dist/App/Storage/utils.d.ts +5 -0
- package/dist/AppContext.d.ts +3 -1
- package/dist/AppListener.d.ts +0 -1
- package/dist/AppManager.d.ts +7 -7
- package/dist/AppProxy.d.ts +2 -3
- package/dist/Base/Context.d.ts +0 -1
- package/dist/BoxManager.d.ts +2 -1
- package/dist/BuiltinApps.d.ts +6 -0
- package/dist/ContainerResizeObserver.d.ts +10 -0
- package/dist/Cursor/Cursor.d.ts +2 -3
- package/dist/Cursor/index.d.ts +7 -4
- package/dist/Helper.d.ts +6 -0
- package/dist/ReconnectRefresher.d.ts +5 -2
- package/dist/Utils/Common.d.ts +3 -1
- package/dist/Utils/Reactive.d.ts +1 -1
- package/dist/{MainView.d.ts → View/MainView.d.ts} +2 -4
- package/dist/View/ViewManager.d.ts +13 -0
- package/dist/constants.d.ts +1 -6
- package/dist/index.d.ts +13 -13
- package/dist/index.es.js +1 -1
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/typings.d.ts +1 -0
- package/package.json +5 -4
- package/src/App/Storage/StorageEvent.ts +21 -0
- package/src/App/Storage/index.ts +243 -0
- package/src/App/Storage/typings.ts +21 -0
- package/src/App/Storage/utils.ts +17 -0
- package/src/AppContext.ts +10 -2
- package/src/AppListener.ts +1 -8
- package/src/AppManager.ts +54 -35
- package/src/AppProxy.ts +14 -36
- package/src/Base/Context.ts +0 -4
- package/src/BoxManager.ts +9 -7
- package/src/BuiltinApps.ts +24 -0
- package/src/ContainerResizeObserver.ts +62 -0
- package/src/Cursor/Cursor.ts +23 -34
- package/src/Cursor/index.ts +70 -41
- package/src/Helper.ts +30 -0
- package/src/ReconnectRefresher.ts +13 -5
- package/src/Utils/Common.ts +35 -13
- package/src/Utils/Reactive.ts +9 -3
- package/src/Utils/RoomHacker.ts +15 -0
- package/src/{MainView.ts → View/MainView.ts} +9 -25
- package/src/View/ViewManager.ts +53 -0
- package/src/constants.ts +1 -3
- package/src/index.ts +46 -86
- package/src/shim.d.ts +4 -0
- package/src/style.css +6 -0
- package/src/typings.ts +1 -0
- package/vite.config.js +4 -1
- package/dist/Utils/CameraStore.d.ts +0 -15
- package/dist/ViewManager.d.ts +0 -29
- package/dist/sdk.d.ts +0 -14
- package/src/Utils/CameraStore.ts +0 -72
- package/src/sdk.ts +0 -39
- package/src/viewManager.ts +0 -177
package/src/AppProxy.ts
CHANGED
@@ -2,16 +2,15 @@ import Emittery from "emittery";
|
|
2
2
|
import { AppAttributes, AppEvents, Events } from "./constants";
|
3
3
|
import { AppContext } from "./AppContext";
|
4
4
|
import { appRegister } from "./Register";
|
5
|
-
import { autorun
|
6
|
-
import {
|
5
|
+
import { autorun } from "white-web-sdk";
|
6
|
+
import { emitter } from "./index";
|
7
7
|
import { Fields } from "./AttributesDelegate";
|
8
8
|
import { get } from "lodash";
|
9
9
|
import { log } from "./Utils/log";
|
10
10
|
import {
|
11
|
-
notifyMainViewModeChange,
|
12
11
|
setScenePath,
|
13
12
|
setViewFocusScenePath,
|
14
|
-
|
13
|
+
getScenePath
|
15
14
|
} from "./Utils/Common";
|
16
15
|
import type {
|
17
16
|
AppEmitterEvent,
|
@@ -37,7 +36,6 @@ export class AppProxy extends Base {
|
|
37
36
|
private boxManager = this.manager.boxManager;
|
38
37
|
private appProxies = this.manager.appProxies;
|
39
38
|
private viewManager = this.manager.viewManager;
|
40
|
-
private cameraStore = this.manager.cameraStore;
|
41
39
|
private kind: string;
|
42
40
|
public isAddApp: boolean;
|
43
41
|
private status: "normal" | "destroyed" = "normal";
|
@@ -98,17 +96,25 @@ export class AppProxy extends Base {
|
|
98
96
|
|
99
97
|
public getFullScenePath(): string | undefined {
|
100
98
|
if (this.scenePath) {
|
101
|
-
return get(this.appAttributes, [Fields.FullPath], this.
|
99
|
+
return get(this.appAttributes, [Fields.FullPath], this.getFullScenePathFromScenes());
|
102
100
|
}
|
103
101
|
}
|
104
102
|
|
103
|
+
private getFullScenePathFromScenes() {
|
104
|
+
const sceneIndex = get(this.appAttributes, ["state", "SceneIndex"], 0);
|
105
|
+
const fullPath = getScenePath(this.manager.room, this.scenePath, sceneIndex);
|
106
|
+
if (fullPath) {
|
107
|
+
this.setFullPath(fullPath);
|
108
|
+
}
|
109
|
+
return fullPath;
|
110
|
+
}
|
111
|
+
|
105
112
|
public setFullPath(path: string) {
|
106
113
|
this.manager.safeUpdateAttributes(["apps", this.id, Fields.FullPath], path);
|
107
114
|
}
|
108
115
|
|
109
116
|
public async baseInsertApp(
|
110
117
|
skipUpdate = false,
|
111
|
-
focus?: boolean
|
112
118
|
): Promise<{ appId: string; app: NetlessApp }> {
|
113
119
|
const params = this.params;
|
114
120
|
if (!params.kind) {
|
@@ -122,9 +128,6 @@ export class AppProxy extends Base {
|
|
122
128
|
throw new Error(`[WindowManager]: app load failed ${params.kind} ${params.src}`);
|
123
129
|
}
|
124
130
|
this.context.updateManagerRect();
|
125
|
-
if (focus) {
|
126
|
-
this.focusApp();
|
127
|
-
}
|
128
131
|
return {
|
129
132
|
appId: this.id,
|
130
133
|
app: appImpl,
|
@@ -133,7 +136,6 @@ export class AppProxy extends Base {
|
|
133
136
|
|
134
137
|
private focusApp() {
|
135
138
|
this.focusBox();
|
136
|
-
this.context.switchAppToWriter(this.id);
|
137
139
|
this.store.setMainViewFocusPath(this.manager.mainView);
|
138
140
|
}
|
139
141
|
|
@@ -205,9 +207,6 @@ export class AppProxy extends Base {
|
|
205
207
|
|
206
208
|
private afterSetupApp(boxInitState: AppInitState | undefined): void {
|
207
209
|
if (boxInitState) {
|
208
|
-
if (boxInitState.focus && this.scenePath) {
|
209
|
-
this.context.switchAppToWriter(this.id);
|
210
|
-
}
|
211
210
|
if (!boxInitState?.x || !boxInitState.y) {
|
212
211
|
this.boxManager?.setBoxInitState(this.id);
|
213
212
|
}
|
@@ -226,29 +225,10 @@ export class AppProxy extends Base {
|
|
226
225
|
await this.destroy(true, false, true);
|
227
226
|
const params = this.params;
|
228
227
|
const appProxy = new AppProxy(params, this.manager, this.id, this.isAddApp);
|
229
|
-
await appProxy.baseInsertApp(true
|
228
|
+
await appProxy.baseInsertApp(true);
|
230
229
|
this.boxManager?.updateBoxState(currentAppState);
|
231
230
|
}
|
232
231
|
|
233
|
-
public switchToWritable() {
|
234
|
-
appRegister.notifyApp(this.kind, "focus", { appId: this.id });
|
235
|
-
this.cameraStore.switchView(this.id, this.view, () => {
|
236
|
-
if (this.view) {
|
237
|
-
if (this.view.mode === ViewVisionMode.Writable) return;
|
238
|
-
try {
|
239
|
-
if (this.manager.mainView.mode === ViewVisionMode.Writable) {
|
240
|
-
this.store.setMainViewFocusPath(this.manager.mainView);
|
241
|
-
notifyMainViewModeChange(callbacks, ViewVisionMode.Freedom);
|
242
|
-
setViewMode(this.manager.mainView, ViewVisionMode.Freedom);
|
243
|
-
}
|
244
|
-
setViewMode(this.view, ViewVisionMode.Writable);
|
245
|
-
} catch (error) {
|
246
|
-
log("switch view failed", error);
|
247
|
-
}
|
248
|
-
}
|
249
|
-
});
|
250
|
-
}
|
251
|
-
|
252
232
|
public getAppInitState = (id: string) => {
|
253
233
|
const attrs = this.store.getAppState(id);
|
254
234
|
if (!attrs) return;
|
@@ -364,7 +344,6 @@ export class AppProxy extends Base {
|
|
364
344
|
|
365
345
|
private async createView(): Promise<View> {
|
366
346
|
const view = await this.viewManager.createView(this.id);
|
367
|
-
this.cameraStore.register(this.id, view);
|
368
347
|
this.setViewFocusScenePath();
|
369
348
|
return view;
|
370
349
|
}
|
@@ -388,7 +367,6 @@ export class AppProxy extends Base {
|
|
388
367
|
this.store.cleanAppAttributes(this.id);
|
389
368
|
}
|
390
369
|
this.appProxies.delete(this.id);
|
391
|
-
this.cameraStore.unregister(this.id, this.view);
|
392
370
|
|
393
371
|
this.viewManager.destroyView(this.id);
|
394
372
|
this.manager.appStatus.delete(this.id);
|
package/src/Base/Context.ts
CHANGED
package/src/BoxManager.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { AppAttributes,
|
1
|
+
import { AppAttributes, Events, MIN_HEIGHT, MIN_WIDTH } from "./constants";
|
2
2
|
import { debounce, maxBy } from "lodash";
|
3
3
|
import {
|
4
4
|
TELE_BOX_MANAGER_EVENT,
|
@@ -6,7 +6,7 @@ import {
|
|
6
6
|
TeleBoxCollector,
|
7
7
|
TeleBoxManager,
|
8
8
|
} from "@netless/telebox-insider";
|
9
|
-
import { WindowManager } from "./index";
|
9
|
+
import { emitter, WindowManager } from "./index";
|
10
10
|
import type { AddAppOptions, AppInitState, EmitterType, CallbacksType } from "./index";
|
11
11
|
import type {
|
12
12
|
TeleBoxManagerUpdateConfig,
|
@@ -145,6 +145,11 @@ export class BoxManager {
|
|
145
145
|
this.teleBoxManager.events.on("z_index", box => {
|
146
146
|
this.context.updateAppState(box.id, AppAttributes.ZIndex, box.zIndex);
|
147
147
|
});
|
148
|
+
emitter.on("playgroundSizeChange", this.playgroundSizeChangeListener);
|
149
|
+
}
|
150
|
+
|
151
|
+
private playgroundSizeChangeListener = () => {
|
152
|
+
this.updateManagerRect();
|
148
153
|
}
|
149
154
|
|
150
155
|
private get mainView() {
|
@@ -251,12 +256,8 @@ export class BoxManager {
|
|
251
256
|
}
|
252
257
|
|
253
258
|
public setCollectorContainer(container: HTMLElement) {
|
254
|
-
const styles = {
|
255
|
-
...DEFAULT_COLLECTOR_STYLE,
|
256
|
-
...this.createTeleBoxManagerConfig?.collectorStyles,
|
257
|
-
};
|
258
259
|
const collector = new TeleBoxCollector({
|
259
|
-
styles
|
260
|
+
styles: this.createTeleBoxManagerConfig?.collectorStyles,
|
260
261
|
}).mount(container);
|
261
262
|
this.teleBoxManager.setCollector(collector);
|
262
263
|
}
|
@@ -391,6 +392,7 @@ export class BoxManager {
|
|
391
392
|
}
|
392
393
|
|
393
394
|
public destroy() {
|
395
|
+
emitter.off("playgroundSizeChange", this.playgroundSizeChangeListener);
|
394
396
|
this.teleBoxManager.destroy();
|
395
397
|
}
|
396
398
|
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import AppDocsViewer from "@netless/app-docs-viewer";
|
2
|
+
import AppMediaPlayer, { setOptions } from "@netless/app-media-player";
|
3
|
+
import { WindowManager } from "./index";
|
4
|
+
import "@netless/app-docs-viewer/dist/style.css";
|
5
|
+
|
6
|
+
export const setupBuiltin = () => {
|
7
|
+
if (WindowManager.debug) {
|
8
|
+
setOptions({ verbose: true });
|
9
|
+
}
|
10
|
+
|
11
|
+
WindowManager.register({
|
12
|
+
kind: AppDocsViewer.kind,
|
13
|
+
src: AppDocsViewer,
|
14
|
+
});
|
15
|
+
WindowManager.register({
|
16
|
+
kind: AppMediaPlayer.kind,
|
17
|
+
src: AppMediaPlayer,
|
18
|
+
});
|
19
|
+
};
|
20
|
+
|
21
|
+
export const BuiltinApps = {
|
22
|
+
DocsViewer: AppDocsViewer.kind as string,
|
23
|
+
MediaPlayer: AppMediaPlayer.kind as string,
|
24
|
+
};
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import { ResizeObserver as ResizeObserverPolyfill } from "@juggle/resize-observer";
|
2
|
+
import { WindowManager } from "./index";
|
3
|
+
import type { EmitterType} from "./index";
|
4
|
+
|
5
|
+
const ResizeObserver = window.ResizeObserver || ResizeObserverPolyfill;
|
6
|
+
|
7
|
+
export class ContainerResizeObserver {
|
8
|
+
private containerResizeObserver?: ResizeObserver;
|
9
|
+
|
10
|
+
constructor(private emitter: EmitterType) {}
|
11
|
+
|
12
|
+
public static create(
|
13
|
+
container: HTMLElement,
|
14
|
+
sizer: HTMLElement,
|
15
|
+
wrapper: HTMLDivElement,
|
16
|
+
emitter: EmitterType,
|
17
|
+
) {
|
18
|
+
const containerResizeObserver = new ContainerResizeObserver(emitter);
|
19
|
+
containerResizeObserver.observePlaygroundSize(container, sizer, wrapper);
|
20
|
+
return containerResizeObserver;
|
21
|
+
}
|
22
|
+
|
23
|
+
public observePlaygroundSize(
|
24
|
+
container: HTMLElement,
|
25
|
+
sizer: HTMLElement,
|
26
|
+
wrapper: HTMLDivElement
|
27
|
+
) {
|
28
|
+
this.updateSizer(container.getBoundingClientRect(), sizer, wrapper);
|
29
|
+
|
30
|
+
this.containerResizeObserver = new ResizeObserver(entries => {
|
31
|
+
const containerRect = entries[0]?.contentRect;
|
32
|
+
if (containerRect) {
|
33
|
+
this.updateSizer(containerRect, sizer, wrapper);
|
34
|
+
this.emitter.emit("playgroundSizeChange", containerRect)
|
35
|
+
}
|
36
|
+
});
|
37
|
+
|
38
|
+
this.containerResizeObserver.observe(container);
|
39
|
+
}
|
40
|
+
|
41
|
+
private updateSizer(
|
42
|
+
{ width, height }: DOMRectReadOnly,
|
43
|
+
sizer: HTMLElement,
|
44
|
+
wrapper: HTMLDivElement
|
45
|
+
) {
|
46
|
+
if (width && height) {
|
47
|
+
if (height / width > WindowManager.containerSizeRatio) {
|
48
|
+
height = width * WindowManager.containerSizeRatio;
|
49
|
+
sizer.classList.toggle("netless-window-manager-sizer-horizontal", true);
|
50
|
+
} else {
|
51
|
+
width = height / WindowManager.containerSizeRatio;
|
52
|
+
sizer.classList.toggle("netless-window-manager-sizer-horizontal", false);
|
53
|
+
}
|
54
|
+
wrapper.style.width = `${width}px`;
|
55
|
+
wrapper.style.height = `${height}px`;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
public disconnect() {
|
60
|
+
this.containerResizeObserver?.disconnect();
|
61
|
+
}
|
62
|
+
}
|
package/src/Cursor/Cursor.ts
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import App from './Cursor.svelte';
|
2
|
-
import pRetry from 'p-retry';
|
3
2
|
import { ApplianceMap } from './icons';
|
4
|
-
import { ApplianceNames
|
3
|
+
import { ApplianceNames } from 'white-web-sdk';
|
5
4
|
import { CursorState } from '../constants';
|
6
5
|
import { Fields } from '../AttributesDelegate';
|
7
6
|
import { get, omit } from 'lodash';
|
@@ -19,54 +18,44 @@ export type Payload = {
|
|
19
18
|
|
20
19
|
export class Cursor extends Base {
|
21
20
|
private member?: RoomMember;
|
22
|
-
private disposer: any;
|
23
21
|
private timer?: number;
|
24
22
|
private component?: SvelteComponent;
|
25
23
|
|
26
24
|
constructor(
|
27
25
|
manager: AppManager,
|
26
|
+
addCursorChangeListener: (uid: string, callback: (position: Position, state: CursorState) => void) => void,
|
28
27
|
private cursors: any,
|
29
28
|
private memberId: string,
|
30
29
|
private cursorManager: CursorManager,
|
31
|
-
private wrapper?: HTMLElement
|
30
|
+
private wrapper?: HTMLElement,
|
32
31
|
) {
|
33
32
|
super(manager);
|
34
33
|
this.setMember();
|
35
34
|
this.createCursor();
|
36
|
-
|
37
|
-
this.disposer && this.disposer();
|
38
|
-
if (!this.cursorPosition) {
|
39
|
-
console.warn(`${memberId} not exist`);
|
40
|
-
}
|
41
|
-
this.startReaction();
|
42
|
-
}, { retries: 3 });
|
35
|
+
addCursorChangeListener(this.memberId, this.onCursorChange);
|
43
36
|
this.autoHidden();
|
44
37
|
}
|
45
38
|
|
46
|
-
private
|
47
|
-
|
48
|
-
const
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
const rect = this.cursorManager.wrapperRect;
|
53
|
-
if (this.component && rect) {
|
54
|
-
this.autoHidden();
|
55
|
-
this.moveCursor(cursor, rect, this.manager.mainView);
|
56
|
-
}
|
57
|
-
} else {
|
58
|
-
const focusView = this.cursorManager.focusView;
|
59
|
-
const viewRect = focusView?.divElement?.getBoundingClientRect();
|
60
|
-
const viewCamera = focusView?.camera;
|
61
|
-
if (focusView && viewRect && viewCamera && this.component) {
|
62
|
-
this.autoHidden();
|
63
|
-
this.moveCursor(cursor, viewRect, focusView);
|
64
|
-
}
|
39
|
+
private onCursorChange = (position: Position, state: CursorState) => {
|
40
|
+
if (position.type === "main") {
|
41
|
+
const rect = this.cursorManager.wrapperRect;
|
42
|
+
if (this.component && rect) {
|
43
|
+
this.autoHidden();
|
44
|
+
this.moveCursor(position, rect, this.manager.mainView);
|
65
45
|
}
|
66
|
-
|
67
|
-
|
46
|
+
} else {
|
47
|
+
const focusView = this.cursorManager.focusView;
|
48
|
+
// TODO 可以存一个当前 focusView 的 Rect 这样只有 focus 切换的时候才调用 getBoundingClientRect
|
49
|
+
const viewRect = focusView?.divElement?.getBoundingClientRect();
|
50
|
+
const viewCamera = focusView?.camera;
|
51
|
+
if (focusView && viewRect && viewCamera && this.component) {
|
52
|
+
this.autoHidden();
|
53
|
+
this.moveCursor(position, viewRect, focusView);
|
68
54
|
}
|
69
|
-
}
|
55
|
+
}
|
56
|
+
if (state && state === CursorState.Leave) {
|
57
|
+
this.hide();
|
58
|
+
}
|
70
59
|
}
|
71
60
|
|
72
61
|
private moveCursor(cursor: Position, rect: DOMRect, view: any) {
|
@@ -196,10 +185,10 @@ export class Cursor extends Base {
|
|
196
185
|
}
|
197
186
|
|
198
187
|
public destroy() {
|
199
|
-
this.disposer && this.disposer();
|
200
188
|
if (this.component) {
|
201
189
|
this.component.$destroy();
|
202
190
|
}
|
191
|
+
this.manager.refresher?.remove(this.memberId);
|
203
192
|
this.cursorManager.cursorInstances.delete(this.memberId);
|
204
193
|
}
|
205
194
|
|
package/src/Cursor/index.ts
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
import {
|
2
|
-
import {
|
3
|
-
import {
|
4
|
-
import {
|
5
|
-
import {
|
6
|
-
import {
|
7
|
-
import {
|
8
|
-
import
|
1
|
+
import { autorun } from "white-web-sdk";
|
2
|
+
import { Base } from "../Base";
|
3
|
+
import { compact, debounce, get, uniq } from "lodash";
|
4
|
+
import { Cursor } from "./Cursor";
|
5
|
+
import { CursorState } from "../constants";
|
6
|
+
import { emitter, WindowManager } from "../index";
|
7
|
+
import { Fields } from "../AttributesDelegate";
|
8
|
+
import { onObjectInserted } from "../Utils/Reactive";
|
9
|
+
import { SideEffectManager } from "side-effect-manager";
|
10
|
+
import type { PositionType, Position } from "../AttributesDelegate";
|
9
11
|
import type { Point, RoomMember, View } from "white-web-sdk";
|
10
12
|
import type { AppManager } from "../AppManager";
|
11
13
|
|
@@ -18,32 +20,42 @@ export type MoveCursorParams = {
|
|
18
20
|
uid: string;
|
19
21
|
x: number;
|
20
22
|
y: number;
|
21
|
-
}
|
23
|
+
};
|
22
24
|
export class CursorManager extends Base {
|
23
25
|
public containerRect?: DOMRect;
|
24
26
|
public wrapperRect?: DOMRect;
|
25
27
|
public cursorInstances: Map<string, Cursor> = new Map();
|
26
28
|
public roomMembers?: readonly RoomMember[];
|
27
29
|
private mainViewElement?: HTMLDivElement;
|
30
|
+
private sideEffectManager = new SideEffectManager();
|
28
31
|
|
29
32
|
constructor(private appManager: AppManager) {
|
30
33
|
super(appManager);
|
31
34
|
this.roomMembers = this.appManager.room?.state.roomMembers;
|
32
35
|
const wrapper = WindowManager.wrapper;
|
33
36
|
if (wrapper) {
|
34
|
-
|
37
|
+
this.setupWrapper(wrapper);
|
35
38
|
}
|
39
|
+
emitter.on("onReconnected", () => {
|
40
|
+
this.onReconnect();
|
41
|
+
});
|
36
42
|
}
|
37
43
|
|
38
44
|
public setupWrapper(wrapper: HTMLElement) {
|
39
45
|
if (this.manager.refresher?.hasReactor("cursors")) {
|
40
46
|
this.destroy();
|
41
47
|
}
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
48
|
+
this.sideEffectManager.add(() => {
|
49
|
+
wrapper.addEventListener("pointerenter", this.mouseMoveListener);
|
50
|
+
wrapper.addEventListener("pointermove", this.mouseMoveListener);
|
51
|
+
wrapper.addEventListener("pointerleave", this.mouseLeaveListener);
|
52
|
+
return () => {
|
53
|
+
wrapper.removeEventListener("pointerenter", this.mouseMoveListener);
|
54
|
+
wrapper.removeEventListener("pointermove", this.mouseMoveListener);
|
55
|
+
wrapper.removeEventListener("pointerleave", this.mouseLeaveListener);
|
56
|
+
};
|
57
|
+
});
|
58
|
+
|
47
59
|
this.initCursorAttributes();
|
48
60
|
this.wrapperRect = wrapper.getBoundingClientRect();
|
49
61
|
this.startReaction(wrapper);
|
@@ -58,27 +70,25 @@ export class CursorManager extends Base {
|
|
58
70
|
return onObjectInserted(this.cursors, () => {
|
59
71
|
this.handleRoomMembersChange(wrapper);
|
60
72
|
});
|
61
|
-
})
|
73
|
+
});
|
62
74
|
}
|
63
75
|
|
64
76
|
private getUids = (members: readonly RoomMember[] | undefined) => {
|
65
77
|
return compact(uniq(members?.map(member => member.payload?.uid)));
|
66
|
-
}
|
78
|
+
};
|
67
79
|
|
68
80
|
private handleRoomMembersChange = debounce((wrapper: HTMLElement) => {
|
69
81
|
const uids = this.getUids(this.roomMembers);
|
70
82
|
const cursors = Object.keys(this.cursors);
|
71
83
|
if (uids?.length) {
|
72
84
|
cursors.map(uid => {
|
73
|
-
if (
|
74
|
-
uids.includes(uid) &&
|
75
|
-
!this.cursorInstances.has(uid)
|
76
|
-
) {
|
85
|
+
if (uids.includes(uid) && !this.cursorInstances.has(uid)) {
|
77
86
|
if (uid === this.context.uid) {
|
78
87
|
return;
|
79
88
|
}
|
80
89
|
const component = new Cursor(
|
81
90
|
this.appManager,
|
91
|
+
this.addCursorChangeListener,
|
82
92
|
this.cursors,
|
83
93
|
uid,
|
84
94
|
this,
|
@@ -86,7 +96,7 @@ export class CursorManager extends Base {
|
|
86
96
|
);
|
87
97
|
this.cursorInstances.set(uid, component);
|
88
98
|
}
|
89
|
-
})
|
99
|
+
});
|
90
100
|
}
|
91
101
|
}, 100);
|
92
102
|
|
@@ -106,13 +116,6 @@ export class CursorManager extends Base {
|
|
106
116
|
this.updateCursor(this.getType(event), event.clientX, event.clientY);
|
107
117
|
}, 5);
|
108
118
|
|
109
|
-
private touchMoveListener = debounce((event: TouchEvent) => {
|
110
|
-
if (event.touches.length === 1) {
|
111
|
-
const touchEvent = event.touches[0];
|
112
|
-
this.updateCursor(this.getType(touchEvent), touchEvent.clientX, touchEvent.clientY);
|
113
|
-
}
|
114
|
-
}, 5);
|
115
|
-
|
116
119
|
private updateCursor(event: EventType, clientX: number, clientY: number) {
|
117
120
|
if (this.wrapperRect && this.manager.canOperate) {
|
118
121
|
const view = event.type === "main" ? this.appManager.mainView : this.focusView;
|
@@ -128,7 +131,11 @@ export class CursorManager extends Base {
|
|
128
131
|
}
|
129
132
|
}
|
130
133
|
|
131
|
-
private getPoint = (
|
134
|
+
private getPoint = (
|
135
|
+
view: View | undefined,
|
136
|
+
clientX: number,
|
137
|
+
clientY: number
|
138
|
+
): Point | undefined => {
|
132
139
|
const rect = view?.divElement?.getBoundingClientRect();
|
133
140
|
if (rect) {
|
134
141
|
const point = view?.convertToPointInWorld({
|
@@ -137,7 +144,7 @@ export class CursorManager extends Base {
|
|
137
144
|
});
|
138
145
|
return point;
|
139
146
|
}
|
140
|
-
}
|
147
|
+
};
|
141
148
|
|
142
149
|
/**
|
143
150
|
* 因为窗口内框在不同分辨率下的大小不一样,所以这里通过来鼠标事件的 target 来判断是在主白板还是在 APP 中
|
@@ -147,7 +154,7 @@ export class CursorManager extends Base {
|
|
147
154
|
const focusApp = this.appManager.focusApp;
|
148
155
|
switch (target.parentElement) {
|
149
156
|
case this.mainViewElement: {
|
150
|
-
return { type: "main" };
|
157
|
+
return { type: "main" };
|
151
158
|
}
|
152
159
|
case focusApp?.view?.divElement: {
|
153
160
|
return { type: "app" };
|
@@ -224,19 +231,41 @@ export class CursorManager extends Base {
|
|
224
231
|
});
|
225
232
|
}
|
226
233
|
|
227
|
-
public
|
228
|
-
const wrapper = WindowManager.wrapper;
|
229
|
-
if (wrapper) {
|
230
|
-
wrapper.removeEventListener("mousemove", this.mouseMoveListener);
|
231
|
-
wrapper.removeEventListener("touchstart", this.touchMoveListener);
|
232
|
-
wrapper.removeEventListener("touchmove", this.touchMoveListener);
|
233
|
-
wrapper.removeEventListener("mouseleave", this.mouseLeaveListener);
|
234
|
-
wrapper.removeEventListener("touchend", this.mouseLeaveListener);
|
235
|
-
}
|
234
|
+
public onReconnect() {
|
236
235
|
if (this.cursorInstances.size) {
|
237
236
|
this.cursorInstances.forEach(cursor => cursor.destroy());
|
238
237
|
this.cursorInstances.clear();
|
239
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
|
+
public destroy() {
|
262
|
+
this.sideEffectManager.flushAll();
|
263
|
+
if (this.cursorInstances.size) {
|
264
|
+
this.cursorInstances.forEach(cursor => {
|
265
|
+
cursor.destroy();
|
266
|
+
});
|
267
|
+
this.cursorInstances.clear();
|
268
|
+
}
|
240
269
|
this.manager.refresher?.remove("cursors");
|
241
270
|
}
|
242
271
|
}
|
package/src/Helper.ts
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
import { WindowManager } from "./index";
|
2
|
+
|
3
|
+
export const setupWrapper = (
|
4
|
+
root: HTMLElement
|
5
|
+
): {
|
6
|
+
playground: HTMLDivElement;
|
7
|
+
wrapper: HTMLDivElement;
|
8
|
+
sizer: HTMLDivElement;
|
9
|
+
mainViewElement: HTMLDivElement;
|
10
|
+
} => {
|
11
|
+
const playground = document.createElement("div");
|
12
|
+
playground.className = "netless-window-manager-playground";
|
13
|
+
|
14
|
+
const sizer = document.createElement("div");
|
15
|
+
sizer.className = "netless-window-manager-sizer";
|
16
|
+
|
17
|
+
const wrapper = document.createElement("div");
|
18
|
+
wrapper.className = "netless-window-manager-wrapper";
|
19
|
+
|
20
|
+
const mainViewElement = document.createElement("div");
|
21
|
+
mainViewElement.className = "netless-window-manager-main-view";
|
22
|
+
|
23
|
+
playground.appendChild(sizer);
|
24
|
+
sizer.appendChild(wrapper);
|
25
|
+
wrapper.appendChild(mainViewElement);
|
26
|
+
root.appendChild(playground);
|
27
|
+
WindowManager.wrapper = wrapper;
|
28
|
+
|
29
|
+
return { playground, wrapper, sizer, mainViewElement };
|
30
|
+
};
|
@@ -1,11 +1,12 @@
|
|
1
|
-
import {
|
1
|
+
import { debounce, isFunction } from "lodash";
|
2
2
|
import { log } from "./Utils/log";
|
3
3
|
import { RoomPhase } from "white-web-sdk";
|
4
4
|
import type { Room } from "white-web-sdk";
|
5
|
+
import type { EmitterType } from "./index";
|
5
6
|
|
6
7
|
export type ReconnectRefresherContext = {
|
7
|
-
|
8
|
-
}
|
8
|
+
emitter: EmitterType;
|
9
|
+
};
|
9
10
|
|
10
11
|
// 白板重连之后会刷新所有的对象,导致 listener 失效, 所以这里在重连之后重新对所有对象进行监听
|
11
12
|
export class ReconnectRefresher {
|
@@ -14,12 +15,19 @@ export class ReconnectRefresher {
|
|
14
15
|
private reactors: Map<string, any> = new Map();
|
15
16
|
private disposers: Map<string, any> = new Map();
|
16
17
|
|
17
|
-
constructor(
|
18
|
+
constructor(private ctx: ReconnectRefresherContext) {}
|
19
|
+
|
20
|
+
public setRoom(room: Room | undefined) {
|
18
21
|
this.room = room;
|
19
22
|
this.phase = room?.phase;
|
23
|
+
room?.callbacks.off("onPhaseChanged", this.onPhaseChanged);
|
20
24
|
room?.callbacks.on("onPhaseChanged", this.onPhaseChanged);
|
21
25
|
}
|
22
26
|
|
27
|
+
public setContext(ctx: ReconnectRefresherContext) {
|
28
|
+
this.ctx = ctx;
|
29
|
+
}
|
30
|
+
|
23
31
|
private onPhaseChanged = (phase: RoomPhase) => {
|
24
32
|
if (phase === RoomPhase.Connected && this.phase === RoomPhase.Reconnecting) {
|
25
33
|
this.onReconnected();
|
@@ -35,7 +43,7 @@ export class ReconnectRefresher {
|
|
35
43
|
this.disposers.set(id, func());
|
36
44
|
}
|
37
45
|
});
|
38
|
-
this.ctx.
|
46
|
+
this.ctx.emitter.emit("onReconnected", undefined);
|
39
47
|
}, 3000);
|
40
48
|
|
41
49
|
private releaseDisposers() {
|