@netless/window-manager 1.0.13-test.9 → 1.0.13
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/index.d.ts +61 -4
- package/dist/index.js +14 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +429 -95
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/App/AppProxy.ts +6 -6
- package/src/AppListener.ts +0 -1
- package/src/AppManager.ts +13 -16
- package/src/AttributesDelegate.ts +0 -7
- package/src/BoxManager.ts +0 -1
- package/src/ContainerResizeObserver.ts +18 -14
- package/src/InternalEmitter.ts +1 -0
- package/src/Utils/Common.ts +0 -1
- package/src/Utils/Reactive.ts +2 -2
- package/src/Utils/RoomHacker.ts +15 -1
- package/src/Utils/attributesLogStringify.ts +78 -0
- package/src/Utils/log.ts +228 -0
- package/src/View/MainView.ts +138 -13
- package/src/index.ts +19 -14
package/src/Utils/log.ts
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
|
+
import type { Logger } from "white-web-sdk";
|
|
2
|
+
import { isShallowMergeAttributesRecord, stringifyForAttributesLog } from "./attributesLogStringify";
|
|
1
3
|
import { WindowManager } from "../index";
|
|
2
4
|
|
|
5
|
+
/** ArgusLog 经 `logger.info` 上报的单条字符串上限(含前缀) */
|
|
6
|
+
const ARGUS_LOG_INFO_MAX_LENGTH = 1500;
|
|
7
|
+
|
|
8
|
+
function truncateArgusLogInfoMessage(message: string): string {
|
|
9
|
+
if (message.length <= ARGUS_LOG_INFO_MAX_LENGTH) {
|
|
10
|
+
return message;
|
|
11
|
+
}
|
|
12
|
+
const ellipsis = "…";
|
|
13
|
+
return message.slice(0, ARGUS_LOG_INFO_MAX_LENGTH - ellipsis.length) + ellipsis;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function keysPathEqual(a: string[], b: string[]): boolean {
|
|
17
|
+
if (a.length !== b.length) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
for (let i = 0; i < a.length; i++) {
|
|
21
|
+
if (a[i] !== b[i]) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
3
28
|
export const log = (...args: any[]): void => {
|
|
4
29
|
if (WindowManager.debug) {
|
|
5
30
|
console.log(`[WindowManager]:`, ...args);
|
|
@@ -41,4 +66,207 @@ export class LocalConsole {
|
|
|
41
66
|
}
|
|
42
67
|
console.log(`[window-manager][${this.name}]: ${args.join(", ")}`);
|
|
43
68
|
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 销毁:清除 debounce 定时器与未输出的暂存参数。
|
|
72
|
+
* 持有 LocalConsole 的类在销毁时应调用。
|
|
73
|
+
*/
|
|
74
|
+
destroy(): void {
|
|
75
|
+
if (this.flushTimer != null) {
|
|
76
|
+
clearTimeout(this.flushTimer);
|
|
77
|
+
this.flushTimer = null;
|
|
78
|
+
}
|
|
79
|
+
this.pendingArgs = null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 按 `[WindowManager][tagName]` 前缀输出。
|
|
85
|
+
* 若传入 `debounceTime`(毫秒):窗口内多次 `log` 不立即输出,只在连续停止调用满 `debounceTime` 后输出**最后一次**的参数(尾部 debounce)。
|
|
86
|
+
*/
|
|
87
|
+
export class ArgusLog {
|
|
88
|
+
private pendingArgs: unknown[] | null = null;
|
|
89
|
+
private flushTimer: ReturnType<typeof setTimeout> | null = null;
|
|
90
|
+
|
|
91
|
+
/** debounce 窗口内按一层 key 合并;同 key 后者覆盖;非普通对象则整段待输出被本次值替换 */
|
|
92
|
+
private pendingShallowMerge:
|
|
93
|
+
| { kind: "record"; label: string; data: Record<string, unknown> }
|
|
94
|
+
| { kind: "atom"; label: string; value: unknown }
|
|
95
|
+
| null = null;
|
|
96
|
+
private shallowMergeTimer: ReturnType<typeof setTimeout> | null = null;
|
|
97
|
+
|
|
98
|
+
/** debounce 窗口内 safeUpdateAttributes:同 keys 数组则只更新 value,否则追加一段,flush 时拼成一条 */
|
|
99
|
+
private pendingUpdateSegments: { keys: string[]; value: unknown }[] | null = null;
|
|
100
|
+
private updateMergeTimer: ReturnType<typeof setTimeout> | null = null;
|
|
101
|
+
|
|
102
|
+
constructor(
|
|
103
|
+
private readonly logger: Logger,
|
|
104
|
+
private readonly name: string,
|
|
105
|
+
private readonly debounceTime?: number,
|
|
106
|
+
) {}
|
|
107
|
+
|
|
108
|
+
private emitInfo(message: string): void {
|
|
109
|
+
this.logger.info(truncateArgusLogInfoMessage(message));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private flush(): void {
|
|
113
|
+
this.flushTimer = null;
|
|
114
|
+
const args = this.pendingArgs;
|
|
115
|
+
this.pendingArgs = null;
|
|
116
|
+
if (args === null) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
this.emitInfo(`[WindowManager][${this.name}]: ${args.join(", ")}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private flushShallowMerge(): void {
|
|
123
|
+
this.shallowMergeTimer = null;
|
|
124
|
+
const p = this.pendingShallowMerge;
|
|
125
|
+
this.pendingShallowMerge = null;
|
|
126
|
+
if (p === null) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const body =
|
|
130
|
+
p.kind === "record" ? stringifyForAttributesLog(p.data) : stringifyForAttributesLog(p.value);
|
|
131
|
+
this.emitInfo(`[WindowManager][${this.name}]: ${p.label} ${body}`);
|
|
132
|
+
// 输出后释放合并对象引用,避免长时间持有 attributes 快照
|
|
133
|
+
if (p.kind === "record") {
|
|
134
|
+
for (const k of Object.keys(p.data)) {
|
|
135
|
+
delete p.data[k];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
log(...args: unknown[]): void {
|
|
141
|
+
const ms = this.debounceTime;
|
|
142
|
+
if (ms != null && ms > 0) {
|
|
143
|
+
this.pendingArgs = args;
|
|
144
|
+
if (this.flushTimer != null) {
|
|
145
|
+
clearTimeout(this.flushTimer);
|
|
146
|
+
}
|
|
147
|
+
this.flushTimer = setTimeout(() => this.flush(), ms);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
this.emitInfo(`[WindowManager][${this.name}]: ${args.join(", ")}`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 带 debounce 时:窗口内多次调用会把「一层 key」合并进同一条日志(不同 key 并存,同 key 取最后一次)。
|
|
155
|
+
* `payload` 为普通对象时做浅合并;否则视为原子值,覆盖当前待合并状态(丢弃此前累积的对象 key)。
|
|
156
|
+
* 无 debounce 或时间为 0 时立即输出。
|
|
157
|
+
*/
|
|
158
|
+
logDebouncedShallowMerge(label: string, payload: unknown): void {
|
|
159
|
+
const ms = this.debounceTime;
|
|
160
|
+
const debounced = ms != null && ms > 0;
|
|
161
|
+
|
|
162
|
+
const emit = (text: string): void => {
|
|
163
|
+
this.emitInfo(`[WindowManager][${this.name}]: ${label} ${text}`);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
if (!debounced) {
|
|
167
|
+
emit(stringifyForAttributesLog(payload));
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (this.shallowMergeTimer != null) {
|
|
172
|
+
clearTimeout(this.shallowMergeTimer);
|
|
173
|
+
this.shallowMergeTimer = null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (isShallowMergeAttributesRecord(payload)) {
|
|
177
|
+
if (this.pendingShallowMerge?.kind === "record") {
|
|
178
|
+
this.pendingShallowMerge = {
|
|
179
|
+
kind: "record",
|
|
180
|
+
label,
|
|
181
|
+
data: { ...this.pendingShallowMerge.data, ...payload },
|
|
182
|
+
};
|
|
183
|
+
} else {
|
|
184
|
+
this.pendingShallowMerge = { kind: "record", label, data: { ...payload } };
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
this.pendingShallowMerge = { kind: "atom", label, value: payload };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
this.shallowMergeTimer = setTimeout(() => this.flushShallowMerge(), ms);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private flushUpdateAttributesMerge(): void {
|
|
194
|
+
this.updateMergeTimer = null;
|
|
195
|
+
const segments = this.pendingUpdateSegments;
|
|
196
|
+
this.pendingUpdateSegments = null;
|
|
197
|
+
if (segments === null || segments.length === 0) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const parts = segments.map(
|
|
201
|
+
(s) => `${s.keys.join(", ")} ${stringifyForAttributesLog(s.value)}`,
|
|
202
|
+
);
|
|
203
|
+
this.emitInfo(`[WindowManager][${this.name}]: safeUpdateAttributes ${parts.join(" | ")}`);
|
|
204
|
+
for (const s of segments) {
|
|
205
|
+
s.keys.length = 0;
|
|
206
|
+
s.value = undefined;
|
|
207
|
+
}
|
|
208
|
+
segments.length = 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* 带 debounce 时:连续调用若 `keys` 与上一段完全相同则覆盖该段的 `value`;否则追加一段。
|
|
213
|
+
* flush 时输出一条日志,多段用 ` | ` 连接。
|
|
214
|
+
*/
|
|
215
|
+
logDebouncedUpdateAttributes(keys: string[], value: unknown): void {
|
|
216
|
+
const ms = this.debounceTime;
|
|
217
|
+
const debounced = ms != null && ms > 0;
|
|
218
|
+
const keysCopy = [...keys];
|
|
219
|
+
|
|
220
|
+
if (!debounced) {
|
|
221
|
+
this.emitInfo(
|
|
222
|
+
`[WindowManager][${this.name}]: safeUpdateAttributes ${keysCopy.join(", ")} ${stringifyForAttributesLog(value)}`,
|
|
223
|
+
);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (this.updateMergeTimer != null) {
|
|
228
|
+
clearTimeout(this.updateMergeTimer);
|
|
229
|
+
this.updateMergeTimer = null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (this.pendingUpdateSegments === null || this.pendingUpdateSegments.length === 0) {
|
|
233
|
+
this.pendingUpdateSegments = [{ keys: keysCopy, value }];
|
|
234
|
+
} else {
|
|
235
|
+
const last = this.pendingUpdateSegments[this.pendingUpdateSegments.length - 1];
|
|
236
|
+
if (keysPathEqual(last.keys, keysCopy)) {
|
|
237
|
+
last.value = value;
|
|
238
|
+
} else {
|
|
239
|
+
this.pendingUpdateSegments.push({ keys: keysCopy, value });
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
this.updateMergeTimer = setTimeout(() => this.flushUpdateAttributesMerge(), ms);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* 销毁:清除所有 `setTimeout` debounce 定时器,并丢弃尚未输出的暂存日志(不补打日志)。
|
|
248
|
+
* WindowManager 销毁时应调用,避免泄漏与销毁后仍触发 `logger.info`。
|
|
249
|
+
*/
|
|
250
|
+
destroy(): void {
|
|
251
|
+
if (this.flushTimer != null) {
|
|
252
|
+
clearTimeout(this.flushTimer);
|
|
253
|
+
this.flushTimer = null;
|
|
254
|
+
}
|
|
255
|
+
if (this.shallowMergeTimer != null) {
|
|
256
|
+
clearTimeout(this.shallowMergeTimer);
|
|
257
|
+
this.shallowMergeTimer = null;
|
|
258
|
+
}
|
|
259
|
+
if (this.updateMergeTimer != null) {
|
|
260
|
+
clearTimeout(this.updateMergeTimer);
|
|
261
|
+
this.updateMergeTimer = null;
|
|
262
|
+
}
|
|
263
|
+
this.pendingArgs = null;
|
|
264
|
+
this.pendingShallowMerge = null;
|
|
265
|
+
this.pendingUpdateSegments = null;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** 与 `destroy()` 相同,保留旧名以兼容 */
|
|
269
|
+
dispose(): void {
|
|
270
|
+
this.destroy();
|
|
271
|
+
}
|
|
44
272
|
}
|
package/src/View/MainView.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { callbacks } from "../callback";
|
|
|
3
3
|
import { createView } from "./ViewManager";
|
|
4
4
|
import { debounce, get, isEmpty, isEqual } from "lodash";
|
|
5
5
|
import { internalEmitter } from "../InternalEmitter";
|
|
6
|
-
import { Fields } from "../AttributesDelegate";
|
|
6
|
+
import { Fields, type MainViewCamera } from "../AttributesDelegate";
|
|
7
7
|
import { setViewFocusScenePath } from "../Utils/Common";
|
|
8
8
|
import { SideEffectManager } from "side-effect-manager";
|
|
9
9
|
import type { Camera, Room, Size, View } from "white-web-sdk";
|
|
@@ -11,7 +11,14 @@ import type { AppManager } from "../AppManager";
|
|
|
11
11
|
import { Events } from "../constants";
|
|
12
12
|
import { LocalConsole } from "../Utils/log";
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
type MainViewScreenLike = {
|
|
15
|
+
refreshSize?: (width: number, height: number) => void;
|
|
16
|
+
resizeObserver?: {
|
|
17
|
+
disconnect?: () => void;
|
|
18
|
+
observe?: (target: Element) => void;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
15
22
|
export class MainViewProxy {
|
|
16
23
|
/** Refresh the view's camera in an interval of 1.5s. */
|
|
17
24
|
public polling = false;
|
|
@@ -19,13 +26,19 @@ export class MainViewProxy {
|
|
|
19
26
|
private scale?: number;
|
|
20
27
|
private started = false;
|
|
21
28
|
private mainViewIsAddListener = false;
|
|
29
|
+
private isForcingMainViewDivElement = false;
|
|
30
|
+
private wrapperRectWorkaroundFrame = 0;
|
|
31
|
+
private pendingWrapperRectChange?: { width: number; height: number; origin?: string };
|
|
22
32
|
private mainView: View;
|
|
23
33
|
private store = this.manager.store;
|
|
24
34
|
private viewMode = this.manager.windowManger.viewMode;
|
|
25
35
|
|
|
26
36
|
private sideEffectManager = new SideEffectManager();
|
|
27
37
|
|
|
28
|
-
private playgroundSizeChangeListenerLocalConsole = new LocalConsole("playgroundSizeChangeListener",
|
|
38
|
+
private playgroundSizeChangeListenerLocalConsole = new LocalConsole("playgroundSizeChangeListener", 100);
|
|
39
|
+
private sizeUpdatedLocalConsole = new LocalConsole("sizeUpdated", 100);
|
|
40
|
+
private cameraUpdatedLocalConsole = new LocalConsole("cameraUpdated", 100);
|
|
41
|
+
private cameraReactionLocalConsole = new LocalConsole("cameraReaction", 100);
|
|
29
42
|
|
|
30
43
|
constructor(private manager: AppManager) {
|
|
31
44
|
this.mainView = this.createMainView();
|
|
@@ -54,6 +67,9 @@ export class MainViewProxy {
|
|
|
54
67
|
this.sideEffectManager.add(() => {
|
|
55
68
|
return internalEmitter.on("containerSizeRatioUpdate", this.onUpdateContainerSizeRatio);
|
|
56
69
|
});
|
|
70
|
+
this.sideEffectManager.add(() => {
|
|
71
|
+
return internalEmitter.on("wrapperRectChange", this.onWrapperRectChange);
|
|
72
|
+
});
|
|
57
73
|
this.sideEffectManager.add(() => {
|
|
58
74
|
return internalEmitter.on("startReconnect", () => {
|
|
59
75
|
if (!this.didRelease) {
|
|
@@ -109,14 +125,105 @@ export class MainViewProxy {
|
|
|
109
125
|
this.moveCamera(this.mainViewCamera);
|
|
110
126
|
}
|
|
111
127
|
|
|
128
|
+
private onWrapperRectChange = (payload: { width: number; height: number; origin?: string }) => {
|
|
129
|
+
this.pendingWrapperRectChange = payload;
|
|
130
|
+
if (this.wrapperRectWorkaroundFrame) {
|
|
131
|
+
cancelAnimationFrame(this.wrapperRectWorkaroundFrame);
|
|
132
|
+
}
|
|
133
|
+
this.wrapperRectWorkaroundFrame = requestAnimationFrame(this.runWrapperRectWorkaround);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
private runWrapperRectWorkaround = () => {
|
|
137
|
+
this.wrapperRectWorkaroundFrame = 0;
|
|
138
|
+
const payload = this.pendingWrapperRectChange;
|
|
139
|
+
const element = this.mainView.divElement;
|
|
140
|
+
this.pendingWrapperRectChange = undefined;
|
|
141
|
+
if (!payload || !element) return;
|
|
142
|
+
|
|
143
|
+
const rect = element.getBoundingClientRect();
|
|
144
|
+
const observedSize = { width: rect.width, height: rect.height };
|
|
145
|
+
const wrapperMatchesDom =
|
|
146
|
+
Math.abs(payload.width - observedSize.width) <= 0.5 &&
|
|
147
|
+
Math.abs(payload.height - observedSize.height) <= 0.5;
|
|
148
|
+
const viewIsStale =
|
|
149
|
+
Math.abs(this.mainView.size.width - observedSize.width) > 0.5 ||
|
|
150
|
+
Math.abs(this.mainView.size.height - observedSize.height) > 0.5;
|
|
151
|
+
|
|
152
|
+
if (wrapperMatchesDom && viewIsStale) {
|
|
153
|
+
this.forceSyncMainViewDivElement(
|
|
154
|
+
`wrapperRectChange:${payload.origin || "unknown"}`,
|
|
155
|
+
observedSize,
|
|
156
|
+
element
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
private forceSyncMainViewDivElement(
|
|
162
|
+
reason: string,
|
|
163
|
+
observedSize: Pick<Size, "width" | "height">,
|
|
164
|
+
element: HTMLDivElement
|
|
165
|
+
) {
|
|
166
|
+
const { width: viewWidth, height: viewHeight } = this.mainView.size;
|
|
167
|
+
let targetElement = element;
|
|
168
|
+
if (
|
|
169
|
+
Math.abs(viewWidth - observedSize.width) <= 0.5 &&
|
|
170
|
+
Math.abs(viewHeight - observedSize.height) <= 0.5
|
|
171
|
+
) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (this.isForcingMainViewDivElement) {
|
|
175
|
+
console.log("[window-manager] skipForceSyncMainViewDivElement " + JSON.stringify({
|
|
176
|
+
reason,
|
|
177
|
+
observedSize,
|
|
178
|
+
viewSize: this.mainView.size,
|
|
179
|
+
}));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
this.isForcingMainViewDivElement = true;
|
|
183
|
+
try {
|
|
184
|
+
const mainView = this.mainView as View & { screen?: MainViewScreenLike };
|
|
185
|
+
const screen = mainView.screen;
|
|
186
|
+
const resizeObserver = screen?.resizeObserver;
|
|
187
|
+
if (typeof screen?.refreshSize === "function") {
|
|
188
|
+
console.log(
|
|
189
|
+
"[window-manager] forceSyncMainViewDivElement observerReset " +
|
|
190
|
+
JSON.stringify({
|
|
191
|
+
reason,
|
|
192
|
+
viewSize: this.mainView.size,
|
|
193
|
+
observedSize,
|
|
194
|
+
})
|
|
195
|
+
);
|
|
196
|
+
// Reset the observer queue so we sync against the current DOM box,
|
|
197
|
+
// not a stale ResizeObserver entry from a rapid resize burst.
|
|
198
|
+
resizeObserver?.disconnect?.();
|
|
199
|
+
screen.refreshSize(observedSize.width, observedSize.height);
|
|
200
|
+
resizeObserver?.observe?.(element);
|
|
201
|
+
}
|
|
202
|
+
} finally {
|
|
203
|
+
queueMicrotask(() => {
|
|
204
|
+
const rect = targetElement.getBoundingClientRect();
|
|
205
|
+
console.log("[window-manager] forceSyncMainViewDivElementResult " + JSON.stringify({
|
|
206
|
+
reason,
|
|
207
|
+
viewSize: this.mainView.size,
|
|
208
|
+
rect: { width: rect.width, height: rect.height },
|
|
209
|
+
}));
|
|
210
|
+
this.isForcingMainViewDivElement = false;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
112
215
|
public start() {
|
|
113
|
-
console.log("[window-manager] start
|
|
216
|
+
console.log("[window-manager] start attributes size:" + JSON.stringify(this.mainViewSize));
|
|
114
217
|
this.sizeChangeHandler(this.mainViewSize);
|
|
115
218
|
if (this.started) return;
|
|
116
219
|
this.addCameraListener();
|
|
117
220
|
this.addCameraReaction();
|
|
118
221
|
if (this.manager.room) this.syncMainView(this.manager.room);
|
|
119
222
|
this.started = true;
|
|
223
|
+
if(this.mainView.focusScenePath) {
|
|
224
|
+
this.manager.windowManger.onMainViewScenePathChangeHandler(this.mainView.focusScenePath);
|
|
225
|
+
}
|
|
226
|
+
console.log("[window-manager] start end mainView size:" + JSON.stringify(this.mainView.size));
|
|
120
227
|
}
|
|
121
228
|
|
|
122
229
|
public addCameraReaction = () => {
|
|
@@ -132,11 +239,11 @@ export class MainViewProxy {
|
|
|
132
239
|
private cameraReaction = () => {
|
|
133
240
|
return reaction(
|
|
134
241
|
() => this.mainViewCamera,
|
|
135
|
-
camera => {
|
|
242
|
+
(camera: MainViewCamera | undefined) => {
|
|
136
243
|
if (camera && camera.id !== this.manager.uid) {
|
|
137
|
-
console.log("[window-manager] cameraReaction " + JSON.stringify(camera) + JSON.stringify(this.mainViewSize));
|
|
138
244
|
this.moveCameraToContian(this.mainViewSize);
|
|
139
245
|
this.moveCamera(camera);
|
|
246
|
+
this.cameraReactionLocalConsole.log(`camera: ${JSON.stringify(camera)}, current size: ${JSON.stringify(this.mainViewSize)}`);
|
|
140
247
|
}
|
|
141
248
|
},
|
|
142
249
|
{ fireImmediately: true }
|
|
@@ -147,7 +254,7 @@ export class MainViewProxy {
|
|
|
147
254
|
if (size) {
|
|
148
255
|
this.moveCameraToContian(size);
|
|
149
256
|
this.moveCamera(this.mainViewCamera);
|
|
150
|
-
console.log("[window-manager] sizeChangeHandler
|
|
257
|
+
console.log("[window-manager] sizeChangeHandler current size and camera" + JSON.stringify(size) + JSON.stringify(this.mainViewCamera) +
|
|
151
258
|
JSON.stringify(this.mainView.camera) + JSON.stringify(this.mainView.size));
|
|
152
259
|
}
|
|
153
260
|
this.ensureMainViewSize();
|
|
@@ -248,18 +355,18 @@ export class MainViewProxy {
|
|
|
248
355
|
|
|
249
356
|
private addCameraListener() {
|
|
250
357
|
this.view.callbacks.on("onCameraUpdatedByDevice", this.onCameraUpdatedByDevice);
|
|
251
|
-
this.view.callbacks.on("onCameraUpdated", this.
|
|
252
|
-
this.view.callbacks.on("onSizeUpdated", this.
|
|
358
|
+
this.view.callbacks.on("onCameraUpdated", this.onCameraUpdated);
|
|
359
|
+
this.view.callbacks.on("onSizeUpdated", this.onSizeUpdated);
|
|
253
360
|
}
|
|
254
361
|
|
|
255
362
|
private removeCameraListener() {
|
|
256
363
|
this.view.callbacks.off("onCameraUpdatedByDevice", this.onCameraUpdatedByDevice);
|
|
257
|
-
this.view.callbacks.off("onCameraUpdated", this.
|
|
258
|
-
this.view.callbacks.off("onSizeUpdated", this.
|
|
364
|
+
this.view.callbacks.off("onCameraUpdated", this.onCameraUpdated);
|
|
365
|
+
this.view.callbacks.off("onSizeUpdated", this.onSizeUpdated);
|
|
259
366
|
}
|
|
260
367
|
|
|
261
368
|
private _syncMainViewTimer = 0;
|
|
262
|
-
private
|
|
369
|
+
private handleCameraOrSizeUpdated = () => {
|
|
263
370
|
callbacks.emit("cameraStateChange", this.cameraState);
|
|
264
371
|
// sdk >= 2.16.43 的 syncMainView() 可以写入当前 main view 的 camera, 以修复复制粘贴元素的位置
|
|
265
372
|
// 注意到这个操作会发送信令,应当避免频繁调用
|
|
@@ -270,6 +377,16 @@ export class MainViewProxy {
|
|
|
270
377
|
this.ensureMainViewSize();
|
|
271
378
|
};
|
|
272
379
|
|
|
380
|
+
private onCameraUpdated = (camera: Camera) => {
|
|
381
|
+
this.cameraUpdatedLocalConsole.log(JSON.stringify(camera));
|
|
382
|
+
this.handleCameraOrSizeUpdated();
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
private onSizeUpdated = (size: Size) => {
|
|
386
|
+
this.sizeUpdatedLocalConsole.log(JSON.stringify(size));
|
|
387
|
+
this.handleCameraOrSizeUpdated();
|
|
388
|
+
};
|
|
389
|
+
|
|
273
390
|
private ensureMainViewSize() {
|
|
274
391
|
if (
|
|
275
392
|
(!this.mainViewSize ||
|
|
@@ -284,7 +401,6 @@ export class MainViewProxy {
|
|
|
284
401
|
|
|
285
402
|
private syncMainView = (room: Room) => {
|
|
286
403
|
if (room.isWritable) {
|
|
287
|
-
console.log("[window-manager] syncMainView ");
|
|
288
404
|
room.syncMainView(this.mainView);
|
|
289
405
|
}
|
|
290
406
|
};
|
|
@@ -328,6 +444,15 @@ export class MainViewProxy {
|
|
|
328
444
|
};
|
|
329
445
|
|
|
330
446
|
public destroy() {
|
|
447
|
+
console.log("[window-manager] destroy ");
|
|
448
|
+
if (this.wrapperRectWorkaroundFrame) {
|
|
449
|
+
cancelAnimationFrame(this.wrapperRectWorkaroundFrame);
|
|
450
|
+
this.wrapperRectWorkaroundFrame = 0;
|
|
451
|
+
}
|
|
452
|
+
this.playgroundSizeChangeListenerLocalConsole.destroy();
|
|
453
|
+
this.sizeUpdatedLocalConsole.destroy();
|
|
454
|
+
this.cameraUpdatedLocalConsole.destroy();
|
|
455
|
+
this.cameraReactionLocalConsole.destroy();
|
|
331
456
|
this.removeMainViewListener();
|
|
332
457
|
this.stop();
|
|
333
458
|
this.sideEffectManager.flushAll();
|
package/src/index.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { Fields } from "./AttributesDelegate";
|
|
|
12
12
|
import { initDb } from "./Register/storage";
|
|
13
13
|
import { InvisiblePlugin, isPlayer, isRoom, RoomPhase, ViewMode } from "white-web-sdk";
|
|
14
14
|
import { isEqual, isNull, isObject, omit, isNumber } from "lodash";
|
|
15
|
-
import { log } from "./Utils/log";
|
|
15
|
+
import { ArgusLog, log } from "./Utils/log";
|
|
16
16
|
import { PageStateImpl } from "./PageState";
|
|
17
17
|
import { ReconnectRefresher } from "./ReconnectRefresher";
|
|
18
18
|
import { replaceRoomFunction } from "./Utils/RoomHacker";
|
|
@@ -245,6 +245,8 @@ export class WindowManager
|
|
|
245
245
|
|
|
246
246
|
private _roomLogger?: Logger;
|
|
247
247
|
|
|
248
|
+
public attributesDeboundceLog?: ArgusLog;
|
|
249
|
+
|
|
248
250
|
get Logger(): Logger | undefined {
|
|
249
251
|
return this._roomLogger;
|
|
250
252
|
}
|
|
@@ -253,16 +255,9 @@ export class WindowManager
|
|
|
253
255
|
super(context);
|
|
254
256
|
WindowManager.displayer = context.displayer;
|
|
255
257
|
(window as any).NETLESS_DEPS = __APP_DEPENDENCIES__;
|
|
256
|
-
this.visibleStateListener();
|
|
257
|
-
document.addEventListener("visibilitychange", this.visibleStateListener);
|
|
258
258
|
this.emitter.on('mainViewScenePathChange', this.onMainViewScenePathChangeHandler)
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
-
|
|
262
|
-
private visibleStateListener = () => {
|
|
263
|
-
console.log("[window-manager] visibleStateListener isVisible:" + !document.hidden);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
261
|
public static onCreate(manager: WindowManager) {
|
|
267
262
|
WindowManager._resolve(manager);
|
|
268
263
|
}
|
|
@@ -294,6 +289,7 @@ export class WindowManager
|
|
|
294
289
|
manager = await this.initManager(room);
|
|
295
290
|
if (manager) {
|
|
296
291
|
manager._roomLogger = (room as unknown as { logger: Logger }).logger;
|
|
292
|
+
manager.attributesDeboundceLog = new ArgusLog(manager._roomLogger, "attributes", 300);
|
|
297
293
|
if (WindowManager.registered.size > 0) {
|
|
298
294
|
manager._roomLogger.info(
|
|
299
295
|
`[WindowManager] registered apps: ${JSON.stringify(
|
|
@@ -403,16 +399,17 @@ export class WindowManager
|
|
|
403
399
|
if (mainViewElement) {
|
|
404
400
|
const backgroundImage = mainViewElement.querySelector('.background img');
|
|
405
401
|
if (backgroundImage) {
|
|
406
|
-
// todo 获取到 back ground image 的 rect情况以及css情况是否可见
|
|
407
402
|
const backgroundImageRect = backgroundImage?.getBoundingClientRect();
|
|
408
403
|
const backgroundImageCSS = window.getComputedStyle(backgroundImage);
|
|
409
404
|
const backgroundImageVisible = backgroundImageRect?.width > 0 && backgroundImageRect?.height > 0 && backgroundImageCSS.display !== 'none';
|
|
410
|
-
|
|
405
|
+
const camera = this.mainView.camera;
|
|
406
|
+
console.log("[window-manager] backgroundImageVisible:" + backgroundImageVisible + " camera:" + JSON.stringify(camera));
|
|
411
407
|
return;
|
|
412
408
|
}
|
|
413
|
-
console.log("[window-manager] onMainViewScenePathChange" + scenePath + 'backgroundImageVisible is not found');
|
|
409
|
+
console.log("[window-manager] onMainViewScenePathChange scenePath:" + scenePath + ' backgroundImageVisible is not found');
|
|
410
|
+
return;
|
|
414
411
|
}
|
|
415
|
-
console.log("[window-manager] onMainViewScenePathChange" + scenePath + 'mainViewElement is not found');
|
|
412
|
+
console.log("[window-manager] onMainViewScenePathChange scenePath:" + scenePath + ' mainViewElement is not found');
|
|
416
413
|
}
|
|
417
414
|
|
|
418
415
|
private static initManager(room: Room): Promise<WindowManager | undefined> {
|
|
@@ -1047,7 +1044,6 @@ export class WindowManager
|
|
|
1047
1044
|
animationMode?: AnimationMode;
|
|
1048
1045
|
}>
|
|
1049
1046
|
): void {
|
|
1050
|
-
console.log("[window-manager] moveCameraToContain" + JSON.stringify(rectangle));
|
|
1051
1047
|
this.mainView.moveCameraToContain(rectangle);
|
|
1052
1048
|
setTimeout(() => {
|
|
1053
1049
|
this.appManager?.mainViewProxy.setCameraAndSize();
|
|
@@ -1071,6 +1067,8 @@ export class WindowManager
|
|
|
1071
1067
|
}
|
|
1072
1068
|
|
|
1073
1069
|
private _destroy() {
|
|
1070
|
+
this.attributesDeboundceLog?.destroy();
|
|
1071
|
+
this.attributesDeboundceLog = undefined;
|
|
1074
1072
|
this.containerResizeObserver?.disconnect();
|
|
1075
1073
|
this.appManager?.destroy();
|
|
1076
1074
|
this.cursorManager?.destroy();
|
|
@@ -1083,7 +1081,6 @@ export class WindowManager
|
|
|
1083
1081
|
WindowManager.playground.parentNode?.removeChild(WindowManager.playground);
|
|
1084
1082
|
}
|
|
1085
1083
|
WindowManager.params = undefined;
|
|
1086
|
-
document.removeEventListener("visibilitychange", this.visibleStateListener);
|
|
1087
1084
|
this.emitter.off('mainViewScenePathChange', this.onMainViewScenePathChangeHandler);
|
|
1088
1085
|
this._iframeBridge?.destroy();
|
|
1089
1086
|
this._iframeBridge = undefined;
|
|
@@ -1115,12 +1112,18 @@ export class WindowManager
|
|
|
1115
1112
|
public safeSetAttributes(attributes: any): void {
|
|
1116
1113
|
if (this.canOperate) {
|
|
1117
1114
|
this.setAttributes(attributes);
|
|
1115
|
+
if (this.attributesDeboundceLog) {
|
|
1116
|
+
this.attributesDeboundceLog.logDebouncedShallowMerge("safeSetAttributes", attributes);
|
|
1117
|
+
}
|
|
1118
1118
|
}
|
|
1119
1119
|
}
|
|
1120
1120
|
|
|
1121
1121
|
public safeUpdateAttributes(keys: string[], value: any): void {
|
|
1122
1122
|
if (this.canOperate) {
|
|
1123
1123
|
this.updateAttributes(keys, value);
|
|
1124
|
+
if (this.attributesDeboundceLog) {
|
|
1125
|
+
this.attributesDeboundceLog.logDebouncedUpdateAttributes(keys, value);
|
|
1126
|
+
}
|
|
1124
1127
|
}
|
|
1125
1128
|
}
|
|
1126
1129
|
|
|
@@ -1131,6 +1134,7 @@ export class WindowManager
|
|
|
1131
1134
|
public cleanCurrentScene(): void {
|
|
1132
1135
|
log("clean current scene");
|
|
1133
1136
|
this.focusedView?.cleanCurrentScene();
|
|
1137
|
+
this.Logger && this.Logger.info(`[WindowManager]: cleanCurrentScene ${this.focusedView?.focusScenePath}`);
|
|
1134
1138
|
}
|
|
1135
1139
|
|
|
1136
1140
|
public redo(): number {
|
|
@@ -1143,6 +1147,7 @@ export class WindowManager
|
|
|
1143
1147
|
|
|
1144
1148
|
public delete(): void {
|
|
1145
1149
|
this.focusedView?.delete();
|
|
1150
|
+
this.Logger && this.Logger.info(`[WindowManager]: delete ${this.focusedView?.focusScenePath}`);
|
|
1146
1151
|
}
|
|
1147
1152
|
|
|
1148
1153
|
public copy(): void {
|