@applicaster/zapp-react-native-utils 16.0.0-alpha.8101951023 → 16.0.0-alpha.8114431136
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/actionUtils/index.ts +3 -1
- package/actionsExecutor/actions/__tests__/navigateToScreen.test.ts +139 -5
- package/actionsExecutor/actions/__tests__/openBottomSheet.customContent.test.ts +24 -0
- package/actionsExecutor/actions/__tests__/openBottomSheet.inlineItems.test.ts +88 -0
- package/actionsExecutor/actions/navigateToScreen.ts +29 -45
- package/actionsExecutor/actions/openBottomSheet.ts +15 -5
- package/appUtils/playerManager/OverlayObserver/__tests__/getUpcomingQueue.test.ts +67 -0
- package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.ts +15 -2
- package/appUtils/playerManager/__tests__/playerNative.test.ts +76 -0
- package/appUtils/playerManager/__tests__/playerNativeCommand.test.ts +62 -0
- package/appUtils/playerManager/__tests__/playerToolbox.test.ts +97 -0
- package/appUtils/playerManager/__tests__/usePlayerControllerSetup.test.tsx +62 -0
- package/appUtils/playerManager/const.ts +13 -0
- package/appUtils/playerManager/index.ts +7 -0
- package/appUtils/playerManager/player.ts +10 -0
- package/appUtils/playerManager/playerNative.ts +22 -0
- package/appUtils/playerManager/playerNativeCommand.ts +3 -6
- package/appUtils/playerManager/playerToolbox.ts +157 -0
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +6 -0
- package/arrayUtils/__tests__/reorderByIds.test.ts +13 -0
- package/arrayUtils/index.ts +26 -43
- package/configurationUtils/__tests__/modalBlocksParser.test.ts +123 -0
- package/configurationUtils/modalBlocksParser.ts +38 -0
- package/focusManager/FocusManager.ts +26 -0
- package/focusManager/__tests__/FocusManager.test.ts +9 -0
- package/logger/__tests__/xrayToolbox.test.ts +69 -0
- package/logger/xrayToolbox.ts +67 -0
- package/manifestUtils/keys.js +0 -1
- package/manifestUtils/mobileAction/groups/__tests__/buildMobileActionButtonGroups.test.js +1 -1
- package/manifestUtils/mobileAction/groups/defaults.js +1 -1
- package/modalState/ModalOrchestrator.ts +1 -0
- package/modalState/__tests__/ModalOrchestrator.phase3.test.ts +16 -0
- package/modalState/types.ts +2 -0
- package/navigationUtils/xrayNavigator.ts +12 -0
- package/package.json +2 -2
- package/reactHooks/connection/index.tsx +8 -5
- package/reactHooks/feed/__tests__/useMarkPipesDataStale.test.tsx +243 -0
- package/reactHooks/feed/useMarkPipesDataStale.ts +68 -23
- package/riverComponetsMeasurementProvider/index.tsx +3 -1
- package/uiActionsRegistrator/__tests__/subscribe.test.ts +26 -0
- package/uiActionsRegistrator/registry.ts +16 -0
|
@@ -257,6 +257,69 @@ describe("getEmptyModalBlocksStructure", () => {
|
|
|
257
257
|
);
|
|
258
258
|
});
|
|
259
259
|
|
|
260
|
+
it("maps text label and now playing font keys onto the label style", () => {
|
|
261
|
+
const result = parseModalBlocksConfiguration(
|
|
262
|
+
{
|
|
263
|
+
"item.text_label1.ios.font_family": "SFProText-Bold",
|
|
264
|
+
"item.text_label1.letter_spacing": -0.4,
|
|
265
|
+
"item.text_label1.text_transform": "uppercase",
|
|
266
|
+
"now_playing_item.now_playing_label.ios.font_family":
|
|
267
|
+
"SFProText-Semibold",
|
|
268
|
+
"now_playing_item.now_playing_label.font_size": 18,
|
|
269
|
+
},
|
|
270
|
+
"queue-action"
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
expect(result.item.textLabel1.default).toEqual(
|
|
274
|
+
expect.objectContaining({
|
|
275
|
+
fontFamily: "SFProText-Bold",
|
|
276
|
+
letterSpacing: -0.4,
|
|
277
|
+
textTransform: "uppercase",
|
|
278
|
+
})
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
expect(result.nowPlayingItem?.nowPlayingLabel.default).toEqual(
|
|
282
|
+
expect.objectContaining({
|
|
283
|
+
fontFamily: "SFProText-Semibold",
|
|
284
|
+
fontSize: 18,
|
|
285
|
+
})
|
|
286
|
+
);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it("maps queue remove and reorder assets onto item action buttons", () => {
|
|
290
|
+
const result = parseModalBlocksConfiguration(
|
|
291
|
+
{
|
|
292
|
+
"item.remove_asset": "https://example.com/remove.png",
|
|
293
|
+
"item.reorder_handle_asset": "https://example.com/reorder.png",
|
|
294
|
+
},
|
|
295
|
+
"queue-action"
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
expect(result.item.actionButtons.default).toEqual(
|
|
299
|
+
expect.objectContaining({
|
|
300
|
+
removeAsset: "https://example.com/remove.png",
|
|
301
|
+
dragAsset: "https://example.com/reorder.png",
|
|
302
|
+
})
|
|
303
|
+
);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it("maps now playing play and pause assets onto action buttons", () => {
|
|
307
|
+
const result = parseModalBlocksConfiguration(
|
|
308
|
+
{
|
|
309
|
+
"now_playing_item.play_asset": "https://example.com/play.png",
|
|
310
|
+
"now_playing_item.pause_asset": "https://example.com/pause.png",
|
|
311
|
+
},
|
|
312
|
+
"queue-action"
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
expect(result.nowPlayingItem?.actionButtons.default).toEqual(
|
|
316
|
+
expect.objectContaining({
|
|
317
|
+
playAsset: "https://example.com/play.png",
|
|
318
|
+
pauseAsset: "https://example.com/pause.png",
|
|
319
|
+
})
|
|
320
|
+
);
|
|
321
|
+
});
|
|
322
|
+
|
|
260
323
|
it("should leave sectionHeader empty when keys are absent", () => {
|
|
261
324
|
const result = parseModalBlocksConfiguration(
|
|
262
325
|
{ "item.text_label1.font_color": "#FFFFFF" },
|
|
@@ -266,4 +329,64 @@ describe("getEmptyModalBlocksStructure", () => {
|
|
|
266
329
|
expect(result.sectionHeader.title).toEqual({ default: {} });
|
|
267
330
|
expect(result.sectionHeader.actionButtons).toEqual({ default: {} });
|
|
268
331
|
});
|
|
332
|
+
|
|
333
|
+
it("maps playlist button font family and the new green style keys", () => {
|
|
334
|
+
const result = parseModalBlocksConfiguration(
|
|
335
|
+
{
|
|
336
|
+
"button.title.ios.font_family": "GreycliffCF-Bold",
|
|
337
|
+
"button.title.inactive_font_color": "#111111",
|
|
338
|
+
"button.title.focused_font_color": "#222222",
|
|
339
|
+
"button.title.text_transform": "uppercase",
|
|
340
|
+
"button.background.inactive_color": "#999999",
|
|
341
|
+
"button.background.horizontal_gutter": 6,
|
|
342
|
+
"button.icons.width": 24,
|
|
343
|
+
"button.icons.height": 24,
|
|
344
|
+
},
|
|
345
|
+
"playlist-experience"
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
expect(result.button.title.default).toEqual(
|
|
349
|
+
expect.objectContaining({
|
|
350
|
+
fontFamily: "GreycliffCF-Bold",
|
|
351
|
+
inactiveFontColor: "#111111",
|
|
352
|
+
textTransform: "uppercase",
|
|
353
|
+
})
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
// "focused_" is a state prefix, so this key lands on the focused bucket.
|
|
357
|
+
expect(result.button.title.focused).toEqual(
|
|
358
|
+
expect.objectContaining({
|
|
359
|
+
fontColor: "#222222",
|
|
360
|
+
})
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
expect(result.button.background.default).toEqual(
|
|
364
|
+
expect.objectContaining({
|
|
365
|
+
inactiveColor: "#999999",
|
|
366
|
+
horizontalGutter: 6,
|
|
367
|
+
})
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
expect(result.button.icons.default).toEqual(
|
|
371
|
+
expect.objectContaining({
|
|
372
|
+
width: 24,
|
|
373
|
+
height: 24,
|
|
374
|
+
})
|
|
375
|
+
);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it("keeps a font-selector object on the button title", () => {
|
|
379
|
+
const result = parseModalBlocksConfiguration(
|
|
380
|
+
{
|
|
381
|
+
"button.title.ios.font_family": { fontFamily: "GreycliffCF-Bold" },
|
|
382
|
+
},
|
|
383
|
+
"playlist-experience"
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
expect(result.button.title.default).toEqual(
|
|
387
|
+
expect.objectContaining({
|
|
388
|
+
fontFamily: { fontFamily: "GreycliffCF-Bold" },
|
|
389
|
+
})
|
|
390
|
+
);
|
|
391
|
+
});
|
|
269
392
|
});
|
|
@@ -210,5 +210,43 @@ export const parseModalBlocksConfiguration = (
|
|
|
210
210
|
|
|
211
211
|
parseBlock("button", ["background", "title", "icons", "leading_icon"]);
|
|
212
212
|
|
|
213
|
+
// Leaf uploader keys. They are not `<block>_<sub>_<prop>` styles, so
|
|
214
|
+
// getAllSpecificStyles never sees them.
|
|
215
|
+
const assignLeafAsset = (
|
|
216
|
+
block: ModalBlocksConfig["item"] | undefined,
|
|
217
|
+
key: string,
|
|
218
|
+
prop: "playAsset" | "pauseAsset" | "removeAsset" | "dragAsset"
|
|
219
|
+
) => {
|
|
220
|
+
if (!block) return;
|
|
221
|
+
|
|
222
|
+
const value = configuration[key];
|
|
223
|
+
|
|
224
|
+
if (typeof value !== "string" || !value.trim()) return;
|
|
225
|
+
|
|
226
|
+
block.actionButtons = {
|
|
227
|
+
...block.actionButtons,
|
|
228
|
+
default: {
|
|
229
|
+
...(block.actionButtons?.default || {}),
|
|
230
|
+
[prop]: value,
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
assignLeafAsset(
|
|
236
|
+
result.nowPlayingItem,
|
|
237
|
+
"now_playing_item.play_asset",
|
|
238
|
+
"playAsset"
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
assignLeafAsset(
|
|
242
|
+
result.nowPlayingItem,
|
|
243
|
+
"now_playing_item.pause_asset",
|
|
244
|
+
"pauseAsset"
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
assignLeafAsset(result.item, "item.remove_asset", "removeAsset");
|
|
248
|
+
|
|
249
|
+
assignLeafAsset(result.item, "item.reorder_handle_asset", "dragAsset");
|
|
250
|
+
|
|
213
251
|
return result;
|
|
214
252
|
};
|
|
@@ -224,12 +224,38 @@ class FocusManager {
|
|
|
224
224
|
|
|
225
225
|
unregisterFocusable(focusableId: string) {
|
|
226
226
|
const node = this.tree.find(focusableId);
|
|
227
|
+
const parentNode = this.tree.findParent(focusableId);
|
|
227
228
|
|
|
228
229
|
this.focusableComponents = this.focusableComponents.filter(
|
|
229
230
|
(c) => c !== node?.component
|
|
230
231
|
);
|
|
231
232
|
|
|
232
233
|
this.tree.remove(focusableId);
|
|
234
|
+
|
|
235
|
+
if (this._focusedId === focusableId) {
|
|
236
|
+
this._focusedId = null;
|
|
237
|
+
this.retainFocus(parentNode);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Empty groups drop their focusable list after the feed fails, which is
|
|
242
|
+
// after Layout's one-shot focus pass. Land on the nearest ancestor that is
|
|
243
|
+
// still mounted so key handlers keep a target.
|
|
244
|
+
private retainFocus(node: TreeNode | null) {
|
|
245
|
+
let current = node;
|
|
246
|
+
|
|
247
|
+
while (current) {
|
|
248
|
+
const ref = current.component;
|
|
249
|
+
|
|
250
|
+
if (ref?.current?.props?.id) {
|
|
251
|
+
this.setFocusLocal(ref);
|
|
252
|
+
ref.current.onFocus?.(ref.current, {}, undefined);
|
|
253
|
+
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
current = current.parentId ? this.tree.find(current.parentId) : null;
|
|
258
|
+
}
|
|
233
259
|
}
|
|
234
260
|
|
|
235
261
|
private setNextFocus(
|
|
@@ -231,6 +231,15 @@ describe("FocusManager", () => {
|
|
|
231
231
|
).toBeFalsy();
|
|
232
232
|
});
|
|
233
233
|
|
|
234
|
+
it("moves focus to the mounted parent when the focused element unregisters", () => {
|
|
235
|
+
focusManager.setFocus("child1");
|
|
236
|
+
|
|
237
|
+
focusManager.unregisterFocusable("child1");
|
|
238
|
+
|
|
239
|
+
expect(focusManager.focusedId).toBe("group");
|
|
240
|
+
expect(focusManager.focused).toBe(group.current);
|
|
241
|
+
});
|
|
242
|
+
|
|
234
243
|
it("focusManager registerFocusable should be defined", () => {
|
|
235
244
|
expect(focusManager.registerFocusable).toBeDefined();
|
|
236
245
|
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const xray = require("@applicaster/quick-brick-xray");
|
|
2
|
+
|
|
3
|
+
const toolbox = { name: "test", description: "", tools: [] };
|
|
4
|
+
|
|
5
|
+
const load = (): typeof import("../xrayToolbox") => {
|
|
6
|
+
let module;
|
|
7
|
+
|
|
8
|
+
jest.isolateModules(() => {
|
|
9
|
+
module = require("../xrayToolbox");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
return module;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
describe("registerXrayToolbox", () => {
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
delete xray.registerReactToolbox;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("registers once per name", async () => {
|
|
21
|
+
xray.registerReactToolbox = jest.fn().mockResolvedValue(undefined);
|
|
22
|
+
const { registerXrayToolbox } = load();
|
|
23
|
+
|
|
24
|
+
await expect(registerXrayToolbox(toolbox)).resolves.toBe(true);
|
|
25
|
+
await expect(registerXrayToolbox(toolbox)).resolves.toBe(true);
|
|
26
|
+
|
|
27
|
+
expect(xray.registerReactToolbox).toHaveBeenCalledTimes(1);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("resolves false without a toolbox API", async () => {
|
|
31
|
+
await expect(load().registerXrayToolbox(toolbox)).resolves.toBe(false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("keeps a failed registration instead of retrying it", async () => {
|
|
35
|
+
xray.registerReactToolbox = jest
|
|
36
|
+
.fn()
|
|
37
|
+
.mockRejectedValue(new Error("XRayToolboxBridge is not available"));
|
|
38
|
+
|
|
39
|
+
const { registerXrayToolbox } = load();
|
|
40
|
+
|
|
41
|
+
await expect(registerXrayToolbox(toolbox)).resolves.toBe(false);
|
|
42
|
+
await expect(registerXrayToolbox(toolbox)).resolves.toBe(false);
|
|
43
|
+
|
|
44
|
+
expect(xray.registerReactToolbox).toHaveBeenCalledTimes(1);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("registers new tools under the same name again (Fast Refresh)", async () => {
|
|
48
|
+
xray.registerReactToolbox = jest.fn().mockResolvedValue(undefined);
|
|
49
|
+
const { registerXrayToolbox } = load();
|
|
50
|
+
|
|
51
|
+
await registerXrayToolbox(toolbox);
|
|
52
|
+
await registerXrayToolbox({ ...toolbox, tools: [] });
|
|
53
|
+
|
|
54
|
+
expect(xray.registerReactToolbox).toHaveBeenCalledTimes(2);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("stops trying every toolbox once the bridge is missing", async () => {
|
|
58
|
+
xray.registerReactToolbox = jest.fn().mockRejectedValue(new Error("no"));
|
|
59
|
+
const { registerXrayToolbox } = load();
|
|
60
|
+
|
|
61
|
+
await registerXrayToolbox(toolbox);
|
|
62
|
+
|
|
63
|
+
await expect(
|
|
64
|
+
registerXrayToolbox({ ...toolbox, name: "other" })
|
|
65
|
+
).resolves.toBe(false);
|
|
66
|
+
|
|
67
|
+
expect(xray.registerReactToolbox).toHaveBeenCalledTimes(1);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { getXray } from ".";
|
|
2
|
+
|
|
3
|
+
/** One tool of an X-Ray React toolbox: an agent calls it by name with JSON arguments. */
|
|
4
|
+
export type XrayToolSpec = {
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
parameters?: Array<{
|
|
8
|
+
name: string;
|
|
9
|
+
type: "number" | "string";
|
|
10
|
+
description: string;
|
|
11
|
+
}>;
|
|
12
|
+
handler: (args: Record<string, unknown>) => unknown;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type XrayToolbox = {
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
tools: XrayToolSpec[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type RegisterReactToolbox = (toolbox: XrayToolbox) => Promise<void>;
|
|
22
|
+
|
|
23
|
+
const registered = new Map<
|
|
24
|
+
string,
|
|
25
|
+
{ tools: XrayToolSpec[]; registration: Promise<boolean> }
|
|
26
|
+
>();
|
|
27
|
+
|
|
28
|
+
/** Set once a registration fails: the build has no X-Ray bridge, which cannot appear later. */
|
|
29
|
+
let bridgeMissing = false;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Registers an X-Ray React toolbox and resolves whether it is registered. The same tools are
|
|
33
|
+
* registered once; new tools under the same name (Fast Refresh re-ran the module) replace them.
|
|
34
|
+
* A build without the X-Ray native bridge (release builds) rejects the first attempt and every
|
|
35
|
+
* later call resolves false at once, so callers on hot paths pay for one attempt only.
|
|
36
|
+
*/
|
|
37
|
+
export const registerXrayToolbox = (toolbox: XrayToolbox): Promise<boolean> => {
|
|
38
|
+
const cached = registered.get(toolbox.name);
|
|
39
|
+
|
|
40
|
+
if (cached?.tools === toolbox.tools) return cached.registration;
|
|
41
|
+
|
|
42
|
+
if (bridgeMissing) return Promise.resolve(false);
|
|
43
|
+
|
|
44
|
+
const register = (
|
|
45
|
+
getXray() as
|
|
46
|
+
| { registerReactToolbox?: RegisterReactToolbox }
|
|
47
|
+
| null
|
|
48
|
+
| undefined
|
|
49
|
+
)?.registerReactToolbox;
|
|
50
|
+
|
|
51
|
+
const registration = register
|
|
52
|
+
? register(toolbox).then(
|
|
53
|
+
() => true,
|
|
54
|
+
() => {
|
|
55
|
+
bridgeMissing = true;
|
|
56
|
+
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
: Promise.resolve(false);
|
|
61
|
+
|
|
62
|
+
if (!register) bridgeMissing = true;
|
|
63
|
+
|
|
64
|
+
registered.set(toolbox.name, { tools: toolbox.tools, registration });
|
|
65
|
+
|
|
66
|
+
return registration;
|
|
67
|
+
};
|
package/manifestUtils/keys.js
CHANGED
|
@@ -472,7 +472,6 @@ const MOBILE_ACTION_BUTTON_FIELDS = [
|
|
|
472
472
|
suffix: "assign action",
|
|
473
473
|
options: [
|
|
474
474
|
{ text: "primary navigation", value: "navigation_action" },
|
|
475
|
-
{ text: "secondary navigation", value: "secondary_navigation" },
|
|
476
475
|
{ text: "favorite", value: "local_storage_favourites_action" },
|
|
477
476
|
{ text: "more", value: "open-modal-bottom-sheet-cell-action" },
|
|
478
477
|
{ text: "add to calendar", value: "add_to_calendar_action" },
|
|
@@ -67,7 +67,7 @@ describe("buildMobileActionButtonGroups", () => {
|
|
|
67
67
|
);
|
|
68
68
|
|
|
69
69
|
expect(button2EnabledField.initial_value).toBe(false);
|
|
70
|
-
expect(button2AssignActionField.initial_value).toBe("
|
|
70
|
+
expect(button2AssignActionField.initial_value).toBe("trailer_action");
|
|
71
71
|
|
|
72
72
|
const button3Fields = groups[3].fields;
|
|
73
73
|
|
|
@@ -44,4 +44,20 @@ describe("ModalOrchestrator phase 3", () => {
|
|
|
44
44
|
expect(props.title).toBe("Sleep Timer");
|
|
45
45
|
expect(props.summary).toBe("remaining");
|
|
46
46
|
});
|
|
47
|
+
|
|
48
|
+
it("forwards the queue empty message onto the sheet props", () => {
|
|
49
|
+
modalOrchestrator.open({
|
|
50
|
+
header: { title: "Queue" },
|
|
51
|
+
content: {
|
|
52
|
+
title: "Queue",
|
|
53
|
+
items: [],
|
|
54
|
+
emptyMessage: "Queued items will be added here",
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const props = (modalStore.getState().modalState.screen as any)
|
|
59
|
+
?.modalBottomSheetContentProps;
|
|
60
|
+
|
|
61
|
+
expect(props.emptyMessage).toBe("Queued items will be added here");
|
|
62
|
+
});
|
|
47
63
|
});
|
package/modalState/types.ts
CHANGED
|
@@ -118,6 +118,8 @@ export interface Content {
|
|
|
118
118
|
dynamicCollection?: DynamicCollectionOptions;
|
|
119
119
|
themePluginId?: string;
|
|
120
120
|
styleOverrides?: Record<string, any>;
|
|
121
|
+
/** Shown under Next Up when the queue sheet has no upcoming items. */
|
|
122
|
+
emptyMessage?: string;
|
|
121
123
|
context?: Record<string, any>;
|
|
122
124
|
}
|
|
123
125
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The app navigator for the X-Ray toolboxes (nav, screen), which run outside React. The
|
|
3
|
+
* NavigationProvider sets it after each commit; it is a plain module variable, so updating it
|
|
4
|
+
* never renders anything.
|
|
5
|
+
*/
|
|
6
|
+
let navigator: QuickBrickAppNavigator | undefined;
|
|
7
|
+
|
|
8
|
+
export const setXrayNavigator = (value: QuickBrickAppNavigator | undefined) => {
|
|
9
|
+
navigator = value;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const getXrayNavigator = () => navigator;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-utils",
|
|
3
|
-
"version": "16.0.0-alpha.
|
|
3
|
+
"version": "16.0.0-alpha.8114431136",
|
|
4
4
|
"description": "Applicaster Zapp React Native utilities package",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@applicaster/applicaster-types": "16.0.0-alpha.
|
|
30
|
+
"@applicaster/applicaster-types": "16.0.0-alpha.8114431136",
|
|
31
31
|
"buffer": "^5.2.1",
|
|
32
32
|
"camelize": "^1.0.0",
|
|
33
33
|
"dayjs": "^1.11.10",
|
|
@@ -4,6 +4,11 @@ import { NetInfoState } from "@react-native-community/netinfo";
|
|
|
4
4
|
import { NetworkStatusContext } from "@applicaster/zapp-react-native-ui-components/Contexts/NetworkStatusContext";
|
|
5
5
|
import { isOnline } from "@applicaster/quick-brick-core/App/NetworkStatusProvider/utils";
|
|
6
6
|
|
|
7
|
+
type ConnectionInfo = NetInfoState & {
|
|
8
|
+
deviceStatus: NetStatus;
|
|
9
|
+
stillOfflineAfterResumeCount?: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
7
12
|
/**
|
|
8
13
|
* This hook returns connection type from RN NetInfo component or boolean for iConnected if justOnlineStatus flag is set
|
|
9
14
|
* @param {boolean} justOnlineStatus - flag if to return boolean for isConnected
|
|
@@ -12,16 +17,14 @@ import { isOnline } from "@applicaster/quick-brick-core/App/NetworkStatusProvide
|
|
|
12
17
|
|
|
13
18
|
export function useConnectionInfo(justOnlineStatus: true): boolean;
|
|
14
19
|
|
|
15
|
-
export function useConnectionInfo(
|
|
16
|
-
justOnlineStatus: false
|
|
17
|
-
): NetInfoState & { deviceStatus: NetStatus };
|
|
20
|
+
export function useConnectionInfo(justOnlineStatus: false): ConnectionInfo;
|
|
18
21
|
|
|
19
22
|
export function useConnectionInfo(
|
|
20
23
|
justOnlineStatus: boolean
|
|
21
|
-
): boolean |
|
|
24
|
+
): boolean | ConnectionInfo {
|
|
22
25
|
const connectionInfo = React.useContext(
|
|
23
26
|
NetworkStatusContext
|
|
24
|
-
) as
|
|
27
|
+
) as ConnectionInfo;
|
|
25
28
|
|
|
26
29
|
return justOnlineStatus ? isOnline(connectionInfo) === true : connectionInfo;
|
|
27
30
|
}
|