@applicaster/zapp-react-native-utils 16.0.0-alpha.1232235100 → 16.0.0-alpha.1314750780

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 (213) hide show
  1. package/actionUtils/index.ts +3 -1
  2. package/actionsExecutor/ActionExecutor.ts +57 -12
  3. package/actionsExecutor/ActionExecutorContext.tsx +110 -333
  4. package/actionsExecutor/__tests__/ActionExecutor.test.ts +71 -0
  5. package/actionsExecutor/__tests__/StorageActions.test.ts +98 -0
  6. package/actionsExecutor/__tests__/feedDecorator.test.ts +91 -0
  7. package/actionsExecutor/__tests__/screenToggleFlag.test.ts +90 -0
  8. package/actionsExecutor/actions/__tests__/dismissBottomSheet.test.ts +25 -0
  9. package/actionsExecutor/actions/__tests__/goBack.test.ts +62 -0
  10. package/actionsExecutor/actions/__tests__/goHome.test.ts +19 -0
  11. package/actionsExecutor/actions/__tests__/loadItemsFromSource.test.ts +88 -0
  12. package/actionsExecutor/actions/__tests__/navigateToScreen.test.ts +314 -0
  13. package/actionsExecutor/actions/__tests__/openBottomSheet.customContent.test.ts +100 -0
  14. package/actionsExecutor/actions/__tests__/openBottomSheet.inlineItems.test.ts +372 -0
  15. package/actionsExecutor/actions/__tests__/presentScreen.test.ts +127 -0
  16. package/actionsExecutor/actions/__tests__/showAlert.test.ts +49 -0
  17. package/actionsExecutor/actions/__tests__/showToast.test.ts +88 -0
  18. package/actionsExecutor/actions/appRestart.ts +24 -0
  19. package/actionsExecutor/actions/confirmDialog.ts +53 -0
  20. package/actionsExecutor/actions/dismissBottomSheet.ts +23 -0
  21. package/actionsExecutor/actions/goBack.ts +60 -0
  22. package/actionsExecutor/actions/goHome.ts +31 -0
  23. package/actionsExecutor/actions/index.ts +42 -0
  24. package/actionsExecutor/actions/localStorageRemove.ts +27 -0
  25. package/actionsExecutor/actions/localStorageSet.ts +28 -0
  26. package/actionsExecutor/actions/localStorageToggleFlag.ts +28 -0
  27. package/actionsExecutor/actions/navigateToScreen.ts +127 -0
  28. package/actionsExecutor/actions/openBottomSheet.ts +340 -0
  29. package/actionsExecutor/actions/presentScreen.ts +78 -0
  30. package/actionsExecutor/actions/refreshComponent.ts +85 -0
  31. package/actionsExecutor/actions/screenSetVariable.ts +35 -0
  32. package/actionsExecutor/actions/screenToggleFlag.ts +36 -0
  33. package/actionsExecutor/actions/sendCloudEvent.ts +93 -0
  34. package/actionsExecutor/actions/sessionStorageRemove.ts +19 -0
  35. package/actionsExecutor/actions/sessionStorageSet.ts +20 -0
  36. package/actionsExecutor/actions/sessionStorageToggleFlag.ts +15 -0
  37. package/actionsExecutor/actions/showAlert.ts +51 -0
  38. package/actionsExecutor/actions/showToast.ts +87 -0
  39. package/actionsExecutor/actions/switchLayout.ts +40 -0
  40. package/actionsExecutor/consts.ts +27 -0
  41. package/actionsExecutor/feedDecorator.ts +12 -6
  42. package/actionsExecutor/types.ts +66 -0
  43. package/analyticsUtils/PlayerAnalyticsManager.ts +1 -1
  44. package/appUtils/HooksManager/index.ts +9 -0
  45. package/appUtils/contextKeysManager/__tests__/getKey/failure.test.ts +2 -23
  46. package/appUtils/contextKeysManager/__tests__/getKey/success/fullSearch.test.ts +1 -25
  47. package/appUtils/contextKeysManager/__tests__/getKey/success/searchInSecure.test.ts +1 -7
  48. package/appUtils/contextKeysManager/__tests__/removeKey/failure.test.ts +1 -26
  49. package/appUtils/contextKeysManager/__tests__/removeKey/success.test.ts +0 -6
  50. package/appUtils/contextKeysManager/__tests__/setKey/failure/failedToSaveInStorage.test.ts +0 -40
  51. package/appUtils/contextKeysManager/__tests__/setKey/failure/invalidKey.test.ts +4 -4
  52. package/appUtils/contextKeysManager/__tests__/setKey/{success/saveItemAndReference.test.ts → success.test.ts} +2 -42
  53. package/appUtils/contextKeysManager/index.ts +29 -130
  54. package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +142 -59
  55. package/appUtils/playerManager/OverlayObserver/__tests__/getUpcomingQueue.test.ts +67 -0
  56. package/appUtils/playerManager/OverlayObserver/__tests__/liveContent.test.ts +205 -0
  57. package/appUtils/playerManager/OverlayObserver/__tests__/utils.test.ts +56 -0
  58. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.android.tv.ts +4 -0
  59. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.ios.tv.ts +4 -0
  60. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.ts +24 -0
  61. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.web.ts +4 -0
  62. package/appUtils/playerManager/OverlayObserver/utils.ts +51 -20
  63. package/appUtils/playerManager/__tests__/playerContent.test.ts +403 -0
  64. package/appUtils/playerManager/__tests__/playerDiagnostics.test.ts +62 -0
  65. package/appUtils/playerManager/__tests__/playerFactory.test.ts +1 -1
  66. package/appUtils/playerManager/__tests__/playerNative.test.ts +114 -0
  67. package/appUtils/playerManager/__tests__/playerNativeCommand.test.ts +62 -0
  68. package/appUtils/playerManager/__tests__/playerToolbox.test.ts +281 -0
  69. package/appUtils/playerManager/__tests__/usePlayerContent.test.tsx +64 -0
  70. package/appUtils/playerManager/__tests__/usePlayerControllerSetup.test.tsx +62 -0
  71. package/appUtils/playerManager/{conts.ts → const.ts} +33 -0
  72. package/appUtils/playerManager/index.ts +8 -1
  73. package/appUtils/playerManager/player.ts +140 -3
  74. package/appUtils/playerManager/playerFactory.ts +1 -1
  75. package/appUtils/playerManager/playerNative.ts +28 -4
  76. package/appUtils/playerManager/playerNativeCommand.ts +3 -6
  77. package/appUtils/playerManager/playerToolbox.ts +196 -0
  78. package/appUtils/playerManager/usePlayerContent.tsx +43 -0
  79. package/appUtils/playerManager/usePlayerControllerSetup.tsx +7 -1
  80. package/appUtils/playerManager/userLanguagePreference.ts +1 -1
  81. package/arrayUtils/__tests__/{anyThruthy.test.ts → anyTruthy.test.ts} +8 -0
  82. package/arrayUtils/__tests__/arrayUtils.test.ts +165 -108
  83. package/arrayUtils/__tests__/isEmptyArray.test.ts +11 -0
  84. package/arrayUtils/__tests__/isFilledArray.test.ts +12 -0
  85. package/arrayUtils/__tests__/isFirst.test.ts +17 -0
  86. package/arrayUtils/__tests__/isIndexInRange.test.ts +14 -0
  87. package/arrayUtils/__tests__/isLast.test.ts +31 -0
  88. package/arrayUtils/__tests__/makeListOf.test.ts +31 -0
  89. package/arrayUtils/__tests__/makeListOfIndexes.test.ts +20 -0
  90. package/arrayUtils/__tests__/reorderByIds.test.ts +63 -0
  91. package/arrayUtils/__tests__/sample.test.ts +61 -0
  92. package/arrayUtils/index.ts +119 -20
  93. package/bottomSheet/__tests__/scrollContext.test.tsx +35 -0
  94. package/bottomSheet/index.ts +17 -0
  95. package/cellUtils/__tests__/getButtonsEnabled.test.ts +28 -0
  96. package/cellUtils/__tests__/getCellNavigationEnabled.test.ts +36 -0
  97. package/cellUtils/__tests__/getIsCellNavigationEnabled.test.ts +35 -0
  98. package/cellUtils/__tests__/isCellNavigationEnabled.test.ts +21 -0
  99. package/cellUtils/index.ts +70 -3
  100. package/configurationUtils/__tests__/manifestKeyParser.test.ts +21 -0
  101. package/configurationUtils/__tests__/modalBlocksParser.test.ts +403 -0
  102. package/configurationUtils/manifestKeyParser.ts +50 -10
  103. package/configurationUtils/modalBlocksParser.ts +252 -0
  104. package/entryActions/__tests__/buildEntryActions.test.ts +235 -0
  105. package/entryActions/aliasPresentation.ts +108 -0
  106. package/entryActions/buildEntryActions.ts +167 -0
  107. package/entryActions/index.ts +15 -0
  108. package/entryActions/types.ts +39 -0
  109. package/focusManager/FocusManager.ts +26 -0
  110. package/focusManager/__tests__/FocusManager.test.ts +9 -0
  111. package/localizationUtils/index.ts +12 -6
  112. package/logger/__tests__/xrayToolbox.test.ts +69 -0
  113. package/logger/xrayToolbox.ts +67 -0
  114. package/manifestUtils/__tests__/player.audioControls.test.js +158 -0
  115. package/manifestUtils/_internals/index.js +8 -3
  116. package/manifestUtils/defaultManifestConfigurations/generalContent.js +6 -0
  117. package/manifestUtils/defaultManifestConfigurations/player.js +357 -20
  118. package/manifestUtils/index.js +3 -0
  119. package/manifestUtils/keys.js +15 -2
  120. package/manifestUtils/mobileAction/button/index.js +16 -0
  121. package/manifestUtils/mobileAction/container/__tests__/mobileActionButtonsContainer.test.js +21 -0
  122. package/manifestUtils/mobileAction/container/index.js +3 -1
  123. package/manifestUtils/mobileAction/groups/__tests__/buildMobileActionButtonGroups.test.js +1 -1
  124. package/manifestUtils/mobileAction/groups/defaults.js +5 -2
  125. package/manifestUtils/modalBlocks.js +304 -0
  126. package/modalState/ContentViewModel.ts +113 -0
  127. package/modalState/EditableCollection.ts +17 -0
  128. package/modalState/EditableCollectionRegistry.ts +51 -0
  129. package/modalState/ModalOrchestrator.ts +213 -0
  130. package/modalState/RemoteEditableCollection.ts +235 -0
  131. package/modalState/__tests__/ContentViewModel.editable.test.ts +69 -0
  132. package/modalState/__tests__/ContentViewModel.test.ts +165 -0
  133. package/modalState/__tests__/EditableCollectionRegistry.test.ts +112 -0
  134. package/modalState/__tests__/ModalOrchestrator.phase3.test.ts +86 -0
  135. package/modalState/__tests__/RemoteEditableCollection.test.ts +415 -0
  136. package/modalState/__tests__/index.test.ts +30 -1
  137. package/modalState/__tests__/useBottomSheetContent.test.ts +349 -0
  138. package/modalState/__tests__/useModalStoreState.test.ts +142 -0
  139. package/modalState/index.ts +37 -83
  140. package/modalState/store.ts +109 -0
  141. package/modalState/types.ts +173 -0
  142. package/modalState/useBottomSheetContent.ts +109 -0
  143. package/navigationUtils/xrayNavigator.ts +12 -0
  144. package/numberUtils/__tests__/isMinusZero.test.ts +20 -0
  145. package/numberUtils/__tests__/isZero.test.ts +27 -0
  146. package/numberUtils/__tests__/requireNumber.test.ts +50 -0
  147. package/numberUtils/__tests__/toNumber.test.ts +27 -0
  148. package/numberUtils/__tests__/toNumberWithDefault.test.ts +64 -0
  149. package/numberUtils/__tests__/toNumberWithDefaultZero.test.ts +48 -0
  150. package/numberUtils/index.ts +27 -8
  151. package/package.json +3 -2
  152. package/playerUtils/__tests__/contentDataKeys.test.ts +297 -0
  153. package/playerUtils/__tests__/resolvePlayerActions.test.ts +138 -0
  154. package/playerUtils/__tests__/resolvePlayerTitleSubtitle.test.ts +180 -0
  155. package/playerUtils/contentDataKeys.ts +134 -0
  156. package/playerUtils/index.ts +37 -16
  157. package/playerUtils/isAudioItem.ts +26 -0
  158. package/playerUtils/resolvePlayerActions.ts +71 -0
  159. package/playerUtils/resolvePlayerTitleSubtitle.ts +76 -0
  160. package/pluginUtils/__tests__/pluginUtils.test.js +32 -0
  161. package/pluginUtils/index.ts +29 -0
  162. package/quickBrickAssets/__tests__/index.test.ts +18 -0
  163. package/quickBrickAssets/index.ts +35 -0
  164. package/reactHooks/__tests__/getPluginConfiguration.test.ts +82 -0
  165. package/reactHooks/actions/index.ts +124 -3
  166. package/reactHooks/cell-click/__tests__/index.test.js +131 -1
  167. package/reactHooks/cell-click/index.ts +33 -18
  168. package/reactHooks/connection/index.tsx +8 -5
  169. package/reactHooks/feed/__tests__/feedInvalidation.test.tsx +338 -0
  170. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +8 -2
  171. package/reactHooks/feed/__tests__/useInflatedUrl.test.tsx +25 -0
  172. package/reactHooks/feed/__tests__/useMarkPipesDataStale.test.tsx +243 -0
  173. package/reactHooks/feed/useFeedLoader.tsx +28 -1
  174. package/reactHooks/feed/useLoadPipesDataDispatch.ts +12 -11
  175. package/reactHooks/feed/useMarkPipesDataStale.ts +68 -23
  176. package/reactHooks/index.ts +2 -0
  177. package/reactHooks/layout/index.ts +1 -1
  178. package/reactHooks/navigation/__mocks__/index.ts +4 -0
  179. package/reactHooks/navigation/__tests__/index.test.tsx +176 -1
  180. package/reactHooks/navigation/__tests__/useIsScreenActive.test.ts +42 -0
  181. package/reactHooks/navigation/__tests__/usePresentScreen.test.ts +154 -0
  182. package/reactHooks/navigation/index.ts +3 -1
  183. package/reactHooks/navigation/useIsScreenActive.ts +29 -8
  184. package/reactHooks/navigation/usePresentScreen.ts +157 -0
  185. package/reactHooks/navigation/useRoute.ts +22 -5
  186. package/reactHooks/state/useComponentScreenState.ts +1 -1
  187. package/reactHooks/usePluginConfiguration.ts +88 -47
  188. package/reactHooks/utils/__tests__/index.test.js +22 -2
  189. package/reactHooks/utils/index.ts +11 -1
  190. package/riverComponetsMeasurementProvider/index.tsx +3 -1
  191. package/stringUtils/__tests__/stringUtils.test.js +42 -0
  192. package/stringUtils/index.ts +2 -2
  193. package/uiActionsRegistrator/__tests__/resolveActionIdentifiers.test.ts +93 -0
  194. package/uiActionsRegistrator/__tests__/subscribe.test.ts +133 -0
  195. package/uiActionsRegistrator/__tests__/usableActionItems.test.ts +52 -0
  196. package/uiActionsRegistrator/index.ts +25 -0
  197. package/uiActionsRegistrator/registry.ts +351 -0
  198. package/uiActionsRegistrator/resolveActionIdentifiers.ts +100 -0
  199. package/uiActionsRegistrator/usableActionItems.ts +66 -0
  200. package/zappFrameworkUtils/__tests__/localStorageHelper.test.ts +146 -0
  201. package/zappFrameworkUtils/localStorageHelper.ts +19 -15
  202. package/appUtils/contextKeysManager/__tests__/getKey/success/byReference.test.ts +0 -56
  203. package/appUtils/contextKeysManager/__tests__/getKeyByReference/failure.test.ts +0 -265
  204. package/appUtils/contextKeysManager/__tests__/getKeyByReference/success.test.ts +0 -303
  205. package/appUtils/contextKeysManager/__tests__/removeReferenceForKey/failure.test.ts +0 -82
  206. package/appUtils/contextKeysManager/__tests__/removeReferenceForKey/success.test.ts +0 -41
  207. package/appUtils/contextKeysManager/__tests__/setKey/success/saveItemButNotReference.test.ts +0 -208
  208. package/appUtils/contextKeysManager/__tests__/setReferenceForKey/failure.test.ts +0 -93
  209. package/appUtils/contextKeysManager/__tests__/setReferenceForKey/success.test.ts +0 -45
  210. package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +0 -56
  211. package/playerUtils/__tests__/getPlayerActionButtons.test.ts +0 -54
  212. package/playerUtils/getPlayerActionButtons.ts +0 -17
  213. /package/reactHooks/actions/__tests__/{index.test.js → index.test.tsx} +0 -0
@@ -0,0 +1,20 @@
1
+ /// <reference types="@applicaster/applicaster-types" />
2
+ import { sessionStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/SessionStorage";
3
+ import { batchSave } from "../../zappFrameworkUtils/localStorageHelper";
4
+ import { ActionResult } from "../ActionExecutor";
5
+ import { ActionHandler } from "../types";
6
+ import { StorageSetOptions } from "./localStorageSet";
7
+
8
+ /**
9
+ * Saves values to session storage organized by namespaces.
10
+ * The values persist only for the current app session.
11
+ */
12
+ export const sessionStorageSetAction: ActionHandler<StorageSetOptions> = async (
13
+ action
14
+ ): Promise<ActionResult> => {
15
+ const namespaces = (action.options as StorageSetOptions).content;
16
+ await batchSave(namespaces, sessionStorage);
17
+ // TODO: Add support for ownershipKey and ownershipNamespace
18
+
19
+ return ActionResult.Success;
20
+ };
@@ -0,0 +1,15 @@
1
+ /// <reference types="@applicaster/applicaster-types" />
2
+ import { sessionStorageToggleFlag } from "../StorageActions";
3
+ import { ActionResult } from "../ActionExecutor";
4
+ import { ActionHandler, ActionExecutionContext } from "../types";
5
+ import { StorageToggleFlagOptions } from "./localStorageToggleFlag";
6
+
7
+ /**
8
+ * Toggles a flag in session storage (adds or removes an item from a collection).
9
+ * Values persist only for the current app session.
10
+ */
11
+ export const sessionStorageToggleFlagAction: ActionHandler<
12
+ StorageToggleFlagOptions
13
+ > = async (action, context?: ActionExecutionContext): Promise<ActionResult> => {
14
+ return await sessionStorageToggleFlag(context, action);
15
+ };
@@ -0,0 +1,51 @@
1
+ /// <reference types="@applicaster/applicaster-types" />
2
+ import { showAlertDialog } from "../../alertUtils";
3
+ import { ActionResult } from "../ActionExecutor";
4
+ import { ActionHandler } from "../types";
5
+ import { createLogger } from "../../logger";
6
+
7
+ const { log_info } = createLogger({
8
+ subsystem: "ActionExecutorContext",
9
+ category: "General",
10
+ });
11
+
12
+ /**
13
+ * Options for the showAlert action.
14
+ */
15
+ export interface ShowAlertOptions {
16
+ /** Dialog title (required) */
17
+ title: string;
18
+
19
+ /** Dialog message */
20
+ message?: string;
21
+
22
+ /** Dismiss button text. Defaults to "OK" */
23
+ okButtonText?: string;
24
+ }
25
+
26
+ /**
27
+ * Tells the user something and waits for them to acknowledge it.
28
+ *
29
+ * One button, because there is nothing to decide. Use `confirmDialog` when the
30
+ * user is choosing between going on and backing out; a `confirmDialog` without
31
+ * a cancel label still renders two buttons, the second one blank.
32
+ *
33
+ * Always resolves to Success: dismissing a notice is not a cancellation, so the
34
+ * rest of the chain runs.
35
+ */
36
+ export const showAlertAction: ActionHandler<ShowAlertOptions> = async (
37
+ action
38
+ ): Promise<ActionResult> => {
39
+ log_info("handleAction: showAlert event");
40
+
41
+ const options = action.options as ShowAlertOptions;
42
+
43
+ return new Promise<ActionResult>((resolve) => {
44
+ showAlertDialog({
45
+ title: options?.title,
46
+ message: options?.message,
47
+ okButtonText: options?.okButtonText || "OK",
48
+ completion: () => resolve(ActionResult.Success),
49
+ });
50
+ });
51
+ };
@@ -0,0 +1,87 @@
1
+ /// <reference types="@applicaster/applicaster-types" />
2
+ import { ActionResult } from "../ActionExecutor";
3
+ import { ActionHandler } from "../types";
4
+ import { postEvent } from "../../reactHooks/useSubscriberFor";
5
+
6
+ export const CONFIRMATION_TOAST_SOURCE = "confirmation";
7
+
8
+ export const CONFIRMATION_TOAST_DEFAULT_TIMEOUT = 5000;
9
+
10
+ /**
11
+ * Shows a mobile confirmation toast with Theme defaults from
12
+ * `Confirmation Toast / Message` in the base theme plugin.
13
+ *
14
+ * This action is confirmation-only by design: it always stamps
15
+ * `source: "confirmation"` (so Theme confirmation styles apply) and defaults
16
+ * `timeout` to 5000 ms. Non-confirmation toasts (e.g. Offline Experience)
17
+ * must post on the shared `"showToast"` event bus directly and omit `source`.
18
+ * Callers may override individual style fields; Theme supplies the rest.
19
+ *
20
+ * @example From React via ActionExecutorContext
21
+ * ```tsx
22
+ * import React, { useContext } from "react";
23
+ * import { ActionExecutorContext } from "@applicaster/zapp-react-native-utils/actionsExecutor/ActionExecutorContext";
24
+ *
25
+ * function QueueButton() {
26
+ * const { handleAction } = useContext(ActionExecutorContext);
27
+ *
28
+ * const onPress = () => {
29
+ * void handleAction({
30
+ * type: "showToast",
31
+ * options: { message: "Added to queue" },
32
+ * });
33
+ * };
34
+ *
35
+ * return <Button onPress={onPress} title="Add to queue" />;
36
+ * }
37
+ * ```
38
+ *
39
+ * @example From code via the actionExecutor singleton
40
+ * ```ts
41
+ * import { actionExecutor } from "@applicaster/zapp-react-native-utils/actionsExecutor/ActionExecutor";
42
+ *
43
+ * await actionExecutor.handleAction({
44
+ * type: "showToast",
45
+ * options: {
46
+ * message: "Playlist created",
47
+ * style: { backgroundColor: "#00AA55" }, // optional partial override
48
+ * },
49
+ * });
50
+ * ```
51
+ *
52
+ * @example From entry / pipes actions JSON
53
+ * ```json
54
+ * {
55
+ * "type": "showToast",
56
+ * "options": {
57
+ * "message": "Item removed from playlist"
58
+ * }
59
+ * }
60
+ * ```
61
+ */
62
+ export interface ShowToastActionOptions {
63
+ id?: string;
64
+ message?: string;
65
+ extraMessage?: string;
66
+ style?: any;
67
+ timeout?: number;
68
+ }
69
+
70
+ export const showToastAction: ActionHandler<ShowToastActionOptions> = async (
71
+ action
72
+ ): Promise<ActionResult> => {
73
+ const options = action?.options;
74
+
75
+ postEvent("showToast", [
76
+ {
77
+ id: options?.id,
78
+ message: options?.message,
79
+ extraMessage: options?.extraMessage,
80
+ style: options?.style,
81
+ timeout: options?.timeout ?? CONFIRMATION_TOAST_DEFAULT_TIMEOUT,
82
+ source: CONFIRMATION_TOAST_SOURCE,
83
+ },
84
+ ]);
85
+
86
+ return ActionResult.Success;
87
+ };
@@ -0,0 +1,40 @@
1
+ /// <reference types="@applicaster/applicaster-types" />
2
+ import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
3
+ import * as QuickBrickManager from "@applicaster/zapp-react-native-bridge/QuickBrick";
4
+ import { QUICK_BRICK_EVENTS } from "@applicaster/zapp-react-native-bridge/QuickBrick";
5
+ import { ACTIVE_LAYOUT_ID_STORAGE_KEY } from "@applicaster/quick-brick-core/App/remoteContextReloader/consts";
6
+ import { ActionResult } from "../ActionExecutor";
7
+ import { ActionHandler } from "../types";
8
+ import { createLogger } from "../../logger";
9
+
10
+ const { log_info } = createLogger({
11
+ subsystem: "ActionExecutorContext",
12
+ category: "General",
13
+ });
14
+
15
+ /**
16
+ * Options for the switchLayout action.
17
+ */
18
+ export interface SwitchLayoutOptions {
19
+ /** The ID of the layout to switch to */
20
+ layoutId: string;
21
+ }
22
+
23
+ /**
24
+ * Switches the active layout and triggers an app reload.
25
+ * Saves the new layout ID to local storage before reloading.
26
+ */
27
+ export const switchLayoutAction: ActionHandler<SwitchLayoutOptions> = async (
28
+ action
29
+ ): Promise<ActionResult> => {
30
+ log_info("handleAction: switchLayout event");
31
+
32
+ await localStorage.setItem(
33
+ ACTIVE_LAYOUT_ID_STORAGE_KEY,
34
+ action.options.layoutId
35
+ );
36
+
37
+ QuickBrickManager.sendQuickBrickEvent(QUICK_BRICK_EVENTS.FORCE_APP_RELOAD);
38
+
39
+ return ActionResult.Success;
40
+ };
@@ -2,3 +2,30 @@ export const TOGGLE_FLAG_MAX_ITEMS_REACHED_EVENT =
2
2
  "action.localStorageToggleFlag.maxItemsReached";
3
3
 
4
4
  export const ACTION_EXECUTOR_EVENT_SOURCE = "ActionExecutor";
5
+
6
+ export const ACTION_TYPES = {
7
+ OPEN_BOTTOM_SHEET: "openBottomSheet",
8
+ DISMISS_BOTTOM_SHEET: "dismissBottomSheet",
9
+ PRESENT_SUB_MENU: "presentSubMenu",
10
+ SEND_CLOUD_EVENT: "sendCloudEvent",
11
+ NAVIGATE_TO_SCREEN: "navigateToScreen",
12
+ PRESENT_SCREEN: "presentScreen",
13
+ GO_BACK: "goBack",
14
+ GO_HOME: "goHome",
15
+ SHOW_TOAST: "showToast",
16
+ LOCAL_STORAGE_SET: "localStorageSet",
17
+ LOCAL_STORAGE_REMOVE: "localStorageRemove",
18
+ LOCAL_STORAGE_TOGGLE_FLAG: "localStorageToggleFlag",
19
+ SESSION_STORAGE_SET: "sessionStorageSet",
20
+ SESSION_STORAGE_REMOVE: "sessionStorageRemove",
21
+ SESSION_STORAGE_TOGGLE_FLAG: "sessionStorageToggleFlag",
22
+ REFRESH_COMPONENT: "refreshComponent",
23
+ SWITCH_LAYOUT: "switchLayout",
24
+ APP_RESTART: "appRestart",
25
+ CONFIRM_DIALOG: "confirmDialog",
26
+ SHOW_ALERT: "showAlert",
27
+ SCREEN_SET_VARIABLE: "screenSetVariable",
28
+ SCREEN_TOGGLE_FLAG: "screenToggleFlag",
29
+ } as const;
30
+
31
+ export type KnownActionType = (typeof ACTION_TYPES)[keyof typeof ACTION_TYPES];
@@ -47,6 +47,11 @@ function makeMultiSelect(feed: ZappFeed, key, decoratedFeed) {
47
47
  ? "sessionStorageToggleFlag"
48
48
  : "localStorageToggleFlag";
49
49
 
50
+ // the toggle actions read their limit from their own options
51
+ const maxItems = Number(
52
+ decoratedFeed.extensions?.preference_editor_options?.max_items
53
+ );
54
+
50
55
  decoratedFeed.entry?.forEach((entry: ZappEntry) => {
51
56
  entry.type.value = "action";
52
57
  entry.id = entry.extensions?.tag || entry.id;
@@ -59,6 +64,7 @@ function makeMultiSelect(feed: ZappFeed, key, decoratedFeed) {
59
64
  type: actionType,
60
65
  options: {
61
66
  key,
67
+ ...(maxItems > 0 && { max_items: maxItems }),
62
68
  },
63
69
  },
64
70
  ],
@@ -159,13 +165,13 @@ export const decorateFeed = (feed: ZappFeed) => {
159
165
 
160
166
  const decoratedFeed = R.clone(feed);
161
167
 
162
- const isSingleSelect =
163
- (feed.extensions?.preference_editor_options?.select_mode ||
164
- feed.extensions?.behavior?.select_mode) === "single";
168
+ const selectMode =
169
+ feed.extensions?.preference_editor_options?.select_mode ||
170
+ feed.extensions?.behavior?.select_mode;
165
171
 
166
- if (isSingleSelect) {
172
+ if (selectMode === "single") {
167
173
  return makeSingleSelect(feed, key, decoratedFeed);
168
- } else {
169
- return makeMultiSelect(feed, key, decoratedFeed);
170
174
  }
175
+
176
+ return makeMultiSelect(feed, key, decoratedFeed);
171
177
  };
@@ -0,0 +1,66 @@
1
+ /// <reference types="@applicaster/applicaster-types" />
2
+ import { ActionResult } from "./ActionExecutor";
3
+ import { useScreenStateStore } from "../reactHooks/navigation/useScreenStateStore";
4
+
5
+ /**
6
+ * Base context object passed to action handlers during execution.
7
+ * Contains references to the current entry, screen, and other execution state.
8
+ */
9
+ export interface ActionExecutionContext {
10
+ /** The entry associated with this action (e.g., the item that was tapped) */
11
+ entry?: ZappEntry;
12
+
13
+ /** The current screen/river data */
14
+ screenData?: ZappRiver;
15
+
16
+ /** The entry that defines the screen (if screen was opened from an entry) */
17
+ screenEntry?: ZappEntry;
18
+
19
+ /** The UI component that triggered the action.
20
+ * From UIComponentContext as `{ ...component, parent }`.
21
+ */
22
+ component?: ZappUIComponent & { parent?: ZappUIComponent };
23
+
24
+ /** The route identifier for the current screen */
25
+ screenRoute?: string;
26
+
27
+ /** The screen state store for managing screen-level state (Zustand store) */
28
+ screenStateStore?: ReturnType<typeof useScreenStateStore>;
29
+
30
+ /** Context entry for additional data */
31
+ entryContext?: ZappEntry;
32
+
33
+ /**
34
+ * Sets the entry that the screen opened by this tap reads as `@{entry/...}`.
35
+ * Actions that open a screen call it themselves, so a tap that does not open
36
+ * one (a selection toggle) does not update the shared entry context.
37
+ */
38
+ setEntryContext?: (entry: ZappEntry) => void;
39
+
40
+ /** Callback function for action completion */
41
+ callback?: (result: { success: boolean; error: any; abort: boolean }) => void;
42
+
43
+ screenState?: Record<any, any>;
44
+ }
45
+
46
+ /**
47
+ * Type for action handler functions.
48
+ * Handlers receive the action (which may have typed options) and execution context.
49
+ *
50
+ * @template _TOptions - Optional type hint for the action's options object (for documentation/intellisense)
51
+ * @param action - The action to execute
52
+ * @param context - The execution context
53
+ * @returns A promise that resolves to the action result
54
+ */
55
+ export type ActionHandler<_TOptions = any> = (
56
+ action: ActionType,
57
+ context?: ActionExecutionContext
58
+ ) => Promise<ActionResult>;
59
+
60
+ /**
61
+ * Factory function that creates an action handler.
62
+ * Used for actions that need to capture dependencies at registration time.
63
+ */
64
+ export type ActionHandlerFactory<_TOptions = any> = (
65
+ ...dependencies: any[]
66
+ ) => ActionHandler<_TOptions>;
@@ -8,7 +8,7 @@ import {
8
8
  } from "../appUtils/playerManager";
9
9
  import { Player } from "../appUtils/playerManager/player";
10
10
  import { AnalyticPlayerListener } from "./AnalyticPlayerListener";
11
- import { PlayerRole } from "../appUtils/playerManager/conts";
11
+ import { PlayerRole } from "../appUtils/playerManager/const";
12
12
 
13
13
  enum PlayerAnalyticsTrackerType {
14
14
  PlayerAnalyticsTracker = "PlayerAnalyticsTracker",
@@ -498,6 +498,15 @@ export function HooksManager({
498
498
  presentUI
499
499
  );
500
500
  } catch (e) {
501
+ // Without this the failure is invisible: the hook stops right after the
502
+ // "Executing hook" line, with nothing in the log explaining why and no
503
+ // remaining hook ever running.
504
+ logHookEvent(
505
+ hooksManagerLogger.error,
506
+ `runInBackground: hook ${hookPlugin.identifier} threw: ${e?.message}`,
507
+ { hook: hookPlugin, payload, error: e, stack: e?.stack }
508
+ );
509
+
501
510
  hookPlugin.setStateAndNotify(HOOKS_EVENTS.ERROR, {
502
511
  error: e,
503
512
  hookPlugin,
@@ -1,4 +1,4 @@
1
- import { ContextKeysManager, NOTHING } from "../..";
1
+ import { ContextKeysManager } from "../..";
2
2
 
3
3
  describe("Context Keys Manager - getKey", () => {
4
4
  it("returns null if key is invalid", async () => {
@@ -34,8 +34,6 @@ describe("Context Keys Manager - getKey", () => {
34
34
  secureStorage: mockedSecureStorage,
35
35
  });
36
36
 
37
- const getKeyByReference = jest.spyOn(contextManager, "getKeyByReference");
38
-
39
37
  // run
40
38
  const result = await contextManager.getKey(invalidKey);
41
39
 
@@ -46,11 +44,10 @@ describe("Context Keys Manager - getKey", () => {
46
44
 
47
45
  expect(mockedLogger.warn).toHaveBeenCalledWith({
48
46
  message:
49
- "Provided key is not valid - expecing an object with namespace and key properties",
47
+ "Provided key is not valid - expecting an object with namespace and key properties",
50
48
  data: { key: invalidKey },
51
49
  });
52
50
 
53
- expect(getKeyByReference).not.toHaveBeenCalled();
54
51
  expect(mockedSessionStorage.getItem).not.toHaveBeenCalled();
55
52
  expect(mockedLocalStorage.getItem).not.toHaveBeenCalled();
56
53
  expect(mockedSecureStorage.getItem).not.toHaveBeenCalled();
@@ -90,10 +87,6 @@ describe("Context Keys Manager - getKey", () => {
90
87
  secureStorage: mockedSecureStorage,
91
88
  });
92
89
 
93
- const getKeyByReference = jest
94
- .spyOn(contextManager, "getKeyByReference")
95
- .mockResolvedValueOnce(NOTHING);
96
-
97
90
  // run
98
91
  const result = await contextManager.getKey(key);
99
92
 
@@ -107,8 +100,6 @@ describe("Context Keys Manager - getKey", () => {
107
100
  data: { key },
108
101
  });
109
102
 
110
- expect(getKeyByReference).toHaveBeenCalledWith("key", "namespace");
111
-
112
103
  expect(mockedSessionStorage.getItem).toHaveBeenCalledWith(
113
104
  "key",
114
105
  "namespace"
@@ -152,10 +143,6 @@ describe("Context Keys Manager - getKey", () => {
152
143
  secureStorage: mockedSecureStorage,
153
144
  });
154
145
 
155
- const getKeyByReference = jest
156
- .spyOn(contextManager, "getKeyByReference")
157
- .mockResolvedValueOnce(NOTHING);
158
-
159
146
  // run
160
147
  const result = await contextManager.getKey(key);
161
148
 
@@ -169,8 +156,6 @@ describe("Context Keys Manager - getKey", () => {
169
156
  data: { key },
170
157
  });
171
158
 
172
- expect(getKeyByReference).toHaveBeenCalledWith("key", "namespace");
173
-
174
159
  expect(mockedSessionStorage.getItem).toHaveBeenCalledWith(
175
160
  "key",
176
161
  "namespace"
@@ -214,10 +199,6 @@ describe("Context Keys Manager - getKey", () => {
214
199
  secureStorage: mockedSecureStorage,
215
200
  });
216
201
 
217
- const getKeyByReference = jest
218
- .spyOn(contextManager, "getKeyByReference")
219
- .mockResolvedValueOnce(NOTHING);
220
-
221
202
  // run
222
203
  const result = await contextManager.getKey(key);
223
204
 
@@ -231,8 +212,6 @@ describe("Context Keys Manager - getKey", () => {
231
212
  data: { key },
232
213
  });
233
214
 
234
- expect(getKeyByReference).toHaveBeenCalledWith("key", "namespace");
235
-
236
215
  expect(mockedSessionStorage.getItem).toHaveBeenCalledWith(
237
216
  "key",
238
217
  "namespace"
@@ -1,4 +1,4 @@
1
- import { ContextKeysManager, NOTHING } from "../../..";
1
+ import { ContextKeysManager } from "../../..";
2
2
 
3
3
  describe("Context Keys Manager - getKey", () => {
4
4
  it("returns value if found in session-storage", async () => {
@@ -35,10 +35,6 @@ describe("Context Keys Manager - getKey", () => {
35
35
  secureStorage: mockedSecureStorage,
36
36
  });
37
37
 
38
- const getKeyByReference = jest
39
- .spyOn(contextManager, "getKeyByReference")
40
- .mockResolvedValueOnce(NOTHING);
41
-
42
38
  // run
43
39
  const result = await contextManager.getKey(key);
44
40
 
@@ -47,8 +43,6 @@ describe("Context Keys Manager - getKey", () => {
47
43
 
48
44
  expect(mockedLogger.warn).not.toHaveBeenCalled();
49
45
 
50
- expect(getKeyByReference).toHaveBeenCalledWith("key", "namespace");
51
-
52
46
  expect(mockedSessionStorage.getItem).toHaveBeenCalledWith(
53
47
  "key",
54
48
  "namespace"
@@ -92,10 +86,6 @@ describe("Context Keys Manager - getKey", () => {
92
86
  secureStorage: mockedSecureStorage,
93
87
  });
94
88
 
95
- const getKeyByReference = jest
96
- .spyOn(contextManager, "getKeyByReference")
97
- .mockResolvedValueOnce(NOTHING);
98
-
99
89
  // run
100
90
  const result = await contextManager.getKey(key);
101
91
 
@@ -104,8 +94,6 @@ describe("Context Keys Manager - getKey", () => {
104
94
 
105
95
  expect(mockedLogger.warn).not.toHaveBeenCalled();
106
96
 
107
- expect(getKeyByReference).toHaveBeenCalledWith("key", "namespace");
108
-
109
97
  expect(mockedSessionStorage.getItem).toHaveBeenCalledWith(
110
98
  "key",
111
99
  "namespace"
@@ -149,10 +137,6 @@ describe("Context Keys Manager - getKey", () => {
149
137
  secureStorage: mockedSecureStorage,
150
138
  });
151
139
 
152
- const getKeyByReference = jest
153
- .spyOn(contextManager, "getKeyByReference")
154
- .mockResolvedValueOnce(NOTHING);
155
-
156
140
  // run
157
141
  const result = await contextManager.getKey(key);
158
142
 
@@ -161,8 +145,6 @@ describe("Context Keys Manager - getKey", () => {
161
145
 
162
146
  expect(mockedLogger.warn).not.toHaveBeenCalled();
163
147
 
164
- expect(getKeyByReference).toHaveBeenCalledWith("key", "namespace");
165
-
166
148
  expect(mockedSessionStorage.getItem).toHaveBeenCalledWith(
167
149
  "key",
168
150
  "namespace"
@@ -209,10 +191,6 @@ describe("Context Keys Manager - getKey", () => {
209
191
  secureStorage: mockedSecureStorage,
210
192
  });
211
193
 
212
- const getKeyByReference = jest
213
- .spyOn(contextManager, "getKeyByReference")
214
- .mockResolvedValueOnce(NOTHING);
215
-
216
194
  // run
217
195
  const result = await contextManager.getKey(key);
218
196
 
@@ -221,8 +199,6 @@ describe("Context Keys Manager - getKey", () => {
221
199
 
222
200
  expect(mockedLogger.warn).not.toHaveBeenCalled();
223
201
 
224
- expect(getKeyByReference).toHaveBeenCalledWith("key", "namespace");
225
-
226
202
  expect(mockedSessionStorage.getItem).toHaveBeenCalledWith(
227
203
  "key",
228
204
  "namespace"
@@ -1,4 +1,4 @@
1
- import { ContextKeysManager, NOTHING } from "../../..";
1
+ import { ContextKeysManager } from "../../..";
2
2
  import { DEFAULT_NAMESPACE } from "../../../consts";
3
3
 
4
4
  describe("Context Keys Manager - getKey", () => {
@@ -36,10 +36,6 @@ describe("Context Keys Manager - getKey", () => {
36
36
  secureStorage: mockedSecureStorage,
37
37
  });
38
38
 
39
- const getKeyByReference = jest
40
- .spyOn(contextManager, "getKeyByReference")
41
- .mockResolvedValueOnce(NOTHING);
42
-
43
39
  // run
44
40
  const result = await contextManager.getKey(key);
45
41
 
@@ -48,8 +44,6 @@ describe("Context Keys Manager - getKey", () => {
48
44
 
49
45
  expect(mockedLogger.warn).not.toHaveBeenCalled();
50
46
 
51
- expect(getKeyByReference).toHaveBeenCalledWith("key", DEFAULT_NAMESPACE);
52
-
53
47
  expect(mockedSessionStorage.getItem).toHaveBeenCalledWith(
54
48
  "key",
55
49
  DEFAULT_NAMESPACE
@@ -34,11 +34,6 @@ describe("Context Keys Manager - removeKey", () => {
34
34
  secureStorage: mockedSecureStorage,
35
35
  });
36
36
 
37
- const removeReferenceForKey = jest.spyOn(
38
- contextManager,
39
- "removeReferenceForKey"
40
- );
41
-
42
37
  // run
43
38
  const result = await contextManager.removeKey(key);
44
39
 
@@ -47,12 +42,10 @@ describe("Context Keys Manager - removeKey", () => {
47
42
 
48
43
  expect(mockedLogger.warn).toHaveBeenCalledWith({
49
44
  message:
50
- "Provided key is not valid - expecing an object with namespace and key properties",
45
+ "Provided key is not valid - expecting an object with namespace and key properties",
51
46
  data: { key },
52
47
  });
53
48
 
54
- expect(removeReferenceForKey).not.toHaveBeenCalled();
55
-
56
49
  expect(mockedSessionStorage.removeItem).not.toHaveBeenCalled();
57
50
  expect(mockedLocalStorage.removeItem).not.toHaveBeenCalled();
58
51
  expect(mockedSecureStorage.removeItem).not.toHaveBeenCalled();
@@ -92,10 +85,6 @@ describe("Context Keys Manager - removeKey", () => {
92
85
  secureStorage: mockedSecureStorage,
93
86
  });
94
87
 
95
- const removeReferenceForKey = jest
96
- .spyOn(contextManager, "removeReferenceForKey")
97
- .mockResolvedValueOnce(true);
98
-
99
88
  // run
100
89
  const result = await contextManager.removeKey(key);
101
90
 
@@ -107,8 +96,6 @@ describe("Context Keys Manager - removeKey", () => {
107
96
  data: { key },
108
97
  });
109
98
 
110
- expect(removeReferenceForKey).toHaveBeenCalledWith("key", "namespace");
111
-
112
99
  expect(mockedSessionStorage.removeItem).toHaveBeenCalledWith(
113
100
  "key",
114
101
  "namespace"
@@ -159,10 +146,6 @@ describe("Context Keys Manager - removeKey", () => {
159
146
  secureStorage: mockedSecureStorage,
160
147
  });
161
148
 
162
- const removeReferenceForKey = jest
163
- .spyOn(contextManager, "removeReferenceForKey")
164
- .mockResolvedValueOnce(true);
165
-
166
149
  // run
167
150
  const result = await contextManager.removeKey(key);
168
151
 
@@ -174,8 +157,6 @@ describe("Context Keys Manager - removeKey", () => {
174
157
  data: { key },
175
158
  });
176
159
 
177
- expect(removeReferenceForKey).toHaveBeenCalledWith("key", "namespace");
178
-
179
160
  expect(mockedSessionStorage.removeItem).toHaveBeenCalledWith(
180
161
  "key",
181
162
  "namespace"
@@ -226,10 +207,6 @@ describe("Context Keys Manager - removeKey", () => {
226
207
  secureStorage: mockedSecureStorage,
227
208
  });
228
209
 
229
- const removeReferenceForKey = jest
230
- .spyOn(contextManager, "removeReferenceForKey")
231
- .mockResolvedValueOnce(true);
232
-
233
210
  // run
234
211
  const result = await contextManager.removeKey(key);
235
212
 
@@ -241,8 +218,6 @@ describe("Context Keys Manager - removeKey", () => {
241
218
  data: { key },
242
219
  });
243
220
 
244
- expect(removeReferenceForKey).toHaveBeenCalledWith("key", "namespace");
245
-
246
221
  expect(mockedSessionStorage.removeItem).toHaveBeenCalledWith(
247
222
  "key",
248
223
  "namespace"
@@ -34,10 +34,6 @@ describe("Context Keys Manager - removeKey", () => {
34
34
  secureStorage: mockedSecureStorage,
35
35
  });
36
36
 
37
- const removeReferenceForKey = jest
38
- .spyOn(contextManager, "removeReferenceForKey")
39
- .mockResolvedValueOnce(true);
40
-
41
37
  // run
42
38
  const result = await contextManager.removeKey(key);
43
39
 
@@ -46,8 +42,6 @@ describe("Context Keys Manager - removeKey", () => {
46
42
 
47
43
  expect(mockedLogger.warn).not.toHaveBeenCalled();
48
44
 
49
- expect(removeReferenceForKey).toHaveBeenCalledWith("key", "namespace");
50
-
51
45
  expect(mockedSessionStorage.removeItem).toHaveBeenCalledWith(
52
46
  "key",
53
47
  "namespace"