@netless/window-manager 0.4.0-canary.3 → 0.4.0-canary.33

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.
Files changed (89) hide show
  1. package/.idea/inspectionProfiles/Project_Default.xml +7 -0
  2. package/.idea/modules.xml +8 -0
  3. package/.idea/vcs.xml +6 -0
  4. package/.idea/window-manager.iml +12 -0
  5. package/.vscode/settings.json +1 -0
  6. package/CHANGELOG.md +32 -1
  7. package/README.md +3 -0
  8. package/dist/App/MagixEvent/index.d.ts +29 -0
  9. package/dist/App/Storage/StorageEvent.d.ts +8 -0
  10. package/dist/App/Storage/index.d.ts +39 -0
  11. package/dist/App/Storage/typings.d.ts +22 -0
  12. package/dist/App/Storage/utils.d.ts +5 -0
  13. package/dist/AppContext.d.ts +40 -16
  14. package/dist/AppListener.d.ts +2 -1
  15. package/dist/AppManager.d.ts +26 -12
  16. package/dist/AppProxy.d.ts +7 -8
  17. package/dist/AttributesDelegate.d.ts +2 -2
  18. package/dist/BoxManager.d.ts +6 -3
  19. package/dist/BuiltinApps.d.ts +5 -0
  20. package/dist/ContainerResizeObserver.d.ts +10 -0
  21. package/dist/Cursor/Cursor.d.ts +10 -12
  22. package/dist/Cursor/index.d.ts +6 -16
  23. package/dist/Helper.d.ts +7 -0
  24. package/dist/ReconnectRefresher.d.ts +0 -1
  25. package/dist/Register/storage.d.ts +5 -1
  26. package/dist/Utils/AppCreateQueue.d.ts +11 -0
  27. package/dist/Utils/Common.d.ts +7 -2
  28. package/dist/Utils/Reactive.d.ts +1 -1
  29. package/dist/Utils/RoomHacker.d.ts +3 -3
  30. package/dist/{MainView.d.ts → View/MainView.d.ts} +5 -6
  31. package/dist/View/ViewManager.d.ts +13 -0
  32. package/dist/constants.d.ts +5 -7
  33. package/dist/index.d.ts +36 -14
  34. package/dist/index.es.js +41 -1
  35. package/dist/index.es.js.map +1 -1
  36. package/dist/index.umd.js +41 -1
  37. package/dist/index.umd.js.map +1 -1
  38. package/dist/style.css +1 -1
  39. package/dist/typings.d.ts +3 -2
  40. package/docs/advanced.md +39 -0
  41. package/docs/api.md +69 -6
  42. package/docs/concept.md +9 -0
  43. package/docs/replay.md +40 -0
  44. package/package.json +7 -6
  45. package/src/App/MagixEvent/index.ts +68 -0
  46. package/src/App/Storage/StorageEvent.ts +21 -0
  47. package/src/App/Storage/index.ts +289 -0
  48. package/src/App/Storage/typings.ts +23 -0
  49. package/src/App/Storage/utils.ts +17 -0
  50. package/src/AppContext.ts +69 -24
  51. package/src/AppListener.ts +28 -16
  52. package/src/AppManager.ts +261 -83
  53. package/src/AppProxy.ts +53 -64
  54. package/src/AttributesDelegate.ts +2 -2
  55. package/src/BoxManager.ts +40 -24
  56. package/src/BuiltinApps.ts +23 -0
  57. package/src/ContainerResizeObserver.ts +62 -0
  58. package/src/Cursor/Cursor.svelte +25 -21
  59. package/src/Cursor/Cursor.ts +25 -38
  60. package/src/Cursor/icons.ts +2 -0
  61. package/src/Cursor/index.ts +45 -139
  62. package/src/Helper.ts +41 -0
  63. package/src/ReconnectRefresher.ts +0 -5
  64. package/src/Register/index.ts +25 -16
  65. package/src/Register/loader.ts +2 -2
  66. package/src/Register/storage.ts +6 -1
  67. package/src/Utils/AppCreateQueue.ts +54 -0
  68. package/src/Utils/Common.ts +69 -14
  69. package/src/Utils/Reactive.ts +9 -3
  70. package/src/Utils/RoomHacker.ts +44 -14
  71. package/src/{MainView.ts → View/MainView.ts} +25 -36
  72. package/src/View/ViewManager.ts +52 -0
  73. package/src/constants.ts +6 -4
  74. package/src/image/laser-pointer-cursor.svg +17 -0
  75. package/src/index.ts +168 -101
  76. package/src/shim.d.ts +5 -0
  77. package/src/style.css +7 -1
  78. package/src/typings.ts +3 -2
  79. package/vite.config.js +8 -2
  80. package/dist/Base/Context.d.ts +0 -13
  81. package/dist/Base/index.d.ts +0 -7
  82. package/dist/Utils/CameraStore.d.ts +0 -15
  83. package/dist/ViewManager.d.ts +0 -29
  84. package/dist/sdk.d.ts +0 -14
  85. package/src/Base/Context.ts +0 -49
  86. package/src/Base/index.ts +0 -10
  87. package/src/Utils/CameraStore.ts +0 -72
  88. package/src/sdk.ts +0 -39
  89. package/src/viewManager.ts +0 -177
@@ -0,0 +1,289 @@
1
+ import type { AkkoObjectUpdatedProperty } from "white-web-sdk";
2
+ import { get, has, mapValues, isObject, size, noop } from "lodash";
3
+ import { SideEffectManager } from "side-effect-manager";
4
+ import type { AppContext } from "../../AppContext";
5
+ import { safeListenPropsUpdated } from "../../Utils/Reactive";
6
+ import { isRef, makeRef, plainObjectKeys } from "./utils";
7
+ import type { Diff, MaybeRefValue, RefValue, StorageStateChangedEvent, StorageStateChangedListener, StorageStateChangedListenerDisposer } from "./typings";
8
+ import { StorageEvent } from "./StorageEvent";
9
+
10
+ export * from './typings';
11
+
12
+ export const STORAGE_NS = "_WM-STORAGE_";
13
+
14
+ export class Storage<TState extends Record<string, any> = any> implements Storage<TState> {
15
+ readonly id: string | null;
16
+
17
+ private readonly _context: AppContext;
18
+ private readonly _sideEffect = new SideEffectManager();
19
+ private _state: TState;
20
+ private _destroyed = false;
21
+
22
+ private _refMap = new WeakMap<any, RefValue>();
23
+
24
+ /**
25
+ * `setState` alters local state immediately before sending to server. This will cache the old value for onStateChanged diffing.
26
+ */
27
+ private _lastValue = new Map<string | number | symbol, TState[Extract<keyof TState, string>]>();
28
+
29
+ constructor(context: AppContext, id?: string, defaultState?: TState) {
30
+ if (defaultState && !isObject(defaultState)) {
31
+ throw new Error(`Default state for Storage ${id} is not an object.`);
32
+ }
33
+
34
+ this._context = context;
35
+ this.id = id || null;
36
+
37
+ this._state = {} as TState;
38
+ const rawState = this._getRawState(this._state);
39
+
40
+ if (this.id !== null && this._context.getIsWritable()) {
41
+ if (rawState === this._state || !isObject(rawState)) {
42
+ if (!get(this._context.getAttributes(), [STORAGE_NS])) {
43
+ this._context.updateAttributes([STORAGE_NS], {});
44
+ }
45
+ this._context.updateAttributes([STORAGE_NS, this.id], this._state);
46
+ }
47
+ if (defaultState) {
48
+ this.setState(defaultState);
49
+ }
50
+ }
51
+
52
+ // strip mobx
53
+ plainObjectKeys(rawState).forEach(key => {
54
+ if (this.id === null && key === STORAGE_NS) {
55
+ return;
56
+ }
57
+ try {
58
+ const rawValue = isObject(rawState[key]) ? JSON.parse(JSON.stringify(rawState[key])) : rawState[key];
59
+ if (isRef<TState[Extract<keyof TState, string>]>(rawValue)) {
60
+ this._state[key] = rawValue.v;
61
+ if (isObject(rawValue.v)) {
62
+ this._refMap.set(rawValue.v, rawValue);
63
+ }
64
+ } else {
65
+ this._state[key] = rawValue;
66
+ }
67
+ } catch (e) {
68
+ console.error(e);
69
+ }
70
+ });
71
+
72
+ this._sideEffect.addDisposer(
73
+ safeListenPropsUpdated(
74
+ () => this.id === null ? context.getAttributes() : get(context.getAttributes(), [STORAGE_NS, this.id]),
75
+ this._updateProperties.bind(this),
76
+ this.destroy.bind(this)
77
+ )
78
+ );
79
+ }
80
+
81
+ get state(): Readonly<TState> {
82
+ if (this._destroyed) {
83
+ console.warn(`Accessing state on destroyed Storage "${this.id}"`)
84
+ }
85
+ return this._state;
86
+ }
87
+
88
+ readonly onStateChanged = new StorageEvent<StorageStateChangedEvent<TState>>();
89
+
90
+ addStateChangedListener(handler: StorageStateChangedListener<TState>): StorageStateChangedListenerDisposer {
91
+ this.onStateChanged.addListener(handler);
92
+ return () => this.onStateChanged.removeListener(handler);
93
+ }
94
+
95
+ ensureState(state: Partial<TState>): void {
96
+ return this.setState(
97
+ plainObjectKeys(state).reduce((payload, key) => {
98
+ if (!has(this._state, key)) {
99
+ payload[key] = state[key];
100
+ }
101
+ return payload;
102
+ }, {} as Partial<TState>)
103
+ );
104
+ }
105
+
106
+ setState(state: Partial<TState>): void {
107
+ if (this._destroyed) {
108
+ console.error(new Error(`Cannot call setState on destroyed Storage "${this.id}".`));
109
+ return;
110
+ }
111
+
112
+ if (!this._context.getIsWritable()) {
113
+ console.error(new Error(`Cannot setState on Storage "${this.id}" without writable access`), state);
114
+ return;
115
+ }
116
+
117
+ const keys = plainObjectKeys(state);
118
+ if (keys.length > 0) {
119
+ keys.forEach(key => {
120
+ const value = state[key];
121
+ if (value === this._state[key]) {
122
+ return;
123
+ }
124
+
125
+ if (value === void 0) {
126
+ this._lastValue.set(key, this._state[key]);
127
+ delete this._state[key];
128
+ this._setRawState(key, value);
129
+ } else {
130
+ this._lastValue.set(key, this._state[key]);
131
+ this._state[key] = value as TState[Extract<keyof TState, string>];
132
+
133
+ let payload: MaybeRefValue<typeof value> = value;
134
+ if (isObject(value)) {
135
+ let refValue = this._refMap.get(value);
136
+ if (!refValue) {
137
+ refValue = makeRef(value);
138
+ this._refMap.set(value, refValue);
139
+ }
140
+ payload = refValue;
141
+ }
142
+
143
+ this._setRawState(key, payload)
144
+ }
145
+ });
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Empty storage data.
151
+ */
152
+ emptyStorage(): void {
153
+ if (size(this._state) <= 0) {
154
+ return;
155
+ }
156
+
157
+ if (this._destroyed) {
158
+ console.error(new Error(`Cannot empty destroyed Storage "${this.id}".`));
159
+ return;
160
+ }
161
+
162
+ if (!this._context.getIsWritable()) {
163
+ console.error(new Error(`Cannot empty Storage "${this.id}" without writable access.`));
164
+ return;
165
+ }
166
+
167
+ this.setState(mapValues(this._state, noop as () => undefined));
168
+ }
169
+
170
+ /**
171
+ * Delete storage index with all of its data and destroy the Storage instance.
172
+ */
173
+ deleteStorage(): void {
174
+ if (this.id === null) {
175
+ throw new Error(`Cannot delete main Storage`);
176
+ }
177
+
178
+ if (!this._context.getIsWritable()) {
179
+ console.error(new Error(`Cannot delete Storage "${this.id}" without writable access.`));
180
+ return;
181
+ }
182
+
183
+ this.destroy();
184
+
185
+ this._context.updateAttributes([STORAGE_NS, this.id], void 0);
186
+ }
187
+
188
+ get destroyed(): boolean {
189
+ return this._destroyed;
190
+ }
191
+
192
+ /**
193
+ * Destroy the Storage instance. The data will be kept.
194
+ */
195
+ destroy() {
196
+ this._destroyed = true;
197
+ this._sideEffect.flushAll();
198
+ }
199
+
200
+ private _getRawState(): TState | undefined
201
+ private _getRawState(defaultValue: TState): TState
202
+ private _getRawState(defaultValue?: TState): TState | undefined {
203
+ if (this.id === null) {
204
+ return get(this._context.getAttributes(), [], defaultValue);
205
+ } else {
206
+ return get(this._context.getAttributes(), [STORAGE_NS, this.id], defaultValue);
207
+ }
208
+ }
209
+
210
+ private _setRawState(key: string, value: any): void {
211
+ if (this.id === null) {
212
+ if (key === STORAGE_NS) {
213
+ throw new Error(`Cannot set attribute internal filed "${STORAGE_NS}"`)
214
+ }
215
+ return this._context.updateAttributes([key], value);
216
+ } else {
217
+ return this._context.updateAttributes([STORAGE_NS, this.id, key], value);
218
+ }
219
+ }
220
+
221
+ private _updateProperties(actions: ReadonlyArray<AkkoObjectUpdatedProperty<TState, string>>): void {
222
+ if (this._destroyed) {
223
+ console.error(new Error(`Cannot call _updateProperties on destroyed Storage "${this.id}".`));
224
+ return;
225
+ }
226
+
227
+ if (actions.length > 0) {
228
+ const diffs: Diff<TState> = {};
229
+
230
+ for (let i = 0; i < actions.length; i++) {
231
+ try {
232
+ const action = actions[i]
233
+ const key = action.key as Extract<keyof TState, string>;
234
+
235
+ if (this.id === null && key === STORAGE_NS) {
236
+ continue
237
+ }
238
+
239
+ const value = isObject(action.value) ? JSON.parse(JSON.stringify(action.value)) : action.value;
240
+ let oldValue: TState[Extract<keyof TState, string>] | undefined;
241
+ if (this._lastValue.has(key)) {
242
+ oldValue = this._lastValue.get(key);
243
+ this._lastValue.delete(key);
244
+ }
245
+
246
+ switch (action.kind) {
247
+ case 2: {
248
+ // Removed
249
+ if (has(this._state, key)) {
250
+ oldValue = this._state[key];
251
+ delete this._state[key];
252
+ }
253
+ diffs[key] = { oldValue };
254
+ break;
255
+ }
256
+ default: {
257
+ let newValue = value;
258
+
259
+ if (isRef<TState[Extract<keyof TState, string>]>(value)) {
260
+ const { k, v } = value;
261
+ const curValue = this._state[key];
262
+ if (isObject(curValue) && this._refMap.get(curValue)?.k === k) {
263
+ newValue = curValue;
264
+ } else {
265
+ newValue = v;
266
+ if (isObject(v)) {
267
+ this._refMap.set(v, value);
268
+ }
269
+ }
270
+ }
271
+
272
+ if (newValue !== this._state[key]) {
273
+ oldValue = this._state[key];
274
+ this._state[key] = newValue;
275
+ }
276
+
277
+ diffs[key] = { newValue, oldValue };
278
+ break;
279
+ }
280
+ }
281
+ } catch (e) {
282
+ console.error(e)
283
+ }
284
+ }
285
+
286
+ this.onStateChanged.dispatch(diffs);
287
+ }
288
+ }
289
+ }
@@ -0,0 +1,23 @@
1
+ import type { StorageEventListener } from "./StorageEvent";
2
+
3
+ export type RefValue<TValue = any> = { k: string; v: TValue; __isRef: true };
4
+
5
+ export type ExtractRawValue<TValue> = TValue extends RefValue<infer TRefValue> ? TRefValue : TValue;
6
+
7
+ export type AutoRefValue<TValue> = RefValue<ExtractRawValue<TValue>>;
8
+
9
+ export type MaybeRefValue<TValue> = TValue | AutoRefValue<TValue>;
10
+
11
+ export type DiffOne<T> = { oldValue?: T; newValue?: T };
12
+
13
+ export type Diff<T> = { [K in keyof T]?: DiffOne<T[K]> };
14
+
15
+ export type StorageOnSetStatePayload<TState = unknown> = {
16
+ [K in keyof TState]?: MaybeRefValue<TState[K]>;
17
+ };
18
+
19
+ export type StorageStateChangedEvent<TState = any> = Diff<TState>;
20
+
21
+ export type StorageStateChangedListener<TState = any> = StorageEventListener<StorageStateChangedEvent<TState>>;
22
+
23
+ export type StorageStateChangedListenerDisposer = () => void;
@@ -0,0 +1,17 @@
1
+ import { has } from "lodash";
2
+ import { genUID } from "side-effect-manager";
3
+ import type { AutoRefValue, ExtractRawValue, RefValue } from "./typings";
4
+
5
+ export const plainObjectKeys = Object.keys as <T>(o: T) => Array<Extract<keyof T, string>>;
6
+
7
+ export function isRef<TValue = unknown>(e: unknown): e is RefValue<TValue> {
8
+ return Boolean(has(e, '__isRef'));
9
+ }
10
+
11
+ export function makeRef<TValue>(v: TValue): RefValue<TValue> {
12
+ return { k: genUID(), v, __isRef: true };
13
+ }
14
+
15
+ export function makeAutoRef<TValue>(v: TValue): AutoRefValue<TValue> {
16
+ return isRef<ExtractRawValue<TValue>>(v) ? v : makeRef(v as ExtractRawValue<TValue>);
17
+ }
package/src/AppContext.ts CHANGED
@@ -6,18 +6,20 @@ import {
6
6
  unlistenDisposed,
7
7
  unlistenUpdated,
8
8
  toJS
9
- } from 'white-web-sdk';
9
+ } from 'white-web-sdk';
10
10
  import { BoxNotCreatedError } from './Utils/error';
11
- import type { Room, SceneDefinition, View } from "white-web-sdk";
11
+ import type { Room, SceneDefinition, View, EventListener as WhiteEventListener } from "white-web-sdk";
12
12
  import type { ReadonlyTeleBox } from "@netless/telebox-insider";
13
13
  import type Emittery from "emittery";
14
14
  import type { BoxManager } from "./BoxManager";
15
15
  import type { AppEmitterEvent } from "./index";
16
16
  import type { AppManager } from "./AppManager";
17
17
  import type { AppProxy } from "./AppProxy";
18
+ import { Storage } from './App/Storage';
19
+ import type { MagixEventAddListener, MagixEventDispatcher, MagixEventRemoveListener } from './App/MagixEvent';
18
20
 
19
- export class AppContext<TAttrs extends Record<string, any>, AppOptions = any> {
20
- public readonly emitter: Emittery<AppEmitterEvent<TAttrs>>;
21
+ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOptions = any> {
22
+ public readonly emitter: Emittery<AppEmitterEvent<TAttributes>>;
21
23
  public readonly mobxUtils = {
22
24
  autorun,
23
25
  reaction,
@@ -39,45 +41,45 @@ export class AppContext<TAttrs extends Record<string, any>, AppOptions = any> {
39
41
  private boxManager: BoxManager,
40
42
  public appId: string,
41
43
  private appProxy: AppProxy,
42
- private appOptions?: AppOptions | (() => AppOptions),
44
+ private appOptions?: TAppOptions | (() => TAppOptions),
43
45
  ) {
44
46
  this.emitter = appProxy.appEmitter;
45
47
  this.isAddApp = appProxy.isAddApp;
46
48
  }
47
49
 
48
- public getDisplayer() {
50
+ public getDisplayer = () => {
49
51
  return this.manager.displayer;
50
52
  }
51
53
 
52
- public getAttributes(): TAttrs | undefined {
54
+ /** @deprecated Use context.storage.state instead. */
55
+ public getAttributes = (): TAttributes | undefined => {
53
56
  return this.appProxy.attributes;
54
57
  }
55
58
 
56
- public getScenes(): SceneDefinition[] | undefined {
59
+ public getScenes = (): SceneDefinition[] | undefined => {
57
60
  const appAttr = this.store.getAppAttributes(this.appId);
58
61
  if (appAttr?.isDynamicPPT) {
59
- const appProxy = this.manager.appProxies.get(this.appId);
60
- if (appProxy) {
61
- return appProxy.scenes;
62
- }
62
+ return this.appProxy.scenes;
63
63
  } else {
64
64
  return appAttr?.options["scenes"];
65
65
  }
66
66
  }
67
67
 
68
- public getView(): View | undefined {
68
+ public getView = (): View | undefined => {
69
69
  return this.appProxy.view;
70
70
  }
71
71
 
72
- public getInitScenePath() {
72
+ public getInitScenePath = () => {
73
73
  return this.manager.getAppInitPath(this.appId);
74
74
  }
75
75
 
76
- public getIsWritable(): boolean {
76
+ /** Get App writable status. */
77
+ public getIsWritable = (): boolean => {
77
78
  return this.manager.canOperate;
78
79
  }
79
80
 
80
- public getBox(): ReadonlyTeleBox {
81
+ /** Get the App Window UI box. */
82
+ public getBox = (): ReadonlyTeleBox => {
81
83
  const box = this.boxManager.getBox(this.appId);
82
84
  if (box) {
83
85
  return box;
@@ -86,27 +88,30 @@ export class AppContext<TAttrs extends Record<string, any>, AppOptions = any> {
86
88
  }
87
89
  }
88
90
 
89
- public getRoom(): Room | undefined {
91
+ public getRoom = (): Room | undefined => {
90
92
  return this.manager.room;
91
93
  }
92
94
 
93
- public setAttributes(attributes: TAttrs) {
95
+ /** @deprecated Use context.storage.setState instead. */
96
+ public setAttributes = (attributes: TAttributes) => {
94
97
  this.manager.safeSetAttributes({ [this.appId]: attributes });
95
98
  }
96
99
 
97
- public updateAttributes(keys: string[], value: any) {
100
+ /** @deprecated Use context.storage.setState instead. */
101
+ public updateAttributes = (keys: string[], value: any) => {
98
102
  if (this.manager.attributes[this.appId]) {
99
103
  this.manager.safeUpdateAttributes([this.appId, ...keys], value);
100
104
  }
101
105
  }
102
106
 
103
- public async setScenePath(scenePath: string): Promise<void> {
107
+ public setScenePath = async (scenePath: string): Promise<void> => {
104
108
  if (!this.appProxy.box) return;
105
109
  this.appProxy.setFullPath(scenePath);
106
- this.appProxy.context.switchAppToWriter(this.appId);
110
+ // 兼容 15 版本 SDK 的切页
111
+ this.getRoom()?.setScenePath(scenePath);
107
112
  }
108
113
 
109
- public mountView(dom: HTMLDivElement): void {
114
+ public mountView = (dom: HTMLDivElement): void => {
110
115
  const view = this.getView();
111
116
  if (view) {
112
117
  view.divElement = dom;
@@ -117,7 +122,47 @@ export class AppContext<TAttrs extends Record<string, any>, AppOptions = any> {
117
122
  }
118
123
  }
119
124
 
120
- public getAppOptions(): AppOptions | undefined {
121
- return typeof this.appOptions === 'function' ? (this.appOptions as () => AppOptions)() : this.appOptions
125
+ /** Get the local App options. */
126
+ public getAppOptions = (): TAppOptions | undefined => {
127
+ return typeof this.appOptions === 'function' ? (this.appOptions as () => TAppOptions)() : this.appOptions
122
128
  }
129
+
130
+ private _storage?: Storage<TAttributes>
131
+
132
+ /** Main Storage for attributes. */
133
+ public get storage(): Storage<TAttributes> {
134
+ if (!this._storage) {
135
+ this._storage = new Storage(this);
136
+ }
137
+ return this._storage;
138
+ }
139
+
140
+ /**
141
+ * Create separated storages for flexible state management.
142
+ * @param storeId Namespace for the storage. Storages of the same namespace share the same data.
143
+ * @param defaultState Default state for initial storage creation.
144
+ * @returns
145
+ */
146
+ public createStorage = <TState>(storeId: string, defaultState?: TState): Storage<TState> => {
147
+ const storage = new Storage(this, storeId, defaultState);
148
+ this.emitter.on("destroy", () => {
149
+ storage.destroy();
150
+ });
151
+ return storage;
152
+ }
153
+
154
+ /** Dispatch events to other clients (and self). */
155
+ public dispatchMagixEvent: MagixEventDispatcher<TMagixEventPayloads> = (...args) => {
156
+ // can't dispatch events on replay mode
157
+ return this.manager.room?.dispatchMagixEvent(...args);
158
+ }
159
+
160
+ /** Listen to events from others clients (and self messages). */
161
+ public addMagixEventListener: MagixEventAddListener<TMagixEventPayloads> = (event, handler, options) => {
162
+ this.manager.displayer.addMagixEventListener(event, handler as WhiteEventListener, options);
163
+ return () => this.manager.displayer.removeMagixEventListener(event, handler as WhiteEventListener);
164
+ }
165
+
166
+ /** Remove a Magix event listener. */
167
+ public removeMagixEventListener = this.manager.displayer.removeMagixEventListener.bind(this.manager.displayer) as MagixEventRemoveListener<TMagixEventPayloads>
123
168
  }
@@ -1,10 +1,10 @@
1
- import { callbacks } from './index';
2
- import { Events, MagixEventName } from './constants';
3
- import type { Event } from "white-web-sdk";
1
+ import { callbacks, emitter } from "./index";
2
+ import { Events, MagixEventName } from "./constants";
3
+ import { isEqual, omit } from "lodash";
4
+ import { setViewFocusScenePath } from "./Utils/Common";
5
+ import type { AnimationMode, Camera, Event } from "white-web-sdk";
4
6
  import type { AppManager } from "./AppManager";
5
7
  import type { TeleBoxState } from "@netless/telebox-insider";
6
- import { setViewFocusScenePath } from './Utils/Common';
7
-
8
8
  export class AppListeners {
9
9
  private displayer = this.manager.displayer;
10
10
 
@@ -34,10 +34,6 @@ export class AppListeners {
34
34
  this.appResizeHandler(data.payload);
35
35
  break;
36
36
  }
37
- case Events.SwitchViewsToFreedom: {
38
- this.switchViewsToFreedomHandler();
39
- break;
40
- }
41
37
  case Events.AppBoxStateChange: {
42
38
  this.boxStateChangeHandler(data.payload);
43
39
  break;
@@ -46,10 +42,18 @@ export class AppListeners {
46
42
  this.setMainViewScenePathHandler(data.payload);
47
43
  break;
48
44
  }
45
+ case Events.MoveCamera: {
46
+ this.moveCameraHandler(data.payload);
47
+ break;
48
+ }
49
49
  case Events.MoveCameraToContain: {
50
50
  this.moveCameraToContainHandler(data.payload);
51
51
  break;
52
52
  }
53
+ case Events.CursorMove: {
54
+ this.cursorMoveHandler(data.payload);
55
+ break;
56
+ }
53
57
  default:
54
58
  break;
55
59
  }
@@ -65,19 +69,27 @@ export class AppListeners {
65
69
  this.manager.room?.refreshViewSize();
66
70
  };
67
71
 
68
- private switchViewsToFreedomHandler = () => {
69
- this.manager.viewManager.freedomAllViews();
70
- };
71
-
72
72
  private boxStateChangeHandler = (state: TeleBoxState) => {
73
73
  callbacks.emit("boxStateChange", state);
74
- }
74
+ };
75
75
 
76
76
  private setMainViewScenePathHandler = ({ nextScenePath }: { nextScenePath: string }) => {
77
77
  setViewFocusScenePath(this.manager.mainView, nextScenePath);
78
- }
78
+ callbacks.emit("mainViewScenePathChange", nextScenePath);
79
+ };
80
+
81
+ private moveCameraHandler = (
82
+ payload: Camera & { animationMode?: AnimationMode | undefined }
83
+ ) => {
84
+ if (isEqual(omit(payload, ["animationMode"]), { ...this.manager.mainView.camera })) return;
85
+ this.manager.mainView.moveCamera(payload);
86
+ };
79
87
 
80
88
  private moveCameraToContainHandler = (payload: any) => {
81
89
  this.manager.mainView.moveCameraToContain(payload);
82
- }
90
+ };
91
+
92
+ private cursorMoveHandler = (payload: any) => {
93
+ emitter.emit("cursorMove", payload);
94
+ };
83
95
  }