@applicaster/zapp-react-native-utils 12.0.0-alpha.4433422595 → 12.0.0-alpha.4462655310
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/AnalyticsEvents/index.ts +13 -0
- package/analyticsUtils/AnalyticsEvents/searchEvents.ts +21 -0
- package/analyticsUtils/AnalyticsEvents/sendShareEvent.ts +21 -0
- package/analyticsUtils/analyticsPlugins.ts +25 -0
- package/analyticsUtils/events.ts +12 -0
- package/analyticsUtils/index.tsx +12 -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 -77
- package/appUtils/focusManager/treeDataStructure/Utils/index.js +30 -15
- 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/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 +223 -0
- package/appUtils/playerManager/index.ts +11 -5
- package/appUtils/playerManager/nativePlayerManager.ts +12 -0
- package/appUtils/playerManager/player.ts +1 -3
- package/appUtils/playerManager/playerNative.ts +10 -14
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +6 -1
- 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 +6 -12
- package/dateUtils/index.ts +11 -0
- package/headersUtils/index.ts +1 -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 +2 -1
- package/numberUtils/__tests__/isLikeNumber.test.ts +18 -0
- package/numberUtils/index.ts +9 -18
- package/package.json +2 -2
- package/playerUtils/configurationGenerator.ts +3 -1
- package/reactHooks/autoscrolling/__tests__/{useTrackedView.test.ts → useTrackedView.test.tsx} +16 -1
- package/reactHooks/cell-click/__tests__/index.test.js +10 -1
- package/reactHooks/cell-click/index.ts +12 -12
- 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/__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/useDimensions.ts +5 -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 +43 -64
- 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/reactUtils/index.ts +102 -0
- package/screenState/index.ts +0 -8
- package/stringUtils/index.ts +5 -0
- package/testUtils/getByTestId.ts +14 -0
- package/testUtils/index.tsx +24 -22
- 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
|
@@ -7,6 +7,15 @@ import { sendHeaderClickEvent } from "./sendHeaderClickEvent";
|
|
|
7
7
|
import { sendHardwareBackButtonClickEvent } from "./sendHardwareBackButtonClickEvent";
|
|
8
8
|
import { sendDownloadsEvent } from "./sendDownloadsEvent";
|
|
9
9
|
import { sendScreenEvent } from "./sendScreenEvent";
|
|
10
|
+
import {
|
|
11
|
+
sendSearchCanceledEvent,
|
|
12
|
+
sendSearchExecutedEvent,
|
|
13
|
+
} from "./searchEvents";
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
sendShareInitiatedEvent,
|
|
17
|
+
sendShareSuccessEvent,
|
|
18
|
+
} from "./sendShareEvent";
|
|
10
19
|
|
|
11
20
|
export {
|
|
12
21
|
sendScreenEvent,
|
|
@@ -18,4 +27,8 @@ export {
|
|
|
18
27
|
sendHeaderClickEvent,
|
|
19
28
|
sendHardwareBackButtonClickEvent,
|
|
20
29
|
sendDownloadsEvent,
|
|
30
|
+
sendSearchCanceledEvent,
|
|
31
|
+
sendSearchExecutedEvent,
|
|
32
|
+
sendShareInitiatedEvent,
|
|
33
|
+
sendShareSuccessEvent,
|
|
21
34
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { log_debug } from "../logger";
|
|
2
|
+
import { postAnalyticEvent } from "../manager";
|
|
3
|
+
import { ANALYTICS_CORE_EVENTS } from "../events";
|
|
4
|
+
|
|
5
|
+
export const sendSearchCanceledEvent = (analyticsProps) => {
|
|
6
|
+
const eventName = ANALYTICS_CORE_EVENTS.SEARCH_CANCELED;
|
|
7
|
+
postAnalyticEvent(eventName, analyticsProps);
|
|
8
|
+
|
|
9
|
+
log_debug(`sendSearchCanceledEvent: send event: ${eventName}`, {
|
|
10
|
+
analyticsProps,
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const sendSearchExecutedEvent = (analyticsProps) => {
|
|
15
|
+
const eventName = ANALYTICS_CORE_EVENTS.SEARCH_EXECUTED;
|
|
16
|
+
postAnalyticEvent(eventName, analyticsProps);
|
|
17
|
+
|
|
18
|
+
log_debug(`sendSearchExecutedEvent: send event: ${eventName}`, {
|
|
19
|
+
analyticsProps,
|
|
20
|
+
});
|
|
21
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { log_debug } from "../logger";
|
|
2
|
+
import { postAnalyticEvent } from "../manager";
|
|
3
|
+
import { ANALYTICS_CORE_EVENTS } from "../events";
|
|
4
|
+
|
|
5
|
+
export const sendShareInitiatedEvent = (analyticsProps) => {
|
|
6
|
+
const eventName = ANALYTICS_CORE_EVENTS.SHARE_INITIATED;
|
|
7
|
+
postAnalyticEvent(eventName, analyticsProps);
|
|
8
|
+
|
|
9
|
+
log_debug(`sendShareInitiatedEvent: send event: ${eventName}`, {
|
|
10
|
+
analyticsProps,
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const sendShareSuccessEvent = (analyticsProps) => {
|
|
15
|
+
const eventName = ANALYTICS_CORE_EVENTS.SHARE_SUCCESS;
|
|
16
|
+
postAnalyticEvent(eventName, analyticsProps);
|
|
17
|
+
|
|
18
|
+
log_debug(`sendShareSuccessEvent: send event: ${eventName}`, {
|
|
19
|
+
analyticsProps,
|
|
20
|
+
});
|
|
21
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as R from "ramda";
|
|
2
|
+
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
3
|
+
|
|
4
|
+
export const getVideojsAnalyticPluginsConfig = async (entry: ZappEntry) => {
|
|
5
|
+
let videojsConfiguration = {};
|
|
6
|
+
|
|
7
|
+
const plugins = appStore.get("plugins");
|
|
8
|
+
const analyticPlugins = R.filter(R.propEq("type", "analytics"), plugins);
|
|
9
|
+
|
|
10
|
+
const configurations = await Promise.all(
|
|
11
|
+
analyticPlugins.map(async (plugin) => {
|
|
12
|
+
const { configuration, module } = plugin;
|
|
13
|
+
|
|
14
|
+
return module?.getPlayerConfiguration?.(configuration, entry);
|
|
15
|
+
})
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
configurations.forEach((analyticConfig) => {
|
|
19
|
+
if (typeof analyticConfig === "object" && analyticConfig !== null) {
|
|
20
|
+
videojsConfiguration = { ...videojsConfiguration, ...analyticConfig };
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return videojsConfiguration;
|
|
25
|
+
};
|
package/analyticsUtils/events.ts
CHANGED
|
@@ -23,10 +23,22 @@ export const TAPPING_EVENTS = {
|
|
|
23
23
|
TAP_NAVBAR_BACK_BUTTON: "tap_navbar_back_button",
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
export const SEARCH_EVENTS = {
|
|
27
|
+
SEARCH_EXECUTED: "search_executed",
|
|
28
|
+
SEARCH_CANCELED: "search_canceled",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const SHARE_EVENTS = {
|
|
32
|
+
SHARE_INITIATED: "share_initiated",
|
|
33
|
+
SHARE_SUCCESS: "share_success",
|
|
34
|
+
};
|
|
35
|
+
|
|
26
36
|
export const ANALYTICS_CORE_EVENTS = {
|
|
27
37
|
...LEGACY_EVENTS,
|
|
28
38
|
...SCREEN_VIEW_EVENTS,
|
|
29
39
|
...TAPPING_EVENTS,
|
|
40
|
+
...SEARCH_EVENTS,
|
|
41
|
+
...SHARE_EVENTS,
|
|
30
42
|
};
|
|
31
43
|
|
|
32
44
|
export const WEBVIEW_EVENTS = {
|
package/analyticsUtils/index.tsx
CHANGED
|
@@ -18,6 +18,10 @@ import {
|
|
|
18
18
|
sendHardwareBackButtonClickEvent,
|
|
19
19
|
sendDownloadsEvent,
|
|
20
20
|
sendScreenEvent,
|
|
21
|
+
sendSearchCanceledEvent,
|
|
22
|
+
sendSearchExecutedEvent,
|
|
23
|
+
sendShareInitiatedEvent,
|
|
24
|
+
sendShareSuccessEvent,
|
|
21
25
|
} from "./AnalyticsEvents";
|
|
22
26
|
|
|
23
27
|
import { postAnalyticEvent } from "./manager";
|
|
@@ -66,6 +70,10 @@ type AnalyticsFunctions = {
|
|
|
66
70
|
item: ZappEntry;
|
|
67
71
|
state: OfflineItemState;
|
|
68
72
|
}) => void;
|
|
73
|
+
sendSearchCanceledEvent: (props: AnalyticsDefaultProperties) => void;
|
|
74
|
+
sendSearchExecutedEvent: (props: AnalyticsDefaultProperties) => void;
|
|
75
|
+
sendShareInitiatedEvent: (props: AnalyticsDefaultProperties) => void;
|
|
76
|
+
sendShareSuccessEvent: (props: AnalyticsDefaultProperties) => void;
|
|
69
77
|
};
|
|
70
78
|
|
|
71
79
|
type GetAnalyticsFunctions = (
|
|
@@ -102,6 +110,10 @@ export function getAnalyticsFunctions({
|
|
|
102
110
|
sendHardwareBackButtonClickEvent,
|
|
103
111
|
sendScreenEvent,
|
|
104
112
|
sendDownloadsEvent,
|
|
113
|
+
sendSearchCanceledEvent,
|
|
114
|
+
sendSearchExecutedEvent,
|
|
115
|
+
sendShareInitiatedEvent,
|
|
116
|
+
sendShareSuccessEvent,
|
|
105
117
|
};
|
|
106
118
|
}
|
|
107
119
|
|
|
@@ -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,6 +1,15 @@
|
|
|
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";
|
|
@@ -44,21 +53,9 @@ export const focusManager = (function () {
|
|
|
44
53
|
// focusable node which currently holds the focus
|
|
45
54
|
let currentFocusNode = null;
|
|
46
55
|
let lastFocusedId = null;
|
|
47
|
-
let initialFocusOnContent = null;
|
|
48
56
|
let focusDisabled = false;
|
|
49
|
-
let focusAutoEnabled = true;
|
|
50
57
|
const eventHandler = subscriber();
|
|
51
58
|
|
|
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
59
|
function enableFocus() {
|
|
63
60
|
focusDisabled = false;
|
|
64
61
|
}
|
|
@@ -93,7 +90,7 @@ export const focusManager = (function () {
|
|
|
93
90
|
!currentFocusable.isGroup &&
|
|
94
91
|
currentFocusable.isMounted()
|
|
95
92
|
) {
|
|
96
|
-
currentFocusable.blur(
|
|
93
|
+
currentFocusable.blur(direction);
|
|
97
94
|
currentFocusable.hasLostFocus(getCurrentFocus(), direction);
|
|
98
95
|
}
|
|
99
96
|
}
|
|
@@ -224,40 +221,54 @@ export const focusManager = (function () {
|
|
|
224
221
|
lastAddedParentNode?.children || focusableTree.root.children
|
|
225
222
|
);
|
|
226
223
|
|
|
227
|
-
// console.log("debug_2", "FM setInitialFocus", {
|
|
228
|
-
// preferredFocus,
|
|
229
|
-
// lastAddedParentNode,
|
|
230
|
-
// focusableTree,
|
|
231
|
-
// });
|
|
232
|
-
|
|
233
224
|
logger.log({ message: "setInitialFocus", data: { preferredFocus } });
|
|
234
225
|
|
|
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
|
-
}
|
|
226
|
+
if (isNilOrEmpty(preferredFocus)) {
|
|
227
|
+
return { success: false };
|
|
228
|
+
}
|
|
248
229
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
data: {
|
|
252
|
-
focusableItem,
|
|
253
|
-
},
|
|
254
|
-
});
|
|
230
|
+
const focusCandidate = preferredFocus[0];
|
|
231
|
+
const focusableItem = focusCandidate.component;
|
|
255
232
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
233
|
+
const currentFocusedId = R.path(["props", "id"], getCurrentFocus());
|
|
234
|
+
const nextFocusedId = R.path(["props", "id"], focusableItem);
|
|
235
|
+
|
|
236
|
+
if (
|
|
237
|
+
isNotNil(currentFocusedId) &&
|
|
238
|
+
isNotNil(nextFocusedId) &&
|
|
239
|
+
currentFocusedId === nextFocusedId
|
|
240
|
+
) {
|
|
241
|
+
// we are going to move focus to the same node(already focused) -> nothing to do
|
|
242
|
+
return { success: true };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (focusableItem?.isGroup) {
|
|
246
|
+
// focusCandidate is a FocusableGroup, we are not able to land focus on group(only on Focusable)
|
|
247
|
+
return { success: false };
|
|
260
248
|
}
|
|
249
|
+
|
|
250
|
+
if (
|
|
251
|
+
focusableItem &&
|
|
252
|
+
!focusableItem.isGroup &&
|
|
253
|
+
isFocusEnabled(focusableItem)
|
|
254
|
+
) {
|
|
255
|
+
blur();
|
|
256
|
+
|
|
257
|
+
logger.log({
|
|
258
|
+
message: "setInitialFocus – currentFocusNode",
|
|
259
|
+
data: {
|
|
260
|
+
focusableItem,
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
currentFocusNode = focusCandidate;
|
|
265
|
+
focusableItem.willReceiveFocus(focusableItem, null);
|
|
266
|
+
focusableItem && setFocus(focusCandidate.id, null);
|
|
267
|
+
|
|
268
|
+
return { success: true };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return { success: false };
|
|
261
272
|
}
|
|
262
273
|
|
|
263
274
|
function treeLoaded(val, node) {
|
|
@@ -267,17 +278,8 @@ export const focusManager = (function () {
|
|
|
267
278
|
val,
|
|
268
279
|
node,
|
|
269
280
|
currentFocusNode,
|
|
270
|
-
initialFocusOnContent,
|
|
271
281
|
},
|
|
272
282
|
});
|
|
273
|
-
|
|
274
|
-
if ((val && !currentFocusNode) || initialFocusOnContent) {
|
|
275
|
-
logger.log({ message: `focusAutoEnabled ${focusAutoEnabled}` });
|
|
276
|
-
|
|
277
|
-
if (focusAutoEnabled) {
|
|
278
|
-
setInitialFocus(initialFocusOnContent ? node : undefined);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
283
|
}
|
|
282
284
|
|
|
283
285
|
/**
|
|
@@ -329,8 +331,6 @@ export const focusManager = (function () {
|
|
|
329
331
|
* @returns {React.Component} the registered component
|
|
330
332
|
*/
|
|
331
333
|
function registerGroup(id, component) {
|
|
332
|
-
// console.log("debug_2", "FM registerGroup", { id, component });
|
|
333
|
-
|
|
334
334
|
focusableTree.addNode({ id, component });
|
|
335
335
|
|
|
336
336
|
return component;
|
|
@@ -346,8 +346,6 @@ export const focusManager = (function () {
|
|
|
346
346
|
* @returns {React.Component} the registered component
|
|
347
347
|
*/
|
|
348
348
|
function registerItem(id, component) {
|
|
349
|
-
// console.log("debug_2", "FM registerItem", { id, component });
|
|
350
|
-
|
|
351
349
|
focusableTree.addNode({ id, component });
|
|
352
350
|
|
|
353
351
|
if (!currentFocusNode && id === lastFocusedId && !id.includes("menu")) {
|
|
@@ -364,8 +362,6 @@ export const focusManager = (function () {
|
|
|
364
362
|
* @param {String} id of the group to unregister
|
|
365
363
|
*/
|
|
366
364
|
function unregisterGroup(id) {
|
|
367
|
-
// console.log("debug_2", "FM unregisterGroup", { id });
|
|
368
|
-
|
|
369
365
|
focusableTree.findAndRemoveNode(id);
|
|
370
366
|
}
|
|
371
367
|
|
|
@@ -377,8 +373,6 @@ export const focusManager = (function () {
|
|
|
377
373
|
* @param {String} id of the Focusable to unregister
|
|
378
374
|
*/
|
|
379
375
|
function unregisterItem(id) {
|
|
380
|
-
// console.log("debug_2", "FM unregisterItem", { id });
|
|
381
|
-
|
|
382
376
|
focusableTree.findAndRemoveNode(id);
|
|
383
377
|
|
|
384
378
|
if (currentFocusNode && currentFocusNode.id === id) {
|
|
@@ -416,11 +410,21 @@ export const focusManager = (function () {
|
|
|
416
410
|
group ? unregisterGroup(id) : unregisterItem(id);
|
|
417
411
|
}
|
|
418
412
|
|
|
419
|
-
function
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
413
|
+
function isCurrentFocusOnTheTopScreen() {
|
|
414
|
+
const routes = R.pathOr([], ["root", "children"], focusableTree);
|
|
415
|
+
|
|
416
|
+
if (routes.length <= 1) {
|
|
417
|
+
// there is only one or zero routes
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return haveSameParentBeforeRoot(currentFocusNode, R.last(routes));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function recoverFocus() {
|
|
425
|
+
if (!isCurrentFocusOnTheTopScreen()) {
|
|
426
|
+
// We've failed to set focused node on the new screen => run focus recovery
|
|
427
|
+
setInitialFocus();
|
|
424
428
|
}
|
|
425
429
|
}
|
|
426
430
|
|
|
@@ -430,19 +434,17 @@ export const focusManager = (function () {
|
|
|
430
434
|
* @param {Object} direction of the scroll
|
|
431
435
|
*/
|
|
432
436
|
function moveFocus(direction) {
|
|
437
|
+
if (!isCurrentFocusOnTheTopScreen()) {
|
|
438
|
+
// We are opening new screen. We need to ignore movement of focus.
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
|
|
433
442
|
if (focusDisabled) return false;
|
|
434
443
|
|
|
435
444
|
if (currentFocusNode) {
|
|
436
445
|
currentFocusNode.component.willLoseFocus(currentFocusNode, direction);
|
|
437
446
|
const nextFocusable = findFocusableNode(direction, currentFocusNode);
|
|
438
447
|
|
|
439
|
-
// console.log("debug_2", "FM moveFocus", {
|
|
440
|
-
// direction: direction?.value,
|
|
441
|
-
// currentFocusNode,
|
|
442
|
-
// focusableTree,
|
|
443
|
-
// nextFocusable,
|
|
444
|
-
// });
|
|
445
|
-
|
|
446
448
|
if (nextFocusable?.component?.isMounted?.()) {
|
|
447
449
|
const nextRect = nextFocusable.component.getRect();
|
|
448
450
|
|
|
@@ -465,8 +467,6 @@ export const focusManager = (function () {
|
|
|
465
467
|
} else {
|
|
466
468
|
currentFocusNode.component.failedLostFocus(currentFocusNode, direction);
|
|
467
469
|
}
|
|
468
|
-
} else {
|
|
469
|
-
setInitialFocus();
|
|
470
470
|
}
|
|
471
471
|
}
|
|
472
472
|
|
|
@@ -526,7 +526,6 @@ export const focusManager = (function () {
|
|
|
526
526
|
on,
|
|
527
527
|
invokeHandler,
|
|
528
528
|
removeHandler,
|
|
529
|
-
setupInitialFocus,
|
|
530
529
|
longPress,
|
|
531
530
|
pressOut,
|
|
532
531
|
enableFocus,
|
|
@@ -535,9 +534,9 @@ export const focusManager = (function () {
|
|
|
535
534
|
isFocusDisabled,
|
|
536
535
|
getCurrentFocusNode,
|
|
537
536
|
focusableTree,
|
|
538
|
-
disableAutomaticFocus,
|
|
539
|
-
enableAutomaticFocus,
|
|
540
537
|
getGroupRootById,
|
|
541
538
|
isGroupItemFocused,
|
|
539
|
+
recoverFocus,
|
|
540
|
+
isCurrentFocusOnTheTopScreen,
|
|
542
541
|
};
|
|
543
542
|
})();
|
|
@@ -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
|
|
|
@@ -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
|
+
};
|