@applicaster/zapp-react-native-utils 12.0.0-alpha.4433422595 → 12.0.0-alpha.4440903139
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 +76 -78
- package/appUtils/focusManager/treeDataStructure/Utils/index.js +31 -16
- package/appUtils/focusManager/utils/index.ts +100 -0
- 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 +218 -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 +10 -3
- package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +57 -0
- package/appUtils/playerManager/playerNative.ts +11 -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 +16 -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/focusManager/FocusManager.ts +20 -5
- package/focusManager/__tests__/FocusManager.test.ts +0 -1
- 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/generalContent.js +6 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +123 -1
- package/manifestUtils/keys.js +2 -1
- package/navigationUtils/index.ts +49 -1
- package/numberUtils/__tests__/isLikeNumber.test.ts +18 -0
- package/numberUtils/__tests__/toNumber.test.ts +2 -2
- package/numberUtils/__tests__/toPositiveNumberWithDefault.test.ts +66 -0
- package/numberUtils/__tests__/toPositiveNumberWithDefaultZero.test.ts +66 -0
- package/numberUtils/index.ts +27 -16
- package/package.json +2 -2
- package/playerUtils/configurationGenerator.ts +123 -1
- package/playerUtils/imaScriptDom.ts +59 -0
- package/pluginUtils/index.ts +9 -0
- package/reactHooks/autoscrolling/__tests__/{useTrackedView.test.ts → useTrackedView.test.tsx} +16 -1
- package/reactHooks/cell-click/__tests__/index.test.js +13 -4
- package/reactHooks/cell-click/index.ts +28 -14
- package/reactHooks/debugging/__tests__/index.test.js +81 -11
- package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +69 -6
- 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/useBatchLoading.ts +52 -28
- package/reactHooks/feed/useBuildPipesUrl.ts +7 -10
- package/reactHooks/feed/useFeedRefresh.tsx +9 -7
- package/reactHooks/feed/useInflatedUrl.ts +44 -15
- package/reactHooks/layout/__tests__/{index.test.js → index.test.tsx} +43 -29
- package/reactHooks/layout/getDeviceInfo/index.ts +4 -2
- package/reactHooks/layout/types/index.ts +1 -0
- package/reactHooks/layout/useDimensions/__tests__/useDimensions.test.ts +134 -0
- package/reactHooks/layout/useDimensions/useDimensions.ts +7 -2
- package/reactHooks/navigation/__mocks__/index.ts +13 -0
- package/reactHooks/navigation/__mocks__/useNavigation.ts +3 -0
- package/reactHooks/navigation/__tests__/index.test.tsx +11 -8
- package/reactHooks/navigation/index.ts +48 -65
- 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 +3 -3
- package/reactHooks/useConfiguration.ts +2 -2
- package/reactUtils/index.ts +104 -0
- package/screenState/index.ts +0 -8
- package/stringUtils/index.ts +5 -0
- package/testUtils/getByTestId.ts +14 -0
- package/testUtils/index.tsx +65 -30
- 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) => {
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import * as R from "ramda";
|
|
2
|
+
import {
|
|
3
|
+
isNilOrEmpty,
|
|
4
|
+
isNotNil,
|
|
5
|
+
} from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
6
|
+
|
|
2
7
|
import { Tree } from "./treeDataStructure/Tree";
|
|
3
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
findFocusableNode,
|
|
10
|
+
findPriorityItem,
|
|
11
|
+
haveSameParentBeforeRoot,
|
|
12
|
+
} from "./treeDataStructure/Utils";
|
|
4
13
|
import { subscriber } from "../../functionUtils";
|
|
5
14
|
import * as FOCUS_EVENTS from "./events";
|
|
6
15
|
import { coreLogger } from "../../logger";
|
|
7
|
-
|
|
8
16
|
const logger = coreLogger.addSubsystem("focusManager");
|
|
9
17
|
|
|
10
18
|
const isFocusEnabled = (focusableItem): boolean => {
|
|
@@ -44,21 +52,9 @@ export const focusManager = (function () {
|
|
|
44
52
|
// focusable node which currently holds the focus
|
|
45
53
|
let currentFocusNode = null;
|
|
46
54
|
let lastFocusedId = null;
|
|
47
|
-
let initialFocusOnContent = null;
|
|
48
55
|
let focusDisabled = false;
|
|
49
|
-
let focusAutoEnabled = true;
|
|
50
56
|
const eventHandler = subscriber();
|
|
51
57
|
|
|
52
|
-
function disableAutomaticFocus() {
|
|
53
|
-
logger.log({ message: "disableAutomaticFocus" });
|
|
54
|
-
focusAutoEnabled = false;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function enableAutomaticFocus() {
|
|
58
|
-
logger.log({ message: "focusManager: enableAutomaticFocus" });
|
|
59
|
-
focusAutoEnabled = true;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
58
|
function enableFocus() {
|
|
63
59
|
focusDisabled = false;
|
|
64
60
|
}
|
|
@@ -93,7 +89,7 @@ export const focusManager = (function () {
|
|
|
93
89
|
!currentFocusable.isGroup &&
|
|
94
90
|
currentFocusable.isMounted()
|
|
95
91
|
) {
|
|
96
|
-
currentFocusable.blur(
|
|
92
|
+
currentFocusable.blur(direction);
|
|
97
93
|
currentFocusable.hasLostFocus(getCurrentFocus(), direction);
|
|
98
94
|
}
|
|
99
95
|
}
|
|
@@ -224,40 +220,54 @@ export const focusManager = (function () {
|
|
|
224
220
|
lastAddedParentNode?.children || focusableTree.root.children
|
|
225
221
|
);
|
|
226
222
|
|
|
227
|
-
// console.log("debug_2", "FM setInitialFocus", {
|
|
228
|
-
// preferredFocus,
|
|
229
|
-
// lastAddedParentNode,
|
|
230
|
-
// focusableTree,
|
|
231
|
-
// });
|
|
232
|
-
|
|
233
223
|
logger.log({ message: "setInitialFocus", data: { preferredFocus } });
|
|
234
224
|
|
|
235
|
-
if (preferredFocus
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
if (
|
|
240
|
-
focusableItem &&
|
|
241
|
-
!focusableItem.isGroup &&
|
|
242
|
-
isFocusEnabled(focusableItem)
|
|
243
|
-
) {
|
|
244
|
-
if (initialFocusOnContent) {
|
|
245
|
-
blur();
|
|
246
|
-
initialFocusOnContent = false;
|
|
247
|
-
}
|
|
225
|
+
if (isNilOrEmpty(preferredFocus)) {
|
|
226
|
+
return { success: false };
|
|
227
|
+
}
|
|
248
228
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
data: {
|
|
252
|
-
focusableItem,
|
|
253
|
-
},
|
|
254
|
-
});
|
|
229
|
+
const focusCandidate = preferredFocus[0];
|
|
230
|
+
const focusableItem = focusCandidate.component;
|
|
255
231
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
232
|
+
const currentFocusedId = R.path(["props", "id"], getCurrentFocus());
|
|
233
|
+
const nextFocusedId = R.path(["props", "id"], focusableItem);
|
|
234
|
+
|
|
235
|
+
if (
|
|
236
|
+
isNotNil(currentFocusedId) &&
|
|
237
|
+
isNotNil(nextFocusedId) &&
|
|
238
|
+
currentFocusedId === nextFocusedId
|
|
239
|
+
) {
|
|
240
|
+
// we are going to move focus to the same node(already focused) -> nothing to do
|
|
241
|
+
return { success: true };
|
|
242
|
+
}
|
|
243
|
+
|
|
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 };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (
|
|
250
|
+
focusableItem &&
|
|
251
|
+
!focusableItem.isGroup &&
|
|
252
|
+
isFocusEnabled(focusableItem)
|
|
253
|
+
) {
|
|
254
|
+
blur();
|
|
255
|
+
|
|
256
|
+
logger.log({
|
|
257
|
+
message: "setInitialFocus – currentFocusNode",
|
|
258
|
+
data: {
|
|
259
|
+
focusableItem,
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
currentFocusNode = focusCandidate;
|
|
264
|
+
focusableItem.willReceiveFocus(focusableItem, null);
|
|
265
|
+
focusableItem && setFocus(focusCandidate.id, null);
|
|
266
|
+
|
|
267
|
+
return { success: true };
|
|
260
268
|
}
|
|
269
|
+
|
|
270
|
+
return { success: false };
|
|
261
271
|
}
|
|
262
272
|
|
|
263
273
|
function treeLoaded(val, node) {
|
|
@@ -267,17 +277,8 @@ export const focusManager = (function () {
|
|
|
267
277
|
val,
|
|
268
278
|
node,
|
|
269
279
|
currentFocusNode,
|
|
270
|
-
initialFocusOnContent,
|
|
271
280
|
},
|
|
272
281
|
});
|
|
273
|
-
|
|
274
|
-
if ((val && !currentFocusNode) || initialFocusOnContent) {
|
|
275
|
-
logger.log({ message: `focusAutoEnabled ${focusAutoEnabled}` });
|
|
276
|
-
|
|
277
|
-
if (focusAutoEnabled) {
|
|
278
|
-
setInitialFocus(initialFocusOnContent ? node : undefined);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
282
|
}
|
|
282
283
|
|
|
283
284
|
/**
|
|
@@ -329,8 +330,6 @@ export const focusManager = (function () {
|
|
|
329
330
|
* @returns {React.Component} the registered component
|
|
330
331
|
*/
|
|
331
332
|
function registerGroup(id, component) {
|
|
332
|
-
// console.log("debug_2", "FM registerGroup", { id, component });
|
|
333
|
-
|
|
334
333
|
focusableTree.addNode({ id, component });
|
|
335
334
|
|
|
336
335
|
return component;
|
|
@@ -346,8 +345,6 @@ export const focusManager = (function () {
|
|
|
346
345
|
* @returns {React.Component} the registered component
|
|
347
346
|
*/
|
|
348
347
|
function registerItem(id, component) {
|
|
349
|
-
// console.log("debug_2", "FM registerItem", { id, component });
|
|
350
|
-
|
|
351
348
|
focusableTree.addNode({ id, component });
|
|
352
349
|
|
|
353
350
|
if (!currentFocusNode && id === lastFocusedId && !id.includes("menu")) {
|
|
@@ -364,8 +361,6 @@ export const focusManager = (function () {
|
|
|
364
361
|
* @param {String} id of the group to unregister
|
|
365
362
|
*/
|
|
366
363
|
function unregisterGroup(id) {
|
|
367
|
-
// console.log("debug_2", "FM unregisterGroup", { id });
|
|
368
|
-
|
|
369
364
|
focusableTree.findAndRemoveNode(id);
|
|
370
365
|
}
|
|
371
366
|
|
|
@@ -377,8 +372,6 @@ export const focusManager = (function () {
|
|
|
377
372
|
* @param {String} id of the Focusable to unregister
|
|
378
373
|
*/
|
|
379
374
|
function unregisterItem(id) {
|
|
380
|
-
// console.log("debug_2", "FM unregisterItem", { id });
|
|
381
|
-
|
|
382
375
|
focusableTree.findAndRemoveNode(id);
|
|
383
376
|
|
|
384
377
|
if (currentFocusNode && currentFocusNode.id === id) {
|
|
@@ -416,11 +409,21 @@ export const focusManager = (function () {
|
|
|
416
409
|
group ? unregisterGroup(id) : unregisterItem(id);
|
|
417
410
|
}
|
|
418
411
|
|
|
419
|
-
function
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
412
|
+
function isCurrentFocusOnTheTopScreen() {
|
|
413
|
+
const routes = R.pathOr([], ["root", "children"], focusableTree);
|
|
414
|
+
|
|
415
|
+
if (routes.length <= 1) {
|
|
416
|
+
// there is only one or zero routes
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return haveSameParentBeforeRoot(currentFocusNode, R.last(routes));
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function recoverFocus() {
|
|
424
|
+
if (!isCurrentFocusOnTheTopScreen()) {
|
|
425
|
+
// We've failed to set focused node on the new screen => run focus recovery
|
|
426
|
+
setInitialFocus();
|
|
424
427
|
}
|
|
425
428
|
}
|
|
426
429
|
|
|
@@ -430,19 +433,17 @@ export const focusManager = (function () {
|
|
|
430
433
|
* @param {Object} direction of the scroll
|
|
431
434
|
*/
|
|
432
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
|
+
|
|
433
441
|
if (focusDisabled) return false;
|
|
434
442
|
|
|
435
443
|
if (currentFocusNode) {
|
|
436
444
|
currentFocusNode.component.willLoseFocus(currentFocusNode, direction);
|
|
437
445
|
const nextFocusable = findFocusableNode(direction, currentFocusNode);
|
|
438
446
|
|
|
439
|
-
// console.log("debug_2", "FM moveFocus", {
|
|
440
|
-
// direction: direction?.value,
|
|
441
|
-
// currentFocusNode,
|
|
442
|
-
// focusableTree,
|
|
443
|
-
// nextFocusable,
|
|
444
|
-
// });
|
|
445
|
-
|
|
446
447
|
if (nextFocusable?.component?.isMounted?.()) {
|
|
447
448
|
const nextRect = nextFocusable.component.getRect();
|
|
448
449
|
|
|
@@ -465,8 +466,6 @@ export const focusManager = (function () {
|
|
|
465
466
|
} else {
|
|
466
467
|
currentFocusNode.component.failedLostFocus(currentFocusNode, direction);
|
|
467
468
|
}
|
|
468
|
-
} else {
|
|
469
|
-
setInitialFocus();
|
|
470
469
|
}
|
|
471
470
|
}
|
|
472
471
|
|
|
@@ -526,7 +525,6 @@ export const focusManager = (function () {
|
|
|
526
525
|
on,
|
|
527
526
|
invokeHandler,
|
|
528
527
|
removeHandler,
|
|
529
|
-
setupInitialFocus,
|
|
530
528
|
longPress,
|
|
531
529
|
pressOut,
|
|
532
530
|
enableFocus,
|
|
@@ -535,9 +533,9 @@ export const focusManager = (function () {
|
|
|
535
533
|
isFocusDisabled,
|
|
536
534
|
getCurrentFocusNode,
|
|
537
535
|
focusableTree,
|
|
538
|
-
disableAutomaticFocus,
|
|
539
|
-
enableAutomaticFocus,
|
|
540
536
|
getGroupRootById,
|
|
541
537
|
isGroupItemFocused,
|
|
538
|
+
recoverFocus,
|
|
539
|
+
isCurrentFocusOnTheTopScreen,
|
|
542
540
|
};
|
|
543
541
|
})();
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { allPass, pathOr, startsWith } from "ramda";
|
|
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,10 +155,10 @@ const excludeFromFocusSearching = pathOr(false, [
|
|
|
153
155
|
"excludeFromFocusSearching",
|
|
154
156
|
]);
|
|
155
157
|
|
|
156
|
-
const isNavBar = (target) => startsWith(
|
|
158
|
+
const isNavBar = (target) => startsWith(QUICK_BRICK_NAVBAR, target?.id);
|
|
157
159
|
|
|
158
160
|
const ignoreMenuIfMultipleTargets = (nodes) => {
|
|
159
|
-
if (nodes.length > 1
|
|
161
|
+
if (nodes.length > 1) {
|
|
160
162
|
return nodes.filter((n) => !isNavBar(n?.target));
|
|
161
163
|
}
|
|
162
164
|
|
|
@@ -204,7 +206,7 @@ export function getTargetInDirection(
|
|
|
204
206
|
const overrideTarget = currentNode?.component?.props[overridePropName];
|
|
205
207
|
|
|
206
208
|
if (overrideTarget) {
|
|
207
|
-
const target = targetNodes.find((n) => n
|
|
209
|
+
const target = targetNodes.find((n) => n?.id === overrideTarget);
|
|
208
210
|
|
|
209
211
|
if (target) return target;
|
|
210
212
|
}
|
|
@@ -224,17 +226,7 @@ export function getTargetInDirection(
|
|
|
224
226
|
.sort((a, b) => a.distance - b.distance) // sorts the distances
|
|
225
227
|
.filter(ignoreMenuIfMultipleTargets)[0]; // filters out multiple targets if necessary // gets the first element
|
|
226
228
|
|
|
227
|
-
|
|
228
|
-
// direction: direction?.value,
|
|
229
|
-
// currentNode,
|
|
230
|
-
// targetNodes,
|
|
231
|
-
// targets,
|
|
232
|
-
// comparisonRect,
|
|
233
|
-
// distances,
|
|
234
|
-
// match,
|
|
235
|
-
// });
|
|
236
|
-
|
|
237
|
-
return match.target;
|
|
229
|
+
return match?.target;
|
|
238
230
|
}
|
|
239
231
|
|
|
240
232
|
/**
|
|
@@ -325,3 +317,26 @@ export function hasPriorityInDirection(direction, component) {
|
|
|
325
317
|
|
|
326
318
|
return (prioritiseFocusOn & mappedDirection) > 0;
|
|
327
319
|
}
|
|
320
|
+
|
|
321
|
+
export const getNodeBeforeRoot = (node) => {
|
|
322
|
+
if (isNil(node)) {
|
|
323
|
+
return undefined;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (isNil(node.parent)) {
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (node.parent.id === "root") {
|
|
331
|
+
return node;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return getNodeBeforeRoot(node.parent);
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
export const haveSameParentBeforeRoot = (currentNode, nextNode) => {
|
|
338
|
+
const routeForCurrentNode = getNodeBeforeRoot(currentNode);
|
|
339
|
+
const routeForNextNode = getNodeBeforeRoot(nextNode);
|
|
340
|
+
|
|
341
|
+
return routeForCurrentNode?.id === routeForNextNode?.id;
|
|
342
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { isNotNil } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
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";
|
|
7
|
+
|
|
8
|
+
// run check each 300 ms
|
|
9
|
+
// run this check too often could lead to performance penalty on low-end devices
|
|
10
|
+
const HOW_OFTEN_TO_CHECK_CONDITION = 300; // ms
|
|
11
|
+
|
|
12
|
+
type Props = {
|
|
13
|
+
maxTimeout: number;
|
|
14
|
+
conditionFn: () => boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const waitUntil = ({ maxTimeout, conditionFn }: Props) => {
|
|
18
|
+
let interval;
|
|
19
|
+
let timeout;
|
|
20
|
+
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
// run interval check
|
|
23
|
+
interval = setInterval(() => {
|
|
24
|
+
if (conditionFn()) {
|
|
25
|
+
clearInterval(interval);
|
|
26
|
+
clearTimeout(timeout);
|
|
27
|
+
|
|
28
|
+
resolve(undefined);
|
|
29
|
+
}
|
|
30
|
+
}, HOW_OFTEN_TO_CHECK_CONDITION);
|
|
31
|
+
|
|
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);
|
|
42
|
+
}, maxTimeout);
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const waitForActiveScreen = (currentRoute: string, focusableTree) => {
|
|
47
|
+
const isActiveScreenAvailableToFocus = (): boolean => {
|
|
48
|
+
const routes = focusableTree.root.children;
|
|
49
|
+
|
|
50
|
+
const route = find((route) => route.id === currentRoute, routes);
|
|
51
|
+
|
|
52
|
+
return isNotNil(route);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return waitUntil({
|
|
56
|
+
maxTimeout: HOW_OFTEN_TO_CHECK_CONDITION * 10, // ms
|
|
57
|
+
conditionFn: isActiveScreenAvailableToFocus,
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const getActiveScreenChildren = (focusableTree) => {
|
|
62
|
+
return pathOr([], ["children"], last(focusableTree.root.children));
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const getContentNode = (focusableTree) => {
|
|
66
|
+
const content = find(
|
|
67
|
+
(child) =>
|
|
68
|
+
startsWith(QUICK_BRICK_CONTENT, child.id) ||
|
|
69
|
+
startsWith("player", child.id),
|
|
70
|
+
getActiveScreenChildren(focusableTree)
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
return content;
|
|
74
|
+
};
|
|
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
|
+
|
|
85
|
+
export const waitForContent = (focusableTree) => {
|
|
86
|
+
const contentHasAnyChildren = (): boolean => {
|
|
87
|
+
const countOfChildren = pathOr(
|
|
88
|
+
0,
|
|
89
|
+
["children", "length"],
|
|
90
|
+
getContentNode(focusableTree)
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
return countOfChildren > 0;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return waitUntil({
|
|
97
|
+
maxTimeout: HOW_OFTEN_TO_CHECK_CONDITION * 10, // ms
|
|
98
|
+
conditionFn: contentHasAnyChildren,
|
|
99
|
+
});
|
|
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
|
+
}
|