@netless/window-manager 0.4.0-canary.10 → 0.4.0-canary.14
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 +29 -0
- package/dist/App/Storage/index.d.ts +19 -6
- package/dist/App/Storage/typings.d.ts +1 -0
- package/dist/AppContext.d.ts +39 -17
- package/dist/AppManager.d.ts +1 -0
- package/dist/AppProxy.d.ts +1 -0
- package/dist/BoxManager.d.ts +1 -1
- package/dist/index.d.ts +2 -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 +2 -2
- package/src/App/MagixEvent/index.ts +68 -0
- package/src/App/Storage/index.ts +89 -43
- package/src/App/Storage/typings.ts +4 -2
- package/src/AppContext.ts +57 -20
- package/src/AppManager.ts +14 -0
- package/src/AppProxy.ts +24 -11
- package/src/Utils/RoomHacker.ts +1 -0
- package/src/View/ViewManager.ts +1 -2
- package/src/index.ts +6 -1
- 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
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@netless/window-manager",
|
3
|
-
"version": "0.4.0-canary.
|
3
|
+
"version": "0.4.0-canary.14",
|
4
4
|
"description": "",
|
5
5
|
"main": "dist/index.es.js",
|
6
6
|
"module": "dist/index.es.js",
|
@@ -19,7 +19,7 @@
|
|
19
19
|
"license": "ISC",
|
20
20
|
"peerDependencies": {
|
21
21
|
"video.js": ">=7",
|
22
|
-
"white-web-sdk": "^2.
|
22
|
+
"white-web-sdk": "^2.16.0"
|
23
23
|
},
|
24
24
|
"dependencies": {
|
25
25
|
"@juggle/resize-observer": "^3.3.1",
|
@@ -0,0 +1,68 @@
|
|
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 MagixEventListenerDisposer = () => void
|
54
|
+
|
55
|
+
export type MagixEventAddListener<TPayloads = any> = <
|
56
|
+
TEvent extends MagixEventTypes<TPayloads> = MagixEventTypes<TPayloads>
|
57
|
+
>(
|
58
|
+
event: TEvent,
|
59
|
+
handler: MagixEventHandler<TPayloads, TEvent>,
|
60
|
+
options?: MagixEventListenerOptions | undefined
|
61
|
+
) => MagixEventListenerDisposer;
|
62
|
+
|
63
|
+
export type MagixEventRemoveListener<TPayloads = any> = <
|
64
|
+
TEvent extends MagixEventTypes<TPayloads> = MagixEventTypes<TPayloads>
|
65
|
+
>(
|
66
|
+
event: TEvent,
|
67
|
+
handler?: MagixEventHandler<TPayloads, TEvent>
|
68
|
+
) => void;
|
package/src/App/Storage/index.ts
CHANGED
@@ -1,20 +1,20 @@
|
|
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";
|
6
6
|
import { isRef, makeRef, plainObjectKeys } from "./utils";
|
7
|
-
import type { Diff, MaybeRefValue, RefValue, StorageStateChangedEvent } from "./typings";
|
7
|
+
import type { Diff, MaybeRefValue, RefValue, StorageStateChangedEvent, StorageStateChangedListener, StorageStateChangedListenerDisposer } from "./typings";
|
8
8
|
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
|
)
|
@@ -88,6 +86,11 @@ export class Storage<TState = any> implements Storage<TState> {
|
|
88
86
|
}
|
89
87
|
|
90
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
|
+
}
|
91
94
|
|
92
95
|
ensureState(state: Partial<TState>): void {
|
93
96
|
return this.setState(
|
@@ -122,7 +125,7 @@ export class Storage<TState = any> implements Storage<TState> {
|
|
122
125
|
if (value === void 0) {
|
123
126
|
this._lastValue.set(key, this._state[key]);
|
124
127
|
delete this._state[key];
|
125
|
-
this.
|
128
|
+
this._setRawState(key, value);
|
126
129
|
} else {
|
127
130
|
this._lastValue.set(key, this._state[key]);
|
128
131
|
this._state[key] = value as TState[Extract<keyof TState, string>];
|
@@ -137,13 +140,20 @@ export class Storage<TState = any> implements Storage<TState> {
|
|
137
140
|
payload = refValue;
|
138
141
|
}
|
139
142
|
|
140
|
-
this.
|
143
|
+
this._setRawState(key, payload)
|
141
144
|
}
|
142
145
|
});
|
143
146
|
}
|
144
147
|
}
|
145
148
|
|
146
|
-
|
149
|
+
/**
|
150
|
+
* Empty storage data.
|
151
|
+
*/
|
152
|
+
emptyStorage(): void {
|
153
|
+
if (size(this._state) <= 0) {
|
154
|
+
return;
|
155
|
+
}
|
156
|
+
|
147
157
|
if (this._destroyed) {
|
148
158
|
console.error(new Error(`Cannot empty destroyed Storage "${this.id}".`));
|
149
159
|
return;
|
@@ -154,10 +164,17 @@ export class Storage<TState = any> implements Storage<TState> {
|
|
154
164
|
return;
|
155
165
|
}
|
156
166
|
|
157
|
-
this.
|
167
|
+
this.setState(mapValues(this._state, noop as () => undefined));
|
158
168
|
}
|
159
169
|
|
160
|
-
|
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
|
+
|
161
178
|
if (!this._context.getIsWritable()) {
|
162
179
|
console.error(new Error(`Cannot delete Storage "${this.id}" without writable access.`));
|
163
180
|
return;
|
@@ -172,11 +189,35 @@ export class Storage<TState = any> implements Storage<TState> {
|
|
172
189
|
return this._destroyed;
|
173
190
|
}
|
174
191
|
|
192
|
+
/**
|
193
|
+
* Destroy the Storage instance. The data will be kept.
|
194
|
+
*/
|
175
195
|
destroy() {
|
176
196
|
this._destroyed = true;
|
177
197
|
this._sideEffect.flushAll();
|
178
198
|
}
|
179
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
|
+
|
180
221
|
private _updateProperties(actions: ReadonlyArray<AkkoObjectUpdatedProperty<TState, string>>): void {
|
181
222
|
if (this._destroyed) {
|
182
223
|
console.error(new Error(`Cannot call _updateProperties on destroyed Storage "${this.id}".`));
|
@@ -190,6 +231,11 @@ export class Storage<TState = any> implements Storage<TState> {
|
|
190
231
|
try {
|
191
232
|
const action = actions[i]
|
192
233
|
const key = action.key as Extract<keyof TState, string>;
|
234
|
+
|
235
|
+
if (this.id === null && key === STORAGE_NS) {
|
236
|
+
continue
|
237
|
+
}
|
238
|
+
|
193
239
|
const value = isObject(action.value) ? JSON.parse(JSON.stringify(action.value)) : action.value;
|
194
240
|
let oldValue: TState[Extract<keyof TState, string>] | undefined;
|
195
241
|
if (this._lastValue.has(key)) {
|
@@ -16,6 +16,8 @@ export type StorageOnSetStatePayload<TState = unknown> = {
|
|
16
16
|
[K in keyof TState]?: MaybeRefValue<TState[K]>;
|
17
17
|
};
|
18
18
|
|
19
|
-
export type StorageStateChangedEvent<TState = any> = Diff<TState
|
19
|
+
export type StorageStateChangedEvent<TState = any> = Diff<TState>;
|
20
20
|
|
21
|
-
export type StorageStateChangedListener<TState = any> = StorageEventListener<StorageStateChangedEvent<TState
|
21
|
+
export type StorageStateChangedListener<TState = any> = StorageEventListener<StorageStateChangedEvent<TState>>;
|
22
|
+
|
23
|
+
export type StorageStateChangedListenerDisposer = () => void;
|
package/src/AppContext.ts
CHANGED
@@ -6,9 +6,9 @@ import {
|
|
6
6
|
unlistenDisposed,
|
7
7
|
unlistenUpdated,
|
8
8
|
toJS
|
9
|
-
|
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";
|
@@ -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,30 @@ 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);
|
113
|
+
// 兼容 15 版本 SDK 的切页
|
114
|
+
this.getRoom()?.setScenePath(scenePath);
|
107
115
|
}
|
108
116
|
|
109
|
-
public mountView(dom: HTMLDivElement): void {
|
117
|
+
public mountView = (dom: HTMLDivElement): void => {
|
110
118
|
const view = this.getView();
|
111
119
|
if (view) {
|
112
120
|
view.divElement = dom;
|
@@ -117,15 +125,44 @@ export class AppContext<TAttrs extends Record<string, any> = any, AppOptions = a
|
|
117
125
|
}
|
118
126
|
}
|
119
127
|
|
120
|
-
|
121
|
-
|
128
|
+
/** Get the local App options. */
|
129
|
+
public getAppOptions = (): TAppOptions | undefined => {
|
130
|
+
return typeof this.appOptions === 'function' ? (this.appOptions as () => TAppOptions)() : this.appOptions
|
122
131
|
}
|
123
132
|
|
124
|
-
|
133
|
+
private _storage?: Storage<TAttributes>
|
134
|
+
|
135
|
+
/** Main Storage for attributes. */
|
136
|
+
public get storage(): Storage<TAttributes> {
|
137
|
+
if (!this._storage) {
|
138
|
+
this._storage = new Storage(this);
|
139
|
+
}
|
140
|
+
return this._storage;
|
141
|
+
}
|
142
|
+
|
143
|
+
/**
|
144
|
+
* Create separated storages for flexible state management.
|
145
|
+
* @param storeId Namespace for the storage. Storages of the same namespace share the same data.
|
146
|
+
* @param defaultState Default state for initial storage creation.
|
147
|
+
* @returns
|
148
|
+
*/
|
149
|
+
public createStorage = <TState>(storeId: string, defaultState?: TState): Storage<TState> => {
|
125
150
|
const storage = new Storage(this, storeId, defaultState);
|
126
151
|
this.emitter.on("destroy", () => {
|
127
152
|
storage.destroy();
|
128
153
|
});
|
129
154
|
return storage;
|
130
155
|
}
|
156
|
+
|
157
|
+
/** Dispatch events to other clients (and self). */
|
158
|
+
public dispatchMagixEvent: MagixEventDispatcher<TMagixEventPayloads> = (this.manager.displayer as Room).dispatchMagixEvent.bind(this.manager.displayer)
|
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>
|
131
168
|
}
|
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
|
/**
|
@@ -307,6 +318,9 @@ export class AppManager {
|
|
307
318
|
});
|
308
319
|
if (isWritable === true) {
|
309
320
|
this.mainView.disableCameraTransform = false;
|
321
|
+
if (this.room && this.room.disableSerialization === true) {
|
322
|
+
this.room.disableSerialization = false;
|
323
|
+
}
|
310
324
|
} else {
|
311
325
|
this.mainView.disableCameraTransform = true;
|
312
326
|
}
|
package/src/AppProxy.ts
CHANGED
@@ -5,13 +5,9 @@ import { appRegister } from "./Register";
|
|
5
5
|
import { autorun } from "white-web-sdk";
|
6
6
|
import { emitter } from "./index";
|
7
7
|
import { Fields } from "./AttributesDelegate";
|
8
|
-
import { get } from "lodash";
|
8
|
+
import { debounce, get } from "lodash";
|
9
9
|
import { log } from "./Utils/log";
|
10
|
-
import {
|
11
|
-
setScenePath,
|
12
|
-
setViewFocusScenePath,
|
13
|
-
getScenePath
|
14
|
-
} from "./Utils/Common";
|
10
|
+
import { setScenePath, setViewFocusScenePath, getScenePath } from "./Utils/Common";
|
15
11
|
import type {
|
16
12
|
AppEmitterEvent,
|
17
13
|
AppInitState,
|
@@ -113,9 +109,7 @@ export class AppProxy extends Base {
|
|
113
109
|
this.manager.safeUpdateAttributes(["apps", this.id, Fields.FullPath], path);
|
114
110
|
}
|
115
111
|
|
116
|
-
public async baseInsertApp(
|
117
|
-
skipUpdate = false,
|
118
|
-
): Promise<{ appId: string; app: NetlessApp }> {
|
112
|
+
public async baseInsertApp(skipUpdate = false): Promise<{ appId: string; app: NetlessApp }> {
|
119
113
|
const params = this.params;
|
120
114
|
if (!params.kind) {
|
121
115
|
throw new Error("[WindowManager]: kind require");
|
@@ -123,7 +117,13 @@ export class AppProxy extends Base {
|
|
123
117
|
const appImpl = await appRegister.appClasses.get(params.kind)?.();
|
124
118
|
const appParams = appRegister.registered.get(params.kind);
|
125
119
|
if (appImpl) {
|
126
|
-
await this.setupApp(
|
120
|
+
await this.setupApp(
|
121
|
+
this.id,
|
122
|
+
skipUpdate,
|
123
|
+
appImpl,
|
124
|
+
params.options,
|
125
|
+
appParams?.appOptions
|
126
|
+
);
|
127
127
|
} else {
|
128
128
|
throw new Error(`[WindowManager]: app load failed ${params.kind} ${params.src}`);
|
129
129
|
}
|
@@ -317,7 +317,7 @@ export class AppProxy extends Base {
|
|
317
317
|
}
|
318
318
|
});
|
319
319
|
});
|
320
|
-
this.manager.refresher?.add(this.stateKey,() => {
|
320
|
+
this.manager.refresher?.add(this.stateKey, () => {
|
321
321
|
return autorun(() => {
|
322
322
|
const appState = this.appAttributes?.state;
|
323
323
|
if (appState?.zIndex > 0 && appState.zIndex !== this.box?.zIndex) {
|
@@ -325,8 +325,20 @@ export class AppProxy extends Base {
|
|
325
325
|
}
|
326
326
|
});
|
327
327
|
});
|
328
|
+
this.manager.refresher?.add(`${appId}-fullPath`, () => {
|
329
|
+
return autorun(() => {
|
330
|
+
const fullPath = this.appAttributes?.fullPath;
|
331
|
+
this.setFocusScenePathHandler(fullPath);
|
332
|
+
});
|
333
|
+
});
|
328
334
|
};
|
329
335
|
|
336
|
+
private setFocusScenePathHandler = debounce((fullPath: string | undefined) => {
|
337
|
+
if (this.view && fullPath && fullPath !== this.view?.focusScenePath) {
|
338
|
+
setViewFocusScenePath(this.view, fullPath);
|
339
|
+
}
|
340
|
+
}, 50);
|
341
|
+
|
330
342
|
public setScenePath(): void {
|
331
343
|
if (!this.manager.canOperate) return;
|
332
344
|
const fullScenePath = this.getFullScenePath();
|
@@ -372,6 +384,7 @@ export class AppProxy extends Base {
|
|
372
384
|
this.manager.appStatus.delete(this.id);
|
373
385
|
this.manager.refresher?.remove(this.id);
|
374
386
|
this.manager.refresher?.remove(this.stateKey);
|
387
|
+
this.manager.refresher?.remove(`${this.id}-fullPath`);
|
375
388
|
}
|
376
389
|
|
377
390
|
public close(): Promise<void> {
|
package/src/Utils/RoomHacker.ts
CHANGED
@@ -51,6 +51,7 @@ export const replaceRoomFunction = (room: Room, manager: WindowManager) => {
|
|
51
51
|
room.setMemberState = (...args) => manager.mainView.setMemberState(...args);
|
52
52
|
room.redo = () => manager.mainView.redo();
|
53
53
|
room.undo = () => manager.mainView.undo();
|
54
|
+
room.cleanCurrentScene = () => manager.mainView.cleanCurrentScene();
|
54
55
|
}
|
55
56
|
|
56
57
|
};
|
package/src/View/ViewManager.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import type { View
|
1
|
+
import type { View, Displayer } from "white-web-sdk";
|
2
2
|
|
3
3
|
export class ViewManager {
|
4
4
|
public views: Map<string, View> = new Map();
|
@@ -38,7 +38,6 @@ export class ViewManager {
|
|
38
38
|
}
|
39
39
|
}
|
40
40
|
|
41
|
-
|
42
41
|
export const createView = (displayer: Displayer): View => {
|
43
42
|
const view = displayer.views.createView();
|
44
43
|
setDefaultCameraBound(view);
|
package/src/index.ts
CHANGED
@@ -151,6 +151,7 @@ export type PublicEvent = {
|
|
151
151
|
cameraStateChange: CameraState;
|
152
152
|
mainViewScenePathChange: string;
|
153
153
|
mainViewSceneIndexChange: number;
|
154
|
+
focusedChange: string | undefined;
|
154
155
|
};
|
155
156
|
|
156
157
|
export type MountParams = {
|
@@ -219,7 +220,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
|
|
219
220
|
if (room.phase !== RoomPhase.Connected) {
|
220
221
|
throw new Error("[WindowManager]: Room only Connected can be mount");
|
221
222
|
}
|
222
|
-
if (room.phase === RoomPhase.Connected) {
|
223
|
+
if (room.phase === RoomPhase.Connected && room.isWritable) {
|
223
224
|
// redo undo 需要设置这个属性
|
224
225
|
room.disableSerialization = false;
|
225
226
|
}
|
@@ -570,6 +571,10 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
|
|
570
571
|
return this.attributes.focus;
|
571
572
|
}
|
572
573
|
|
574
|
+
public get mainViewSceneIndex(): number {
|
575
|
+
return this.appManager?.store.getMainViewSceneIndex();
|
576
|
+
}
|
577
|
+
|
573
578
|
/**
|
574
579
|
* 查询所有的 App
|
575
580
|
*/
|