@netless/window-manager 0.4.0-canary.10 → 0.4.0-canary.11
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/MagixEvent/index.d.ts +28 -0
- package/dist/App/Storage/index.d.ts +17 -5
- package/dist/AppContext.d.ts +39 -17
- package/dist/AppManager.d.ts +1 -0
- package/dist/BoxManager.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- 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/typings.d.ts +2 -2
- package/package.json +1 -1
- package/src/App/MagixEvent/index.ts +66 -0
- package/src/App/Storage/index.ts +83 -42
- package/src/AppContext.ts +51 -19
- package/src/AppManager.ts +11 -0
- package/src/index.ts +1 -0
- package/src/typings.ts +2 -2
package/dist/typings.d.ts
CHANGED
@@ -2,7 +2,7 @@ import type Emittery from "emittery";
|
|
2
2
|
import type { AnimationMode, Displayer, DisplayerState, Player, Room, SceneDefinition, SceneState, View } from "white-web-sdk";
|
3
3
|
import type { AppContext } from "./AppContext";
|
4
4
|
import type { ReadonlyTeleBox, TeleBoxRect } from "@netless/telebox-insider";
|
5
|
-
export interface NetlessApp<Attributes = any,
|
5
|
+
export interface NetlessApp<Attributes = any, MagixEventPayloads = any, AppOptions = any, SetupResult = any> {
|
6
6
|
kind: string;
|
7
7
|
config?: {
|
8
8
|
/** Box width relative to whiteboard. 0~1. Default 0.5. */
|
@@ -16,7 +16,7 @@ export interface NetlessApp<Attributes = any, SetupResult = any, AppOptions = an
|
|
16
16
|
/** App only single instance. */
|
17
17
|
singleton?: boolean;
|
18
18
|
};
|
19
|
-
setup: (context: AppContext<Attributes, AppOptions>) => SetupResult;
|
19
|
+
setup: (context: AppContext<Attributes, MagixEventPayloads, AppOptions>) => SetupResult;
|
20
20
|
}
|
21
21
|
export declare type AppEmitterEvent<T = any> = {
|
22
22
|
/**
|
package/package.json
CHANGED
@@ -0,0 +1,66 @@
|
|
1
|
+
import type {
|
2
|
+
MagixEventListenerOptions as WhiteMagixListenerOptions,
|
3
|
+
Event as WhiteEvent,
|
4
|
+
EventPhase as WhiteEventPhase,
|
5
|
+
Scope as WhiteScope,
|
6
|
+
} from "white-web-sdk";
|
7
|
+
|
8
|
+
export interface MagixEventListenerOptions extends WhiteMagixListenerOptions {
|
9
|
+
/**
|
10
|
+
* Rapid emitted callbacks will be slowed down to this interval (in ms).
|
11
|
+
*/
|
12
|
+
fireInterval?: number;
|
13
|
+
/**
|
14
|
+
* If `true`, sent events will reach self-listeners after committed to server.
|
15
|
+
* Otherwise the events will reach self-listeners immediately.
|
16
|
+
*/
|
17
|
+
fireSelfEventAfterCommit?: boolean;
|
18
|
+
}
|
19
|
+
|
20
|
+
export interface MagixEventMessage<
|
21
|
+
TPayloads = any,
|
22
|
+
TEvent extends MagixEventTypes<TPayloads> = MagixEventTypes<TPayloads>
|
23
|
+
> extends Omit<WhiteEvent, "scope" | "phase"> {
|
24
|
+
/** Event name */
|
25
|
+
event: TEvent;
|
26
|
+
/** Event Payload */
|
27
|
+
payload: TPayloads[TEvent];
|
28
|
+
/** Whiteboard ID of the client who dispatched the event. It will be AdminObserverId for system events. */
|
29
|
+
authorId: number;
|
30
|
+
scope: `${WhiteScope}`;
|
31
|
+
phase: `${WhiteEventPhase}`;
|
32
|
+
}
|
33
|
+
|
34
|
+
export type MagixEventTypes<TPayloads = any> = Extract<keyof TPayloads, string>;
|
35
|
+
|
36
|
+
export type MagixEventPayload<
|
37
|
+
TPayloads = any,
|
38
|
+
TEvent extends MagixEventTypes<TPayloads> = MagixEventTypes<TPayloads>
|
39
|
+
> = TPayloads[TEvent];
|
40
|
+
|
41
|
+
export type MagixEventDispatcher<TPayloads = any> = <
|
42
|
+
TEvent extends MagixEventTypes<TPayloads> = MagixEventTypes<TPayloads>
|
43
|
+
>(
|
44
|
+
event: TEvent,
|
45
|
+
payload: TPayloads[TEvent]
|
46
|
+
) => void;
|
47
|
+
|
48
|
+
export type MagixEventHandler<
|
49
|
+
TPayloads = any,
|
50
|
+
TEvent extends MagixEventTypes<TPayloads> = MagixEventTypes<TPayloads>
|
51
|
+
> = (message: MagixEventMessage<TPayloads, TEvent>) => void;
|
52
|
+
|
53
|
+
export type MagixEventAddListener<TPayloads = any> = <
|
54
|
+
TEvent extends MagixEventTypes<TPayloads> = MagixEventTypes<TPayloads>
|
55
|
+
>(
|
56
|
+
event: TEvent,
|
57
|
+
handler: MagixEventHandler<TPayloads, TEvent>,
|
58
|
+
options?: MagixEventListenerOptions | undefined
|
59
|
+
) => void;
|
60
|
+
|
61
|
+
export type MagixEventRemoveListener<TPayloads = any> = <
|
62
|
+
TEvent extends MagixEventTypes<TPayloads> = MagixEventTypes<TPayloads>
|
63
|
+
>(
|
64
|
+
event: TEvent,
|
65
|
+
handler?: MagixEventHandler<TPayloads, TEvent>
|
66
|
+
) => void;
|
package/src/App/Storage/index.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import type { AkkoObjectUpdatedProperty } from "white-web-sdk";
|
2
|
-
import { get, has, isObject } from "lodash";
|
2
|
+
import { get, has, mapValues, isObject, size, noop } from "lodash";
|
3
3
|
import { SideEffectManager } from "side-effect-manager";
|
4
4
|
import type { AppContext } from "../../AppContext";
|
5
5
|
import { safeListenPropsUpdated } from "../../Utils/Reactive";
|
@@ -9,12 +9,12 @@ import { StorageEvent } from "./StorageEvent";
|
|
9
9
|
|
10
10
|
export * from './typings';
|
11
11
|
|
12
|
-
const STORAGE_NS = "_WM-STORAGE_";
|
12
|
+
export const STORAGE_NS = "_WM-STORAGE_";
|
13
13
|
|
14
|
-
export class Storage<TState = any> implements Storage<TState> {
|
15
|
-
readonly id: string;
|
14
|
+
export class Storage<TState extends Record<string, any> = any> implements Storage<TState> {
|
15
|
+
readonly id: string | null;
|
16
16
|
|
17
|
-
private readonly _context: AppContext
|
17
|
+
private readonly _context: AppContext;
|
18
18
|
private readonly _sideEffect = new SideEffectManager();
|
19
19
|
private _state: TState;
|
20
20
|
private _destroyed = false;
|
@@ -26,54 +26,52 @@ export class Storage<TState = any> implements Storage<TState> {
|
|
26
26
|
*/
|
27
27
|
private _lastValue = new Map<string | number | symbol, TState[Extract<keyof TState, string>]>();
|
28
28
|
|
29
|
-
constructor(context: AppContext
|
30
|
-
if (id == null) {
|
31
|
-
throw new Error("Cannot create Storage with empty id.");
|
32
|
-
}
|
33
|
-
|
29
|
+
constructor(context: AppContext, id?: string, defaultState?: TState) {
|
34
30
|
if (defaultState && !isObject(defaultState)) {
|
35
31
|
throw new Error(`Default state for Storage ${id} is not an object.`);
|
36
32
|
}
|
37
33
|
|
38
34
|
this._context = context;
|
39
|
-
this.id = id;
|
35
|
+
this.id = id || null;
|
40
36
|
|
41
|
-
const attrs = context.getAttributes();
|
42
37
|
this._state = {} as TState;
|
43
|
-
const rawState =
|
38
|
+
const rawState = this._getRawState(this._state);
|
44
39
|
|
45
|
-
if (this._context.getIsWritable()) {
|
46
|
-
if (
|
47
|
-
if (!
|
40
|
+
if (this.id !== null && this._context.getIsWritable()) {
|
41
|
+
if (rawState === this._state || !isObject(rawState)) {
|
42
|
+
if (!get(this._context.getAttributes(), [STORAGE_NS])) {
|
48
43
|
this._context.updateAttributes([STORAGE_NS], {});
|
49
44
|
}
|
50
45
|
this._context.updateAttributes([STORAGE_NS, this.id], this._state);
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
} else {
|
55
|
-
// strip mobx
|
56
|
-
plainObjectKeys(rawState).forEach(key => {
|
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
|
-
});
|
46
|
+
}
|
47
|
+
if (defaultState) {
|
48
|
+
this.setState(defaultState);
|
71
49
|
}
|
72
50
|
}
|
73
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
|
+
|
74
72
|
this._sideEffect.addDisposer(
|
75
73
|
safeListenPropsUpdated(
|
76
|
-
() => get(context.getAttributes(), [STORAGE_NS, this.id]),
|
74
|
+
() => this.id === null ? context.getAttributes() : get(context.getAttributes(), [STORAGE_NS, this.id]),
|
77
75
|
this._updateProperties.bind(this),
|
78
76
|
this.destroy.bind(this)
|
79
77
|
)
|
@@ -122,7 +120,7 @@ export class Storage<TState = any> implements Storage<TState> {
|
|
122
120
|
if (value === void 0) {
|
123
121
|
this._lastValue.set(key, this._state[key]);
|
124
122
|
delete this._state[key];
|
125
|
-
this.
|
123
|
+
this._setRawState(key, value);
|
126
124
|
} else {
|
127
125
|
this._lastValue.set(key, this._state[key]);
|
128
126
|
this._state[key] = value as TState[Extract<keyof TState, string>];
|
@@ -137,13 +135,20 @@ export class Storage<TState = any> implements Storage<TState> {
|
|
137
135
|
payload = refValue;
|
138
136
|
}
|
139
137
|
|
140
|
-
this.
|
138
|
+
this._setRawState(key, payload)
|
141
139
|
}
|
142
140
|
});
|
143
141
|
}
|
144
142
|
}
|
145
143
|
|
146
|
-
|
144
|
+
/**
|
145
|
+
* Empty storage data.
|
146
|
+
*/
|
147
|
+
emptyStorage(): void {
|
148
|
+
if (size(this._state) <= 0) {
|
149
|
+
return;
|
150
|
+
}
|
151
|
+
|
147
152
|
if (this._destroyed) {
|
148
153
|
console.error(new Error(`Cannot empty destroyed Storage "${this.id}".`));
|
149
154
|
return;
|
@@ -154,10 +159,17 @@ export class Storage<TState = any> implements Storage<TState> {
|
|
154
159
|
return;
|
155
160
|
}
|
156
161
|
|
157
|
-
this.
|
162
|
+
this.setState(mapValues(this._state, noop as () => undefined));
|
158
163
|
}
|
159
164
|
|
160
|
-
|
165
|
+
/**
|
166
|
+
* Delete storage index with all of its data and destroy the Storage instance.
|
167
|
+
*/
|
168
|
+
deleteStorage(): void {
|
169
|
+
if (this.id === null) {
|
170
|
+
throw new Error(`Cannot delete main Storage`);
|
171
|
+
}
|
172
|
+
|
161
173
|
if (!this._context.getIsWritable()) {
|
162
174
|
console.error(new Error(`Cannot delete Storage "${this.id}" without writable access.`));
|
163
175
|
return;
|
@@ -172,11 +184,35 @@ export class Storage<TState = any> implements Storage<TState> {
|
|
172
184
|
return this._destroyed;
|
173
185
|
}
|
174
186
|
|
187
|
+
/**
|
188
|
+
* Destroy the Storage instance. The data will be kept.
|
189
|
+
*/
|
175
190
|
destroy() {
|
176
191
|
this._destroyed = true;
|
177
192
|
this._sideEffect.flushAll();
|
178
193
|
}
|
179
194
|
|
195
|
+
private _getRawState(): TState | undefined
|
196
|
+
private _getRawState(defaultValue: TState): TState
|
197
|
+
private _getRawState(defaultValue?: TState): TState | undefined {
|
198
|
+
if (this.id === null) {
|
199
|
+
return get(this._context.getAttributes(), [], defaultValue);
|
200
|
+
} else {
|
201
|
+
return get(this._context.getAttributes(), [STORAGE_NS, this.id], defaultValue);
|
202
|
+
}
|
203
|
+
}
|
204
|
+
|
205
|
+
private _setRawState(key: string, value: any): void {
|
206
|
+
if (this.id === null) {
|
207
|
+
if (key === STORAGE_NS) {
|
208
|
+
throw new Error(`Cannot set attribute internal filed "${STORAGE_NS}"`)
|
209
|
+
}
|
210
|
+
return this._context.updateAttributes([key], value);
|
211
|
+
} else {
|
212
|
+
return this._context.updateAttributes([STORAGE_NS, this.id, key], value);
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
180
216
|
private _updateProperties(actions: ReadonlyArray<AkkoObjectUpdatedProperty<TState, string>>): void {
|
181
217
|
if (this._destroyed) {
|
182
218
|
console.error(new Error(`Cannot call _updateProperties on destroyed Storage "${this.id}".`));
|
@@ -190,6 +226,11 @@ export class Storage<TState = any> implements Storage<TState> {
|
|
190
226
|
try {
|
191
227
|
const action = actions[i]
|
192
228
|
const key = action.key as Extract<keyof TState, string>;
|
229
|
+
|
230
|
+
if (this.id === null && key === STORAGE_NS) {
|
231
|
+
continue
|
232
|
+
}
|
233
|
+
|
193
234
|
const value = isObject(action.value) ? JSON.parse(JSON.stringify(action.value)) : action.value;
|
194
235
|
let oldValue: TState[Extract<keyof TState, string>] | undefined;
|
195
236
|
if (this._lastValue.has(key)) {
|
package/src/AppContext.ts
CHANGED
@@ -6,7 +6,7 @@ import {
|
|
6
6
|
unlistenDisposed,
|
7
7
|
unlistenUpdated,
|
8
8
|
toJS
|
9
|
-
|
9
|
+
} from 'white-web-sdk';
|
10
10
|
import { BoxNotCreatedError } from './Utils/error';
|
11
11
|
import type { Room, SceneDefinition, View } from "white-web-sdk";
|
12
12
|
import type { ReadonlyTeleBox } from "@netless/telebox-insider";
|
@@ -16,9 +16,10 @@ import type { AppEmitterEvent } from "./index";
|
|
16
16
|
import type { AppManager } from "./AppManager";
|
17
17
|
import type { AppProxy } from "./AppProxy";
|
18
18
|
import { Storage } from './App/Storage';
|
19
|
+
import type { MagixEventAddListener, MagixEventDispatcher, MagixEventRemoveListener } from './App/MagixEvent';
|
19
20
|
|
20
|
-
export class AppContext<
|
21
|
-
public readonly emitter: Emittery<AppEmitterEvent<
|
21
|
+
export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOptions = any> {
|
22
|
+
public readonly emitter: Emittery<AppEmitterEvent<TAttributes>>;
|
22
23
|
public readonly mobxUtils = {
|
23
24
|
autorun,
|
24
25
|
reaction,
|
@@ -40,21 +41,22 @@ export class AppContext<TAttrs extends Record<string, any> = any, AppOptions = a
|
|
40
41
|
private boxManager: BoxManager,
|
41
42
|
public appId: string,
|
42
43
|
private appProxy: AppProxy,
|
43
|
-
private appOptions?:
|
44
|
+
private appOptions?: TAppOptions | (() => TAppOptions),
|
44
45
|
) {
|
45
46
|
this.emitter = appProxy.appEmitter;
|
46
47
|
this.isAddApp = appProxy.isAddApp;
|
47
48
|
}
|
48
49
|
|
49
|
-
public getDisplayer() {
|
50
|
+
public getDisplayer = () => {
|
50
51
|
return this.manager.displayer;
|
51
52
|
}
|
52
53
|
|
53
|
-
|
54
|
+
/** @deprecated Use context.storage.state instead. */
|
55
|
+
public getAttributes = (): TAttributes | undefined => {
|
54
56
|
return this.appProxy.attributes;
|
55
57
|
}
|
56
58
|
|
57
|
-
public getScenes(): SceneDefinition[] | undefined {
|
59
|
+
public getScenes = (): SceneDefinition[] | undefined => {
|
58
60
|
const appAttr = this.store.getAppAttributes(this.appId);
|
59
61
|
if (appAttr?.isDynamicPPT) {
|
60
62
|
const appProxy = this.manager.appProxies.get(this.appId);
|
@@ -66,19 +68,21 @@ export class AppContext<TAttrs extends Record<string, any> = any, AppOptions = a
|
|
66
68
|
}
|
67
69
|
}
|
68
70
|
|
69
|
-
public getView(): View | undefined {
|
71
|
+
public getView = (): View | undefined => {
|
70
72
|
return this.appProxy.view;
|
71
73
|
}
|
72
74
|
|
73
|
-
public getInitScenePath() {
|
75
|
+
public getInitScenePath = () => {
|
74
76
|
return this.manager.getAppInitPath(this.appId);
|
75
77
|
}
|
76
78
|
|
77
|
-
|
79
|
+
/** Get App writable status. */
|
80
|
+
public getIsWritable = (): boolean => {
|
78
81
|
return this.manager.canOperate;
|
79
82
|
}
|
80
83
|
|
81
|
-
|
84
|
+
/** Get the App Window UI box. */
|
85
|
+
public getBox = (): ReadonlyTeleBox => {
|
82
86
|
const box = this.boxManager.getBox(this.appId);
|
83
87
|
if (box) {
|
84
88
|
return box;
|
@@ -87,26 +91,28 @@ export class AppContext<TAttrs extends Record<string, any> = any, AppOptions = a
|
|
87
91
|
}
|
88
92
|
}
|
89
93
|
|
90
|
-
public getRoom(): Room | undefined {
|
94
|
+
public getRoom = (): Room | undefined => {
|
91
95
|
return this.manager.room;
|
92
96
|
}
|
93
97
|
|
94
|
-
|
98
|
+
/** @deprecated Use context.storage.setState instead. */
|
99
|
+
public setAttributes = (attributes: TAttributes) => {
|
95
100
|
this.manager.safeSetAttributes({ [this.appId]: attributes });
|
96
101
|
}
|
97
102
|
|
98
|
-
|
103
|
+
/** @deprecated Use context.storage.setState instead. */
|
104
|
+
public updateAttributes = (keys: string[], value: any) => {
|
99
105
|
if (this.manager.attributes[this.appId]) {
|
100
106
|
this.manager.safeUpdateAttributes([this.appId, ...keys], value);
|
101
107
|
}
|
102
108
|
}
|
103
109
|
|
104
|
-
public async
|
110
|
+
public setScenePath = async (scenePath: string): Promise<void> => {
|
105
111
|
if (!this.appProxy.box) return;
|
106
112
|
this.appProxy.setFullPath(scenePath);
|
107
113
|
}
|
108
114
|
|
109
|
-
public mountView(dom: HTMLDivElement): void {
|
115
|
+
public mountView = (dom: HTMLDivElement): void => {
|
110
116
|
const view = this.getView();
|
111
117
|
if (view) {
|
112
118
|
view.divElement = dom;
|
@@ -117,15 +123,41 @@ export class AppContext<TAttrs extends Record<string, any> = any, AppOptions = a
|
|
117
123
|
}
|
118
124
|
}
|
119
125
|
|
120
|
-
|
121
|
-
|
126
|
+
/** Get the local App options. */
|
127
|
+
public getAppOptions = (): TAppOptions | undefined => {
|
128
|
+
return typeof this.appOptions === 'function' ? (this.appOptions as () => TAppOptions)() : this.appOptions
|
122
129
|
}
|
123
130
|
|
124
|
-
|
131
|
+
private _storage?: Storage<TAttributes>
|
132
|
+
|
133
|
+
/** Main Storage for attributes. */
|
134
|
+
public get storage(): Storage<TAttributes> {
|
135
|
+
if (!this._storage) {
|
136
|
+
this._storage = new Storage(this);
|
137
|
+
}
|
138
|
+
return this._storage;
|
139
|
+
}
|
140
|
+
|
141
|
+
/**
|
142
|
+
* Create separated storages for flexible state management.
|
143
|
+
* @param storeId Namespace for the storage. Storages of the same namespace share the same data.
|
144
|
+
* @param defaultState Default state for initial storage creation.
|
145
|
+
* @returns
|
146
|
+
*/
|
147
|
+
public createStorage = <TState>(storeId: string, defaultState?: TState): Storage<TState> => {
|
125
148
|
const storage = new Storage(this, storeId, defaultState);
|
126
149
|
this.emitter.on("destroy", () => {
|
127
150
|
storage.destroy();
|
128
151
|
});
|
129
152
|
return storage;
|
130
153
|
}
|
154
|
+
|
155
|
+
/** Dispatch events to other clients (and self). */
|
156
|
+
public dispatchMagixEvent: MagixEventDispatcher<TMagixEventPayloads> = (this.manager.displayer as Room).dispatchMagixEvent.bind(this.manager.displayer)
|
157
|
+
|
158
|
+
/** Listen to events from others clients (and self messages). */
|
159
|
+
public addMagixEventListener: MagixEventAddListener<TMagixEventPayloads> = this.manager.displayer.addMagixEventListener.bind(this.manager.displayer)
|
160
|
+
|
161
|
+
/** Remove a Magix event listener. */
|
162
|
+
public removeMagixEventListener = this.manager.displayer.removeMagixEventListener.bind(this.manager.displayer) as MagixEventRemoveListener<TMagixEventPayloads>
|
131
163
|
}
|
package/src/AppManager.ts
CHANGED
@@ -30,6 +30,7 @@ export class AppManager {
|
|
30
30
|
public boxManager?: BoxManager;
|
31
31
|
|
32
32
|
private _prevSceneIndex: number | undefined;
|
33
|
+
private _prevFocused: string | undefined;
|
33
34
|
|
34
35
|
constructor(public windowManger: WindowManager) {
|
35
36
|
this.displayer = windowManger.displayer;
|
@@ -106,6 +107,15 @@ export class AppManager {
|
|
106
107
|
}
|
107
108
|
});
|
108
109
|
});
|
110
|
+
this.refresher?.add("focusedChange", () => {
|
111
|
+
return autorun(() => {
|
112
|
+
const focused = get(this.attributes, "focus");
|
113
|
+
if (this._prevFocused !== focused) {
|
114
|
+
callbacks.emit("focusedChange", focused);
|
115
|
+
this._prevFocused = focused;
|
116
|
+
}
|
117
|
+
});
|
118
|
+
})
|
109
119
|
if (!this.attributes.apps || Object.keys(this.attributes.apps).length === 0) {
|
110
120
|
const mainScenePath = this.store.getMainViewScenePath();
|
111
121
|
if (!mainScenePath) return;
|
@@ -116,6 +126,7 @@ export class AppManager {
|
|
116
126
|
}
|
117
127
|
this.displayerWritableListener(!this.room?.isWritable);
|
118
128
|
this.displayer.callbacks.on("onEnableWriteNowChanged", this.displayerWritableListener);
|
129
|
+
this._prevFocused = this.attributes.focus;
|
119
130
|
}
|
120
131
|
|
121
132
|
/**
|
package/src/index.ts
CHANGED
package/src/typings.ts
CHANGED
@@ -12,7 +12,7 @@ import type {
|
|
12
12
|
import type { AppContext } from "./AppContext";
|
13
13
|
import type { ReadonlyTeleBox, TeleBoxRect } from "@netless/telebox-insider";
|
14
14
|
|
15
|
-
export interface NetlessApp<Attributes = any,
|
15
|
+
export interface NetlessApp<Attributes = any, MagixEventPayloads = any, AppOptions = any, SetupResult = any> {
|
16
16
|
kind: string;
|
17
17
|
config?: {
|
18
18
|
/** Box width relative to whiteboard. 0~1. Default 0.5. */
|
@@ -28,7 +28,7 @@ export interface NetlessApp<Attributes = any, SetupResult = any, AppOptions = an
|
|
28
28
|
/** App only single instance. */
|
29
29
|
singleton?: boolean;
|
30
30
|
};
|
31
|
-
setup: (context: AppContext<Attributes, AppOptions>) => SetupResult;
|
31
|
+
setup: (context: AppContext<Attributes, MagixEventPayloads, AppOptions>) => SetupResult;
|
32
32
|
}
|
33
33
|
|
34
34
|
export type AppEmitterEvent<T = any> = {
|