@applicaster/zapp-react-native-utils 12.0.0-rc.7 → 12.0.0-rc.71
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/actionsExecutor/ActionExecutorContext.tsx +16 -8
- package/analyticsUtils/AnalyticsEvents/index.ts +6 -0
- package/analyticsUtils/AnalyticsEvents/searchEvents.ts +21 -0
- package/analyticsUtils/__tests__/analyticsManager.test.js +75 -73
- package/analyticsUtils/analyticsPlugins.ts +25 -0
- package/analyticsUtils/events.ts +6 -0
- package/analyticsUtils/helpers/hooks.ts +1 -1
- package/analyticsUtils/index.tsx +6 -0
- package/appUtils/HooksManager/index.js +3 -1
- 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 -56
- package/appUtils/focusManager/treeDataStructure/Utils/index.js +74 -34
- 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/__tests__/__snapshots__/keyCodes.test.js.snap +1 -1
- package/appUtils/keyCodes/__tests__/keyCodes.test.js +80 -85
- package/appUtils/keyCodes/keys.web.js +1 -0
- package/appUtils/orientationHelper.ts +1 -1
- package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +53 -0
- package/appUtils/playerManager/conts.ts +5 -0
- package/appUtils/playerManager/player.ts +47 -4
- package/appUtils/playerManager/playerFactory.ts +15 -1
- package/appUtils/playerManager/playerNative.ts +36 -1
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +6 -1
- package/appUtils/playerManager/usePlayerState.tsx +13 -1
- package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +50 -0
- package/arrayUtils/__tests__/isFilledArray.test.ts +63 -0
- package/arrayUtils/index.ts +5 -0
- package/cellUtils/__tests__/cellUtils.test.ts +48 -0
- package/cellUtils/__tests__/fixtures.ts +13 -0
- package/cellUtils/index.ts +40 -2
- package/dateUtils/index.ts +11 -0
- package/focusManager/index.ts +1 -1
- package/functionUtils/__tests__/__snapshots__/functionUtils.test.js.snap +2 -2
- package/functionUtils/__tests__/functionUtils.test.js +244 -248
- package/headersUtils/index.ts +1 -0
- package/inflatedUrlUtils/index.ts +4 -4
- package/localizationUtils/__tests__/localizationUtils.test.ts +78 -55
- package/localizationUtils/index.ts +13 -19
- package/logger/index.ts +1 -1
- package/manifestUtils/defaultManifestConfigurations/generalContent.js +6 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +10 -1
- package/manifestUtils/index.js +5 -1
- package/manifestUtils/secondaryImage/index.js +18 -15
- package/modalState/index.ts +57 -0
- package/navigationUtils/index.ts +56 -7
- package/numberUtils/__tests__/isLikeNumber.test.ts +18 -0
- package/numberUtils/index.ts +9 -18
- package/package.json +3 -2
- package/playerUtils/configurationGenerator.ts +11 -1
- package/pluginUtils/index.ts +11 -7
- package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +2 -0
- package/reactHooks/autoscrolling/__tests__/{useTrackedView.test.ts → useTrackedView.test.tsx} +18 -1
- package/reactHooks/cell-click/__tests__/index.test.js +16 -3
- package/reactHooks/cell-click/index.ts +14 -14
- package/reactHooks/device/index.ts +0 -1
- package/reactHooks/device/useIsTablet.tsx +28 -3
- package/reactHooks/device/useMemoizedIsTablet.ts +1 -1
- package/reactHooks/device/useOrientation.tsx +3 -1
- package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +30 -70
- package/reactHooks/feed/__tests__/useInflatedUrl.test.ts +18 -3
- package/reactHooks/feed/useBuildPipesUrl.ts +7 -10
- package/reactHooks/feed/useInflatedUrl.ts +44 -15
- package/reactHooks/flatList/useLoadNextPageIfNeeded.ts +16 -9
- package/reactHooks/index.ts +2 -0
- package/reactHooks/layout/__tests__/{index.test.js → index.test.tsx} +66 -59
- package/reactHooks/layout/getDeviceInfo/index.ts +4 -2
- package/reactHooks/layout/index.ts +2 -1
- package/reactHooks/layout/types/index.ts +2 -0
- package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.native.test.js +11 -16
- package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.test.js +12 -14
- package/reactHooks/layout/useDimensions/useDimensions.ts +13 -7
- package/reactHooks/navigation/__mocks__/index.ts +15 -0
- package/reactHooks/navigation/__mocks__/useNavigation.ts +7 -0
- package/reactHooks/navigation/__tests__/index.test.tsx +240 -81
- package/reactHooks/navigation/index.ts +234 -81
- package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +3 -1
- package/reactHooks/screen/__mocks__/useScreenContext.ts +3 -1
- package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +21 -23
- package/reactHooks/screen/index.ts +4 -0
- package/reactHooks/screen/useScreenBackgroundColor.ts +23 -0
- package/reactHooks/screen/useScreenContext.ts +4 -4
- package/reactHooks/screen/useScreenData.ts +1 -1
- package/reactHooks/screen/useTargetScreenData.ts +7 -0
- package/reactHooks/state/useRivers.ts +3 -1
- package/reactHooks/useListenEventBusEvent.ts +1 -4
- package/reactHooks/videoModal/hooks/useVideoModalScreenData.tsx +10 -9
- package/riverComponetsMeasurementProvider/index.tsx +2 -2
- package/stringUtils/index.ts +5 -0
- package/testUtils/getByTestId.ts +14 -0
- package/testUtils/index.tsx +38 -23
- package/types.ts +4 -0
- package/reactHooks/device/useIsTablet.android.tsx +0 -14
- package/reactHooks/device/useIsTablet.ios.tsx +0 -7
- package/reactHooks/device/useIsTablet.web.tsx +0 -3
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import
|
|
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 {
|
|
@@ -143,7 +145,47 @@ const hasChildren = (target) =>
|
|
|
143
145
|
|
|
144
146
|
const hasComponent = (target) => !!target?.component;
|
|
145
147
|
|
|
146
|
-
const hasComponentAndChildren =
|
|
148
|
+
const hasComponentAndChildren = allPass([hasComponent, hasChildren]);
|
|
149
|
+
|
|
150
|
+
const positiveOrZero = (value) => value > 0 || isZero(value);
|
|
151
|
+
|
|
152
|
+
const excludeFromFocusSearching = pathOr(false, [
|
|
153
|
+
"component",
|
|
154
|
+
"props",
|
|
155
|
+
"excludeFromFocusSearching",
|
|
156
|
+
]);
|
|
157
|
+
|
|
158
|
+
const isNavBar = (target) => startsWith(QUICK_BRICK_NAVBAR, target?.id);
|
|
159
|
+
|
|
160
|
+
const ignoreMenuIfMultipleTargets = (nodes) => {
|
|
161
|
+
if (nodes.length > 1) {
|
|
162
|
+
return nodes.filter((n) => !isNavBar(n?.target));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return nodes;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const toDistance = ({ target, direction, comparisonRect }) => {
|
|
169
|
+
const componentRect = target.component?.getRect();
|
|
170
|
+
|
|
171
|
+
const orientationDistance = measureDistance(
|
|
172
|
+
direction,
|
|
173
|
+
comparisonRect,
|
|
174
|
+
componentRect
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (orientationDistance === -1) {
|
|
178
|
+
// ignores offscreen targets when moving up/down
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const euclidDistance = measureEuclidianDistanceToCenter(
|
|
183
|
+
comparisonRect,
|
|
184
|
+
componentRect
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
return orientationDistance > 0 ? euclidDistance : orientationDistance;
|
|
188
|
+
};
|
|
147
189
|
|
|
148
190
|
/**
|
|
149
191
|
* Get target in shortest distance in specified direction
|
|
@@ -173,43 +215,18 @@ export function getTargetInDirection(
|
|
|
173
215
|
|
|
174
216
|
const distances = targets
|
|
175
217
|
.filter(hasComponentAndChildren)
|
|
176
|
-
.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
componentRect
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
if (distance === -1) {
|
|
186
|
-
// ignores offscreen targets when moving up/down
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return {
|
|
191
|
-
distance:
|
|
192
|
-
measureDistance(direction, comparisonRect, componentRect) +
|
|
193
|
-
measureEuclidianDistanceToCenter(comparisonRect, componentRect) /
|
|
194
|
-
10000,
|
|
195
|
-
target,
|
|
196
|
-
};
|
|
197
|
-
})
|
|
198
|
-
.filter((obj) => obj && obj.distance >= 0);
|
|
218
|
+
.filter((target) => !excludeFromFocusSearching(target))
|
|
219
|
+
.map((target) => ({
|
|
220
|
+
distance: toDistance({ target, direction, comparisonRect }),
|
|
221
|
+
target,
|
|
222
|
+
}))
|
|
223
|
+
.filter(({ distance }) => positiveOrZero(distance));
|
|
199
224
|
|
|
200
225
|
const match = distances
|
|
201
226
|
.sort((a, b) => a.distance - b.distance) // sorts the distances
|
|
202
227
|
.filter(ignoreMenuIfMultipleTargets)[0]; // filters out multiple targets if necessary // gets the first element
|
|
203
228
|
|
|
204
|
-
return
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function ignoreMenuIfMultipleTargets(nodes) {
|
|
208
|
-
if (nodes.length > 1 && nodes.map((n) => n?.target?.id).includes("navbar")) {
|
|
209
|
-
return nodes.filter((n) => n?.target?.id !== "navbar");
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return nodes;
|
|
229
|
+
return match?.target;
|
|
213
230
|
}
|
|
214
231
|
|
|
215
232
|
/**
|
|
@@ -300,3 +317,26 @@ export function hasPriorityInDirection(direction, component) {
|
|
|
300
317
|
|
|
301
318
|
return (prioritiseFocusOn & mappedDirection) > 0;
|
|
302
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
|
+
}
|
|
@@ -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
|
+
};
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
const { Platform } = require("react-native");
|
|
2
|
+
|
|
3
|
+
Platform.OS = "web";
|
|
4
|
+
|
|
1
5
|
import { directionHelper } from "../../directionHelper";
|
|
2
6
|
|
|
3
7
|
const globalTizen = {
|
|
@@ -6,105 +10,96 @@ const globalTizen = {
|
|
|
6
10
|
},
|
|
7
11
|
};
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
describe("keyCode", () => {
|
|
14
|
-
const { keyCode } = require("../index");
|
|
15
|
-
const { KEYS } = require("../keys.web");
|
|
16
|
-
|
|
17
|
-
it("returns an object for a given key event", () => {
|
|
18
|
-
expect(keyCode(KEYS.Enter)).toMatchSnapshot();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("has a matcher method for a single key", () => {
|
|
22
|
-
expect(keyCode(KEYS.Enter).matches("Enter")).toBeTruthy();
|
|
23
|
-
expect(keyCode(KEYS.Enter).matches(KEYS.Enter)).toBeTruthy();
|
|
24
|
-
expect(keyCode(KEYS.Back).matches("Enter")).toBeFalsy();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("has matcher for multiple keys", () => {
|
|
28
|
-
expect(
|
|
29
|
-
keyCode(KEYS.Enter).matchesAny("Enter", "Back", "ArrowUp")
|
|
30
|
-
).toBeTruthy();
|
|
13
|
+
describe("keyCodes", () => {
|
|
14
|
+
describe("keyCode", () => {
|
|
15
|
+
const { keyCode } = require("..");
|
|
16
|
+
const { KEYS } = require("../keys.web");
|
|
31
17
|
|
|
32
|
-
|
|
33
|
-
keyCode(KEYS.Enter).
|
|
34
|
-
)
|
|
35
|
-
});
|
|
18
|
+
it("returns an object for a given key event", () => {
|
|
19
|
+
expect(keyCode(KEYS.Enter)).toMatchSnapshot();
|
|
20
|
+
});
|
|
36
21
|
|
|
37
|
-
|
|
38
|
-
|
|
22
|
+
it("has a matcher method for a single key", () => {
|
|
23
|
+
expect(keyCode(KEYS.Enter).matches("Enter")).toBeTruthy();
|
|
24
|
+
expect(keyCode(KEYS.Enter).matches(KEYS.Enter)).toBeTruthy();
|
|
25
|
+
expect(keyCode(KEYS.Back).matches("Enter")).toBeFalsy();
|
|
26
|
+
});
|
|
39
27
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
28
|
+
it("has matcher for multiple keys", () => {
|
|
29
|
+
expect(
|
|
30
|
+
keyCode(KEYS.Enter).matchesAny("Enter", "Back", "ArrowUp")
|
|
31
|
+
).toBeTruthy();
|
|
44
32
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
33
|
+
expect(
|
|
34
|
+
keyCode(KEYS.Enter).matchesAny("Back", "ArrowUp", "ArrowDown")
|
|
35
|
+
).toBeFalsy();
|
|
48
36
|
});
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
37
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return { Platform: { OS: "samsung_tv" } };
|
|
55
|
-
});
|
|
38
|
+
it("returns the direction if it matches an arrow key", () => {
|
|
39
|
+
expect(keyCode(KEYS.Enter).direction()).toBe(null);
|
|
56
40
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
41
|
+
expect(keyCode(KEYS.ArrowDown).direction()).toEqual(
|
|
42
|
+
directionHelper(KEYS.ArrowDown)
|
|
43
|
+
);
|
|
44
|
+
});
|
|
61
45
|
|
|
62
|
-
|
|
63
|
-
|
|
46
|
+
describe("in web env", () => {
|
|
47
|
+
it("uses the proper keyCode for the environment", () => {
|
|
48
|
+
expect(keyCode({ keyCode: 13 }).matches(KEYS.Enter)).toBeTruthy();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
64
51
|
});
|
|
65
52
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
53
|
+
describe("in Tizen Env", () => {
|
|
54
|
+
beforeEach(() => {
|
|
55
|
+
jest.resetModules();
|
|
56
|
+
global.tizen = globalTizen;
|
|
57
|
+
});
|
|
69
58
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
"MediaPlayPause",
|
|
74
|
-
"MediaPause",
|
|
75
|
-
"MediaFastForward",
|
|
76
|
-
"MediaRewind",
|
|
77
|
-
"MediaStop",
|
|
78
|
-
"Exit",
|
|
79
|
-
"0",
|
|
80
|
-
"1",
|
|
81
|
-
"2",
|
|
82
|
-
"3",
|
|
83
|
-
"4",
|
|
84
|
-
"5",
|
|
85
|
-
"6",
|
|
86
|
-
"7",
|
|
87
|
-
"8",
|
|
88
|
-
"9",
|
|
89
|
-
]);
|
|
90
|
-
|
|
91
|
-
expect(keyCode({ keyCode: 10009 }).matches(KEYS.Back)).toBeTruthy();
|
|
92
|
-
});
|
|
93
|
-
});
|
|
59
|
+
afterEach(() => {
|
|
60
|
+
global.tizen = undefined;
|
|
61
|
+
});
|
|
94
62
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
63
|
+
it("uses the proper keyCode for the environment", () => {
|
|
64
|
+
Platform.OS = "samsung_tv";
|
|
65
|
+
const { keyCode } = require("../");
|
|
66
|
+
const { KEYS } = require("../keys.web");
|
|
67
|
+
|
|
68
|
+
expect(globalTizen.tvinputdevice.registerKeyBatch).toHaveBeenCalledWith([
|
|
69
|
+
"MediaPlay",
|
|
70
|
+
"MediaPlayPause",
|
|
71
|
+
"MediaPause",
|
|
72
|
+
"MediaFastForward",
|
|
73
|
+
"MediaRewind",
|
|
74
|
+
"MediaStop",
|
|
75
|
+
"Exit",
|
|
76
|
+
"0",
|
|
77
|
+
"1",
|
|
78
|
+
"2",
|
|
79
|
+
"3",
|
|
80
|
+
"4",
|
|
81
|
+
"5",
|
|
82
|
+
"6",
|
|
83
|
+
"7",
|
|
84
|
+
"8",
|
|
85
|
+
"9",
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
expect(keyCode({ keyCode: 10009 }).matches(KEYS.Back)).toBeTruthy();
|
|
89
|
+
});
|
|
98
90
|
});
|
|
99
91
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
92
|
+
describe("in WebOS Env", () => {
|
|
93
|
+
beforeEach(() => {
|
|
94
|
+
jest.resetModules();
|
|
95
|
+
});
|
|
103
96
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
97
|
+
it("uses the proper keyCode for the environment", () => {
|
|
98
|
+
Platform.OS = "lg_tv";
|
|
99
|
+
const { keyCode } = require("..");
|
|
100
|
+
const { KEYS } = require("../keys.web");
|
|
101
|
+
// Back is reserved on LG
|
|
102
|
+
expect(keyCode({ keyCode: 417 }).matches(KEYS.Forward)).toBeTruthy();
|
|
103
|
+
});
|
|
109
104
|
});
|
|
110
105
|
});
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import * as ReactNative from "react-native";
|
|
3
3
|
import { isTV, platformSelect } from "../reactUtils";
|
|
4
4
|
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
5
|
+
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
5
6
|
|
|
6
7
|
import {
|
|
7
8
|
QUICK_BRICK_EVENTS,
|
|
@@ -9,7 +10,6 @@ import {
|
|
|
9
10
|
sendQuickBrickEvent,
|
|
10
11
|
} from "@applicaster/zapp-react-native-bridge/QuickBrick";
|
|
11
12
|
import { utilsLogger } from "../logger";
|
|
12
|
-
import { useIsTablet } from "../reactHooks/device/useIsTablet";
|
|
13
13
|
|
|
14
14
|
const { DeviceEventEmitter, NativeModules, NativeEventEmitter } = ReactNative;
|
|
15
15
|
|