@flighthq/statusbar 0.5.0-next.1944.cd6b6e7 → 0.5.1-edge.638.d4dbd4e
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/README.md +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/statusbar.d.ts +12 -18
- package/dist/statusbar.d.ts.map +1 -1
- package/dist/statusbar.js +92 -227
- package/dist/statusbar.js.map +1 -1
- package/package.json +3 -3
- package/src/statusbar.test.ts +133 -554
package/README.md
CHANGED
|
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/statusbar`. The `@fl
|
|
|
13
13
|
This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
|
|
14
14
|
|
|
15
15
|
- [Flight project](https://github.com/flighthq/flight)
|
|
16
|
-
- [Source for @flighthq/statusbar@0.5.
|
|
17
|
-
- [License](https://github.com/flighthq/flight/blob/
|
|
16
|
+
- [Source for @flighthq/statusbar@0.5.1-edge.638.d4dbd4e](https://github.com/flighthq/flight/tree/d4dbd4e347dc8be10b180c85a766ab50333dcb51/packages/statusbar)
|
|
17
|
+
- [License](https://github.com/flighthq/flight/blob/d4dbd4e347dc8be10b180c85a766ab50333dcb51/LICENSE.md)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { attachStatusBar, clearStatusBarStyleStack, createStatusBar, detachStatusBar, disposeStatusBar,
|
|
1
|
+
export { attachStatusBar, clearStatusBarStyleStack, createStatusBar, detachStatusBar, disposeStatusBar, getStatusBarHeight, getStatusBarInfo, hasStatusBarStyleEntry, popStatusBarStyleEntry, pushStatusBarStyleEntry, setStatusBarColor, setStatusBarOverlaysContent, setStatusBarStyle, setStatusBarVisible, } from './contract';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,EACvB,iBAAiB,EACjB,2BAA2B,EAC3B,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { attachStatusBar, clearStatusBarStyleStack, createStatusBar, detachStatusBar, disposeStatusBar,
|
|
1
|
+
export { attachStatusBar, clearStatusBarStyleStack, createStatusBar, detachStatusBar, disposeStatusBar, getStatusBarHeight, getStatusBarInfo, hasStatusBarStyleEntry, popStatusBarStyleEntry, pushStatusBarStyleEntry, setStatusBarColor, setStatusBarOverlaysContent, setStatusBarStyle, setStatusBarVisible, } from './contract';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,EACvB,iBAAiB,EACjB,2BAA2B,EAC3B,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
|
package/dist/statusbar.d.ts
CHANGED
|
@@ -1,24 +1,18 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare function attachStatusBar(bar: StatusBar): void;
|
|
3
|
-
export declare function clearStatusBarStyleStack(): void;
|
|
1
|
+
import type { HasUiStatusBarChange, HasUiStatusBarColor, HasUiStatusBarInfo, HasUiStatusBarOverlays, HasUiStatusBarStyle, HasUiStatusBarStyleStack, HasUiStatusBarVisibility, StatusBar, StatusBarAnimation, StatusBarInfo, StatusBarStyle, StatusBarStyleEntry, StatusBarStyleEntryHandle } from '@flighthq/types/contract';
|
|
2
|
+
export declare function attachStatusBar(host: HasUiStatusBarChange & HasUiStatusBarInfo, bar: StatusBar): void;
|
|
3
|
+
export declare function clearStatusBarStyleStack(host: HasUiStatusBarStyleStack): void;
|
|
4
4
|
export declare function createStatusBar(): StatusBar;
|
|
5
5
|
export declare function createStatusBarInfo(): StatusBarInfo;
|
|
6
6
|
export declare function detachStatusBar(bar: StatusBar): void;
|
|
7
7
|
export declare function disposeStatusBar(bar: StatusBar): void;
|
|
8
|
-
export declare function
|
|
9
|
-
export declare function
|
|
10
|
-
export declare function
|
|
11
|
-
export declare function getStatusBarInfo(out: StatusBarInfo): StatusBarInfo;
|
|
12
|
-
export declare function hasStatusBarStyleEntry(handle: StatusBarStyleEntryHandle): boolean;
|
|
13
|
-
export declare function installStatusBarHostBackend(backend: StatusBarBackend): void;
|
|
14
|
-
export declare function observeStatusBarHostResult(operation: string, succeeded: boolean): void;
|
|
8
|
+
export declare function getStatusBarHeight(host: HasUiStatusBarInfo): number;
|
|
9
|
+
export declare function getStatusBarInfo(host: HasUiStatusBarInfo, out: StatusBarInfo): StatusBarInfo;
|
|
10
|
+
export declare function hasStatusBarStyleEntry(host: HasUiStatusBarStyleStack, handle: StatusBarStyleEntryHandle): boolean;
|
|
15
11
|
export declare function packedRgbaToHexColor(color: number): string;
|
|
16
|
-
export declare function popStatusBarStyleEntry(handle: StatusBarStyleEntryHandle): void;
|
|
17
|
-
export declare function pushStatusBarStyleEntry(entry: Readonly<StatusBarStyleEntry>): StatusBarStyleEntryHandle;
|
|
18
|
-
export declare function
|
|
19
|
-
export declare function
|
|
20
|
-
export declare function
|
|
21
|
-
export declare function
|
|
22
|
-
export declare function setStatusBarStyle(style: StatusBarStyle): void;
|
|
23
|
-
export declare function setStatusBarVisible(visible: boolean, animation?: StatusBarAnimation): void;
|
|
12
|
+
export declare function popStatusBarStyleEntry(host: HasUiStatusBarStyleStack, handle: StatusBarStyleEntryHandle): void;
|
|
13
|
+
export declare function pushStatusBarStyleEntry(host: HasUiStatusBarStyleStack, entry: Readonly<StatusBarStyleEntry>): StatusBarStyleEntryHandle;
|
|
14
|
+
export declare function setStatusBarColor(host: HasUiStatusBarColor, color: number, animated?: boolean): void;
|
|
15
|
+
export declare function setStatusBarOverlaysContent(host: HasUiStatusBarOverlays, overlay: boolean): void;
|
|
16
|
+
export declare function setStatusBarStyle(host: HasUiStatusBarStyle, style: StatusBarStyle): void;
|
|
17
|
+
export declare function setStatusBarVisible(host: HasUiStatusBarVisibility, visible: boolean, animation?: StatusBarAnimation): void;
|
|
24
18
|
//# sourceMappingURL=statusbar.d.ts.map
|
package/dist/statusbar.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statusbar.d.ts","sourceRoot":"","sources":["../src/statusbar.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,kBAAkB,EAClB,SAAS,EACT,kBAAkB,
|
|
1
|
+
{"version":3,"file":"statusbar.d.ts","sourceRoot":"","sources":["../src/statusbar.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,SAAS,EACT,kBAAkB,EAElB,aAAa,EAEb,cAAc,EAEd,mBAAmB,EACnB,yBAAyB,EAE1B,MAAM,0BAA0B,CAAC;AAElC,wBAAgB,eAAe,CAAC,IAAI,EAAE,oBAAoB,GAAG,kBAAkB,EAAE,GAAG,EAAE,SAAS,GAAG,IAAI,CAQrG;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,wBAAwB,GAAG,IAAI,CAK7E;AAED,wBAAgB,eAAe,IAAI,SAAS,CAE3C;AAED,wBAAgB,mBAAmB,IAAI,aAAa,CAQnD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAKpD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAErD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAEnE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,kBAAkB,EAAE,GAAG,EAAE,aAAa,GAAG,aAAa,CAE5F;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,wBAAwB,EAAE,MAAM,EAAE,yBAAyB,GAAG,OAAO,CAGjH;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,wBAAwB,EAAE,MAAM,EAAE,yBAAyB,GAAG,IAAI,CAQ9G;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,wBAAwB,EAC9B,KAAK,EAAE,QAAQ,CAAC,mBAAmB,CAAC,GACnC,yBAAyB,CAoB3B;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAEpG;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAEhG;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,CAExF;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,wBAAwB,EAC9B,OAAO,EAAE,OAAO,EAChB,SAAS,CAAC,EAAE,kBAAkB,GAC7B,IAAI,CAEN"}
|
package/dist/statusbar.js
CHANGED
|
@@ -1,40 +1,23 @@
|
|
|
1
1
|
import { createSignal, emitSignal } from '@flighthq/signals/contract';
|
|
2
|
-
|
|
3
|
-
// backend. Idempotent: a prior subscription is torn down first. Pair with detachStatusBar /
|
|
4
|
-
// disposeStatusBar.
|
|
5
|
-
//
|
|
6
|
-
// Each event carries a freshly allocated StatusBarInfo the listener owns outright. This is the one
|
|
7
|
-
// place in the package that deliberately allocates per event rather than filling a shared scratch:
|
|
8
|
-
// an emitted payload is retained by whoever listens, and a shared scratch would let the next event —
|
|
9
|
-
// or an unrelated getStatusBarHeight() call — rewrite a snapshot a listener had already stored, and
|
|
10
|
-
// would hand every attached bar the same object. Status bar changes are rare OS events, so the
|
|
11
|
-
// allocation is not on any hot path; a caller that wants zero-allocation reads uses getStatusBarInfo
|
|
12
|
-
// with its own `out`.
|
|
13
|
-
export function attachStatusBar(bar) {
|
|
2
|
+
export function attachStatusBar(host, bar) {
|
|
14
3
|
detachStatusBar(bar);
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
4
|
+
const changeProvider = host.ui.statusBarChange;
|
|
5
|
+
const infoProvider = host.ui.statusBarInfo;
|
|
6
|
+
const unsubscribe = changeProvider.subscribe(() => {
|
|
7
|
+
emitSignal(bar.onChange, infoProvider.getInfo(createStatusBarInfo()));
|
|
18
8
|
});
|
|
19
9
|
_subscriptions.set(bar, unsubscribe);
|
|
20
10
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export function clearStatusBarStyleStack() {
|
|
25
|
-
if (_styleStack.length === 0)
|
|
11
|
+
export function clearStatusBarStyleStack(host) {
|
|
12
|
+
const state = _styleStacks.get(host);
|
|
13
|
+
if (state === undefined || state.entries.length === 0)
|
|
26
14
|
return;
|
|
27
|
-
|
|
28
|
-
|
|
15
|
+
state.entries.length = 0;
|
|
16
|
+
applyTopStyleEntry(host, state);
|
|
29
17
|
}
|
|
30
|
-
// Allocates a StatusBar event entity with inert signals; call attachStatusBar to start delivery.
|
|
31
18
|
export function createStatusBar() {
|
|
32
|
-
return {
|
|
33
|
-
onChange: createSignal(),
|
|
34
|
-
};
|
|
19
|
+
return { onChange: createSignal() };
|
|
35
20
|
}
|
|
36
|
-
// Allocates a zeroed StatusBarInfo, suitable as the `out` for getStatusBarInfo.
|
|
37
|
-
// height defaults to -1 (unknown), color to 0 (transparent black), style to 'default'.
|
|
38
21
|
export function createStatusBarInfo() {
|
|
39
22
|
return {
|
|
40
23
|
color: 0,
|
|
@@ -44,236 +27,118 @@ export function createStatusBarInfo() {
|
|
|
44
27
|
visible: true,
|
|
45
28
|
};
|
|
46
29
|
}
|
|
47
|
-
// Stops delivery to `bar` and forgets its subscription. Safe to call when not attached.
|
|
48
30
|
export function detachStatusBar(bar) {
|
|
49
31
|
const unsubscribe = _subscriptions.get(bar);
|
|
50
|
-
if (unsubscribe
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
32
|
+
if (unsubscribe === undefined)
|
|
33
|
+
return;
|
|
34
|
+
unsubscribe();
|
|
35
|
+
_subscriptions.delete(bar);
|
|
54
36
|
}
|
|
55
|
-
// Releases `bar` for garbage collection by detaching its backend subscription. The signals remain
|
|
56
|
-
// plain GC-managed memory afterward.
|
|
57
37
|
export function disposeStatusBar(bar) {
|
|
58
38
|
detachStatusBar(bar);
|
|
59
39
|
}
|
|
60
|
-
export function
|
|
61
|
-
|
|
62
|
-
return { conflict: _hostConflict, layer: 'custom', operation: null, viability: 'unobserved' };
|
|
63
|
-
}
|
|
64
|
-
if (_host !== null) {
|
|
65
|
-
return {
|
|
66
|
-
conflict: _hostConflict,
|
|
67
|
-
layer: 'host',
|
|
68
|
-
operation: _hostObservation !== null ? _hostObservation.operation : null,
|
|
69
|
-
viability: _hostObservation !== null ? _hostObservation.viability : 'unobserved',
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
return { conflict: false, layer: 'host-not-enabled', operation: null, viability: 'unobserved' };
|
|
73
|
-
}
|
|
74
|
-
// The active status bar backend. Precedence: custom > host > sentinel.
|
|
75
|
-
export function getStatusBarBackend() {
|
|
76
|
-
return _custom ?? _host ?? _sentinel;
|
|
40
|
+
export function getStatusBarHeight(host) {
|
|
41
|
+
return host.ui.statusBarInfo.getInfo(_scratchInfo).height;
|
|
77
42
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// NOTE: On notched/island devices, the safe-area top inset (owned by @flighthq/device) may differ
|
|
81
|
-
// from the status bar height. Use device.getSafeAreaInsets().top for layout-safe top padding;
|
|
82
|
-
// use getStatusBarHeight() when you specifically need the status bar element's intrinsic height.
|
|
83
|
-
export function getStatusBarHeight() {
|
|
84
|
-
return getStatusBarBackend().getInfo(_scratchInfo).height;
|
|
43
|
+
export function getStatusBarInfo(host, out) {
|
|
44
|
+
return host.ui.statusBarInfo.getInfo(out);
|
|
85
45
|
}
|
|
86
|
-
|
|
87
|
-
// be the same object as any internal scratch.
|
|
88
|
-
export function getStatusBarInfo(out) {
|
|
89
|
-
return getStatusBarBackend().getInfo(out);
|
|
90
|
-
}
|
|
91
|
-
// True when `handle` names an entry still on the style stack. False for a popped handle, an unknown
|
|
92
|
-
// one, or the invalid handle — so a component can check before popping without tracking its own
|
|
93
|
-
// mounted flag.
|
|
94
|
-
export function hasStatusBarStyleEntry(handle) {
|
|
46
|
+
export function hasStatusBarStyleEntry(host, handle) {
|
|
95
47
|
if (handle === INVALID_HANDLE)
|
|
96
48
|
return false;
|
|
97
|
-
return
|
|
98
|
-
}
|
|
99
|
-
export function installStatusBarHostBackend(backend) {
|
|
100
|
-
if (_host !== null) {
|
|
101
|
-
if (_host !== backend)
|
|
102
|
-
_hostConflict = true;
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
_host = backend;
|
|
49
|
+
return _styleStacks.get(host)?.entries.some((entry) => entry.handle === handle) ?? false;
|
|
106
50
|
}
|
|
107
|
-
export function observeStatusBarHostResult(operation, succeeded) {
|
|
108
|
-
_hostObservation = {
|
|
109
|
-
operation,
|
|
110
|
-
viability: succeeded ? 'available' : 'runtime-api-unavailable',
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
// Converts a packed RGBA integer (0xRRGGBBAA) to a #rrggbb hex string, dropping alpha.
|
|
114
51
|
export function packedRgbaToHexColor(color) {
|
|
115
52
|
const rgb = (color >>> 8) & 0xffffff;
|
|
116
53
|
return '#' + rgb.toString(16).padStart(6, '0');
|
|
117
54
|
}
|
|
118
|
-
|
|
119
|
-
// this is a no-op. The remaining stack — falling back to the baseline captured before the first
|
|
120
|
-
// push — is re-applied after removal, so a field the popped entry was the last to set returns to
|
|
121
|
-
// what it was rather than staying where that entry left it.
|
|
122
|
-
export function popStatusBarStyleEntry(handle) {
|
|
55
|
+
export function popStatusBarStyleEntry(host, handle) {
|
|
123
56
|
if (handle === INVALID_HANDLE)
|
|
124
57
|
return;
|
|
125
|
-
const
|
|
126
|
-
if (
|
|
58
|
+
const state = _styleStacks.get(host);
|
|
59
|
+
if (state === undefined)
|
|
127
60
|
return;
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
61
|
+
const index = state.entries.findIndex((entry) => entry.handle === handle);
|
|
62
|
+
if (index === -1)
|
|
63
|
+
return;
|
|
64
|
+
state.entries.splice(index, 1);
|
|
65
|
+
applyTopStyleEntry(host, state);
|
|
66
|
+
}
|
|
67
|
+
export function pushStatusBarStyleEntry(host, entry) {
|
|
68
|
+
let state = _styleStacks.get(host);
|
|
69
|
+
if (state === undefined) {
|
|
70
|
+
const infoProvider = host.ui.statusBarInfo;
|
|
71
|
+
const baseline = infoProvider.getInfo(createStatusBarInfo());
|
|
72
|
+
state = {
|
|
73
|
+
applied: { ...baseline },
|
|
74
|
+
baseline,
|
|
75
|
+
colorProvider: host.ui.statusBarColor,
|
|
76
|
+
entries: [],
|
|
77
|
+
overlaysProvider: host.ui.statusBarOverlays,
|
|
78
|
+
styleProvider: host.ui.statusBarStyle,
|
|
79
|
+
visibilityProvider: host.ui.statusBarVisibility,
|
|
80
|
+
};
|
|
81
|
+
_styleStacks.set(host, state);
|
|
145
82
|
}
|
|
146
83
|
const handle = _nextHandle++;
|
|
147
|
-
|
|
148
|
-
|
|
84
|
+
state.entries.push({ entry, handle });
|
|
85
|
+
applyTopStyleEntry(host, state);
|
|
149
86
|
return handle;
|
|
150
87
|
}
|
|
151
|
-
export function
|
|
152
|
-
|
|
153
|
-
_host = null;
|
|
154
|
-
_hostConflict = false;
|
|
155
|
-
_hostObservation = null;
|
|
156
|
-
}
|
|
157
|
-
// Installs a custom status bar backend; pass null to clear the custom override.
|
|
158
|
-
export function setStatusBarBackend(backend) {
|
|
159
|
-
_custom = backend;
|
|
88
|
+
export function setStatusBarColor(host, color, animated) {
|
|
89
|
+
host.ui.statusBarColor.setBackgroundColor(color, animated);
|
|
160
90
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
// (native hosts only; no-op on web).
|
|
164
|
-
export function setStatusBarColor(color, animated) {
|
|
165
|
-
getStatusBarBackend().setBackgroundColor(color, animated);
|
|
91
|
+
export function setStatusBarOverlaysContent(host, overlay) {
|
|
92
|
+
host.ui.statusBarOverlays.setOverlaysContent(overlay);
|
|
166
93
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
getStatusBarBackend().setOverlaysContent(overlay);
|
|
94
|
+
export function setStatusBarStyle(host, style) {
|
|
95
|
+
host.ui.statusBarStyle.setStyle(style);
|
|
170
96
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
getStatusBarBackend().setStyle(style);
|
|
97
|
+
export function setStatusBarVisible(host, visible, animation) {
|
|
98
|
+
host.ui.statusBarVisibility.setVisible(visible, animation);
|
|
174
99
|
}
|
|
175
|
-
|
|
176
|
-
export function setStatusBarVisible(visible, animation) {
|
|
177
|
-
getStatusBarBackend().setVisible(visible, animation);
|
|
178
|
-
}
|
|
179
|
-
const _sentinel = {
|
|
180
|
-
getInfo(out) {
|
|
181
|
-
out.color = 0;
|
|
182
|
-
out.height = -1;
|
|
183
|
-
out.overlaysContent = false;
|
|
184
|
-
out.style = 'default';
|
|
185
|
-
out.visible = true;
|
|
186
|
-
return out;
|
|
187
|
-
},
|
|
188
|
-
setBackgroundColor() { },
|
|
189
|
-
setOverlaysContent() { },
|
|
190
|
-
setStyle() { },
|
|
191
|
-
setVisible() { },
|
|
192
|
-
subscribe() {
|
|
193
|
-
return () => { };
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
let _custom = null;
|
|
197
|
-
let _host = null;
|
|
198
|
-
let _hostConflict = false;
|
|
199
|
-
let _hostObservation = null;
|
|
100
|
+
const INVALID_HANDLE = -1;
|
|
200
101
|
let _nextHandle = 1;
|
|
201
102
|
const _scratchInfo = createStatusBarInfo();
|
|
202
|
-
const
|
|
103
|
+
const _styleStacks = new WeakMap();
|
|
203
104
|
const _subscriptions = new WeakMap();
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
// the value a released field returns to, and `_applied` keeps the backend from being told things it
|
|
207
|
-
// already knows. Both are cleared when the stack empties, so the next push re-reads a baseline that
|
|
208
|
-
// is once again the pre-stack truth.
|
|
209
|
-
let _baseline = null;
|
|
210
|
-
let _applied = null;
|
|
211
|
-
const INVALID_HANDLE = -1;
|
|
212
|
-
// Merges the style stack top-down (last pushed = highest priority per field), falls unset fields
|
|
213
|
-
// through to the captured baseline, and pushes to the backend only what actually changed.
|
|
214
|
-
//
|
|
215
|
-
// The baseline fallback is what makes a pop restore: merging the stack alone leaves a released field
|
|
216
|
-
// undefined, and an undefined field used to mean "call no setter", which left the OS holding the
|
|
217
|
-
// value of an entry that is no longer on the stack. Diffing against `_applied` keeps that fix from
|
|
218
|
-
// costing a burst of redundant setter calls on every push and pop.
|
|
219
|
-
function _applyTopStyleEntry() {
|
|
220
|
-
const backend = getStatusBarBackend();
|
|
221
|
-
let style;
|
|
222
|
-
let visible;
|
|
105
|
+
function applyTopStyleEntry(host, state) {
|
|
106
|
+
let animation;
|
|
223
107
|
let color;
|
|
224
108
|
let overlaysContent;
|
|
225
|
-
let
|
|
226
|
-
|
|
227
|
-
for (let
|
|
228
|
-
const
|
|
229
|
-
if (
|
|
230
|
-
|
|
231
|
-
if (
|
|
232
|
-
|
|
233
|
-
if (
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const baseline = _baseline;
|
|
241
|
-
if (baseline !== null) {
|
|
242
|
-
if (style === undefined)
|
|
243
|
-
style = baseline.style;
|
|
244
|
-
if (visible === undefined)
|
|
245
|
-
visible = baseline.visible;
|
|
246
|
-
if (color === undefined)
|
|
247
|
-
color = baseline.color;
|
|
248
|
-
if (overlaysContent === undefined)
|
|
249
|
-
overlaysContent = baseline.overlaysContent;
|
|
250
|
-
}
|
|
251
|
-
const applied = _applied;
|
|
252
|
-
if (style !== undefined && style !== applied?.style)
|
|
253
|
-
backend.setStyle(style);
|
|
254
|
-
if (visible !== undefined && visible !== applied?.visible)
|
|
255
|
-
backend.setVisible(visible, animation ?? 'none');
|
|
256
|
-
if (color !== undefined && color !== applied?.color)
|
|
257
|
-
backend.setBackgroundColor(color, false);
|
|
258
|
-
if (overlaysContent !== undefined && overlaysContent !== applied?.overlaysContent) {
|
|
259
|
-
backend.setOverlaysContent(overlaysContent);
|
|
260
|
-
}
|
|
261
|
-
if (_styleStack.length === 0) {
|
|
262
|
-
// The stack is spent: the backend is back at the baseline, so forget both and let the next push
|
|
263
|
-
// capture a fresh one.
|
|
264
|
-
_baseline = null;
|
|
265
|
-
_applied = null;
|
|
266
|
-
return;
|
|
109
|
+
let style;
|
|
110
|
+
let visible;
|
|
111
|
+
for (let index = state.entries.length - 1; index >= 0; index--) {
|
|
112
|
+
const entry = state.entries[index].entry;
|
|
113
|
+
if (animation === undefined && entry.animation !== undefined)
|
|
114
|
+
animation = entry.animation;
|
|
115
|
+
if (color === undefined && entry.color !== undefined)
|
|
116
|
+
color = entry.color;
|
|
117
|
+
if (overlaysContent === undefined && entry.overlaysContent !== undefined) {
|
|
118
|
+
overlaysContent = entry.overlaysContent;
|
|
119
|
+
}
|
|
120
|
+
if (style === undefined && entry.style !== undefined)
|
|
121
|
+
style = entry.style;
|
|
122
|
+
if (visible === undefined && entry.visible !== undefined)
|
|
123
|
+
visible = entry.visible;
|
|
267
124
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
125
|
+
color ??= state.baseline.color;
|
|
126
|
+
overlaysContent ??= state.baseline.overlaysContent;
|
|
127
|
+
style ??= state.baseline.style;
|
|
128
|
+
visible ??= state.baseline.visible;
|
|
129
|
+
if (color !== state.applied.color)
|
|
130
|
+
state.colorProvider.setBackgroundColor(color, false);
|
|
131
|
+
if (overlaysContent !== state.applied.overlaysContent)
|
|
132
|
+
state.overlaysProvider.setOverlaysContent(overlaysContent);
|
|
133
|
+
if (style !== state.applied.style)
|
|
134
|
+
state.styleProvider.setStyle(style);
|
|
135
|
+
if (visible !== state.applied.visible)
|
|
136
|
+
state.visibilityProvider.setVisible(visible, animation ?? 'none');
|
|
137
|
+
state.applied.color = color;
|
|
138
|
+
state.applied.overlaysContent = overlaysContent;
|
|
139
|
+
state.applied.style = style;
|
|
140
|
+
state.applied.visible = visible;
|
|
141
|
+
if (state.entries.length === 0)
|
|
142
|
+
_styleStacks.delete(host);
|
|
278
143
|
}
|
|
279
144
|
//# sourceMappingURL=statusbar.js.map
|
package/dist/statusbar.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statusbar.js","sourceRoot":"","sources":["../src/statusbar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"statusbar.js","sourceRoot":"","sources":["../src/statusbar.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/statusbar",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1-edge.638.d4dbd4e",
|
|
4
4
|
"author": "Joshua Granick and other contributors",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@flighthq/signals": "0.5.
|
|
42
|
-
"@flighthq/types": "0.5.
|
|
41
|
+
"@flighthq/signals": "0.5.1-edge.638.d4dbd4e",
|
|
42
|
+
"@flighthq/types": "0.5.1-edge.638.d4dbd4e"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"typescript": "^5.3.0"
|
package/src/statusbar.test.ts
CHANGED
|
@@ -1,622 +1,201 @@
|
|
|
1
1
|
import { connectSignal } from '@flighthq/signals/contract';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
HasUiStatusBarChange,
|
|
4
|
+
HasUiStatusBarStyleStack,
|
|
5
|
+
StatusBarInfo,
|
|
6
|
+
StatusBarStyle,
|
|
7
|
+
} from '@flighthq/types/contract';
|
|
8
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
9
|
|
|
4
|
-
import
|
|
5
|
-
attachStatusBar,
|
|
6
|
-
clearStatusBarStyleStack,
|
|
7
|
-
createStatusBar,
|
|
8
|
-
createStatusBarInfo,
|
|
9
|
-
detachStatusBar,
|
|
10
|
-
disposeStatusBar,
|
|
11
|
-
explainStatusBarBackend,
|
|
12
|
-
getStatusBarBackend,
|
|
13
|
-
getStatusBarHeight,
|
|
14
|
-
getStatusBarInfo,
|
|
15
|
-
hasStatusBarStyleEntry,
|
|
16
|
-
installStatusBarHostBackend,
|
|
17
|
-
observeStatusBarHostResult,
|
|
18
|
-
packedRgbaToHexColor,
|
|
19
|
-
popStatusBarStyleEntry,
|
|
20
|
-
pushStatusBarStyleEntry,
|
|
21
|
-
resetStatusBarBackendForTest,
|
|
22
|
-
setStatusBarBackend,
|
|
23
|
-
setStatusBarColor,
|
|
24
|
-
setStatusBarOverlaysContent,
|
|
25
|
-
setStatusBarStyle,
|
|
26
|
-
setStatusBarVisible,
|
|
27
|
-
} from './statusbar';
|
|
28
|
-
|
|
29
|
-
function fakeBackend(): StatusBarBackend & {
|
|
30
|
-
_emit(): void;
|
|
31
|
-
animatedColor: boolean | undefined;
|
|
32
|
-
animation: string | undefined;
|
|
33
|
-
color: number;
|
|
34
|
-
infoHeight: number;
|
|
35
|
-
overlay: boolean;
|
|
36
|
-
style: StatusBarStyle;
|
|
37
|
-
subscribeCallCount: number;
|
|
38
|
-
visible: boolean;
|
|
39
|
-
} {
|
|
40
|
-
let listener: (() => void) | null = null;
|
|
41
|
-
return {
|
|
42
|
-
animatedColor: undefined,
|
|
43
|
-
animation: undefined,
|
|
44
|
-
color: 0,
|
|
45
|
-
infoHeight: 42,
|
|
46
|
-
overlay: false,
|
|
47
|
-
style: 'default',
|
|
48
|
-
subscribeCallCount: 0,
|
|
49
|
-
visible: true,
|
|
50
|
-
getInfo(out: StatusBarInfo): StatusBarInfo {
|
|
51
|
-
out.color = this.color;
|
|
52
|
-
out.height = this.infoHeight;
|
|
53
|
-
out.overlaysContent = this.overlay;
|
|
54
|
-
out.style = this.style;
|
|
55
|
-
out.visible = this.visible;
|
|
56
|
-
return out;
|
|
57
|
-
},
|
|
58
|
-
setBackgroundColor(color: number, animated?: boolean): void {
|
|
59
|
-
this.color = color;
|
|
60
|
-
this.animatedColor = animated;
|
|
61
|
-
},
|
|
62
|
-
setOverlaysContent(overlay: boolean): void {
|
|
63
|
-
this.overlay = overlay;
|
|
64
|
-
},
|
|
65
|
-
setStyle(style: StatusBarStyle): void {
|
|
66
|
-
this.style = style;
|
|
67
|
-
},
|
|
68
|
-
setVisible(visible: boolean, animation?: string): void {
|
|
69
|
-
this.visible = visible;
|
|
70
|
-
this.animation = animation;
|
|
71
|
-
},
|
|
72
|
-
subscribe(l: () => void): () => void {
|
|
73
|
-
this.subscribeCallCount++;
|
|
74
|
-
listener = l;
|
|
75
|
-
return () => {
|
|
76
|
-
listener = null;
|
|
77
|
-
};
|
|
78
|
-
},
|
|
79
|
-
// Trigger for tests: fire the subscription listener externally.
|
|
80
|
-
_emit(): void {
|
|
81
|
-
if (listener !== null) listener();
|
|
82
|
-
},
|
|
83
|
-
} as ReturnType<typeof fakeBackend>;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
afterEach(() => {
|
|
87
|
-
clearStatusBarStyleStack();
|
|
88
|
-
resetStatusBarBackendForTest();
|
|
89
|
-
});
|
|
10
|
+
import * as statusbar from './statusbar';
|
|
90
11
|
|
|
91
12
|
describe('attachStatusBar', () => {
|
|
92
|
-
it('
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
const bar = createStatusBar();
|
|
96
|
-
const received: StatusBarInfo[] = [];
|
|
97
|
-
bar.onChange.emit = (info) => {
|
|
98
|
-
received.push({ ...info });
|
|
99
|
-
};
|
|
100
|
-
attachStatusBar(bar);
|
|
101
|
-
(backend as ReturnType<typeof fakeBackend>)._emit();
|
|
102
|
-
expect(received.length).toBe(1);
|
|
103
|
-
expect(received[0].height).toBe(42);
|
|
104
|
-
disposeStatusBar(bar);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('is idempotent: re-attaching replaces the subscription', () => {
|
|
108
|
-
const backend = fakeBackend();
|
|
109
|
-
setStatusBarBackend(backend);
|
|
110
|
-
const bar = createStatusBar();
|
|
111
|
-
attachStatusBar(bar);
|
|
112
|
-
attachStatusBar(bar);
|
|
113
|
-
expect(backend.subscribeCallCount).toBe(2);
|
|
114
|
-
disposeStatusBar(bar);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('hands each event a payload the listener owns, not a shared scratch', () => {
|
|
118
|
-
// The regression this pins: the emitted info used to be a single module-level scratch, so a
|
|
119
|
-
// listener that retained a snapshot watched it change under them on the next event — or on an
|
|
120
|
-
// unrelated getStatusBarHeight() call — and two attached bars received the same object.
|
|
121
|
-
const backend = fakeBackend();
|
|
122
|
-
setStatusBarBackend(backend);
|
|
123
|
-
const bar = createStatusBar();
|
|
13
|
+
it('pins both event and snapshot providers at attachment time', () => {
|
|
14
|
+
const first = fakeHost('light');
|
|
15
|
+
const second = fakeHost('dark');
|
|
16
|
+
const bar = statusbar.createStatusBar();
|
|
124
17
|
const seen: StatusBarInfo[] = [];
|
|
125
18
|
connectSignal(bar.onChange, (info) => seen.push(info));
|
|
126
|
-
attachStatusBar(bar);
|
|
127
19
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
expect(seen[0]).not.toBe(seen[1]);
|
|
134
|
-
expect(seen[0].height).toBe(44);
|
|
135
|
-
expect(seen[1].height).toBe(99);
|
|
20
|
+
statusbar.attachStatusBar(first.host, bar);
|
|
21
|
+
first.emit();
|
|
22
|
+
statusbar.attachStatusBar(second.host, bar);
|
|
23
|
+
first.emit();
|
|
24
|
+
second.emit();
|
|
136
25
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
getStatusBarHeight();
|
|
140
|
-
expect(seen[0].height).toBe(44);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('gives two attached bars separate payload objects', () => {
|
|
144
|
-
const backend = fakeBackend();
|
|
145
|
-
setStatusBarBackend(backend);
|
|
146
|
-
const first = createStatusBar();
|
|
147
|
-
const second = createStatusBar();
|
|
148
|
-
const seen: StatusBarInfo[] = [];
|
|
149
|
-
connectSignal(first.onChange, (info) => seen.push(info));
|
|
150
|
-
connectSignal(second.onChange, (info) => seen.push(info));
|
|
151
|
-
attachStatusBar(first);
|
|
152
|
-
attachStatusBar(second);
|
|
153
|
-
// The fake keeps one listener slot, so drive each bar's subscription in turn.
|
|
154
|
-
backend._emit();
|
|
155
|
-
attachStatusBar(first);
|
|
156
|
-
backend._emit();
|
|
157
|
-
expect(seen.length).toBe(2);
|
|
158
|
-
expect(seen[0]).not.toBe(seen[1]);
|
|
26
|
+
expect(first.unsubscribe).toHaveBeenCalledOnce();
|
|
27
|
+
expect(seen.map((info) => info.style)).toEqual(['light', 'dark']);
|
|
159
28
|
});
|
|
160
29
|
});
|
|
161
30
|
|
|
162
31
|
describe('clearStatusBarStyleStack', () => {
|
|
163
|
-
it('
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
expect(backend.style).toBe('default');
|
|
172
|
-
expect(backend.visible).toBe(true);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it('drops every handle', () => {
|
|
176
|
-
const backend = fakeBackend();
|
|
177
|
-
setStatusBarBackend(backend);
|
|
178
|
-
const first = pushStatusBarStyleEntry({ style: 'dark' });
|
|
179
|
-
const second = pushStatusBarStyleEntry({ style: 'light' });
|
|
180
|
-
clearStatusBarStyleStack();
|
|
181
|
-
expect(hasStatusBarStyleEntry(first)).toBe(false);
|
|
182
|
-
expect(hasStatusBarStyleEntry(second)).toBe(false);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it('is a no-op on an empty stack', () => {
|
|
186
|
-
const backend = fakeBackend();
|
|
187
|
-
setStatusBarBackend(backend);
|
|
188
|
-
clearStatusBarStyleStack();
|
|
189
|
-
expect(() => clearStatusBarStyleStack()).not.toThrow();
|
|
190
|
-
expect(backend.style).toBe('default');
|
|
32
|
+
it('restores only the passed host baseline', () => {
|
|
33
|
+
const first = fakeHost('light');
|
|
34
|
+
const second = fakeHost('dark');
|
|
35
|
+
statusbar.pushStatusBarStyleEntry(first.host, { style: 'dark' });
|
|
36
|
+
statusbar.pushStatusBarStyleEntry(second.host, { style: 'light' });
|
|
37
|
+
statusbar.clearStatusBarStyleStack(first.host);
|
|
38
|
+
expect(first.setStyle).toHaveBeenLastCalledWith('light');
|
|
39
|
+
expect(second.setStyle).toHaveBeenCalledTimes(1);
|
|
191
40
|
});
|
|
192
41
|
});
|
|
193
42
|
|
|
194
43
|
describe('createStatusBar', () => {
|
|
195
|
-
it('
|
|
196
|
-
const bar = createStatusBar();
|
|
197
|
-
expect(bar.onChange).not.toBeNull();
|
|
198
|
-
// Signals are inert (no listeners) until attachStatusBar is called.
|
|
199
|
-
});
|
|
44
|
+
it('creates an inert signal entity', () => expect(statusbar.createStatusBar().onChange).toBeDefined());
|
|
200
45
|
});
|
|
201
46
|
|
|
202
47
|
describe('createStatusBarInfo', () => {
|
|
203
|
-
it('
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
48
|
+
it('creates the documented unknown/default snapshot', () => {
|
|
49
|
+
expect(statusbar.createStatusBarInfo()).toEqual({
|
|
50
|
+
color: 0,
|
|
51
|
+
height: -1,
|
|
52
|
+
overlaysContent: false,
|
|
53
|
+
style: 'default',
|
|
54
|
+
visible: true,
|
|
55
|
+
});
|
|
210
56
|
});
|
|
211
57
|
});
|
|
212
58
|
|
|
213
59
|
describe('detachStatusBar', () => {
|
|
214
|
-
it('
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
let emitCount = 0;
|
|
222
|
-
bar.onChange.emit = () => {
|
|
223
|
-
emitCount++;
|
|
224
|
-
};
|
|
225
|
-
(backend as ReturnType<typeof fakeBackend>)._emit();
|
|
226
|
-
expect(emitCount).toBe(0);
|
|
60
|
+
it('releases the exact attached provider once', () => {
|
|
61
|
+
const provider = fakeHost('light');
|
|
62
|
+
const bar = statusbar.createStatusBar();
|
|
63
|
+
statusbar.attachStatusBar(provider.host, bar);
|
|
64
|
+
statusbar.detachStatusBar(bar);
|
|
65
|
+
statusbar.detachStatusBar(bar);
|
|
66
|
+
expect(provider.unsubscribe).toHaveBeenCalledOnce();
|
|
227
67
|
});
|
|
228
68
|
});
|
|
229
69
|
|
|
230
70
|
describe('disposeStatusBar', () => {
|
|
231
|
-
it('detaches subscription
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
let emitCount = 0;
|
|
238
|
-
bar.onChange.emit = () => {
|
|
239
|
-
emitCount++;
|
|
240
|
-
};
|
|
241
|
-
(backend as ReturnType<typeof fakeBackend>)._emit();
|
|
242
|
-
expect(emitCount).toBe(0);
|
|
243
|
-
});
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
describe('explainStatusBarBackend', () => {
|
|
247
|
-
afterEach(() => resetStatusBarBackendForTest());
|
|
248
|
-
|
|
249
|
-
it('reports host-not-enabled when no backend is installed', () => {
|
|
250
|
-
resetStatusBarBackendForTest();
|
|
251
|
-
const explanation = explainStatusBarBackend();
|
|
252
|
-
expect(explanation.layer).toBe('host-not-enabled');
|
|
253
|
-
expect(explanation.conflict).toBe(false);
|
|
254
|
-
expect(explanation.viability).toBe('unobserved');
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it('reports custom layer when a custom backend is set', () => {
|
|
258
|
-
setStatusBarBackend(fakeBackend());
|
|
259
|
-
expect(explainStatusBarBackend().layer).toBe('custom');
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
it('reports host layer when a host backend is installed', () => {
|
|
263
|
-
installStatusBarHostBackend(fakeBackend());
|
|
264
|
-
expect(explainStatusBarBackend().layer).toBe('host');
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
it('reports conflict when two different host backends are installed', () => {
|
|
268
|
-
installStatusBarHostBackend(fakeBackend());
|
|
269
|
-
installStatusBarHostBackend(fakeBackend());
|
|
270
|
-
expect(explainStatusBarBackend().conflict).toBe(true);
|
|
271
|
-
});
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
describe('getStatusBarBackend', () => {
|
|
275
|
-
it('falls back to a web backend', () => {
|
|
276
|
-
expect(getStatusBarBackend()).not.toBeNull();
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
it('returns the registered backend', () => {
|
|
280
|
-
const backend = fakeBackend();
|
|
281
|
-
setStatusBarBackend(backend);
|
|
282
|
-
expect(getStatusBarBackend()).toBe(backend);
|
|
283
|
-
});
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
describe('getStatusBarBackend (sentinel)', () => {
|
|
287
|
-
it('no-ops style/visible/overlay/subscribe without throwing', () => {
|
|
288
|
-
const backend = getStatusBarBackend();
|
|
289
|
-
expect(() => {
|
|
290
|
-
backend.setStyle('light');
|
|
291
|
-
backend.setVisible(false, 'slide');
|
|
292
|
-
backend.setOverlaysContent(true);
|
|
293
|
-
const unsub = backend.subscribe(() => {});
|
|
294
|
-
unsub();
|
|
295
|
-
}).not.toThrow();
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
it('getInfo returns -1 height and defaults', () => {
|
|
299
|
-
const backend = getStatusBarBackend();
|
|
300
|
-
const info = createStatusBarInfo();
|
|
301
|
-
backend.getInfo(info);
|
|
302
|
-
expect(info.height).toBe(-1);
|
|
303
|
-
expect(info.visible).toBe(true);
|
|
304
|
-
expect(info.color).toBe(0);
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
it('setBackgroundColor is a no-op', () => {
|
|
308
|
-
const backend = getStatusBarBackend();
|
|
309
|
-
expect(() => backend.setBackgroundColor(0xff0000ff)).not.toThrow();
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
it('subscribe returns a no-op unsubscribe function', () => {
|
|
313
|
-
const backend = getStatusBarBackend();
|
|
314
|
-
const unsub = backend.subscribe(() => {});
|
|
315
|
-
expect(() => unsub()).not.toThrow();
|
|
71
|
+
it('detaches its subscription', () => {
|
|
72
|
+
const provider = fakeHost('light');
|
|
73
|
+
const bar = statusbar.createStatusBar();
|
|
74
|
+
statusbar.attachStatusBar(provider.host, bar);
|
|
75
|
+
statusbar.disposeStatusBar(bar);
|
|
76
|
+
expect(provider.unsubscribe).toHaveBeenCalledOnce();
|
|
316
77
|
});
|
|
317
78
|
});
|
|
318
79
|
|
|
319
80
|
describe('getStatusBarHeight', () => {
|
|
320
|
-
it('
|
|
321
|
-
const backend = fakeBackend();
|
|
322
|
-
setStatusBarBackend(backend);
|
|
323
|
-
expect(getStatusBarHeight()).toBe(42);
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
it('returns -1 when the web backend reports unknown height', () => {
|
|
327
|
-
document.head.querySelectorAll('meta[name="theme-color"]').forEach((m) => m.remove());
|
|
328
|
-
// Web backend always returns -1 for height.
|
|
329
|
-
expect(getStatusBarHeight()).toBe(-1);
|
|
330
|
-
});
|
|
81
|
+
it('reads from the passed host', () => expect(statusbar.getStatusBarHeight(fakeHost('light').host)).toBe(20));
|
|
331
82
|
});
|
|
332
83
|
|
|
333
84
|
describe('getStatusBarInfo', () => {
|
|
334
|
-
it('fills
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
backend.style = 'light';
|
|
338
|
-
backend.visible = false;
|
|
339
|
-
backend.overlay = true;
|
|
340
|
-
setStatusBarBackend(backend);
|
|
341
|
-
const out = createStatusBarInfo();
|
|
342
|
-
const result = getStatusBarInfo(out);
|
|
343
|
-
expect(result).toBe(out); // returns `out`
|
|
344
|
-
expect(out.color).toBe(0x112233ff);
|
|
345
|
-
expect(out.style).toBe('light');
|
|
346
|
-
expect(out.visible).toBe(false);
|
|
347
|
-
expect(out.overlaysContent).toBe(true);
|
|
348
|
-
expect(out.height).toBe(42);
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
it('is alias-safe: out may be reused across calls', () => {
|
|
352
|
-
const backend = fakeBackend();
|
|
353
|
-
setStatusBarBackend(backend);
|
|
354
|
-
const out = createStatusBarInfo();
|
|
355
|
-
getStatusBarInfo(out);
|
|
356
|
-
backend.style = 'dark';
|
|
357
|
-
getStatusBarInfo(out);
|
|
85
|
+
it('fills and returns the caller out object', () => {
|
|
86
|
+
const out = statusbar.createStatusBarInfo();
|
|
87
|
+
expect(statusbar.getStatusBarInfo(fakeHost('dark').host, out)).toBe(out);
|
|
358
88
|
expect(out.style).toBe('dark');
|
|
359
89
|
});
|
|
360
90
|
});
|
|
361
91
|
|
|
362
92
|
describe('hasStatusBarStyleEntry', () => {
|
|
363
|
-
it('
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
expect(hasStatusBarStyleEntry(handle)).toBe(false);
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
it('is false for an unknown or invalid handle', () => {
|
|
373
|
-
expect(hasStatusBarStyleEntry(-1)).toBe(false);
|
|
374
|
-
expect(hasStatusBarStyleEntry(99999)).toBe(false);
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
it('stays true for an entry below one that was popped', () => {
|
|
378
|
-
const backend = fakeBackend();
|
|
379
|
-
setStatusBarBackend(backend);
|
|
380
|
-
const bottom = pushStatusBarStyleEntry({ style: 'dark' });
|
|
381
|
-
const top = pushStatusBarStyleEntry({ style: 'light' });
|
|
382
|
-
popStatusBarStyleEntry(top);
|
|
383
|
-
expect(hasStatusBarStyleEntry(bottom)).toBe(true);
|
|
384
|
-
});
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
describe('installStatusBarHostBackend', () => {
|
|
388
|
-
afterEach(() => resetStatusBarBackendForTest());
|
|
389
|
-
|
|
390
|
-
it('installs a host backend that getStatusBarBackend returns', () => {
|
|
391
|
-
const backend = fakeBackend();
|
|
392
|
-
installStatusBarHostBackend(backend);
|
|
393
|
-
expect(getStatusBarBackend()).toBe(backend);
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
it('is first-host-wins: a second different backend sets conflict', () => {
|
|
397
|
-
const first = fakeBackend();
|
|
398
|
-
const second = fakeBackend();
|
|
399
|
-
installStatusBarHostBackend(first);
|
|
400
|
-
installStatusBarHostBackend(second);
|
|
401
|
-
expect(getStatusBarBackend()).toBe(first);
|
|
402
|
-
expect(explainStatusBarBackend().conflict).toBe(true);
|
|
403
|
-
});
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
describe('observeStatusBarHostResult', () => {
|
|
407
|
-
afterEach(() => resetStatusBarBackendForTest());
|
|
408
|
-
|
|
409
|
-
it('records a successful observation', () => {
|
|
410
|
-
installStatusBarHostBackend(fakeBackend());
|
|
411
|
-
observeStatusBarHostResult('setBackgroundColor', true);
|
|
412
|
-
const explanation = explainStatusBarBackend();
|
|
413
|
-
expect(explanation.operation).toBe('setBackgroundColor');
|
|
414
|
-
expect(explanation.viability).toBe('available');
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
it('records a failed observation', () => {
|
|
418
|
-
installStatusBarHostBackend(fakeBackend());
|
|
419
|
-
observeStatusBarHostResult('setBackgroundColor', false);
|
|
420
|
-
expect(explainStatusBarBackend().viability).toBe('runtime-api-unavailable');
|
|
93
|
+
it('tracks handles within one explicit host', () => {
|
|
94
|
+
const provider = fakeHost('light');
|
|
95
|
+
const handle = statusbar.pushStatusBarStyleEntry(provider.host, { style: 'dark' });
|
|
96
|
+
expect(statusbar.hasStatusBarStyleEntry(provider.host, handle)).toBe(true);
|
|
97
|
+
statusbar.popStatusBarStyleEntry(provider.host, handle);
|
|
98
|
+
expect(statusbar.hasStatusBarStyleEntry(provider.host, handle)).toBe(false);
|
|
421
99
|
});
|
|
422
100
|
});
|
|
423
101
|
|
|
424
102
|
describe('packedRgbaToHexColor', () => {
|
|
425
|
-
it('
|
|
426
|
-
expect(packedRgbaToHexColor(0xff0000ff)).toBe('#ff0000');
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
it('converts black', () => {
|
|
430
|
-
expect(packedRgbaToHexColor(0x000000ff)).toBe('#000000');
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
it('converts white', () => {
|
|
434
|
-
expect(packedRgbaToHexColor(0xffffffff)).toBe('#ffffff');
|
|
435
|
-
});
|
|
103
|
+
it('drops alpha and pads hex digits', () => expect(statusbar.packedRgbaToHexColor(0x01020304)).toBe('#010203'));
|
|
436
104
|
});
|
|
437
105
|
|
|
438
106
|
describe('popStatusBarStyleEntry', () => {
|
|
439
|
-
it('
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
it('restores the pre-push value rather than leaving the popped entry applied', () => {
|
|
445
|
-
// The regression this replaces: the old assertion stopped at "the pop does not throw", and the
|
|
446
|
-
// implementation matched it — popping re-merged the stack, found no entry setting `style`, and
|
|
447
|
-
// called no setter, so the OS kept the popped entry's value forever. A style stack whose whole
|
|
448
|
-
// purpose is "restore the previous state on unmount" did not restore.
|
|
449
|
-
const backend = fakeBackend();
|
|
450
|
-
backend.style = 'default';
|
|
451
|
-
setStatusBarBackend(backend);
|
|
452
|
-
const handle = pushStatusBarStyleEntry({ style: 'dark' });
|
|
453
|
-
expect(backend.style).toBe('dark');
|
|
454
|
-
popStatusBarStyleEntry(handle);
|
|
455
|
-
expect(backend.style).toBe('default');
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
it('restores every field the popped entry had set', () => {
|
|
459
|
-
const backend = fakeBackend();
|
|
460
|
-
backend.style = 'light';
|
|
461
|
-
backend.visible = true;
|
|
462
|
-
backend.color = 0x112233ff;
|
|
463
|
-
backend.overlay = false;
|
|
464
|
-
setStatusBarBackend(backend);
|
|
465
|
-
const handle = pushStatusBarStyleEntry({
|
|
466
|
-
color: 0xaabbccff,
|
|
467
|
-
overlaysContent: true,
|
|
468
|
-
style: 'dark',
|
|
469
|
-
visible: false,
|
|
470
|
-
});
|
|
471
|
-
popStatusBarStyleEntry(handle);
|
|
472
|
-
expect(backend.style).toBe('light');
|
|
473
|
-
expect(backend.visible).toBe(true);
|
|
474
|
-
expect(backend.color).toBe(0x112233ff);
|
|
475
|
-
expect(backend.overlay).toBe(false);
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
it('falls back to the entry below before the baseline', () => {
|
|
479
|
-
const backend = fakeBackend();
|
|
480
|
-
backend.style = 'default';
|
|
481
|
-
setStatusBarBackend(backend);
|
|
482
|
-
pushStatusBarStyleEntry({ style: 'dark' });
|
|
483
|
-
const top = pushStatusBarStyleEntry({ style: 'light' });
|
|
484
|
-
expect(backend.style).toBe('light');
|
|
485
|
-
popStatusBarStyleEntry(top);
|
|
486
|
-
expect(backend.style).toBe('dark');
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
it('restores after popping out of order', () => {
|
|
490
|
-
const backend = fakeBackend();
|
|
491
|
-
backend.style = 'default';
|
|
492
|
-
setStatusBarBackend(backend);
|
|
493
|
-
const bottom = pushStatusBarStyleEntry({ style: 'dark' });
|
|
494
|
-
const top = pushStatusBarStyleEntry({ visible: false });
|
|
495
|
-
popStatusBarStyleEntry(bottom);
|
|
496
|
-
expect(backend.style).toBe('default');
|
|
497
|
-
expect(backend.visible).toBe(false);
|
|
498
|
-
popStatusBarStyleEntry(top);
|
|
499
|
-
expect(backend.visible).toBe(true);
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
it('captures a fresh baseline after the stack empties and is pushed again', () => {
|
|
503
|
-
const backend = fakeBackend();
|
|
504
|
-
backend.style = 'default';
|
|
505
|
-
setStatusBarBackend(backend);
|
|
506
|
-
popStatusBarStyleEntry(pushStatusBarStyleEntry({ style: 'dark' }));
|
|
507
|
-
// A change made outside the stack becomes the new baseline for the next push.
|
|
508
|
-
setStatusBarStyle('light');
|
|
509
|
-
popStatusBarStyleEntry(pushStatusBarStyleEntry({ style: 'dark' }));
|
|
510
|
-
expect(backend.style).toBe('light');
|
|
107
|
+
it('restores the captured baseline', () => {
|
|
108
|
+
const provider = fakeHost('light');
|
|
109
|
+
const handle = statusbar.pushStatusBarStyleEntry(provider.host, { style: 'dark' });
|
|
110
|
+
statusbar.popStatusBarStyleEntry(provider.host, handle);
|
|
111
|
+
expect(provider.setStyle).toHaveBeenLastCalledWith('light');
|
|
511
112
|
});
|
|
512
113
|
});
|
|
513
114
|
|
|
514
115
|
describe('pushStatusBarStyleEntry', () => {
|
|
515
|
-
it('
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
pushStatusBarStyleEntry({
|
|
519
|
-
expect(
|
|
520
|
-
expect(
|
|
521
|
-
});
|
|
522
|
-
|
|
523
|
-
it('later entries win per-field over earlier entries', () => {
|
|
524
|
-
const backend = fakeBackend();
|
|
525
|
-
setStatusBarBackend(backend);
|
|
526
|
-
pushStatusBarStyleEntry({ style: 'dark' });
|
|
527
|
-
pushStatusBarStyleEntry({ style: 'light' });
|
|
528
|
-
expect(backend.style).toBe('light');
|
|
529
|
-
});
|
|
530
|
-
|
|
531
|
-
it('returns unique handles', () => {
|
|
532
|
-
const backend = fakeBackend();
|
|
533
|
-
setStatusBarBackend(backend);
|
|
534
|
-
const h1 = pushStatusBarStyleEntry({ style: 'dark' });
|
|
535
|
-
const h2 = pushStatusBarStyleEntry({ style: 'light' });
|
|
536
|
-
expect(h1).not.toBe(h2);
|
|
537
|
-
popStatusBarStyleEntry(h1);
|
|
538
|
-
popStatusBarStyleEntry(h2);
|
|
539
|
-
});
|
|
540
|
-
|
|
541
|
-
it('fields not set fall through to lower entries', () => {
|
|
542
|
-
const backend = fakeBackend();
|
|
543
|
-
setStatusBarBackend(backend);
|
|
544
|
-
pushStatusBarStyleEntry({ style: 'dark' });
|
|
545
|
-
pushStatusBarStyleEntry({ visible: false }); // no style set → falls through
|
|
546
|
-
expect(backend.style).toBe('dark');
|
|
547
|
-
expect(backend.visible).toBe(false);
|
|
548
|
-
});
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
describe('resetStatusBarBackendForTest', () => {
|
|
552
|
-
it('clears all backend slots', () => {
|
|
553
|
-
const backend = fakeBackend();
|
|
554
|
-
setStatusBarBackend(backend);
|
|
555
|
-
installStatusBarHostBackend(fakeBackend());
|
|
556
|
-
observeStatusBarHostResult('setBackgroundColor', true);
|
|
557
|
-
resetStatusBarBackendForTest();
|
|
558
|
-
expect(getStatusBarBackend()).not.toBe(backend);
|
|
559
|
-
expect(explainStatusBarBackend().layer).toBe('host-not-enabled');
|
|
560
|
-
expect(explainStatusBarBackend().conflict).toBe(false);
|
|
561
|
-
expect(explainStatusBarBackend().viability).toBe('unobserved');
|
|
562
|
-
});
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
describe('setStatusBarBackend', () => {
|
|
566
|
-
it('clears back to the web fallback when passed null', () => {
|
|
567
|
-
setStatusBarBackend(fakeBackend());
|
|
568
|
-
setStatusBarBackend(null);
|
|
569
|
-
expect(getStatusBarBackend()).not.toBeNull();
|
|
116
|
+
it('merges unset fields through the stack', () => {
|
|
117
|
+
const provider = fakeHost('light');
|
|
118
|
+
statusbar.pushStatusBarStyleEntry(provider.host, { style: 'dark' });
|
|
119
|
+
statusbar.pushStatusBarStyleEntry(provider.host, { visible: false });
|
|
120
|
+
expect(provider.setStyle).toHaveBeenCalledTimes(1);
|
|
121
|
+
expect(provider.setVisible).toHaveBeenLastCalledWith(false, 'none');
|
|
570
122
|
});
|
|
571
123
|
});
|
|
572
124
|
|
|
573
125
|
describe('setStatusBarColor', () => {
|
|
574
|
-
it('
|
|
575
|
-
const
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
expect(backend.color).toBe(0x123456ff);
|
|
579
|
-
expect(backend.animatedColor).toBeUndefined();
|
|
580
|
-
});
|
|
581
|
-
|
|
582
|
-
it('forwards the animated flag when provided', () => {
|
|
583
|
-
const backend = fakeBackend();
|
|
584
|
-
setStatusBarBackend(backend);
|
|
585
|
-
setStatusBarColor(0x123456ff, true);
|
|
586
|
-
expect(backend.animatedColor).toBe(true);
|
|
126
|
+
it('uses only the color slot', () => {
|
|
127
|
+
const provider = fakeHost('light');
|
|
128
|
+
statusbar.setStatusBarColor(provider.host, 0x112233ff, true);
|
|
129
|
+
expect(provider.setBackgroundColor).toHaveBeenCalledWith(0x112233ff, true);
|
|
587
130
|
});
|
|
588
131
|
});
|
|
589
132
|
|
|
590
133
|
describe('setStatusBarOverlaysContent', () => {
|
|
591
|
-
it('
|
|
592
|
-
const
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
expect(backend.overlay).toBe(true);
|
|
134
|
+
it('uses only the overlays slot', () => {
|
|
135
|
+
const provider = fakeHost('light');
|
|
136
|
+
statusbar.setStatusBarOverlaysContent(provider.host, true);
|
|
137
|
+
expect(provider.setOverlaysContent).toHaveBeenCalledWith(true);
|
|
596
138
|
});
|
|
597
139
|
});
|
|
598
140
|
|
|
599
141
|
describe('setStatusBarStyle', () => {
|
|
600
|
-
it('
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
expect(backend.style).toBe('dark');
|
|
142
|
+
it('uses only the style slot', () => {
|
|
143
|
+
const provider = fakeHost('light');
|
|
144
|
+
statusbar.setStatusBarStyle(provider.host, 'dark');
|
|
145
|
+
expect(provider.setStyle).toHaveBeenCalledWith('dark');
|
|
605
146
|
});
|
|
606
147
|
});
|
|
607
148
|
|
|
608
149
|
describe('setStatusBarVisible', () => {
|
|
609
|
-
it('
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
expect(backend.visible).toBe(false);
|
|
614
|
-
});
|
|
615
|
-
|
|
616
|
-
it('forwards animation parameter to the active backend', () => {
|
|
617
|
-
const backend = fakeBackend();
|
|
618
|
-
setStatusBarBackend(backend);
|
|
619
|
-
setStatusBarVisible(false, 'fade');
|
|
620
|
-
expect(backend.animation).toBe('fade');
|
|
150
|
+
it('uses only the visibility slot', () => {
|
|
151
|
+
const provider = fakeHost('light');
|
|
152
|
+
statusbar.setStatusBarVisible(provider.host, false, 'fade');
|
|
153
|
+
expect(provider.setVisible).toHaveBeenCalledWith(false, 'fade');
|
|
621
154
|
});
|
|
622
155
|
});
|
|
156
|
+
|
|
157
|
+
function fakeHost(initialStyle: StatusBarStyle) {
|
|
158
|
+
let listener: (() => void) | null = null;
|
|
159
|
+
const unsubscribe = vi.fn(() => {
|
|
160
|
+
listener = null;
|
|
161
|
+
});
|
|
162
|
+
const setBackgroundColor = vi.fn();
|
|
163
|
+
const setOverlaysContent = vi.fn();
|
|
164
|
+
const setStyle = vi.fn();
|
|
165
|
+
const setVisible = vi.fn();
|
|
166
|
+
const host: HasUiStatusBarChange & HasUiStatusBarStyleStack = {
|
|
167
|
+
ui: {
|
|
168
|
+
statusBarChange: {
|
|
169
|
+
subscribe(next) {
|
|
170
|
+
listener = next;
|
|
171
|
+
return unsubscribe;
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
statusBarColor: { setBackgroundColor },
|
|
175
|
+
statusBarInfo: {
|
|
176
|
+
getInfo(out) {
|
|
177
|
+
Object.assign(out, {
|
|
178
|
+
color: 0,
|
|
179
|
+
height: 20,
|
|
180
|
+
overlaysContent: false,
|
|
181
|
+
style: initialStyle,
|
|
182
|
+
visible: true,
|
|
183
|
+
});
|
|
184
|
+
return out;
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
statusBarOverlays: { setOverlaysContent },
|
|
188
|
+
statusBarStyle: { setStyle },
|
|
189
|
+
statusBarVisibility: { setVisible },
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
return {
|
|
193
|
+
emit: () => listener?.(),
|
|
194
|
+
host,
|
|
195
|
+
setBackgroundColor,
|
|
196
|
+
setOverlaysContent,
|
|
197
|
+
setStyle,
|
|
198
|
+
setVisible,
|
|
199
|
+
unsubscribe,
|
|
200
|
+
};
|
|
201
|
+
}
|