@applicaster/zapp-react-native-utils 13.0.0-alpha.5371423120 → 13.0.0-alpha.5375459737

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 (171) hide show
  1. package/README.md +0 -10
  2. package/actionsExecutor/ActionExecutor.ts +16 -9
  3. package/actionsExecutor/ActionExecutorContext.tsx +207 -11
  4. package/actionsExecutor/consts.ts +4 -0
  5. package/alertUtils/alert.ts +148 -0
  6. package/alertUtils/index.ts +1 -148
  7. package/analyticsUtils/AnalyticPlayerListener.ts +18 -1
  8. package/analyticsUtils/__tests__/__snapshots__/analyticsManagerNoModule.test.js.snap +9 -9
  9. package/analyticsUtils/currentScreenProperities.ts +6 -4
  10. package/analyticsUtils/helpers/hooks.ts +1 -1
  11. package/appUtils/HooksManager/{Hook.js → Hook.ts} +39 -23
  12. package/appUtils/HooksManager/__tests__/__snapshots__/hook.test.js.snap +10 -8
  13. package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +693 -0
  14. package/appUtils/HooksManager/__tests__/hook.test.js +196 -184
  15. package/appUtils/HooksManager/__tests__/hooksManager.test.js +96 -99
  16. package/appUtils/HooksManager/{constants.js → constants.ts} +1 -1
  17. package/appUtils/HooksManager/{index.js → index.ts} +124 -113
  18. package/appUtils/HooksManager/types.ts +119 -0
  19. package/appUtils/RiverFocusManager/index.js +17 -12
  20. package/appUtils/accessibilityManager/index.ts +42 -1
  21. package/appUtils/contextKeysManager/contextResolver.ts +66 -0
  22. package/appUtils/contextKeysManager/index.ts +2 -2
  23. package/appUtils/contextKeysManager/utils/index.ts +2 -1
  24. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +8 -6
  25. package/appUtils/focusManager/index.ts +108 -69
  26. package/appUtils/focusManager/treeDataStructure/Tree/index.js +25 -3
  27. package/appUtils/focusManager/treeDataStructure/Utils/index.js +1 -3
  28. package/appUtils/focusManager/utils/enums.ts +4 -0
  29. package/appUtils/focusManagerAux/utils/index.ts +4 -3
  30. package/appUtils/{index.js → index.ts} +1 -1
  31. package/appUtils/keyCodes/__tests__/__snapshots__/keyCodes.test.js.snap +2 -2
  32. package/appUtils/keyCodes/__tests__/keyCodes.test.js +4 -4
  33. package/appUtils/keyCodes/{index.js → index.ts} +13 -7
  34. package/appUtils/keyCodes/keys/index.ts +3 -0
  35. package/appUtils/keyCodes/{keys.android.js → keys/keyEventMapper.ts} +0 -19
  36. package/appUtils/keyCodes/keys/keys.android.ts +18 -0
  37. package/appUtils/keyCodes/keys/keys.ios.ts +25 -0
  38. package/appUtils/keyCodes/keys/keys.ts +15 -0
  39. package/appUtils/keyCodes/{keysByPlatform.js → keys/keysByPlatform.ts} +4 -0
  40. package/appUtils/keyCodes/keysByPlatform.ts +8 -0
  41. package/appUtils/keyInputHandler/KeyInputHandler.ts +68 -0
  42. package/appUtils/{localizationsHelper.js → localizationsHelper.ts} +23 -0
  43. package/appUtils/orientationHelper.ts +11 -13
  44. package/appUtils/platform/const.ts +14 -0
  45. package/appUtils/platform/platformUtils.ts +47 -18
  46. package/appUtils/playerManager/index.ts +7 -3
  47. package/appUtils/playerManager/player.ts +42 -18
  48. package/appUtils/playerManager/playerFactory.ts +5 -4
  49. package/appUtils/playerManager/playerNative.ts +18 -2
  50. package/appUtils/playerManager/usePlayerControllerSetup.tsx +3 -3
  51. package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +7 -1
  52. package/arrayUtils/__tests__/__snapshots__/arrayUtils.test.ts.snap +4 -4
  53. package/componentsUtils/__tests__/isFirstComponentGallery.test.ts +33 -0
  54. package/componentsUtils/__tests__/isFirstComponentScreenPicker.test.ts +33 -0
  55. package/componentsUtils/__tests__/isGallery.test.ts +33 -0
  56. package/componentsUtils/__tests__/isScreenPicker.test.ts +33 -0
  57. package/componentsUtils/index.ts +32 -0
  58. package/dateUtils/index.ts +19 -0
  59. package/domUtils/{cookieManager.js → cookieManager.ts} +1 -2
  60. package/equalUtils/__tests__/isEqual/combination.test.ts +263 -0
  61. package/equalUtils/index.ts +49 -30
  62. package/focusManager/FocusManager.ts +14 -2
  63. package/focusManager/index.ts +23 -4
  64. package/functionUtils/__tests__/__snapshots__/functionUtils.test.js.snap +4 -4
  65. package/functionUtils/index.ts +61 -19
  66. package/idleUtils/__tests__/withTimeout$.test.ts +37 -0
  67. package/idleUtils/index.ts +1 -1
  68. package/idleUtils/withTimeout.ts +15 -2
  69. package/index.d.ts +0 -12
  70. package/localizationUtils/index.ts +5 -10
  71. package/logger/const.ts +9 -67
  72. package/manifestUtils/__tests__/createConfig.test.js +16 -16
  73. package/manifestUtils/defaultManifestConfigurations/player.js +14 -0
  74. package/manifestUtils/fonts/index.js +5 -6
  75. package/manifestUtils/sharedConfiguration/screenPicker/__tests__/utils.test.js +304 -0
  76. package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +875 -0
  77. package/manifestUtils/sharedConfiguration/screenPicker/utils.js +130 -0
  78. package/modalState/__tests__/index.test.ts +51 -0
  79. package/modalState/index.ts +33 -7
  80. package/navigationUtils/__tests__/__snapshots__/navigationUtils.test.js.snap +7 -6
  81. package/navigationUtils/__tests__/itemTypes.test.js +2 -2
  82. package/navigationUtils/__tests__/navigationUtils.test.js +2 -2
  83. package/navigationUtils/index.ts +20 -9
  84. package/numberUtils/__tests__/toPositiveNumberWithDefault.test.ts +23 -3
  85. package/numberUtils/index.ts +11 -5
  86. package/package.json +5 -10
  87. package/playerUtils/configurationGenerator.ts +14 -0
  88. package/reactHooks/advertising/index.ts +5 -5
  89. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
  90. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +6 -8
  91. package/reactHooks/autoscrolling/useTrackedView.ts +12 -1
  92. package/reactHooks/cell-click/__tests__/index.test.js +10 -2
  93. package/reactHooks/cell-click/index.ts +7 -1
  94. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +4 -4
  95. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +20 -13
  96. package/reactHooks/feed/useFeedLoader.tsx +28 -15
  97. package/reactHooks/feed/usePipesCacheReset.ts +5 -5
  98. package/reactHooks/flatList/__tests__/index.test.tsx +30 -21
  99. package/reactHooks/hookModal/hooks/useHookModalScreenData.ts +39 -0
  100. package/reactHooks/layout/__tests__/index.test.tsx +10 -10
  101. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.native.test.js +14 -28
  102. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.test.js +1 -1
  103. package/reactHooks/layout/useDimensions/useDimensions.ts +8 -0
  104. package/reactHooks/navigation/__mocks__/useNavigation.ts +3 -0
  105. package/reactHooks/navigation/__tests__/index.test.tsx +6 -21
  106. package/reactHooks/navigation/index.ts +41 -240
  107. package/reactHooks/navigation/useGetBottomTabBarHeight.ts +12 -0
  108. package/reactHooks/navigation/useGetNavBarTopBorderWidth.ts +14 -0
  109. package/reactHooks/navigation/useGetTabBarHeight.ts +10 -0
  110. package/reactHooks/navigation/useIsScreenActive.ts +17 -0
  111. package/reactHooks/navigation/useNavigationPluginData.ts +15 -0
  112. package/reactHooks/navigation/useNavigationType.ts +15 -0
  113. package/reactHooks/navigation/usePathname.ts +4 -0
  114. package/reactHooks/navigation/useProfilerLogging.ts +15 -0
  115. package/reactHooks/navigation/useRoute.ts +98 -0
  116. package/reactHooks/navigation/useUniqueRouteSuffix.ts +41 -0
  117. package/reactHooks/player/TVSeekControlller/ISeekController.ts +32 -0
  118. package/reactHooks/player/TVSeekControlller/LongPressSeekController.ts +71 -0
  119. package/reactHooks/player/TVSeekControlller/TVSeekController.ts +258 -0
  120. package/reactHooks/player/TVSeekControlller/TapSeekController.ts +53 -0
  121. package/reactHooks/player/__tests__/{useAutoSeek.test.tsx → useAutoSeek._test.tsx} +2 -3
  122. package/reactHooks/player/const.ts +0 -4
  123. package/reactHooks/player/index.ts +0 -6
  124. package/reactHooks/state/useRefWithInitialValue.ts +14 -0
  125. package/reactHooks/state/useStateFromSubscribe.ts +23 -0
  126. package/reactHooks/usePluginConfiguration.ts +1 -1
  127. package/reactHooks/useSubscriberFor.ts +4 -3
  128. package/reactHooks/utils/index.ts +13 -0
  129. package/reactUtils/__tests__/__snapshots__/reactUtilsHelpers.test.js.snap +16 -4
  130. package/reactUtils/__tests__/reactUtils.test.js +1 -37
  131. package/reactUtils/__tests__/reactUtilsHelpers.test.js +72 -71
  132. package/reactUtils/createContext/__tests__/__snapshots__/helpers.test.js.snap +13 -79
  133. package/reactUtils/createContext/__tests__/__snapshots__/{index.test.js.snap → index.test.ts.snap} +2 -2
  134. package/reactUtils/createContext/__tests__/__snapshots__/provider.test.js.snap +3 -28
  135. package/reactUtils/createContext/__tests__/__snapshots__/withConsumer.test.js.snap +3 -4
  136. package/reactUtils/createContext/__tests__/__snapshots__/withProvider.test.js.snap +7 -3
  137. package/reactUtils/createContext/__tests__/helpers.test.js +107 -121
  138. package/reactUtils/createContext/__tests__/{index.test.js → index.test.ts} +2 -2
  139. package/reactUtils/createContext/__tests__/provider.test.js +4 -6
  140. package/reactUtils/createContext/__tests__/withConsumer.test.js +12 -12
  141. package/reactUtils/createContext/__tests__/withProvider.test.js +4 -4
  142. package/reactUtils/createContext/helpers.ts +6 -1
  143. package/reactUtils/createContext/index.ts +11 -25
  144. package/reactUtils/index.ts +0 -25
  145. package/screenState/index.ts +24 -10
  146. package/services/js2native.ts +673 -0
  147. package/testUtils/index.tsx +0 -10
  148. package/theme/__mocks__/index.ts +4 -0
  149. package/theme/index.ts +1 -1
  150. package/time/BackgroundTimer.ts +55 -0
  151. package/time/Timer.ts +9 -6
  152. package/types.ts +1 -0
  153. package/utils/README.md +30 -0
  154. package/utils/chunk.ts +5 -0
  155. package/utils/index.ts +5 -0
  156. package/utils/times.ts +5 -0
  157. package/zappFrameworkUtils/localStorageHelper.ts +6 -0
  158. package/zappFrameworkUtils/loginPluginUtils.ts +6 -2
  159. package/zappFrameworkUtils/presentPluginOneTime.ts +2 -3
  160. package/zappFrameworkUtils/types.ts +11 -0
  161. package/appUtils/keyCodes/keys.ios.js +0 -46
  162. package/reactHooks/player/useAutoSeek.tsx +0 -87
  163. package/reactHooks/player/useSeek.tsx +0 -66
  164. package/reactHooks/player/useTapSeek.tsx +0 -58
  165. package/reactUtils/createContext/__tests__/index-no-react.test.js +0 -27
  166. /package/appUtils/HooksManager/{logger.js → logger.ts} +0 -0
  167. /package/appUtils/{directionHelper.js → directionHelper.ts} +0 -0
  168. /package/appUtils/keyCodes/{androidTVKeys.js → androidTVKeys.ts} +0 -0
  169. /package/appUtils/keyCodes/{keys.web.js → keys/keys.web.ts} +0 -0
  170. /package/domUtils/{logger.js → logger.ts} +0 -0
  171. /package/reactHooks/player/__tests__/{useTapSeek.test.ts → useTapSeek._test.ts} +0 -0
package/README.md CHANGED
@@ -376,16 +376,6 @@ useEffect(() => {
376
376
  }, []);
377
377
  ```
378
378
 
379
- ### testUtils
380
-
381
- `@applicaster/zapp-react-native-utils/testUtils`
382
-
383
- - `testIDEquals: (testId: any) => boolean | { width: number, height: number }` - Function to be used in tandem with enzyme findWhere
384
-
385
- ```javascript
386
- wrapper.findWhere(testIDEquals("text"));
387
- ```
388
-
389
379
  ### appUtils - contextKeysManager
390
380
 
391
381
  `@applicaster/zapp-react-native-utils/appUtils/contextKeyManager`
@@ -17,9 +17,10 @@ class ActionExecutor {
17
17
 
18
18
  handleAction = async (
19
19
  action: ActionType,
20
- entry: ZappEntry = null
20
+ context?: Record<string, any>
21
21
  ): Promise<ActionResult> => {
22
22
  const handler = this.registeredActions[action.type];
23
+ const entry = context?.entry as ZappEntry;
23
24
 
24
25
  if (!handler) {
25
26
  log_error(
@@ -38,11 +39,11 @@ class ActionExecutor {
38
39
  );
39
40
 
40
41
  try {
41
- return handler(action, entry);
42
+ return handler(action, context);
42
43
  } catch (error) {
43
44
  log_error(
44
45
  `handleAction: error handling action type: ${action.type}, message: ${error.message}`,
45
- { action, entry, error }
46
+ { action, context, error }
46
47
  );
47
48
 
48
49
  throw error;
@@ -51,11 +52,11 @@ class ActionExecutor {
51
52
 
52
53
  handleActions = async (
53
54
  actionTypes: ActionType[],
54
- entry: ZappEntry = null
55
+ context?: Record<string, any>
55
56
  ): Promise<ActionResult> => {
56
57
  for (const action of actionTypes) {
57
58
  // TODO: Handle action result error
58
- const result = await this.handleAction(action, entry);
59
+ const result = await this.handleAction(action, context);
59
60
 
60
61
  if (result === ActionResult.Cancel) {
61
62
  log_info(`handleActions: action: ${action.type} canceled`);
@@ -67,7 +68,10 @@ class ActionExecutor {
67
68
  return ActionResult.Success;
68
69
  };
69
70
 
70
- handleEntryActions = async (entry: ZappEntry): Promise<ActionResult> => {
71
+ handleEntryActions = async (
72
+ entry: ZappEntry,
73
+ context?: Record<string, any>
74
+ ): Promise<ActionResult> => {
71
75
  const tapActions = entry.extensions?.tap_actions?.actions;
72
76
 
73
77
  if (!tapActions) {
@@ -77,7 +81,7 @@ class ActionExecutor {
77
81
  if (!Array.isArray(tapActions)) {
78
82
  log_error(
79
83
  `handleEntryActions: tapActions is not an array for id: ${entry.id} title: ${entry.title}`,
80
- { entry }
84
+ { context, entry }
81
85
  );
82
86
 
83
87
  return;
@@ -87,7 +91,7 @@ class ActionExecutor {
87
91
  return;
88
92
  }
89
93
 
90
- await this.handleActions(tapActions, entry);
94
+ await this.handleActions(tapActions, { ...context, entry });
91
95
  };
92
96
 
93
97
  unregisterAction = (actionType: string) => {
@@ -96,7 +100,10 @@ class ActionExecutor {
96
100
 
97
101
  registerAction = (
98
102
  type: string,
99
- handler: (action: ActionType, entry?: ZappEntry) => Promise<ActionResult>
103
+ handler: (
104
+ action: ActionType,
105
+ context?: Record<string, any>
106
+ ) => Promise<ActionResult>
100
107
  ) => {
101
108
  if (this.registeredActions[type]) {
102
109
  log_error(`registerAction: action type: ${type} already registered`);
@@ -1,6 +1,7 @@
1
1
  /// <reference types="@applicaster/applicaster-types" />
2
-
3
- import React, { createContext, useMemo } from "react";
2
+ import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
3
+ import React, { createContext, useEffect, useMemo } from "react";
4
+ import { NativeModules } from "react-native";
4
5
  import {
5
6
  actionExecutor as _actionExecutor,
6
7
  ActionResult,
@@ -12,8 +13,27 @@ import { showConfirmationDialog } from "../alertUtils";
12
13
  import { createCloudEvent, sendCloudEvent } from "../cloudEventsUtils";
13
14
  import { createLogger } from "../logger";
14
15
  import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageMultiSelectProvider";
16
+ import { ACTIVE_LAYOUT_ID_STORAGE_KEY } from "@applicaster/quick-brick-core/App/remoteContextReloader/consts";
17
+ import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
18
+ import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes";
19
+ import {
20
+ EntryResolver,
21
+ resolveObjectValues,
22
+ } from "../appUtils/contextKeysManager/contextResolver";
23
+ import { useNavigation } from "../reactHooks";
24
+ import { get } from "lodash";
25
+
26
+ import {
27
+ useContentTypes,
28
+ usePickFromState,
29
+ } from "@applicaster/zapp-react-native-redux/hooks";
30
+ import {
31
+ ACTION_EXECUTOR_EVENT_SOURCE,
32
+ TOGGLE_FLAG_MAX_ITEMS_REACHED_EVENT,
33
+ } from "./consts";
34
+ const { ReactNativeEventBusBridge } = NativeModules;
15
35
 
16
- const { log_error, log_info } = createLogger({
36
+ export const { log_error, log_info, log_debug } = createLogger({
17
37
  subsystem: "ActionExecutorContext",
18
38
  category: "General",
19
39
  });
@@ -21,20 +41,65 @@ const { log_error, log_info } = createLogger({
21
41
  type ActionExecutorContextType = {
22
42
  registerAction: (
23
43
  type: string,
24
- handler: (action: ActionType, entry: ZappEntry) => Promise<ActionResult>
44
+ handler: (
45
+ action: ActionType,
46
+ context?: Record<string, any>
47
+ ) => Promise<ActionResult>
25
48
  ) => void;
26
49
  unregisterAction: (type: string) => void;
27
- handleAction: (action: ActionType, entry: ZappEntry) => Promise<ActionResult>;
50
+ handleAction: (
51
+ action: ActionType,
52
+ context?: Record<string, any>
53
+ ) => Promise<ActionResult>;
28
54
  handleActions: (
29
55
  actions: ActionType[],
30
- entry: ZappEntry
56
+ context: Record<string, any>
57
+ ) => Promise<ActionResult>;
58
+ handleEntryActions: (
59
+ entry: ZappEntry,
60
+ context?: Record<string, any>
31
61
  ) => Promise<ActionResult>;
32
- handleEntryActions: (entry: ZappEntry) => Promise<ActionResult>;
33
62
  };
34
63
  type Props = {
35
64
  children: React.ReactNode;
36
65
  };
37
66
 
67
+ function findParentComponent(
68
+ childId: string,
69
+ parent: ZappRiver | ZappUIComponent
70
+ ): ZappRiver | ZappUIComponent | null {
71
+ for (const child of parent.ui_components) {
72
+ if (child.id === childId) return parent;
73
+
74
+ if (child.ui_components) {
75
+ const found = findParentComponent(childId, child);
76
+ if (found) return found;
77
+ }
78
+ }
79
+
80
+ return null;
81
+ }
82
+
83
+ // send all data just in case (like for message string formatting)
84
+ // Type is not exported for now
85
+ type MaxTagsReachedEvent = {
86
+ // todo: current stable Android SDK crashes when we pass array from RN back to RN
87
+ selectedItems?: string[];
88
+ maxItems: number;
89
+ tag: string;
90
+ keyNamespace: string;
91
+ };
92
+
93
+ async function onMaxTagsReached(data: MaxTagsReachedEvent) {
94
+ const event = {
95
+ type: TOGGLE_FLAG_MAX_ITEMS_REACHED_EVENT,
96
+ source: ACTION_EXECUTOR_EVENT_SOURCE,
97
+ data,
98
+ };
99
+
100
+ await ReactNativeEventBusBridge?.postEvent(event);
101
+ }
102
+
38
103
  const prepareDefaultActions = (actionExecutor) => {
39
104
  actionExecutor.registerAction("localStorageSet", async (action) => {
40
105
  const namespaces = action.options.content;
@@ -44,6 +109,46 @@ const prepareDefaultActions = (actionExecutor) => {
44
109
  return ActionResult.Success;
45
110
  });
46
111
 
112
+ actionExecutor.registerAction(
113
+ "refreshComponent",
114
+ async (_action, context) => {
115
+ const dispatch = appStore.getDispatch();
116
+
117
+ const dataSource =
118
+ context?.component?.data?.source ||
119
+ findParentComponent(context?.component.id, context?.screenData)?.data
120
+ ?.source;
121
+
122
+ log_info(`handleAction: refreshComponent for dataSource:${dataSource}`);
123
+
124
+ // TODO: In theory we should wait callback to complete, before completing the action, but now it's not needed
125
+ // TODO: handle focused item removal
126
+ dispatch(
127
+ loadPipesData(dataSource, {
128
+ silentRefresh: false,
129
+ clearCache: true,
130
+ callback: (_data) => {},
131
+ riverId: context?.screenData?.id,
132
+ })
133
+ );
134
+
135
+ return ActionResult.Success;
136
+ }
137
+ );
138
+
139
+ actionExecutor.registerAction("switchLayout", async (action) => {
140
+ log_info("handleAction: switchLayout event");
141
+
142
+ await localStorage.setItem(
143
+ ACTIVE_LAYOUT_ID_STORAGE_KEY,
144
+ action.options.layoutId
145
+ );
146
+
147
+ QuickBrickManager.sendQuickBrickEvent(QUICK_BRICK_EVENTS.FORCE_APP_RELOAD);
148
+
149
+ return ActionResult.Success;
150
+ });
151
+
47
152
  actionExecutor.registerAction("appRestart", async () => {
48
153
  log_info(`handleAction: ${QUICK_BRICK_EVENTS.FORCE_APP_RELOAD} event`);
49
154
 
@@ -66,13 +171,21 @@ const prepareDefaultActions = (actionExecutor) => {
66
171
  return confirmationPromise;
67
172
  });
68
173
 
69
- actionExecutor.registerAction("sendCloudEvent", async (action, entry) => {
174
+ actionExecutor.registerAction("sendCloudEvent", async (action, context) => {
70
175
  try {
71
176
  const options = action.options;
72
177
 
178
+ const entry = context?.entry || {};
179
+ const entryResolver = new EntryResolver(entry);
180
+
181
+ const data =
182
+ options?.data && options.inflateData
183
+ ? await resolveObjectValues(options.data, { entry: entryResolver })
184
+ : options?.data || entry;
185
+
73
186
  const cloudEvent = await createCloudEvent({
74
187
  type: options.type || "com.applicaster.selector.action.v1",
75
- data: options.data || entry,
188
+ data,
76
189
  subject: options.subject || entry?.id,
77
190
  });
78
191
 
@@ -103,9 +216,33 @@ const prepareDefaultActions = (actionExecutor) => {
103
216
 
104
217
  actionExecutor.registerAction(
105
218
  "localStorageToggleFlag",
106
- async (action: ActionType, entry?: ZappEntry): Promise<ActionResult> => {
219
+ async (
220
+ action: ActionType,
221
+ context?: Record<string, any>
222
+ ): Promise<ActionResult> => {
223
+ if (!context) {
224
+ log_error(
225
+ "handleAction: localStorageToggleFlag action missing context"
226
+ );
227
+
228
+ return ActionResult.Error;
229
+ }
230
+
231
+ const entry = context?.entry as ZappEntry;
232
+
233
+ if (!entry) {
234
+ log_error(
235
+ "handleAction: localStorageToggleFlag action missing entry. Entry is required to get the tag."
236
+ );
237
+
238
+ return ActionResult.Error;
239
+ }
240
+
241
+ const tag = action.options?.selector
242
+ ? get(entry, action.options.selector)
243
+ : (entry.extensions?.tag ?? entry.id);
244
+
107
245
  const keyNamespace = action.options?.key;
108
- const tag = entry?.extensions?.tag;
109
246
 
110
247
  if (keyNamespace && tag) {
111
248
  const multiSelectProvider =
@@ -123,6 +260,23 @@ const prepareDefaultActions = (actionExecutor) => {
123
260
  if (selectedItems.includes(tag)) {
124
261
  await multiSelectProvider.removeItem(tag);
125
262
  } else {
263
+ const maxItems = action.options?.max_items;
264
+
265
+ if (maxItems && selectedItems.length >= maxItems) {
266
+ log_info(
267
+ `handleAction: localStorageToggleFlag event reached max items limit: ${maxItems}, cannot add tag: ${tag}`
268
+ );
269
+
270
+ await onMaxTagsReached({
271
+ selectedItems,
272
+ maxItems,
273
+ tag,
274
+ keyNamespace,
275
+ });
276
+
277
+ return ActionResult.Cancel;
278
+ }
279
+
126
280
  await multiSelectProvider.addItem(tag);
127
281
  }
128
282
  } else {
@@ -146,6 +300,10 @@ export function withActionExecutor(Component) {
146
300
  prepareDefaultActions(_actionExecutor);
147
301
 
148
302
  return function ActionExecutorComponent(props: Props) {
303
+ const navigator = useNavigation();
304
+ const { rivers } = usePickFromState(["rivers"]);
305
+ const contentTypes = useContentTypes();
306
+
149
307
  const handlers = useMemo(() => {
150
308
  return {
151
309
  unregisterAction: _actionExecutor.unregisterAction,
@@ -156,6 +314,44 @@ export function withActionExecutor(Component) {
156
314
  };
157
315
  }, []);
158
316
 
317
+ useEffect(() => {
318
+ return _actionExecutor.registerAction(
319
+ "navigateToScreen",
320
+ async (action: ActionType, context?: Record<string, any>) => {
321
+ const screenType = action.options?.typeMapping;
322
+
323
+ if (!screenType) {
324
+ log_error("navigateToScreen: typeMapping option is missing");
325
+
326
+ return ActionResult.Error;
327
+ }
328
+
329
+ const screenId = contentTypes?.[screenType]?.screen_id || null;
330
+
331
+ if (!screenId) {
332
+ log_error(
333
+ `navigateToScreen: can not resolve screen type mapping: ${screenType}`
334
+ );
335
+
336
+ return ActionResult.Error;
337
+ }
338
+
339
+ const river = rivers[screenId];
340
+
341
+ if (!river) {
342
+ log_error("navigateToScreen: can not resolve river");
343
+
344
+ return ActionResult.Error;
345
+ }
346
+
347
+ context?.callback?.({ success: false, error: null, abort: true });
348
+ navigator.replace(river);
349
+
350
+ return ActionResult.Success;
351
+ }
352
+ );
353
+ }, [navigator, rivers, contentTypes]);
354
+
159
355
  return (
160
356
  <ActionExecutorContext.Provider value={handlers}>
161
357
  <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";
@@ -0,0 +1,148 @@
1
+ import { Alert } from "react-native";
2
+ import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
3
+ import { confirmationDialogStore } from "@applicaster/zapp-react-native-ui-components/Contexts/ConfirmDialogState";
4
+
5
+ // #region "Alert Dialog"
6
+
7
+ const globalAny: any = global;
8
+
9
+ const showWebAlertDialog = ({ message, completion }: AlertWebDialog) => {
10
+ globalAny?.alert?.(message);
11
+ completion?.();
12
+ };
13
+
14
+ const showNativeAlertDialog = ({
15
+ title,
16
+ message,
17
+ okButtonText,
18
+ completion,
19
+ }: AlertDialogType) => {
20
+ Alert.alert(title, message, [
21
+ {
22
+ text: okButtonText,
23
+ onPress: completion,
24
+ },
25
+ ]);
26
+ };
27
+
28
+ export const showAlertDialog = ({
29
+ title,
30
+ message,
31
+ okButtonText,
32
+ completion,
33
+ }: AlertDialogType) => {
34
+ if (isWeb()) {
35
+ showWebAlertDialog({ message, completion });
36
+ } else {
37
+ showNativeAlertDialog({
38
+ title,
39
+ message,
40
+ okButtonText,
41
+ completion,
42
+ });
43
+ }
44
+ };
45
+
46
+ // #endregion "Alert Dialog"
47
+
48
+ // #region "Confirmation Dialog"
49
+
50
+ const showWebDialog = ({
51
+ message,
52
+ confirmCompletion,
53
+ cancelCompletion,
54
+ }: WebConfirmationDialog) => {
55
+ const isConfirmed = globalAny?.confirm?.(message);
56
+
57
+ if (isConfirmed) {
58
+ confirmCompletion?.();
59
+ } else {
60
+ cancelCompletion?.();
61
+ }
62
+ };
63
+
64
+ const showNativeDialog = ({
65
+ title,
66
+ message,
67
+ okButtonText,
68
+ cancelButtonText,
69
+ confirmCompletion,
70
+ cancelCompletion,
71
+ }: ConfirmationDialog) => {
72
+ Alert.alert(title, message, [
73
+ {
74
+ text: cancelButtonText,
75
+ onPress: cancelCompletion,
76
+ },
77
+ {
78
+ text: okButtonText,
79
+ style: "destructive",
80
+ onPress: confirmCompletion,
81
+ },
82
+ ]);
83
+ };
84
+
85
+ const handleCustomDialog = (data) => {
86
+ const {
87
+ showDialog,
88
+ hideDialog,
89
+ setConfirmAction,
90
+ setCancelAction,
91
+ setMessages,
92
+ } = confirmationDialogStore.getState();
93
+
94
+ setMessages(data.messages);
95
+
96
+ if (data?.cancelCompletion) {
97
+ setCancelAction(() => {
98
+ data.cancelCompletion();
99
+ hideDialog();
100
+ });
101
+ }
102
+
103
+ if (data?.confirmCompletion) {
104
+ setConfirmAction(() => {
105
+ data.confirmCompletion();
106
+ hideDialog();
107
+ });
108
+ }
109
+
110
+ showDialog();
111
+ };
112
+
113
+ export const showConfirmationDialog = ({
114
+ title,
115
+ message,
116
+ okButtonText,
117
+ cancelButtonText,
118
+ confirmCompletion,
119
+ cancelCompletion,
120
+ }: CustomConfirmationDialog): void => {
121
+ if (isWeb()) {
122
+ if (cancelCompletion) {
123
+ handleCustomDialog({
124
+ messages: {
125
+ title,
126
+ message,
127
+ okButtonText,
128
+ cancelButtonText,
129
+ },
130
+ confirmCompletion,
131
+ cancelCompletion,
132
+ });
133
+ } else {
134
+ showWebDialog({ message, confirmCompletion, cancelCompletion });
135
+ }
136
+ } else {
137
+ showNativeDialog({
138
+ title,
139
+ message,
140
+ okButtonText,
141
+ cancelButtonText,
142
+ confirmCompletion,
143
+ cancelCompletion,
144
+ });
145
+ }
146
+ };
147
+
148
+ // #endregion "Confirmation Dialog"
@@ -1,148 +1 @@
1
- import { Alert } from "react-native";
2
- import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
3
- import { confirmationDialogStore } from "@applicaster/zapp-react-native-ui-components/Contexts/ConfirmDialogState";
4
-
5
- // #region "Alert Dialog"
6
-
7
- const globalAny: any = global;
8
-
9
- const showWebAlertDialog = ({ message, completion }: AlertWebDialog) => {
10
- globalAny?.alert?.(message);
11
- completion?.();
12
- };
13
-
14
- const showNativeAlertDialog = ({
15
- title,
16
- message,
17
- okButtonText,
18
- completion,
19
- }: AlertDialogType) => {
20
- Alert.alert(title, message, [
21
- {
22
- text: okButtonText,
23
- onPress: completion,
24
- },
25
- ]);
26
- };
27
-
28
- export const showAlertDialog = ({
29
- title,
30
- message,
31
- okButtonText,
32
- completion,
33
- }: AlertDialogType) => {
34
- if (isWeb()) {
35
- showWebAlertDialog({ message, completion });
36
- } else {
37
- showNativeAlertDialog({
38
- title,
39
- message,
40
- okButtonText,
41
- completion,
42
- });
43
- }
44
- };
45
-
46
- // #endregion "Alert Dialog"
47
-
48
- // #region "Confirmation Dialog"
49
-
50
- const showWebDialog = ({
51
- message,
52
- confirmCompletion,
53
- cancelCompletion,
54
- }: WebConfirmationDialog) => {
55
- const isConfirmed = globalAny?.confirm?.(message);
56
-
57
- if (isConfirmed) {
58
- confirmCompletion?.();
59
- } else {
60
- cancelCompletion?.();
61
- }
62
- };
63
-
64
- const showNativeDialog = ({
65
- title,
66
- message,
67
- okButtonText,
68
- cancelButtonText,
69
- confirmCompletion,
70
- cancelCompletion,
71
- }: ConfirmationDialog) => {
72
- Alert.alert(title, message, [
73
- {
74
- text: cancelButtonText,
75
- onPress: cancelCompletion,
76
- },
77
- {
78
- text: okButtonText,
79
- style: "destructive",
80
- onPress: confirmCompletion,
81
- },
82
- ]);
83
- };
84
-
85
- const handleCustomDialog = (data) => {
86
- const {
87
- showDialog,
88
- hideDialog,
89
- setConfirmAction,
90
- setCancelAction,
91
- setMessages,
92
- } = confirmationDialogStore.getState();
93
-
94
- setMessages(data.messages);
95
-
96
- if (data?.cancelCompletion) {
97
- setCancelAction(() => {
98
- data.cancelCompletion();
99
- hideDialog();
100
- });
101
- }
102
-
103
- if (data?.confirmCompletion) {
104
- setConfirmAction(() => {
105
- data.confirmCompletion();
106
- hideDialog();
107
- });
108
- }
109
-
110
- showDialog();
111
- };
112
-
113
- export const showConfirmationDialog = ({
114
- title,
115
- message,
116
- okButtonText,
117
- cancelButtonText,
118
- confirmCompletion,
119
- cancelCompletion,
120
- }: CustomConfirmationDialog): void => {
121
- if (isWeb()) {
122
- if (cancelCompletion) {
123
- handleCustomDialog({
124
- messages: {
125
- title,
126
- message,
127
- okButtonText,
128
- cancelButtonText,
129
- },
130
- confirmCompletion,
131
- cancelCompletion,
132
- });
133
- } else {
134
- showWebDialog({ message, confirmCompletion, cancelCompletion });
135
- }
136
- } else {
137
- showNativeDialog({
138
- title,
139
- message,
140
- okButtonText,
141
- cancelButtonText,
142
- confirmCompletion,
143
- cancelCompletion,
144
- });
145
- }
146
- };
147
-
148
- // #endregion "Confirmation Dialog"
1
+ export { showAlertDialog, showConfirmationDialog } from "./alert";