@applicaster/zapp-react-native-utils 13.0.0-alpha.4244246222 → 13.0.0-alpha.4302300656

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 (69) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +159 -12
  2. package/actionsExecutor/consts.ts +4 -0
  3. package/appUtils/HooksManager/Hook.ts +4 -0
  4. package/appUtils/HooksManager/index.ts +2 -0
  5. package/appUtils/__tests__/__snapshots__/localizationsHelper.test.ts.snap +151 -0
  6. package/appUtils/__tests__/allZappLocales.ts +79 -0
  7. package/appUtils/__tests__/{localizationsHelper.test.js → localizationsHelper.test.ts} +11 -0
  8. package/appUtils/accessibilityManager/const.ts +18 -0
  9. package/appUtils/contextKeysManager/contextResolver.ts +1 -12
  10. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +2 -0
  11. package/appUtils/focusManager/index.ios.ts +14 -4
  12. package/appUtils/focusManager/index.ts +13 -0
  13. package/appUtils/focusManager/utils/__tests__/findChild.test.ts +35 -0
  14. package/appUtils/focusManager/utils/index.ts +5 -0
  15. package/appUtils/localizationsHelper.ts +31 -0
  16. package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +11 -7
  17. package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +7 -1
  18. package/cellUtils/index.ts +9 -5
  19. package/componentsUtils/__tests__/isFirstComponentGallery.test.ts +33 -0
  20. package/componentsUtils/__tests__/isFirstComponentScreenPicker.test.ts +33 -0
  21. package/componentsUtils/__tests__/isGallery.test.ts +33 -0
  22. package/componentsUtils/__tests__/isScreenPicker.test.ts +33 -0
  23. package/componentsUtils/index.ts +39 -0
  24. package/dateUtils/index.ts +19 -0
  25. package/focusManager/FocusManager.ts +3 -1
  26. package/focusManager/index.ts +4 -30
  27. package/functionUtils/index.ts +25 -0
  28. package/index.d.ts +0 -12
  29. package/localizationUtils/index.ts +8 -13
  30. package/manifestUtils/defaultManifestConfigurations/generalContent.js +13 -0
  31. package/manifestUtils/defaultManifestConfigurations/player.js +6 -8
  32. package/manifestUtils/index.js +2 -0
  33. package/manifestUtils/keys.js +27 -2
  34. package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +1 -2
  35. package/modalState/__tests__/index.test.ts +51 -0
  36. package/modalState/index.ts +32 -2
  37. package/navigationUtils/__tests__/__snapshots__/navigationUtils.test.js.snap +1 -0
  38. package/navigationUtils/index.ts +3 -1
  39. package/numberUtils/__tests__/toPositiveNumberWithDefault.test.ts +23 -3
  40. package/numberUtils/index.ts +4 -0
  41. package/package.json +2 -2
  42. package/playerUtils/configurationGenerator.ts +6 -16
  43. package/playerUtils/index.ts +23 -0
  44. package/reactHooks/advertising/index.ts +5 -5
  45. package/reactHooks/app/useAppState.ts +2 -2
  46. package/reactHooks/feed/useBatchLoading.ts +10 -12
  47. package/reactHooks/feed/useFeedLoader.tsx +17 -8
  48. package/reactHooks/hookModal/hooks/useHookModalScreenData.ts +39 -0
  49. package/reactHooks/navigation/{useGetTabBarHeight.ts → getTabBarHeight.ts} +1 -1
  50. package/reactHooks/navigation/useGetBottomTabBarHeight.ts +10 -3
  51. package/reactHooks/navigation/useNavigationPluginData.ts +8 -4
  52. package/reactHooks/navigation/useNavigationType.ts +4 -2
  53. package/reactHooks/navigation/useRoute.ts +4 -6
  54. package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +69 -0
  55. package/reactHooks/screen/useScreenBackgroundColor.ts +3 -15
  56. package/reactHooks/state/README.md +79 -0
  57. package/reactHooks/state/ZStoreProvider.tsx +71 -0
  58. package/reactHooks/state/__tests__/ZStoreProvider.test.tsx +66 -0
  59. package/reactHooks/state/index.ts +2 -0
  60. package/reactHooks/useListenEventBusEvent.ts +1 -1
  61. package/reactUtils/index.ts +9 -0
  62. package/typeGuards/index.ts +3 -0
  63. package/utils/README.md +30 -0
  64. package/utils/chunk.ts +5 -0
  65. package/utils/index.ts +5 -0
  66. package/utils/times.ts +5 -0
  67. package/zappFrameworkUtils/localStorageHelper.ts +38 -10
  68. package/zappFrameworkUtils/loginPluginUtils.ts +6 -2
  69. package/zappFrameworkUtils/types.ts +11 -0
@@ -1,13 +1,18 @@
1
1
  /// <reference types="@applicaster/applicaster-types" />
2
2
  import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
3
- import React, { createContext, useMemo } from "react";
3
+ import React, { createContext, useEffect, useMemo } from "react";
4
4
  import {
5
5
  actionExecutor as _actionExecutor,
6
6
  ActionResult,
7
7
  } from "./ActionExecutor";
8
- import { batchSaveToLocalStorage } from "../zappFrameworkUtils/localStorageHelper";
9
- import { QUICK_BRICK_EVENTS } from "@applicaster/zapp-react-native-bridge/QuickBrick";
8
+ import {
9
+ batchRemoveAllFromNamespaceForStorage,
10
+ batchSave,
11
+ } from "../zappFrameworkUtils/localStorageHelper";
12
+ import { sessionStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/SessionStorage";
13
+
10
14
  import * as QuickBrickManager from "@applicaster/zapp-react-native-bridge/QuickBrick";
15
+ import { QUICK_BRICK_EVENTS } from "@applicaster/zapp-react-native-bridge/QuickBrick";
11
16
  import { showConfirmationDialog } from "../alertUtils";
12
17
  import { createCloudEvent, sendCloudEvent } from "../cloudEventsUtils";
13
18
  import { createLogger } from "../logger";
@@ -19,8 +24,18 @@ import {
19
24
  EntryResolver,
20
25
  resolveObjectValues,
21
26
  } from "../appUtils/contextKeysManager/contextResolver";
27
+ import { useNavigation } from "../reactHooks";
28
+ import { get } from "lodash";
22
29
 
23
- const { log_error, log_info } = createLogger({
30
+ import {
31
+ useContentTypes,
32
+ usePickFromState,
33
+ } from "@applicaster/zapp-react-native-redux/hooks";
34
+ import { TOGGLE_FLAG_MAX_ITEMS_REACHED_EVENT } from "./consts";
35
+ import { postEvent, useSubscriberFor } from "../reactHooks/useSubscriberFor";
36
+ import { APP_EVENTS } from "../appUtils/events";
37
+
38
+ export const { log_error, log_info, log_debug } = createLogger({
24
39
  subsystem: "ActionExecutorContext",
25
40
  category: "General",
26
41
  });
@@ -51,10 +66,47 @@ type Props = {
51
66
  children: React.ReactNode;
52
67
  };
53
68
 
69
+ function findParentComponent(
70
+ childId: string,
71
+ parent: ZappRiver | ZappUIComponent
72
+ ): ZappRiver | ZappUIComponent | null {
73
+ for (const child of parent.ui_components) {
74
+ if (child.id === childId) return parent;
75
+
76
+ if (child.ui_components) {
77
+ const found = findParentComponent(childId, child);
78
+ if (found) return found;
79
+ }
80
+ }
81
+
82
+ return null;
83
+ }
84
+
85
+ // send all data just in case (like for message string formatting)
86
+ // Type is not exported for now
87
+ type MaxTagsReachedEvent = {
88
+ selectedItems: string[];
89
+ maxItems: number;
90
+ tag: string;
91
+ keyNamespace: string;
92
+ };
93
+
94
+ async function onMaxTagsReached(data: MaxTagsReachedEvent) {
95
+ postEvent(TOGGLE_FLAG_MAX_ITEMS_REACHED_EVENT, [data]);
96
+ }
97
+
54
98
  const prepareDefaultActions = (actionExecutor) => {
55
99
  actionExecutor.registerAction("localStorageSet", async (action) => {
56
100
  const namespaces = action.options.content;
57
- await batchSaveToLocalStorage(namespaces);
101
+ await batchSave(namespaces, localStorage);
102
+ // TODO: Add support for ownershipKey and ownershipNamespace
103
+
104
+ return ActionResult.Success;
105
+ });
106
+
107
+ actionExecutor.registerAction("sessionStorageSet", async (action) => {
108
+ const namespaces = action.options.content;
109
+ await batchSave(namespaces, sessionStorage);
58
110
  // TODO: Add support for ownershipKey and ownershipNamespace
59
111
 
60
112
  return ActionResult.Success;
@@ -65,11 +117,13 @@ const prepareDefaultActions = (actionExecutor) => {
65
117
  async (_action, context) => {
66
118
  const dispatch = appStore.getDispatch();
67
119
 
68
- log_info("handleAction: refreshComponent event");
120
+ const dataSource =
121
+ context?.component?.data?.source ||
122
+ findParentComponent(context?.component.id, context?.screenData)?.data
123
+ ?.source;
69
124
 
70
- const dataSource = context?.component?.data?.source;
125
+ log_info(`handleAction: refreshComponent for dataSource:${dataSource}`);
71
126
 
72
- // TODO: Later we can pass component ids to do refresh instead of current one
73
127
  // TODO: In theory we should wait callback to complete, before completing the action, but now it's not needed
74
128
  // TODO: handle focused item removal
75
129
  dispatch(
@@ -109,15 +163,13 @@ const prepareDefaultActions = (actionExecutor) => {
109
163
  actionExecutor.registerAction("confirmDialog", async (action) => {
110
164
  log_info("handleAction: confirmDialog event");
111
165
 
112
- const confirmationPromise = new Promise<ActionResult>((resolve) => {
166
+ return new Promise<ActionResult>((resolve) => {
113
167
  showConfirmationDialog({
114
168
  ...action.options,
115
169
  confirmCompletion: () => resolve(ActionResult.Success),
116
170
  cancelCompletion: () => resolve(ActionResult.Cancel),
117
171
  });
118
172
  });
119
-
120
- return confirmationPromise;
121
173
  });
122
174
 
123
175
  actionExecutor.registerAction("sendCloudEvent", async (action, context) => {
@@ -169,8 +221,29 @@ const prepareDefaultActions = (actionExecutor) => {
169
221
  action: ActionType,
170
222
  context?: Record<string, any>
171
223
  ): Promise<ActionResult> => {
224
+ if (!context) {
225
+ log_error(
226
+ "handleAction: localStorageToggleFlag action missing context"
227
+ );
228
+
229
+ return ActionResult.Error;
230
+ }
231
+
232
+ const entry = context?.entry as ZappEntry;
233
+
234
+ if (!entry) {
235
+ log_error(
236
+ "handleAction: localStorageToggleFlag action missing entry. Entry is required to get the tag."
237
+ );
238
+
239
+ return ActionResult.Error;
240
+ }
241
+
242
+ const tag = action.options?.selector
243
+ ? get(entry, action.options.selector)
244
+ : (entry.extensions?.tag ?? entry.id);
245
+
172
246
  const keyNamespace = action.options?.key;
173
- const tag = context?.entry?.extensions?.tag;
174
247
 
175
248
  if (keyNamespace && tag) {
176
249
  const multiSelectProvider =
@@ -188,6 +261,23 @@ const prepareDefaultActions = (actionExecutor) => {
188
261
  if (selectedItems.includes(tag)) {
189
262
  await multiSelectProvider.removeItem(tag);
190
263
  } else {
264
+ const maxItems = action.options?.max_items;
265
+
266
+ if (maxItems && selectedItems.length >= maxItems) {
267
+ log_info(
268
+ `handleAction: localStorageToggleFlag event reached max items limit: ${maxItems}, cannot add tag: ${tag}`
269
+ );
270
+
271
+ await onMaxTagsReached({
272
+ selectedItems,
273
+ maxItems,
274
+ tag,
275
+ keyNamespace,
276
+ });
277
+
278
+ return ActionResult.Cancel;
279
+ }
280
+
191
281
  await multiSelectProvider.addItem(tag);
192
282
  }
193
283
  } else {
@@ -211,6 +301,10 @@ export function withActionExecutor(Component) {
211
301
  prepareDefaultActions(_actionExecutor);
212
302
 
213
303
  return function ActionExecutorComponent(props: Props) {
304
+ const navigator = useNavigation();
305
+ const { rivers } = usePickFromState(["rivers"]);
306
+ const contentTypes = useContentTypes();
307
+
214
308
  const handlers = useMemo(() => {
215
309
  return {
216
310
  unregisterAction: _actionExecutor.unregisterAction,
@@ -221,6 +315,59 @@ export function withActionExecutor(Component) {
221
315
  };
222
316
  }, []);
223
317
 
318
+ useSubscriberFor(APP_EVENTS.onLogout, () => {
319
+ log_debug(
320
+ "User profile: onLogout event received, clearing user profile data"
321
+ );
322
+
323
+ const userAccountKey = "user_account";
324
+ void batchRemoveAllFromNamespaceForStorage(userAccountKey, localStorage);
325
+
326
+ void batchRemoveAllFromNamespaceForStorage(
327
+ userAccountKey,
328
+ sessionStorage
329
+ );
330
+ });
331
+
332
+ useEffect(() => {
333
+ return _actionExecutor.registerAction(
334
+ "navigateToScreen",
335
+
336
+ async (action: ActionType, context?: Record<string, any>) => {
337
+ const screenType = action.options?.typeMapping;
338
+
339
+ if (!screenType) {
340
+ log_error("navigateToScreen: typeMapping option is missing");
341
+
342
+ return ActionResult.Error;
343
+ }
344
+
345
+ const screenId = contentTypes?.[screenType]?.screen_id || null;
346
+
347
+ if (!screenId) {
348
+ log_error(
349
+ `navigateToScreen: can not resolve screen type mapping: ${screenType}`
350
+ );
351
+
352
+ return ActionResult.Error;
353
+ }
354
+
355
+ const river = rivers[screenId];
356
+
357
+ if (!river) {
358
+ log_error("navigateToScreen: can not resolve river");
359
+
360
+ return ActionResult.Error;
361
+ }
362
+
363
+ context?.callback?.({ success: false, error: null, abort: true });
364
+ navigator.replace(river);
365
+
366
+ return ActionResult.Success;
367
+ }
368
+ );
369
+ }, [navigator, rivers, contentTypes]);
370
+
224
371
  return (
225
372
  <ActionExecutorContext.Provider value={handlers}>
226
373
  <Component {...props} />
@@ -0,0 +1,4 @@
1
+ export const TOGGLE_FLAG_MAX_ITEMS_REACHED_EVENT =
2
+ "action.localStorageToggleFlag.maxItemsReached";
3
+
4
+ export const ACTION_EXECUTOR_EVENT_SOURCE = "ActionExecutor";
@@ -65,6 +65,10 @@ export class Hook implements HookInterface {
65
65
  event: (typeof HOOKS_EVENTS)[keyof typeof HOOKS_EVENTS],
66
66
  ...args
67
67
  ) {
68
+ if (this.state === hookState(HOOKS_EVENTS.CANCEL)) {
69
+ return;
70
+ }
71
+
68
72
  this.state = hookState(event);
69
73
  this.manager.subscriber.invokeHandler(event, ...args);
70
74
  }
@@ -269,6 +269,7 @@ export function HooksManager({
269
269
  payload = initialPayload,
270
270
  callback: callbackArg,
271
271
  cancelled,
272
+ abort,
272
273
  }) {
273
274
  let callback = callbackArg;
274
275
 
@@ -308,6 +309,7 @@ export function HooksManager({
308
309
  hookPlugin.setStateAndNotify(HOOKS_EVENTS.CANCEL, {
309
310
  hookPlugin,
310
311
  payload,
312
+ abort,
311
313
  });
312
314
 
313
315
  const isHookInHomescreen = payload?.home;
@@ -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;
@@ -1,12 +1,6 @@
1
- import { createLogger } from "../../logger";
2
1
  import { ContextKeysManager } from "./index";
3
2
  import * as R from "ramda";
4
3
 
5
- const { log_error } = createLogger({
6
- subsystem: "contextResolver",
7
- category: "General",
8
- });
9
-
10
4
  interface IResolver {
11
5
  resolve: (string) => Promise<string | number | object>;
12
6
  }
@@ -64,12 +58,7 @@ export const resolveObjectValues = async (
64
58
  return [key, resolvedValue];
65
59
  }
66
60
 
67
- log_error("resolveObjectValues: invalid context string", {
68
- key,
69
- value,
70
- });
71
-
72
- return [key, null];
61
+ return [key, value];
73
62
  })
74
63
  );
75
64
 
@@ -5,6 +5,7 @@ exports[`focusManager should be defined 1`] = `
5
5
  "blur": [Function],
6
6
  "disableFocus": [Function],
7
7
  "enableFocus": [Function],
8
+ "findPreferredFocusChild": [Function],
8
9
  "focusableTree": Tree {
9
10
  "loadingCounter": 0,
10
11
  "root": {
@@ -60,6 +61,7 @@ exports[`focusManagerIOS should be defined 1`] = `
60
61
  "getCurrentGroup": [Function],
61
62
  "getGroupById": [Function],
62
63
  "getGroupRootById": [Function],
64
+ "getPreferredFocusChild": [Function],
63
65
  "invokeHandler": [Function],
64
66
  "isGroupItemFocused": [Function],
65
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
  })();
@@ -534,6 +534,18 @@ export const focusManager = (function () {
534
534
 
535
535
  const { on, invokeHandler, removeHandler } = eventHandler;
536
536
 
537
+ function findPreferredFocusChild(id) {
538
+ const node = getCurrentFocusNode(id);
539
+
540
+ const preferredFocus = findPriorityItem([node]);
541
+
542
+ if (isNilOrEmpty(preferredFocus)) {
543
+ return undefined;
544
+ }
545
+
546
+ return preferredFocus[0];
547
+ }
548
+
537
549
  /**
538
550
  * this is the list of the functions available externally
539
551
  * when importing the focus manager
@@ -563,5 +575,6 @@ export const focusManager = (function () {
563
575
  isGroupItemFocused,
564
576
  recoverFocus,
565
577
  isCurrentFocusOnTheTopScreen,
578
+ findPreferredFocusChild,
566
579
  };
567
580
  })();