@applicaster/zapp-react-native-utils 13.0.0-rc.99 → 14.0.0-alpha.1216545755

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.
Files changed (83) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +87 -67
  2. package/actionsExecutor/ScreenActions.ts +92 -0
  3. package/actionsExecutor/StorageActions.ts +110 -0
  4. package/actionsExecutor/consts.ts +4 -0
  5. package/actionsExecutor/feedDecorator.ts +171 -0
  6. package/actionsExecutor/screenResolver.ts +11 -0
  7. package/appUtils/__tests__/__snapshots__/localizationsHelper.test.ts.snap +151 -0
  8. package/appUtils/__tests__/allZappLocales.ts +79 -0
  9. package/appUtils/__tests__/{localizationsHelper.test.js → localizationsHelper.test.ts} +11 -0
  10. package/appUtils/accessibilityManager/const.ts +18 -0
  11. package/appUtils/accessibilityManager/index.ts +4 -1
  12. package/appUtils/contextKeysManager/contextResolver.ts +14 -1
  13. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +1 -0
  14. package/appUtils/focusManager/index.ios.ts +14 -4
  15. package/appUtils/focusManager/utils/__tests__/findChild.test.ts +35 -0
  16. package/appUtils/focusManager/utils/index.ts +5 -0
  17. package/appUtils/localizationsHelper.ts +10 -2
  18. package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +11 -7
  19. package/arrayUtils/__tests__/isEmptyArray.test.ts +63 -0
  20. package/arrayUtils/__tests__/isFilledArray.test.ts +1 -1
  21. package/arrayUtils/index.ts +7 -2
  22. package/audioPlayerUtils/__tests__/getArtworkImage.test.ts +144 -0
  23. package/audioPlayerUtils/__tests__/getBackgroundImage.test.ts +72 -0
  24. package/audioPlayerUtils/__tests__/getImageFromEntry.test.ts +110 -0
  25. package/audioPlayerUtils/assets/index.ts +2 -0
  26. package/audioPlayerUtils/index.ts +242 -0
  27. package/cellUtils/index.ts +9 -5
  28. package/componentsUtils/index.ts +8 -1
  29. package/conf/player/__tests__/selectors.test.ts +34 -0
  30. package/conf/player/selectors.ts +10 -0
  31. package/configurationUtils/__tests__/configurationUtils.test.js +0 -31
  32. package/configurationUtils/__tests__/getMediaItems.test.ts +65 -0
  33. package/configurationUtils/__tests__/imageSrcFromMediaItem.test.ts +34 -0
  34. package/configurationUtils/index.ts +63 -34
  35. package/localizationUtils/index.ts +3 -3
  36. package/manifestUtils/_internals/getDefaultConfiguration.js +28 -0
  37. package/manifestUtils/{_internals.js → _internals/index.js} +2 -25
  38. package/manifestUtils/createConfig.js +4 -1
  39. package/manifestUtils/defaultManifestConfigurations/generalContent.js +13 -0
  40. package/manifestUtils/defaultManifestConfigurations/player.js +1228 -205
  41. package/manifestUtils/index.js +2 -0
  42. package/manifestUtils/keys.js +27 -2
  43. package/manifestUtils/progressBar/__tests__/mobileProgressBar.test.js +0 -30
  44. package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +1 -2
  45. package/navigationUtils/__tests__/navigationUtils.test.js +0 -65
  46. package/navigationUtils/index.ts +0 -31
  47. package/package.json +2 -2
  48. package/playerUtils/__tests__/configurationUtils.test.ts +1 -65
  49. package/playerUtils/__tests__/getPlayerActionButtons.test.ts +54 -0
  50. package/playerUtils/_internals/__tests__/utils.test.ts +71 -0
  51. package/playerUtils/_internals/index.ts +1 -0
  52. package/playerUtils/_internals/utils.ts +31 -0
  53. package/playerUtils/configurationUtils.ts +0 -44
  54. package/playerUtils/getPlayerActionButtons.ts +17 -0
  55. package/playerUtils/index.ts +25 -0
  56. package/playerUtils/useValidatePlayerConfig.tsx +22 -19
  57. package/reactHooks/app/useAppState.ts +2 -2
  58. package/reactHooks/cell-click/index.ts +8 -1
  59. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +20 -0
  60. package/reactHooks/feed/useBatchLoading.ts +12 -14
  61. package/reactHooks/feed/useFeedLoader.tsx +12 -5
  62. package/reactHooks/navigation/{useGetTabBarHeight.ts → getTabBarHeight.ts} +1 -1
  63. package/reactHooks/navigation/useGetBottomTabBarHeight.ts +10 -3
  64. package/reactHooks/navigation/useNavigationPluginData.ts +8 -4
  65. package/reactHooks/navigation/useNavigationType.ts +4 -2
  66. package/reactHooks/navigation/useRoute.ts +7 -2
  67. package/reactHooks/navigation/useScreenStateStore.ts +11 -0
  68. package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +69 -0
  69. package/reactHooks/screen/useScreenBackgroundColor.ts +3 -15
  70. package/reactHooks/state/README.md +79 -0
  71. package/reactHooks/state/ZStoreProvider.tsx +71 -0
  72. package/reactHooks/state/__tests__/ZStoreProvider.test.tsx +66 -0
  73. package/reactHooks/state/index.ts +2 -0
  74. package/reactHooks/useListenEventBusEvent.ts +1 -1
  75. package/reactUtils/index.ts +9 -0
  76. package/storage/ScreenSingleValueProvider.ts +198 -0
  77. package/storage/ScreenStateMultiSelectProvider.ts +293 -0
  78. package/storage/StorageMultiSelectProvider.ts +192 -0
  79. package/storage/StorageSingleSelectProvider.ts +108 -0
  80. package/typeGuards/index.ts +3 -0
  81. package/utils/index.ts +12 -1
  82. package/zappFrameworkUtils/localStorageHelper.ts +32 -10
  83. package/playerUtils/configurationGenerator.ts +0 -2588
@@ -0,0 +1,151 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`toDayJSLocaleMap [af] returns a valid dayjs locale 1`] = `"af"`;
4
+
5
+ exports[`toDayJSLocaleMap [ar] returns a valid dayjs locale 1`] = `"ar"`;
6
+
7
+ exports[`toDayJSLocaleMap [bg] returns a valid dayjs locale 1`] = `"bg"`;
8
+
9
+ exports[`toDayJSLocaleMap [bn] returns a valid dayjs locale 1`] = `"bn"`;
10
+
11
+ exports[`toDayJSLocaleMap [bo] returns a valid dayjs locale 1`] = `"bo"`;
12
+
13
+ exports[`toDayJSLocaleMap [ca] returns a valid dayjs locale 1`] = `"ca"`;
14
+
15
+ exports[`toDayJSLocaleMap [cs] returns a valid dayjs locale 1`] = `"cs"`;
16
+
17
+ exports[`toDayJSLocaleMap [da] returns a valid dayjs locale 1`] = `"da"`;
18
+
19
+ exports[`toDayJSLocaleMap [de] returns a valid dayjs locale 1`] = `"de"`;
20
+
21
+ exports[`toDayJSLocaleMap [el] returns a valid dayjs locale 1`] = `"el"`;
22
+
23
+ exports[`toDayJSLocaleMap [en] returns a valid dayjs locale 1`] = `"en"`;
24
+
25
+ exports[`toDayJSLocaleMap [en-GB] returns a valid dayjs locale 1`] = `"en-gb"`;
26
+
27
+ exports[`toDayJSLocaleMap [en-UK] returns a valid dayjs locale 1`] = `"en-gb"`;
28
+
29
+ exports[`toDayJSLocaleMap [es] returns a valid dayjs locale 1`] = `"es"`;
30
+
31
+ exports[`toDayJSLocaleMap [es-LA] returns a valid dayjs locale 1`] = `"es"`;
32
+
33
+ exports[`toDayJSLocaleMap [es-MX] returns a valid dayjs locale 1`] = `"es-mx"`;
34
+
35
+ exports[`toDayJSLocaleMap [es-US] returns a valid dayjs locale 1`] = `"es-us"`;
36
+
37
+ exports[`toDayJSLocaleMap [et] returns a valid dayjs locale 1`] = `"et"`;
38
+
39
+ exports[`toDayJSLocaleMap [eu] returns a valid dayjs locale 1`] = `"eu"`;
40
+
41
+ exports[`toDayJSLocaleMap [fa] returns a valid dayjs locale 1`] = `"fa"`;
42
+
43
+ exports[`toDayJSLocaleMap [fi] returns a valid dayjs locale 1`] = `"fi"`;
44
+
45
+ exports[`toDayJSLocaleMap [fj] returns a valid dayjs locale 1`] = `"en"`;
46
+
47
+ exports[`toDayJSLocaleMap [fr] returns a valid dayjs locale 1`] = `"fr"`;
48
+
49
+ exports[`toDayJSLocaleMap [ga] returns a valid dayjs locale 1`] = `"ga"`;
50
+
51
+ exports[`toDayJSLocaleMap [gu] returns a valid dayjs locale 1`] = `"gu"`;
52
+
53
+ exports[`toDayJSLocaleMap [he] returns a valid dayjs locale 1`] = `"he"`;
54
+
55
+ exports[`toDayJSLocaleMap [hi] returns a valid dayjs locale 1`] = `"hi"`;
56
+
57
+ exports[`toDayJSLocaleMap [hr] returns a valid dayjs locale 1`] = `"hr"`;
58
+
59
+ exports[`toDayJSLocaleMap [hu] returns a valid dayjs locale 1`] = `"hu"`;
60
+
61
+ exports[`toDayJSLocaleMap [hy] returns a valid dayjs locale 1`] = `"hy-am"`;
62
+
63
+ exports[`toDayJSLocaleMap [id] returns a valid dayjs locale 1`] = `"id"`;
64
+
65
+ exports[`toDayJSLocaleMap [is] returns a valid dayjs locale 1`] = `"is"`;
66
+
67
+ exports[`toDayJSLocaleMap [it] returns a valid dayjs locale 1`] = `"it"`;
68
+
69
+ exports[`toDayJSLocaleMap [ja] returns a valid dayjs locale 1`] = `"ja"`;
70
+
71
+ exports[`toDayJSLocaleMap [ka] returns a valid dayjs locale 1`] = `"ka"`;
72
+
73
+ exports[`toDayJSLocaleMap [km] returns a valid dayjs locale 1`] = `"km"`;
74
+
75
+ exports[`toDayJSLocaleMap [ko] returns a valid dayjs locale 1`] = `"ko"`;
76
+
77
+ exports[`toDayJSLocaleMap [la] returns a valid dayjs locale 1`] = `"en"`;
78
+
79
+ exports[`toDayJSLocaleMap [lt] returns a valid dayjs locale 1`] = `"lt"`;
80
+
81
+ exports[`toDayJSLocaleMap [lv] returns a valid dayjs locale 1`] = `"lv"`;
82
+
83
+ exports[`toDayJSLocaleMap [mi] returns a valid dayjs locale 1`] = `"mi"`;
84
+
85
+ exports[`toDayJSLocaleMap [mk] returns a valid dayjs locale 1`] = `"mk"`;
86
+
87
+ exports[`toDayJSLocaleMap [ml] returns a valid dayjs locale 1`] = `"ml"`;
88
+
89
+ exports[`toDayJSLocaleMap [mn] returns a valid dayjs locale 1`] = `"mn"`;
90
+
91
+ exports[`toDayJSLocaleMap [mr] returns a valid dayjs locale 1`] = `"mr"`;
92
+
93
+ exports[`toDayJSLocaleMap [ms] returns a valid dayjs locale 1`] = `"ms"`;
94
+
95
+ exports[`toDayJSLocaleMap [mt] returns a valid dayjs locale 1`] = `"mt"`;
96
+
97
+ exports[`toDayJSLocaleMap [ne] returns a valid dayjs locale 1`] = `"ne"`;
98
+
99
+ exports[`toDayJSLocaleMap [nl] returns a valid dayjs locale 1`] = `"nl"`;
100
+
101
+ exports[`toDayJSLocaleMap [no] returns a valid dayjs locale 1`] = `"en"`;
102
+
103
+ exports[`toDayJSLocaleMap [pa] returns a valid dayjs locale 1`] = `"pa-in"`;
104
+
105
+ exports[`toDayJSLocaleMap [pl] returns a valid dayjs locale 1`] = `"pl"`;
106
+
107
+ exports[`toDayJSLocaleMap [pt] returns a valid dayjs locale 1`] = `"pt"`;
108
+
109
+ exports[`toDayJSLocaleMap [pt-BR] returns a valid dayjs locale 1`] = `"pt-br"`;
110
+
111
+ exports[`toDayJSLocaleMap [qu] returns a valid dayjs locale 1`] = `"en"`;
112
+
113
+ exports[`toDayJSLocaleMap [ro] returns a valid dayjs locale 1`] = `"ro"`;
114
+
115
+ exports[`toDayJSLocaleMap [ru] returns a valid dayjs locale 1`] = `"ru"`;
116
+
117
+ exports[`toDayJSLocaleMap [sk] returns a valid dayjs locale 1`] = `"sk"`;
118
+
119
+ exports[`toDayJSLocaleMap [sl] returns a valid dayjs locale 1`] = `"sl"`;
120
+
121
+ exports[`toDayJSLocaleMap [sm] returns a valid dayjs locale 1`] = `"en"`;
122
+
123
+ exports[`toDayJSLocaleMap [sq] returns a valid dayjs locale 1`] = `"sq"`;
124
+
125
+ exports[`toDayJSLocaleMap [sr] returns a valid dayjs locale 1`] = `"sr"`;
126
+
127
+ exports[`toDayJSLocaleMap [sv] returns a valid dayjs locale 1`] = `"sv"`;
128
+
129
+ exports[`toDayJSLocaleMap [sw] returns a valid dayjs locale 1`] = `"sw"`;
130
+
131
+ exports[`toDayJSLocaleMap [ta] returns a valid dayjs locale 1`] = `"ta"`;
132
+
133
+ exports[`toDayJSLocaleMap [te] returns a valid dayjs locale 1`] = `"te"`;
134
+
135
+ exports[`toDayJSLocaleMap [th] returns a valid dayjs locale 1`] = `"th"`;
136
+
137
+ exports[`toDayJSLocaleMap [to] returns a valid dayjs locale 1`] = `"en"`;
138
+
139
+ exports[`toDayJSLocaleMap [tr] returns a valid dayjs locale 1`] = `"tr"`;
140
+
141
+ exports[`toDayJSLocaleMap [tt] returns a valid dayjs locale 1`] = `"en"`;
142
+
143
+ exports[`toDayJSLocaleMap [uk] returns a valid dayjs locale 1`] = `"uk"`;
144
+
145
+ exports[`toDayJSLocaleMap [ur] returns a valid dayjs locale 1`] = `"ur"`;
146
+
147
+ exports[`toDayJSLocaleMap [uz] returns a valid dayjs locale 1`] = `"uz"`;
148
+
149
+ exports[`toDayJSLocaleMap [vi] returns a valid dayjs locale 1`] = `"vi"`;
150
+
151
+ exports[`toDayJSLocaleMap [zh] returns a valid dayjs locale 1`] = `"zh"`;
@@ -0,0 +1,79 @@
1
+ export default [
2
+ // common locales from https://rubygems.org/gems/language_list
3
+ "en",
4
+ "fr",
5
+ "de",
6
+ "nl",
7
+ "ru",
8
+ "es",
9
+ "he",
10
+ "hy",
11
+ "sq",
12
+ "af",
13
+ "ar",
14
+ "eu",
15
+ "bn",
16
+ "bg",
17
+ "zh",
18
+ "cs",
19
+ "km",
20
+ "ca",
21
+ "hr",
22
+ "da",
23
+ "et",
24
+ "fj",
25
+ "fi",
26
+ "gu",
27
+ "ka",
28
+ "hu",
29
+ "hi",
30
+ "it",
31
+ "is",
32
+ "id",
33
+ "ga",
34
+ "ja",
35
+ "ko",
36
+ "la",
37
+ "lv",
38
+ "lt",
39
+ "ms",
40
+ "mi",
41
+ "mn",
42
+ "mt",
43
+ "mk",
44
+ "el",
45
+ "mr",
46
+ "ml",
47
+ "ne",
48
+ "no",
49
+ "pa",
50
+ "pl",
51
+ "pt",
52
+ "fa",
53
+ "qu",
54
+ "ro",
55
+ "sk",
56
+ "sm",
57
+ "sr",
58
+ "sw",
59
+ "sv",
60
+ "sl",
61
+ "to",
62
+ "tr",
63
+ "th",
64
+ "te",
65
+ "bo",
66
+ "tt",
67
+ "ta",
68
+ "uk",
69
+ "ur",
70
+ "uz",
71
+ "vi",
72
+ // Extra locales https://github.com/applicaster/zapp/blob/5604794f15cfd270ae494b0db85e542de4cebfaa/config/additional_languages.yml
73
+ "en-UK",
74
+ "en-GB",
75
+ "es-LA",
76
+ "pt-BR",
77
+ "es-MX",
78
+ "es-US",
79
+ ];
@@ -8,6 +8,7 @@ const {
8
8
  getLocale,
9
9
  getLanguageCode,
10
10
  getCountryCode,
11
+ toDayJSLocaleMap,
11
12
  } = require("../localizationsHelper");
12
13
 
13
14
  describe("getLocale", () => {
@@ -69,3 +70,13 @@ describe("getCountryCode", () => {
69
70
  expect(currentValue).toEqual(expectedValue);
70
71
  });
71
72
  });
73
+
74
+ describe("toDayJSLocaleMap", () => {
75
+ const allZappLocales: string[] = require("./allZappLocales").default;
76
+
77
+ it.each(allZappLocales)("[%s] returns a valid dayjs locale", (locale) => {
78
+ const dayJSLocale = toDayJSLocaleMap(locale);
79
+ expect(dayJSLocale).toBeDefined();
80
+ expect(dayJSLocale).toMatchSnapshot();
81
+ });
82
+ });
@@ -47,4 +47,22 @@ export const BUTTON_ACCESSIBILITY_KEYS = {
47
47
  label: "accessibility_fullscreen_label",
48
48
  hint: "accessibility_fullscreen_hint",
49
49
  },
50
+ // EPG-specific buttons
51
+ now: {
52
+ label: "accessibility_now_label",
53
+ hint: "accessibility_now_hint",
54
+ },
55
+ day: {
56
+ label: "accessibility_day_label",
57
+ hint: "accessibility_day_hint",
58
+ },
59
+ program: {
60
+ label: "accessibility_program_label",
61
+ hint: "accessibility_program_hint",
62
+ },
63
+ // Menu-specific buttons
64
+ menu_item: {
65
+ label: "accessibility_menu_item_label",
66
+ hint: "accessibility_menu_item_hint",
67
+ },
50
68
  } as const;
@@ -3,6 +3,7 @@ import { accessibilityManagerLogger as logger } from "./logger";
3
3
  import { TTSManager } from "../platform/platformUtils";
4
4
  import { BUTTON_ACCESSIBILITY_KEYS } from "./const";
5
5
  import { AccessibilityRole } from "react-native";
6
+ import _ from "lodash";
6
7
 
7
8
  export class AccessibilityManager {
8
9
  private static _instance: AccessibilityManager | null = null;
@@ -135,7 +136,9 @@ export class AccessibilityManager {
135
136
  });
136
137
  }
137
138
 
138
- public getButtonAccessibilityProps(buttonName: string): AccessibilityProps {
139
+ public getButtonAccessibilityProps(name: string): AccessibilityProps {
140
+ const buttonName = _.toString(name);
141
+
139
142
  const buttonConfig = BUTTON_ACCESSIBILITY_KEYS[buttonName];
140
143
 
141
144
  if (!buttonConfig) {
@@ -1,10 +1,11 @@
1
1
  import { ContextKeysManager } from "./index";
2
2
  import * as R from "ramda";
3
3
 
4
- interface IResolver {
4
+ export interface IResolver {
5
5
  resolve: (string) => Promise<string | number | object>;
6
6
  }
7
7
 
8
+ // TODO: Rename to ObjectKeyResolver or similar
8
9
  export class EntryResolver implements IResolver {
9
10
  entry: ZappEntry;
10
11
 
@@ -21,6 +22,18 @@ export class EntryResolver implements IResolver {
21
22
  }
22
23
  }
23
24
 
25
+ // TODO: Move to proper place
26
+
27
+ export class ScreenStateResolver implements IResolver {
28
+ constructor(private screenStateStore: ScreenStateStore) {}
29
+
30
+ async resolve(key: string) {
31
+ const screenState = this.screenStateStore.getState().data;
32
+
33
+ return screenState?.[key];
34
+ }
35
+ }
36
+
24
37
  export class ContextResolver implements IResolver {
25
38
  resolve = async (compositeKey: string) =>
26
39
  ContextKeysManager.instance.getKey(compositeKey);
@@ -61,6 +61,7 @@ exports[`focusManagerIOS should be defined 1`] = `
61
61
  "getCurrentGroup": [Function],
62
62
  "getGroupById": [Function],
63
63
  "getGroupRootById": [Function],
64
+ "getPreferredFocusChild": [Function],
64
65
  "invokeHandler": [Function],
65
66
  "isGroupItemFocused": [Function],
66
67
  "moveFocus": [Function],
@@ -1,8 +1,10 @@
1
1
  import { NativeModules } from "react-native";
2
2
  import * as R from "ramda";
3
+
3
4
  import { Tree } from "./treeDataStructure/Tree";
4
5
  import { findFocusableNode } from "./treeDataStructure/Utils";
5
6
  import { subscriber } from "../../functionUtils";
7
+ import { findChild } from "./utils";
6
8
 
7
9
  const { FocusableManagerModule } = NativeModules;
8
10
 
@@ -52,10 +54,7 @@ export const focusManager = (function () {
52
54
  const node = focusableTree.findInTree(groupId);
53
55
 
54
56
  if (node?.children?.length > 0) {
55
- const preferredFocus = R.compose(
56
- R.find(R.pathEq(["component", "props", "preferredFocus"], true)),
57
- R.prop("children")
58
- )(node);
57
+ const preferredFocus = findChild(true, node);
59
58
 
60
59
  if (preferredFocus?.component?.isGroup) {
61
60
  return getPreferredFocusInGroup({ groupId: preferredFocus?.id });
@@ -382,6 +381,16 @@ export const focusManager = (function () {
382
381
  forceFocusOnItem({ focusableItem, callback });
383
382
  }
384
383
 
384
+ function getPreferredFocusChild(id) {
385
+ const node = focusableTree.findInTree(id);
386
+
387
+ if (node?.children?.length > 0) {
388
+ return findChild(true, node) || findChild(false, node);
389
+ }
390
+
391
+ return node;
392
+ }
393
+
385
394
  return {
386
395
  on,
387
396
  invokeHandler,
@@ -402,5 +411,6 @@ export const focusManager = (function () {
402
411
  getCurrentGroup,
403
412
  getGroupRootById,
404
413
  isGroupItemFocused,
414
+ getPreferredFocusChild,
405
415
  };
406
416
  })();
@@ -0,0 +1,35 @@
1
+ import { findChild } from "../index";
2
+
3
+ describe("findChild", () => {
4
+ const makeNode = (children = []) => ({ children });
5
+
6
+ const child = (preferredFocus: boolean) => ({
7
+ component: { props: { preferredFocus } },
8
+ });
9
+
10
+ it("returns the child with preferredFocus=true", () => {
11
+ const node = makeNode([child(false), child(true), child(false)]);
12
+ expect(findChild(true, node)).toBe(node.children[1]);
13
+ });
14
+
15
+ it("returns the child with preferredFocus=false", () => {
16
+ const node = makeNode([child(false), child(true), child(false)]);
17
+ expect(findChild(false, node)).toBe(node.children[0]);
18
+ });
19
+
20
+ it("returns undefined if no child matches preferredFocus", () => {
21
+ const node = makeNode([child(false), child(false)]);
22
+ expect(findChild(true, node)).toBeUndefined();
23
+ });
24
+
25
+ it("returns undefined if node has no children", () => {
26
+ const node = makeNode();
27
+ expect(findChild(true, node)).toBeUndefined();
28
+ expect(findChild(false, node)).toBeUndefined();
29
+ });
30
+
31
+ it("returns undefined if children is not an array", () => {
32
+ const node = { children: null };
33
+ expect(findChild(true, node)).toBeUndefined();
34
+ });
35
+ });
@@ -98,3 +98,8 @@ export const waitForContent = (focusableTree) => {
98
98
  conditionFn: contentHasAnyChildren,
99
99
  });
100
100
  };
101
+
102
+ export const findChild = (preferredFocus: boolean, node) =>
103
+ (node?.children || []).find(
104
+ (child) => child?.component?.props?.preferredFocus === preferredFocus
105
+ );
@@ -65,6 +65,12 @@ export function getUILanguage() {
65
65
  return getLanguageCode();
66
66
  }
67
67
 
68
+ /**
69
+ * fallback to en for missing dayjslocalization
70
+ * converts the locale code to a dayjs compatible locale code
71
+ * lowercase input code is expected, e.g. "en-GB" => "en-gb"
72
+ * @param {string} code - locale code, e.g. "en", "fr", "es-LA"
73
+ * */
68
74
  export const toDayJSLocaleMap = (code) => {
69
75
  const map = {
70
76
  hy: "hy-am",
@@ -73,10 +79,12 @@ export const toDayJSLocaleMap = (code) => {
73
79
  no: null,
74
80
  pa: "pa-in",
75
81
  qu: null,
76
- "es-LA": "es",
77
82
  sm: null,
78
83
  to: null,
79
84
  xh: null,
85
+ tt: null,
86
+ "es-LA": "es",
87
+ "en-UK": "en-gb",
80
88
  };
81
89
 
82
90
  if (map[code] === null) {
@@ -84,7 +92,7 @@ export const toDayJSLocaleMap = (code) => {
84
92
  } else if (map[code]) {
85
93
  return map[code];
86
94
  } else {
87
- return code;
95
+ return code.toLowerCase();
88
96
  }
89
97
  };
90
98
 
@@ -12,7 +12,7 @@ import { useThrottle } from "../../../functionUtils";
12
12
  export const usePlayerCurrentTime = (
13
13
  listenerId: string,
14
14
  playerId?: string
15
- ): number | null => {
15
+ ): { currentTime: number | null; cancel: () => void } => {
16
16
  const [currentTime, setCurrentTime] = useState<number | null>(null);
17
17
  const player: Player = usePlayer(playerId);
18
18
 
@@ -20,15 +20,17 @@ export const usePlayerCurrentTime = (
20
20
  if (player) {
21
21
  const newCurrentTime = player.getContentPosition();
22
22
 
23
- setCurrentTime((prevTime) =>
24
- prevTime !== newCurrentTime ? newCurrentTime : prevTime
25
- );
23
+ setCurrentTime(newCurrentTime);
26
24
  }
27
25
  }, [player]);
28
26
 
27
+ // We update the current time every ~250ms since player can have rate set to 2x.
28
+ // We update more often than 2x a second to avoid noticiable 'pulsation' in update,
29
+ // since native level also updates the current time every ~250ms,
30
+ // and combined with assorted delays it creates interference pattern.
29
31
  const { throttledFunction: throttledUpdate, cancel } = useThrottle(
30
32
  updateCurrentTime,
31
- 1000
33
+ 1000 / 4
32
34
  );
33
35
 
34
36
  useEffect(() => {
@@ -36,7 +38,9 @@ export const usePlayerCurrentTime = (
36
38
  throttledUpdate();
37
39
 
38
40
  const listeners = {
39
- onVideoProgress: throttledUpdate,
41
+ onVideoProgress: () => {
42
+ setCurrentTime(player.getContentPosition());
43
+ },
40
44
  };
41
45
 
42
46
  const id = `${listenerId}_player_current_time`;
@@ -53,5 +57,5 @@ export const usePlayerCurrentTime = (
53
57
  }
54
58
  }, [player, listenerId, throttledUpdate, cancel]);
55
59
 
56
- return currentTime;
60
+ return { currentTime, cancel };
57
61
  };
@@ -0,0 +1,63 @@
1
+ import { isEmptyArray } from "..";
2
+
3
+ describe("isEmptyArray", () => {
4
+ it("non-empty array is not empty", () => {
5
+ const value = [1, 2, 3];
6
+
7
+ expect(isEmptyArray(value)).toBe(false);
8
+ });
9
+
10
+ it("empty array is empty", () => {
11
+ const value = [];
12
+
13
+ expect(isEmptyArray(value)).toBe(true);
14
+ });
15
+
16
+ it("number is not array", () => {
17
+ const value = 123;
18
+
19
+ expect(isEmptyArray(value)).toBe(false);
20
+ });
21
+
22
+ it("string is not array", () => {
23
+ const value = "vfnjdk";
24
+
25
+ expect(isEmptyArray(value)).toBe(false);
26
+ });
27
+
28
+ it("empty string is not array", () => {
29
+ const value = "";
30
+
31
+ expect(isEmptyArray(value)).toBe(false);
32
+ });
33
+
34
+ it("NaN is not array", () => {
35
+ const value = NaN;
36
+
37
+ expect(isEmptyArray(value)).toBe(false);
38
+ });
39
+
40
+ it("object is not array", () => {
41
+ const value = { test: 1 };
42
+
43
+ expect(isEmptyArray(value)).toBe(false);
44
+ });
45
+
46
+ it("empty object is not array", () => {
47
+ const value = {};
48
+
49
+ expect(isEmptyArray(value)).toBe(false);
50
+ });
51
+
52
+ it("undefined is not array", () => {
53
+ const value = undefined;
54
+
55
+ expect(isEmptyArray(value)).toBe(false);
56
+ });
57
+
58
+ it("null is not array", () => {
59
+ const value = null;
60
+
61
+ expect(isEmptyArray(value)).toBe(false);
62
+ });
63
+ });
@@ -32,7 +32,7 @@ describe("isFilledArray", () => {
32
32
  });
33
33
 
34
34
  it("NaN is not array", () => {
35
- const value = "";
35
+ const value = NaN;
36
36
 
37
37
  expect(isFilledArray(value)).toBe(false);
38
38
  });
@@ -99,12 +99,17 @@ export const makeListOf = (value: unknown, size: number): number[] => {
99
99
 
100
100
  /** Checks if a value is a non-empty array */
101
101
  export function isFilledArray(value: unknown): boolean {
102
- return R.is(Array, value) && R.length(value) > 0;
102
+ return Array.isArray(value) && value.length > 0;
103
+ }
104
+
105
+ /** Checks if a value is a empty array */
106
+ export function isEmptyArray(value: unknown): boolean {
107
+ return Array.isArray(value) && value.length === 0;
103
108
  }
104
109
 
105
110
  // get random item from the list
106
111
  export const sample = (xs: unknown[]): unknown => {
107
- invariant(R.is(Array, xs), `input value is not a array: ${xs}`);
112
+ invariant(Array.isArray(xs), `input value is not an array: ${xs}`);
108
113
  invariant(isFilledArray(xs), `input array is empty: ${xs}`);
109
114
 
110
115
  const index = Math.floor(Math.random() * xs.length);