@applicaster/zapp-react-native-utils 12.0.0-alpha.5208030571 → 12.0.0-alpha.5230293331
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/analyticsUtils/AnalyticPlayerListener.ts +141 -0
- package/analyticsUtils/AnalyticsEvents/index.ts +13 -0
- package/analyticsUtils/AnalyticsEvents/searchEvents.ts +21 -0
- package/analyticsUtils/AnalyticsEvents/sendShareEvent.ts +21 -0
- package/analyticsUtils/PlayerAnalyticsManager.ts +90 -0
- package/analyticsUtils/analyticsPlugins.ts +25 -0
- package/analyticsUtils/events.ts +12 -0
- package/analyticsUtils/index.tsx +12 -0
- package/analyticsUtils/{analyticsTracker.ts → playerAnalyticsTracker.ts} +19 -10
- package/appUtils/accessibilityManager/const.ts +50 -0
- package/appUtils/accessibilityManager/hooks.ts +40 -0
- package/appUtils/accessibilityManager/index.ts +166 -0
- package/appUtils/accessibilityManager/logger.ts +5 -0
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +2 -3
- package/appUtils/focusManager/__tests__/focusManager.test.js +4 -24
- package/appUtils/focusManager/__tests__/index.test.ts +2 -3
- package/appUtils/focusManager/index.ts +38 -118
- package/appUtils/focusManager/treeDataStructure/Utils/index.js +14 -38
- package/appUtils/focusManager/utils/index.ts +46 -11
- package/appUtils/focusManagerAux/index.ts +76 -0
- package/appUtils/focusManagerAux/utils/index.ts +100 -0
- package/appUtils/index.js +2 -0
- package/appUtils/keyCodes/keys.web.js +8 -0
- package/appUtils/keyCodes/keysByPlatform.js +11 -1
- package/appUtils/platform/const.ts +25 -0
- package/appUtils/platform/index.ts +3 -0
- package/appUtils/platform/platformUtils.ts +217 -0
- package/appUtils/playerManager/conts.ts +1 -0
- package/appUtils/playerManager/index.ts +20 -32
- package/appUtils/playerManager/nativePlayerManager.ts +12 -0
- package/appUtils/playerManager/player.ts +9 -3
- package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +57 -0
- package/appUtils/playerManager/playerNative.ts +6 -15
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +8 -2
- package/appUtils/playerManager/usePlayerState.tsx +19 -34
- package/arrayUtils/__tests__/isFilledArray.test.ts +63 -0
- package/arrayUtils/index.ts +5 -0
- package/booleanUtils/index.ts +4 -4
- package/cellUtils/__tests__/cellUtils.test.ts +9 -20
- package/cellUtils/index.ts +44 -17
- package/dateUtils/index.ts +11 -0
- package/functionUtils/index.ts +38 -0
- package/headersUtils/index.ts +1 -0
- package/index.d.ts +15 -0
- package/inflatedUrlUtils/index.ts +4 -4
- package/keyboardState/index.ts +0 -0
- package/localizationUtils/__tests__/localizationUtils.test.ts +72 -53
- package/localizationUtils/index.ts +13 -22
- package/manifestUtils/defaultManifestConfigurations/player.js +121 -0
- package/manifestUtils/keys.js +2 -1
- package/navigationUtils/index.ts +48 -0
- package/numberUtils/__tests__/isLikeNumber.test.ts +18 -0
- package/numberUtils/__tests__/toNumber.test.ts +2 -2
- package/numberUtils/index.ts +9 -18
- package/package.json +2 -2
- package/playerUtils/configurationGenerator.ts +120 -0
- package/playerUtils/imaScriptDom.ts +59 -0
- package/pluginUtils/index.ts +9 -0
- package/reactHooks/cell-click/__tests__/index.test.js +10 -1
- package/reactHooks/cell-click/index.ts +12 -12
- package/reactHooks/debugging/__tests__/index.test.js +81 -11
- package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +30 -70
- package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +6 -2
- package/reactHooks/feed/__tests__/useInflatedUrl.test.ts +18 -3
- package/reactHooks/feed/useBuildPipesUrl.ts +7 -10
- package/reactHooks/feed/useFeedRefresh.tsx +9 -7
- package/reactHooks/feed/useInflatedUrl.ts +44 -15
- package/reactHooks/layout/useDimensions/useDimensions.ts +2 -0
- package/reactHooks/navigation/__tests__/index.test.tsx +11 -8
- package/reactHooks/navigation/index.ts +46 -68
- package/reactHooks/player/useAutoSeek.tsx +9 -7
- package/reactHooks/resolvers/useCellResolver.ts +5 -1
- package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +21 -23
- package/reactHooks/screen/index.ts +2 -0
- package/reactHooks/screen/useScreenBackgroundColor.ts +23 -0
- package/reactHooks/screen/useScreenContext.ts +1 -2
- package/reactUtils/helpers.tsx +1 -1
- package/reactUtils/index.ts +104 -0
- package/types.ts +4 -0
- package/appUtils/focusManager/FocusableEvents.ts +0 -8
- package/appUtils/focusManager/treeDataStructure/Utils/__tests__/Utils_android.test.js +0 -1094
- package/appUtils/focusManager/treeDataStructure/Utils/index.android.js +0 -261
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
exports[`focusManager should be defined 1`] = `
|
|
4
4
|
Object {
|
|
5
5
|
"blur": [Function],
|
|
6
|
-
"disableAutomaticFocus": [Function],
|
|
7
6
|
"disableFocus": [Function],
|
|
8
|
-
"enableAutomaticFocus": [Function],
|
|
9
7
|
"enableFocus": [Function],
|
|
10
8
|
"focusableTree": Tree {
|
|
11
9
|
"loadingCounter": 0,
|
|
@@ -23,6 +21,7 @@ Object {
|
|
|
23
21
|
"getGroupById": [Function],
|
|
24
22
|
"getGroupRootById": [Function],
|
|
25
23
|
"invokeHandler": [Function],
|
|
24
|
+
"isCurrentFocusOnTheTopScreen": [Function],
|
|
26
25
|
"isFocusDisabled": [Function],
|
|
27
26
|
"isGroupItemFocused": [Function],
|
|
28
27
|
"longPress": [Function],
|
|
@@ -30,11 +29,11 @@ Object {
|
|
|
30
29
|
"on": [Function],
|
|
31
30
|
"press": [Function],
|
|
32
31
|
"pressOut": [Function],
|
|
32
|
+
"recoverFocus": [Function],
|
|
33
33
|
"register": [Function],
|
|
34
34
|
"removeHandler": [Function],
|
|
35
35
|
"setFocus": [Function],
|
|
36
36
|
"setInitialFocus": [Function],
|
|
37
|
-
"setupInitialFocus": [Function],
|
|
38
37
|
"unregister": [Function],
|
|
39
38
|
}
|
|
40
39
|
`;
|
|
@@ -14,10 +14,8 @@ describe("focusManager", () => {
|
|
|
14
14
|
expect(focusManager).toMatchSnapshot();
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
-
it("
|
|
17
|
+
it("manages to land initial focus", () => {
|
|
18
18
|
const mockSetFocus = jest.fn();
|
|
19
|
-
focusManager.setupInitialFocus("1", true);
|
|
20
|
-
focusManager.enableAutomaticFocus();
|
|
21
19
|
|
|
22
20
|
focusManager.register({
|
|
23
21
|
id: "1",
|
|
@@ -31,31 +29,13 @@ describe("focusManager", () => {
|
|
|
31
29
|
},
|
|
32
30
|
});
|
|
33
31
|
|
|
32
|
+
const { success } = focusManager.setInitialFocus();
|
|
33
|
+
|
|
34
|
+
expect(success).toBe(true);
|
|
34
35
|
expect(mockSetFocus).toBeCalledTimes(1);
|
|
35
36
|
expect(mockSetFocus).toBeCalledWith(null);
|
|
36
37
|
});
|
|
37
38
|
|
|
38
|
-
it("enableAutomaticFocus should block setting focus", () => {
|
|
39
|
-
const mockSetFocus = jest.fn();
|
|
40
|
-
focusManager.setupInitialFocus("1", true);
|
|
41
|
-
focusManager.disableAutomaticFocus();
|
|
42
|
-
|
|
43
|
-
focusManager.register({
|
|
44
|
-
id: "1",
|
|
45
|
-
component: {
|
|
46
|
-
setFocus: mockSetFocus,
|
|
47
|
-
willReceiveFocus: function () {},
|
|
48
|
-
isMounted: function () {
|
|
49
|
-
return true;
|
|
50
|
-
},
|
|
51
|
-
props: { groupId: "121" },
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
expect(mockSetFocus).toBeCalledTimes(0);
|
|
56
|
-
expect(mockSetFocus).not.toBeCalledWith(null);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
39
|
describe("register", () => {});
|
|
60
40
|
describe("unregister", () => {});
|
|
61
41
|
describe("find", () => {});
|
|
@@ -18,7 +18,6 @@ describe("focusManager", () => {
|
|
|
18
18
|
"on",
|
|
19
19
|
"invokeHandler",
|
|
20
20
|
"removeHandler",
|
|
21
|
-
"setupInitialFocus",
|
|
22
21
|
"longPress",
|
|
23
22
|
"pressOut",
|
|
24
23
|
"enableFocus",
|
|
@@ -27,10 +26,10 @@ describe("focusManager", () => {
|
|
|
27
26
|
"isFocusDisabled",
|
|
28
27
|
"getCurrentFocusNode",
|
|
29
28
|
"focusableTree",
|
|
30
|
-
"disableAutomaticFocus",
|
|
31
|
-
"enableAutomaticFocus",
|
|
32
29
|
"getGroupRootById",
|
|
33
30
|
"isGroupItemFocused",
|
|
31
|
+
"recoverFocus",
|
|
32
|
+
"isCurrentFocusOnTheTopScreen",
|
|
34
33
|
];
|
|
35
34
|
|
|
36
35
|
expectedMethods.forEach((method) => {
|
|
@@ -5,17 +5,14 @@ import {
|
|
|
5
5
|
} from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
6
6
|
|
|
7
7
|
import { Tree } from "./treeDataStructure/Tree";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
findFocusableNode,
|
|
10
|
+
findPriorityItem,
|
|
11
|
+
haveSameParentBeforeRoot,
|
|
12
|
+
} from "./treeDataStructure/Utils";
|
|
9
13
|
import { subscriber } from "../../functionUtils";
|
|
10
14
|
import * as FOCUS_EVENTS from "./events";
|
|
11
15
|
import { coreLogger } from "../../logger";
|
|
12
|
-
import {
|
|
13
|
-
waitForActiveScreen,
|
|
14
|
-
waitForContent,
|
|
15
|
-
getContentNode,
|
|
16
|
-
getActiveScreen,
|
|
17
|
-
} from "./utils";
|
|
18
|
-
|
|
19
16
|
const logger = coreLogger.addSubsystem("focusManager");
|
|
20
17
|
|
|
21
18
|
const isFocusEnabled = (focusableItem): boolean => {
|
|
@@ -55,21 +52,9 @@ export const focusManager = (function () {
|
|
|
55
52
|
// focusable node which currently holds the focus
|
|
56
53
|
let currentFocusNode = null;
|
|
57
54
|
let lastFocusedId = null;
|
|
58
|
-
let initialFocusOnContent = null;
|
|
59
55
|
let focusDisabled = false;
|
|
60
|
-
let focusAutoEnabled = false;
|
|
61
56
|
const eventHandler = subscriber();
|
|
62
57
|
|
|
63
|
-
function disableAutomaticFocus() {
|
|
64
|
-
logger.log({ message: "disableAutomaticFocus" });
|
|
65
|
-
focusAutoEnabled = false;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function enableAutomaticFocus() {
|
|
69
|
-
logger.log({ message: "focusManager: enableAutomaticFocus" });
|
|
70
|
-
focusAutoEnabled = true;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
58
|
function enableFocus() {
|
|
74
59
|
focusDisabled = false;
|
|
75
60
|
}
|
|
@@ -230,7 +215,7 @@ export const focusManager = (function () {
|
|
|
230
215
|
/**
|
|
231
216
|
* sets the initial focus when the screen loads, or when focus is lost
|
|
232
217
|
*/
|
|
233
|
-
function setInitialFocus(lastAddedParentNode?: any
|
|
218
|
+
function setInitialFocus(lastAddedParentNode?: any) {
|
|
234
219
|
const preferredFocus = findPriorityItem(
|
|
235
220
|
lastAddedParentNode?.children || focusableTree.root.children
|
|
236
221
|
);
|
|
@@ -238,8 +223,7 @@ export const focusManager = (function () {
|
|
|
238
223
|
logger.log({ message: "setInitialFocus", data: { preferredFocus } });
|
|
239
224
|
|
|
240
225
|
if (isNilOrEmpty(preferredFocus)) {
|
|
241
|
-
|
|
242
|
-
return;
|
|
226
|
+
return { success: false };
|
|
243
227
|
}
|
|
244
228
|
|
|
245
229
|
const focusCandidate = preferredFocus[0];
|
|
@@ -248,15 +232,18 @@ export const focusManager = (function () {
|
|
|
248
232
|
const currentFocusedId = R.path(["props", "id"], getCurrentFocus());
|
|
249
233
|
const nextFocusedId = R.path(["props", "id"], focusableItem);
|
|
250
234
|
|
|
251
|
-
if (
|
|
235
|
+
if (
|
|
236
|
+
isNotNil(currentFocusedId) &&
|
|
237
|
+
isNotNil(nextFocusedId) &&
|
|
238
|
+
currentFocusedId === nextFocusedId
|
|
239
|
+
) {
|
|
252
240
|
// we are going to move focus to the same node(already focused) -> nothing to do
|
|
253
|
-
return;
|
|
241
|
+
return { success: true };
|
|
254
242
|
}
|
|
255
243
|
|
|
256
|
-
if (
|
|
257
|
-
//
|
|
258
|
-
|
|
259
|
-
return;
|
|
244
|
+
if (focusableItem?.isGroup) {
|
|
245
|
+
// focusCandidate is a FocusableGroup, we are not able to land focus on group(only on Focusable)
|
|
246
|
+
return { success: false };
|
|
260
247
|
}
|
|
261
248
|
|
|
262
249
|
if (
|
|
@@ -264,10 +251,7 @@ export const focusManager = (function () {
|
|
|
264
251
|
!focusableItem.isGroup &&
|
|
265
252
|
isFocusEnabled(focusableItem)
|
|
266
253
|
) {
|
|
267
|
-
|
|
268
|
-
blur();
|
|
269
|
-
initialFocusOnContent = false;
|
|
270
|
-
}
|
|
254
|
+
blur();
|
|
271
255
|
|
|
272
256
|
logger.log({
|
|
273
257
|
message: "setInitialFocus – currentFocusNode",
|
|
@@ -279,7 +263,11 @@ export const focusManager = (function () {
|
|
|
279
263
|
currentFocusNode = focusCandidate;
|
|
280
264
|
focusableItem.willReceiveFocus(focusableItem, null);
|
|
281
265
|
focusableItem && setFocus(focusCandidate.id, null);
|
|
266
|
+
|
|
267
|
+
return { success: true };
|
|
282
268
|
}
|
|
269
|
+
|
|
270
|
+
return { success: false };
|
|
283
271
|
}
|
|
284
272
|
|
|
285
273
|
function treeLoaded(val, node) {
|
|
@@ -289,17 +277,8 @@ export const focusManager = (function () {
|
|
|
289
277
|
val,
|
|
290
278
|
node,
|
|
291
279
|
currentFocusNode,
|
|
292
|
-
initialFocusOnContent,
|
|
293
280
|
},
|
|
294
281
|
});
|
|
295
|
-
|
|
296
|
-
if ((val && !currentFocusNode) || initialFocusOnContent) {
|
|
297
|
-
logger.log({ message: `focusAutoEnabled ${focusAutoEnabled}` });
|
|
298
|
-
|
|
299
|
-
if (focusAutoEnabled) {
|
|
300
|
-
setInitialFocus(initialFocusOnContent ? node : undefined);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
282
|
}
|
|
304
283
|
|
|
305
284
|
/**
|
|
@@ -351,8 +330,6 @@ export const focusManager = (function () {
|
|
|
351
330
|
* @returns {React.Component} the registered component
|
|
352
331
|
*/
|
|
353
332
|
function registerGroup(id, component) {
|
|
354
|
-
// console.log("debug_2", "FM registerGroup", { id, component });
|
|
355
|
-
|
|
356
333
|
focusableTree.addNode({ id, component });
|
|
357
334
|
|
|
358
335
|
return component;
|
|
@@ -368,8 +345,6 @@ export const focusManager = (function () {
|
|
|
368
345
|
* @returns {React.Component} the registered component
|
|
369
346
|
*/
|
|
370
347
|
function registerItem(id, component) {
|
|
371
|
-
// console.log("debug_2", "FM registerItem", { id, component });
|
|
372
|
-
|
|
373
348
|
focusableTree.addNode({ id, component });
|
|
374
349
|
|
|
375
350
|
if (!currentFocusNode && id === lastFocusedId && !id.includes("menu")) {
|
|
@@ -386,8 +361,6 @@ export const focusManager = (function () {
|
|
|
386
361
|
* @param {String} id of the group to unregister
|
|
387
362
|
*/
|
|
388
363
|
function unregisterGroup(id) {
|
|
389
|
-
// console.log("debug_2", "FM unregisterGroup", { id });
|
|
390
|
-
|
|
391
364
|
focusableTree.findAndRemoveNode(id);
|
|
392
365
|
}
|
|
393
366
|
|
|
@@ -399,8 +372,6 @@ export const focusManager = (function () {
|
|
|
399
372
|
* @param {String} id of the Focusable to unregister
|
|
400
373
|
*/
|
|
401
374
|
function unregisterItem(id) {
|
|
402
|
-
// console.log("debug_2", "FM unregisterItem", { id });
|
|
403
|
-
|
|
404
375
|
focusableTree.findAndRemoveNode(id);
|
|
405
376
|
|
|
406
377
|
if (currentFocusNode && currentFocusNode.id === id) {
|
|
@@ -438,65 +409,21 @@ export const focusManager = (function () {
|
|
|
438
409
|
group ? unregisterGroup(id) : unregisterItem(id);
|
|
439
410
|
}
|
|
440
411
|
|
|
441
|
-
function
|
|
442
|
-
|
|
412
|
+
function isCurrentFocusOnTheTopScreen() {
|
|
413
|
+
const routes = R.pathOr([], ["root", "children"], focusableTree);
|
|
443
414
|
|
|
444
|
-
if (
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
setFocus(id);
|
|
415
|
+
if (routes.length <= 1) {
|
|
416
|
+
// there is only one or zero routes
|
|
417
|
+
return true;
|
|
448
418
|
}
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
async function setFocusOnMenu(currentRoute: string) {
|
|
452
|
-
try {
|
|
453
|
-
await waitForActiveScreen(currentRoute, focusableTree);
|
|
454
|
-
|
|
455
|
-
const navbar = R.find(
|
|
456
|
-
(child) => R.startsWith("navbar", child.id),
|
|
457
|
-
getActiveScreen(focusableTree).children
|
|
458
|
-
);
|
|
459
419
|
|
|
460
|
-
|
|
461
|
-
(child) => child.component.props.selected,
|
|
462
|
-
navbar?.children || []
|
|
463
|
-
)?.id;
|
|
464
|
-
|
|
465
|
-
if (selectedMenuItemId) {
|
|
466
|
-
// set focus on selected menu item
|
|
467
|
-
setFocus(selectedMenuItemId);
|
|
468
|
-
return;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
const firstMenuItemId = R.path(["children", 0, "id"], navbar);
|
|
472
|
-
|
|
473
|
-
if (firstMenuItemId) {
|
|
474
|
-
// set focus on first menu item
|
|
475
|
-
setFocus(firstMenuItemId);
|
|
476
|
-
}
|
|
477
|
-
} catch (error) {
|
|
478
|
-
// failed to put focus on top-menu => log it
|
|
479
|
-
}
|
|
420
|
+
return haveSameParentBeforeRoot(currentFocusNode, R.last(routes));
|
|
480
421
|
}
|
|
481
422
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
try {
|
|
487
|
-
await waitForActiveScreen(currentRoute, focusableTree);
|
|
488
|
-
await waitForContent(focusableTree);
|
|
489
|
-
|
|
490
|
-
const content = getContentNode(focusableTree);
|
|
491
|
-
|
|
492
|
-
const fallback = () => {
|
|
493
|
-
throw Error("Failed to land focus on content");
|
|
494
|
-
};
|
|
495
|
-
|
|
496
|
-
setInitialFocus(content, fallback);
|
|
497
|
-
} catch (e) {
|
|
498
|
-
// we've failed to put initial focus on content, put focus on menu as a last chance
|
|
499
|
-
setFocusOnMenu(currentRoute);
|
|
423
|
+
function recoverFocus() {
|
|
424
|
+
if (!isCurrentFocusOnTheTopScreen()) {
|
|
425
|
+
// We've failed to set focused node on the new screen => run focus recovery
|
|
426
|
+
setInitialFocus();
|
|
500
427
|
}
|
|
501
428
|
}
|
|
502
429
|
|
|
@@ -506,19 +433,17 @@ export const focusManager = (function () {
|
|
|
506
433
|
* @param {Object} direction of the scroll
|
|
507
434
|
*/
|
|
508
435
|
function moveFocus(direction) {
|
|
436
|
+
if (!isCurrentFocusOnTheTopScreen()) {
|
|
437
|
+
// We are opening new screen. We need to ignore movement of focus.
|
|
438
|
+
return false;
|
|
439
|
+
}
|
|
440
|
+
|
|
509
441
|
if (focusDisabled) return false;
|
|
510
442
|
|
|
511
443
|
if (currentFocusNode) {
|
|
512
444
|
currentFocusNode.component.willLoseFocus(currentFocusNode, direction);
|
|
513
445
|
const nextFocusable = findFocusableNode(direction, currentFocusNode);
|
|
514
446
|
|
|
515
|
-
// console.log("debug_2", "FM moveFocus", {
|
|
516
|
-
// direction: direction?.value,
|
|
517
|
-
// currentFocusNode,
|
|
518
|
-
// focusableTree,
|
|
519
|
-
// nextFocusable,
|
|
520
|
-
// });
|
|
521
|
-
|
|
522
447
|
if (nextFocusable?.component?.isMounted?.()) {
|
|
523
448
|
const nextRect = nextFocusable.component.getRect();
|
|
524
449
|
|
|
@@ -541,8 +466,6 @@ export const focusManager = (function () {
|
|
|
541
466
|
} else {
|
|
542
467
|
currentFocusNode.component.failedLostFocus(currentFocusNode, direction);
|
|
543
468
|
}
|
|
544
|
-
} else {
|
|
545
|
-
setInitialFocus();
|
|
546
469
|
}
|
|
547
470
|
}
|
|
548
471
|
|
|
@@ -602,7 +525,6 @@ export const focusManager = (function () {
|
|
|
602
525
|
on,
|
|
603
526
|
invokeHandler,
|
|
604
527
|
removeHandler,
|
|
605
|
-
setupInitialFocus,
|
|
606
528
|
longPress,
|
|
607
529
|
pressOut,
|
|
608
530
|
enableFocus,
|
|
@@ -611,11 +533,9 @@ export const focusManager = (function () {
|
|
|
611
533
|
isFocusDisabled,
|
|
612
534
|
getCurrentFocusNode,
|
|
613
535
|
focusableTree,
|
|
614
|
-
disableAutomaticFocus,
|
|
615
|
-
enableAutomaticFocus,
|
|
616
536
|
getGroupRootById,
|
|
617
537
|
isGroupItemFocused,
|
|
618
|
-
|
|
619
|
-
|
|
538
|
+
recoverFocus,
|
|
539
|
+
isCurrentFocusOnTheTopScreen,
|
|
620
540
|
};
|
|
621
541
|
})();
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { allPass, pathOr, startsWith, isNil } from "ramda";
|
|
2
2
|
import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
3
|
+
import { isZero } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
4
|
+
import { QUICK_BRICK_NAVBAR } from "@applicaster/quick-brick-core/const";
|
|
3
5
|
|
|
4
6
|
import { rejectItemByPath } from "../../../../objectUtils";
|
|
5
7
|
import {
|
|
@@ -145,7 +147,7 @@ const hasComponent = (target) => !!target?.component;
|
|
|
145
147
|
|
|
146
148
|
const hasComponentAndChildren = allPass([hasComponent, hasChildren]);
|
|
147
149
|
|
|
148
|
-
const positiveOrZero = (value) => value
|
|
150
|
+
const positiveOrZero = (value) => value > 0 || isZero(value);
|
|
149
151
|
|
|
150
152
|
const excludeFromFocusSearching = pathOr(false, [
|
|
151
153
|
"component",
|
|
@@ -153,12 +155,10 @@ const excludeFromFocusSearching = pathOr(false, [
|
|
|
153
155
|
"excludeFromFocusSearching",
|
|
154
156
|
]);
|
|
155
157
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const isNavBar = (target) => startsWith("navbar", target?.id);
|
|
158
|
+
const isNavBar = (target) => startsWith(QUICK_BRICK_NAVBAR, target?.id);
|
|
159
159
|
|
|
160
160
|
const ignoreMenuIfMultipleTargets = (nodes) => {
|
|
161
|
-
if (nodes.length > 1
|
|
161
|
+
if (nodes.length > 1) {
|
|
162
162
|
return nodes.filter((n) => !isNavBar(n?.target));
|
|
163
163
|
}
|
|
164
164
|
|
|
@@ -206,7 +206,7 @@ export function getTargetInDirection(
|
|
|
206
206
|
const overrideTarget = currentNode?.component?.props[overridePropName];
|
|
207
207
|
|
|
208
208
|
if (overrideTarget) {
|
|
209
|
-
const target = targetNodes.find((n) => n
|
|
209
|
+
const target = targetNodes.find((n) => n?.id === overrideTarget);
|
|
210
210
|
|
|
211
211
|
if (target) return target;
|
|
212
212
|
}
|
|
@@ -222,24 +222,10 @@ export function getTargetInDirection(
|
|
|
222
222
|
}))
|
|
223
223
|
.filter(({ distance }) => positiveOrZero(distance));
|
|
224
224
|
|
|
225
|
-
// if (isAllScreens(targets)) {
|
|
226
|
-
// return last(targets);
|
|
227
|
-
// }
|
|
228
|
-
|
|
229
225
|
const match = distances
|
|
230
226
|
.sort((a, b) => a.distance - b.distance) // sorts the distances
|
|
231
227
|
.filter(ignoreMenuIfMultipleTargets)[0]; // filters out multiple targets if necessary // gets the first element
|
|
232
228
|
|
|
233
|
-
// console.log("debug_2", "FM getTargetInDirection", {
|
|
234
|
-
// direction: direction?.value,
|
|
235
|
-
// currentNode,
|
|
236
|
-
// targetNodes,
|
|
237
|
-
// targets,
|
|
238
|
-
// comparisonRect,
|
|
239
|
-
// distances,
|
|
240
|
-
// match,
|
|
241
|
-
// });
|
|
242
|
-
|
|
243
229
|
return match?.target;
|
|
244
230
|
}
|
|
245
231
|
|
|
@@ -337,6 +323,10 @@ export const getNodeBeforeRoot = (node) => {
|
|
|
337
323
|
return undefined;
|
|
338
324
|
}
|
|
339
325
|
|
|
326
|
+
if (isNil(node.parent)) {
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
|
|
340
330
|
if (node.parent.id === "root") {
|
|
341
331
|
return node;
|
|
342
332
|
}
|
|
@@ -344,23 +334,9 @@ export const getNodeBeforeRoot = (node) => {
|
|
|
344
334
|
return getNodeBeforeRoot(node.parent);
|
|
345
335
|
};
|
|
346
336
|
|
|
347
|
-
export const
|
|
348
|
-
const
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
return screenForCurrentNode.id === screenForNextNode.id;
|
|
352
|
-
|
|
353
|
-
// console.log("debug_2", "isAllowedToMoveTo", {
|
|
354
|
-
// nodeBeforeRoot,
|
|
355
|
-
// });
|
|
356
|
-
|
|
357
|
-
// if (nodeBeforeRoot?.id === "navbar") {
|
|
358
|
-
// return true;
|
|
359
|
-
// }
|
|
360
|
-
|
|
361
|
-
// if (nodeBeforeRoot?.component?.props?.isActive) {
|
|
362
|
-
// return true;
|
|
363
|
-
// }
|
|
337
|
+
export const haveSameParentBeforeRoot = (currentNode, nextNode) => {
|
|
338
|
+
const routeForCurrentNode = getNodeBeforeRoot(currentNode);
|
|
339
|
+
const routeForNextNode = getNodeBeforeRoot(nextNode);
|
|
364
340
|
|
|
365
|
-
|
|
341
|
+
return routeForCurrentNode?.id === routeForNextNode?.id;
|
|
366
342
|
};
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { isNotNil } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
2
|
-
import { find, last, startsWith } from "ramda";
|
|
2
|
+
import { find, last, startsWith, pathOr } from "ramda";
|
|
3
|
+
import {
|
|
4
|
+
QUICK_BRICK_NAVBAR,
|
|
5
|
+
QUICK_BRICK_CONTENT,
|
|
6
|
+
} from "@applicaster/quick-brick-core/const";
|
|
3
7
|
|
|
4
8
|
// run check each 300 ms
|
|
5
9
|
// run this check too often could lead to performance penalty on low-end devices
|
|
6
|
-
const HOW_OFTEN_TO_CHECK_CONDITION = 300; //ms
|
|
10
|
+
const HOW_OFTEN_TO_CHECK_CONDITION = 300; // ms
|
|
7
11
|
|
|
8
12
|
type Props = {
|
|
9
13
|
maxTimeout: number;
|
|
@@ -12,16 +16,29 @@ type Props = {
|
|
|
12
16
|
|
|
13
17
|
export const waitUntil = ({ maxTimeout, conditionFn }: Props) => {
|
|
14
18
|
let interval;
|
|
19
|
+
let timeout;
|
|
20
|
+
|
|
15
21
|
return new Promise((resolve, reject) => {
|
|
22
|
+
// run interval check
|
|
16
23
|
interval = setInterval(() => {
|
|
17
24
|
if (conditionFn()) {
|
|
18
25
|
clearInterval(interval);
|
|
26
|
+
clearTimeout(timeout);
|
|
27
|
+
|
|
19
28
|
resolve(undefined);
|
|
20
29
|
}
|
|
21
30
|
}, HOW_OFTEN_TO_CHECK_CONDITION);
|
|
22
31
|
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
// max timeout when we need to stop interval check
|
|
33
|
+
timeout = setTimeout(() => {
|
|
34
|
+
clearInterval(interval);
|
|
35
|
+
clearTimeout(timeout);
|
|
36
|
+
|
|
37
|
+
const error = Error(
|
|
38
|
+
"Failed to wait satisfy condition. Max timeout is reached."
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
reject(error);
|
|
25
42
|
}, maxTimeout);
|
|
26
43
|
});
|
|
27
44
|
};
|
|
@@ -31,35 +48,53 @@ export const waitForActiveScreen = (currentRoute: string, focusableTree) => {
|
|
|
31
48
|
const routes = focusableTree.root.children;
|
|
32
49
|
|
|
33
50
|
const route = find((route) => route.id === currentRoute, routes);
|
|
51
|
+
|
|
34
52
|
return isNotNil(route);
|
|
35
53
|
};
|
|
36
54
|
|
|
37
55
|
return waitUntil({
|
|
38
|
-
maxTimeout:
|
|
56
|
+
maxTimeout: HOW_OFTEN_TO_CHECK_CONDITION * 10, // ms
|
|
39
57
|
conditionFn: isActiveScreenAvailableToFocus,
|
|
40
58
|
});
|
|
41
59
|
};
|
|
42
60
|
|
|
43
|
-
|
|
44
|
-
return last(focusableTree.root.children);
|
|
61
|
+
const getActiveScreenChildren = (focusableTree) => {
|
|
62
|
+
return pathOr([], ["children"], last(focusableTree.root.children));
|
|
45
63
|
};
|
|
46
64
|
|
|
47
65
|
export const getContentNode = (focusableTree) => {
|
|
48
66
|
const content = find(
|
|
49
|
-
(child) =>
|
|
50
|
-
|
|
67
|
+
(child) =>
|
|
68
|
+
startsWith(QUICK_BRICK_CONTENT, child.id) ||
|
|
69
|
+
startsWith("player", child.id),
|
|
70
|
+
getActiveScreenChildren(focusableTree)
|
|
51
71
|
);
|
|
52
72
|
|
|
53
73
|
return content;
|
|
54
74
|
};
|
|
55
75
|
|
|
76
|
+
export const getNavbarNode = (focusableTree) => {
|
|
77
|
+
const navbar = find(
|
|
78
|
+
(child) => startsWith(QUICK_BRICK_NAVBAR, child.id),
|
|
79
|
+
getActiveScreenChildren(focusableTree)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return navbar;
|
|
83
|
+
};
|
|
84
|
+
|
|
56
85
|
export const waitForContent = (focusableTree) => {
|
|
57
86
|
const contentHasAnyChildren = (): boolean => {
|
|
58
|
-
|
|
87
|
+
const countOfChildren = pathOr(
|
|
88
|
+
0,
|
|
89
|
+
["children", "length"],
|
|
90
|
+
getContentNode(focusableTree)
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
return countOfChildren > 0;
|
|
59
94
|
};
|
|
60
95
|
|
|
61
96
|
return waitUntil({
|
|
62
|
-
maxTimeout:
|
|
97
|
+
maxTimeout: HOW_OFTEN_TO_CHECK_CONDITION * 10, // ms
|
|
63
98
|
conditionFn: contentHasAnyChildren,
|
|
64
99
|
});
|
|
65
100
|
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
|
|
2
|
+
import { find, path } from "ramda";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
waitForActiveScreen,
|
|
6
|
+
waitForContent,
|
|
7
|
+
getContentNode,
|
|
8
|
+
getNavbarNode,
|
|
9
|
+
} from "./utils";
|
|
10
|
+
|
|
11
|
+
import { coreLogger } from "../../logger";
|
|
12
|
+
|
|
13
|
+
const logger = coreLogger.addSubsystem("focusManagerAux");
|
|
14
|
+
|
|
15
|
+
export async function setFocusOnMenu(currentRoute: string) {
|
|
16
|
+
const { setFocus, focusableTree } = focusManager;
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
await waitForActiveScreen(currentRoute, focusableTree);
|
|
20
|
+
|
|
21
|
+
const navbar = getNavbarNode(focusableTree);
|
|
22
|
+
|
|
23
|
+
const selectedMenuItemId = find(
|
|
24
|
+
(child) => child.component.props.selected,
|
|
25
|
+
navbar.children
|
|
26
|
+
)?.id;
|
|
27
|
+
|
|
28
|
+
if (selectedMenuItemId) {
|
|
29
|
+
// set focus on selected menu item
|
|
30
|
+
setFocus(selectedMenuItemId);
|
|
31
|
+
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const firstMenuItemId = path(["children", 0, "id"], navbar);
|
|
36
|
+
|
|
37
|
+
if (firstMenuItemId) {
|
|
38
|
+
// set focus on first menu item
|
|
39
|
+
setFocus(firstMenuItemId);
|
|
40
|
+
}
|
|
41
|
+
} catch (error) {
|
|
42
|
+
logger.log({
|
|
43
|
+
message: "setFocusOnMenu – failed to put focus on top-menu",
|
|
44
|
+
data: {
|
|
45
|
+
currentRoute,
|
|
46
|
+
error,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function setFocusOnContent(currentRoute: string) {
|
|
53
|
+
// we need to put focus on content here
|
|
54
|
+
// For doing it we have to wait registration of first focusable node into focusableTree
|
|
55
|
+
// we wait until content node starts having children
|
|
56
|
+
|
|
57
|
+
const { setInitialFocus, focusableTree } = focusManager;
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
await waitForActiveScreen(currentRoute, focusableTree);
|
|
61
|
+
await waitForContent(focusableTree);
|
|
62
|
+
|
|
63
|
+
const content = getContentNode(focusableTree);
|
|
64
|
+
|
|
65
|
+
const { success } = setInitialFocus(content);
|
|
66
|
+
|
|
67
|
+
if (!success) {
|
|
68
|
+
// we've failed to put initial focus on content, re-run "setFocusOnContent" again as a fallback
|
|
69
|
+
throw Error(
|
|
70
|
+
"we've failed to put initial focus on content, re-run 'setFocusOnContent' again as a fallback"
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
} catch (error) {
|
|
74
|
+
setFocusOnContent(currentRoute);
|
|
75
|
+
}
|
|
76
|
+
}
|