@applicaster/zapp-react-native-utils 16.0.0-alpha.8901714553 → 16.0.0-alpha.9024462102
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/actionsExecutor/ActionExecutor.ts +57 -12
- package/actionsExecutor/ActionExecutorContext.tsx +111 -313
- package/actionsExecutor/__tests__/ActionExecutor.test.ts +71 -0
- package/actionsExecutor/__tests__/feedDecorator.test.ts +61 -0
- package/actionsExecutor/actions/__tests__/dismissBottomSheet.test.ts +25 -0
- package/actionsExecutor/actions/__tests__/goBack.test.ts +62 -0
- package/actionsExecutor/actions/__tests__/goHome.test.ts +19 -0
- package/actionsExecutor/actions/__tests__/loadItemsFromSource.test.ts +88 -0
- package/actionsExecutor/actions/__tests__/navigateToScreen.test.ts +133 -0
- package/actionsExecutor/actions/__tests__/openBottomSheet.customContent.test.ts +76 -0
- package/actionsExecutor/actions/__tests__/openBottomSheet.inlineItems.test.ts +300 -0
- package/actionsExecutor/actions/__tests__/presentScreen.test.ts +127 -0
- package/actionsExecutor/actions/__tests__/showAlert.test.ts +49 -0
- package/actionsExecutor/actions/__tests__/showToast.test.ts +88 -0
- package/actionsExecutor/actions/appRestart.ts +24 -0
- package/actionsExecutor/actions/confirmDialog.ts +53 -0
- package/actionsExecutor/actions/dismissBottomSheet.ts +23 -0
- package/actionsExecutor/actions/goBack.ts +60 -0
- package/actionsExecutor/actions/goHome.ts +31 -0
- package/actionsExecutor/actions/index.ts +42 -0
- package/actionsExecutor/actions/localStorageRemove.ts +27 -0
- package/actionsExecutor/actions/localStorageSet.ts +28 -0
- package/actionsExecutor/actions/localStorageToggleFlag.ts +28 -0
- package/actionsExecutor/actions/navigateToScreen.ts +137 -0
- package/actionsExecutor/actions/openBottomSheet.ts +338 -0
- package/actionsExecutor/actions/presentScreen.ts +78 -0
- package/actionsExecutor/actions/refreshComponent.ts +85 -0
- package/actionsExecutor/actions/screenSetVariable.ts +35 -0
- package/actionsExecutor/actions/screenToggleFlag.ts +38 -0
- package/actionsExecutor/actions/sendCloudEvent.ts +93 -0
- package/actionsExecutor/actions/sessionStorageRemove.ts +19 -0
- package/actionsExecutor/actions/sessionStorageSet.ts +20 -0
- package/actionsExecutor/actions/sessionStorageToggleFlag.ts +15 -0
- package/actionsExecutor/actions/showAlert.ts +51 -0
- package/actionsExecutor/actions/showToast.ts +87 -0
- package/actionsExecutor/actions/switchLayout.ts +40 -0
- package/actionsExecutor/consts.ts +27 -0
- package/actionsExecutor/feedDecorator.ts +6 -6
- package/actionsExecutor/types.ts +59 -0
- package/analyticsUtils/PlayerAnalyticsManager.ts +1 -1
- package/appUtils/HooksManager/index.ts +9 -0
- package/appUtils/contextKeysManager/__tests__/getKey/failure.test.ts +1 -1
- package/appUtils/contextKeysManager/__tests__/removeKey/failure.test.ts +1 -1
- package/appUtils/contextKeysManager/__tests__/setKey/failure/invalidKey.test.ts +4 -4
- package/appUtils/contextKeysManager/index.ts +1 -1
- package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +142 -59
- package/appUtils/playerManager/OverlayObserver/__tests__/getUpcomingQueue.test.ts +67 -0
- package/appUtils/playerManager/OverlayObserver/__tests__/liveContent.test.ts +205 -0
- package/appUtils/playerManager/OverlayObserver/__tests__/utils.test.ts +56 -0
- package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.android.tv.ts +4 -0
- package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.ios.tv.ts +4 -0
- package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.ts +24 -0
- package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.web.ts +4 -0
- package/appUtils/playerManager/OverlayObserver/utils.ts +51 -20
- package/appUtils/playerManager/__tests__/playerContent.test.ts +403 -0
- package/appUtils/playerManager/__tests__/playerFactory.test.ts +1 -1
- package/appUtils/playerManager/__tests__/playerNative.test.ts +38 -0
- package/appUtils/playerManager/__tests__/usePlayerContent.test.tsx +64 -0
- package/appUtils/playerManager/{conts.ts → const.ts} +20 -0
- package/appUtils/playerManager/index.ts +1 -1
- package/appUtils/playerManager/player.ts +115 -3
- package/appUtils/playerManager/playerFactory.ts +1 -1
- package/appUtils/playerManager/playerNative.ts +6 -4
- package/appUtils/playerManager/usePlayerContent.tsx +43 -0
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +1 -1
- package/appUtils/playerManager/userLanguagePreference.ts +1 -1
- package/arrayUtils/__tests__/{anyThruthy.test.ts → anyTruthy.test.ts} +8 -0
- package/arrayUtils/__tests__/arrayUtils.test.ts +165 -108
- package/arrayUtils/__tests__/isEmptyArray.test.ts +11 -0
- package/arrayUtils/__tests__/isFilledArray.test.ts +12 -0
- package/arrayUtils/__tests__/isFirst.test.ts +17 -0
- package/arrayUtils/__tests__/isIndexInRange.test.ts +14 -0
- package/arrayUtils/__tests__/isLast.test.ts +31 -0
- package/arrayUtils/__tests__/makeListOf.test.ts +31 -0
- package/arrayUtils/__tests__/makeListOfIndexes.test.ts +20 -0
- package/arrayUtils/__tests__/reorderByIds.test.ts +63 -0
- package/arrayUtils/__tests__/sample.test.ts +61 -0
- package/arrayUtils/index.ts +119 -20
- package/bottomSheet/__tests__/scrollContext.test.tsx +35 -0
- package/bottomSheet/index.ts +17 -0
- package/cellUtils/index.ts +33 -8
- package/colorUtils/__tests__/isTransparentColor.test.ts +76 -0
- package/colorUtils/__tests__/isValidColor.test.ts +70 -0
- package/colorUtils/index.ts +50 -0
- package/configurationUtils/__tests__/manifestKeyParser.test.ts +21 -0
- package/configurationUtils/__tests__/modalBlocksParser.test.ts +269 -0
- package/configurationUtils/manifestKeyParser.ts +50 -10
- package/configurationUtils/modalBlocksParser.ts +214 -0
- package/entryActions/__tests__/buildEntryActions.test.ts +235 -0
- package/entryActions/aliasPresentation.ts +108 -0
- package/entryActions/buildEntryActions.ts +167 -0
- package/entryActions/index.ts +15 -0
- package/entryActions/types.ts +39 -0
- package/manifestUtils/__tests__/player.audioControls.test.js +158 -0
- package/manifestUtils/_internals/index.js +14 -3
- package/manifestUtils/defaultManifestConfigurations/generalContent.js +6 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +349 -20
- package/manifestUtils/fieldUtils/__tests__/fieldUtils.test.js +84 -0
- package/manifestUtils/fieldUtils/index.js +125 -0
- package/manifestUtils/index.js +3 -0
- package/manifestUtils/keys.js +9 -0
- package/manifestUtils/mobileAction/button/index.js +16 -0
- package/manifestUtils/mobileAction/container/index.js +3 -1
- package/manifestUtils/mobileAction/groups/defaults.js +3 -1
- package/manifestUtils/modalBlocks.js +304 -0
- package/manifestUtils/platformIsTV.js +1 -0
- package/modalState/ContentViewModel.ts +113 -0
- package/modalState/EditableCollection.ts +17 -0
- package/modalState/EditableCollectionRegistry.ts +51 -0
- package/modalState/ModalOrchestrator.ts +212 -0
- package/modalState/RemoteEditableCollection.ts +235 -0
- package/modalState/__tests__/ContentViewModel.editable.test.ts +69 -0
- package/modalState/__tests__/ContentViewModel.test.ts +165 -0
- package/modalState/__tests__/EditableCollectionRegistry.test.ts +112 -0
- package/modalState/__tests__/ModalOrchestrator.phase3.test.ts +63 -0
- package/modalState/__tests__/RemoteEditableCollection.test.ts +415 -0
- package/modalState/__tests__/index.test.ts +30 -1
- package/modalState/__tests__/useBottomSheetContent.test.ts +349 -0
- package/modalState/__tests__/useModalStoreState.test.ts +142 -0
- package/modalState/index.ts +35 -83
- package/modalState/store.ts +109 -0
- package/modalState/types.ts +162 -0
- package/modalState/useBottomSheetContent.ts +109 -0
- package/numberUtils/__tests__/isMinusZero.test.ts +20 -0
- package/numberUtils/__tests__/isZero.test.ts +27 -0
- package/numberUtils/__tests__/requireNumber.test.ts +50 -0
- package/numberUtils/__tests__/toNumber.test.ts +27 -0
- package/numberUtils/__tests__/toNumberWithDefault.test.ts +64 -0
- package/numberUtils/__tests__/toNumberWithDefaultZero.test.ts +48 -0
- package/numberUtils/index.ts +27 -8
- package/package.json +2 -2
- package/pipesUtils/__tests__/buildUrlWithQuery.test.ts +33 -0
- package/pipesUtils/__tests__/withPipesEndpoint.test.tsx +56 -0
- package/pipesUtils/index.ts +1 -0
- package/pipesUtils/withPipesEndpoint.tsx +54 -0
- package/playerUtils/__tests__/contentDataKeys.test.ts +297 -0
- package/playerUtils/__tests__/resolvePlayerActions.test.ts +138 -0
- package/playerUtils/__tests__/resolvePlayerTitleSubtitle.test.ts +180 -0
- package/playerUtils/contentDataKeys.ts +134 -0
- package/playerUtils/index.ts +37 -16
- package/playerUtils/isAudioItem.ts +26 -0
- package/playerUtils/resolvePlayerActions.ts +71 -0
- package/playerUtils/resolvePlayerTitleSubtitle.ts +76 -0
- package/pluginUtils/__tests__/pluginUtils.test.js +32 -0
- package/pluginUtils/index.ts +29 -0
- package/reactHooks/actions/index.ts +117 -2
- package/reactHooks/cell-click/__tests__/index.test.js +64 -0
- package/reactHooks/cell-click/index.ts +24 -17
- package/reactHooks/connection/__tests__/index.test.js +154 -2
- package/reactHooks/connection/index.tsx +71 -7
- package/reactHooks/feed/__mocks__/useMarkPipesDataStale.ts +4 -0
- package/reactHooks/feed/__tests__/useInflatedUrl.test.tsx +25 -0
- package/reactHooks/feed/index.ts +5 -1
- package/reactHooks/feed/useBatchLoading.ts +9 -1
- package/reactHooks/feed/{usePipesCacheReset.ts → useMarkPipesDataStale.ts} +12 -4
- package/reactHooks/index.ts +2 -0
- package/reactHooks/layout/index.ts +1 -1
- package/reactHooks/navigation/__mocks__/index.ts +4 -0
- package/reactHooks/navigation/__tests__/index.test.tsx +176 -1
- package/reactHooks/navigation/__tests__/useIsScreenActive.test.ts +42 -0
- package/reactHooks/navigation/__tests__/usePresentScreen.test.ts +154 -0
- package/reactHooks/navigation/index.ts +3 -1
- package/reactHooks/navigation/useIsScreenActive.ts +29 -8
- package/reactHooks/navigation/usePresentScreen.ts +157 -0
- package/reactHooks/navigation/useRoute.ts +22 -5
- package/reactHooks/state/useComponentScreenState.ts +1 -1
- package/reactHooks/usePluginConfiguration.ts +4 -1
- package/reactHooks/utils/__tests__/index.test.js +22 -2
- package/reactHooks/utils/index.ts +13 -2
- package/riverComponetsMeasurementProvider/index.tsx +12 -8
- package/stringUtils/__tests__/stringUtils.test.js +42 -0
- package/stringUtils/index.ts +2 -2
- package/uiActionsRegistrator/__tests__/resolveActionIdentifiers.test.ts +93 -0
- package/uiActionsRegistrator/__tests__/subscribe.test.ts +107 -0
- package/uiActionsRegistrator/__tests__/usableActionItems.test.ts +52 -0
- package/uiActionsRegistrator/index.ts +25 -0
- package/uiActionsRegistrator/registry.ts +335 -0
- package/uiActionsRegistrator/resolveActionIdentifiers.ts +100 -0
- package/uiActionsRegistrator/usableActionItems.ts +66 -0
- package/zappFrameworkUtils/__tests__/localStorageHelper.test.ts +146 -0
- package/zappFrameworkUtils/localStorageHelper.ts +19 -15
- package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +0 -56
- package/playerUtils/__tests__/getPlayerActionButtons.test.ts +0 -54
- package/playerUtils/getPlayerActionButtons.ts +0 -17
- package/reactHooks/feed/__mocks__/usePipesCacheReset.ts +0 -1
- /package/reactHooks/actions/__tests__/{index.test.js → index.test.tsx} +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createLogger } from "../logger";
|
|
2
|
+
import type { ActionExecutionContext, ActionHandler } from "./types";
|
|
2
3
|
|
|
3
4
|
export enum ActionResult {
|
|
4
5
|
Success = "success",
|
|
@@ -11,13 +12,13 @@ const { log_error, log_debug, log_info } = createLogger({
|
|
|
11
12
|
category: "General",
|
|
12
13
|
});
|
|
13
14
|
|
|
14
|
-
class ActionExecutor {
|
|
15
|
-
private registeredActions: Record<string,
|
|
16
|
-
// TODO: Remove context
|
|
15
|
+
export class ActionExecutor {
|
|
16
|
+
private registeredActions: Record<string, ActionHandler> = {};
|
|
17
17
|
|
|
18
|
+
// TODO: Remove context
|
|
18
19
|
handleAction = async (
|
|
19
20
|
action: ActionType,
|
|
20
|
-
context?:
|
|
21
|
+
context?: ActionExecutionContext
|
|
21
22
|
): Promise<ActionResult> => {
|
|
22
23
|
const handler = this.registeredActions[action.type];
|
|
23
24
|
const entry = context?.entry as ZappEntry;
|
|
@@ -52,10 +53,9 @@ class ActionExecutor {
|
|
|
52
53
|
|
|
53
54
|
handleActions = async (
|
|
54
55
|
actionTypes: ActionType[],
|
|
55
|
-
context?:
|
|
56
|
+
context?: ActionExecutionContext
|
|
56
57
|
): Promise<ActionResult> => {
|
|
57
58
|
for (const action of actionTypes) {
|
|
58
|
-
// TODO: Handle action result error
|
|
59
59
|
const result = await this.handleAction(action, context);
|
|
60
60
|
|
|
61
61
|
if (result === ActionResult.Cancel) {
|
|
@@ -63,6 +63,16 @@ class ActionExecutor {
|
|
|
63
63
|
|
|
64
64
|
return result;
|
|
65
65
|
}
|
|
66
|
+
|
|
67
|
+
if (result === ActionResult.Error) {
|
|
68
|
+
// Everything after a failed step was written for a state that never
|
|
69
|
+
// happened — the event was refused, the screen never opened — so
|
|
70
|
+
// running it is how a chain ends up telling the user their mail was
|
|
71
|
+
// sent when it was not.
|
|
72
|
+
log_error(`handleActions: action: ${action.type} failed`, { action });
|
|
73
|
+
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
66
76
|
}
|
|
67
77
|
|
|
68
78
|
return ActionResult.Success;
|
|
@@ -70,7 +80,7 @@ class ActionExecutor {
|
|
|
70
80
|
|
|
71
81
|
handleEntryActions = async (
|
|
72
82
|
entry: ZappEntry,
|
|
73
|
-
context?:
|
|
83
|
+
context?: ActionExecutionContext
|
|
74
84
|
): Promise<ActionResult> => {
|
|
75
85
|
const tapActions = entry.extensions?.tap_actions?.actions;
|
|
76
86
|
|
|
@@ -98,12 +108,10 @@ class ActionExecutor {
|
|
|
98
108
|
delete this.registeredActions[actionType];
|
|
99
109
|
};
|
|
100
110
|
|
|
101
|
-
registerAction = (
|
|
111
|
+
registerAction = <TOptions = Record<string, any>>(
|
|
102
112
|
type: string,
|
|
103
|
-
handler:
|
|
104
|
-
|
|
105
|
-
context?: Record<string, any>
|
|
106
|
-
) => Promise<ActionResult>
|
|
113
|
+
handler: ActionHandler<TOptions>,
|
|
114
|
+
stateResolver?: (entry?: ZappEntry) => any
|
|
107
115
|
) => {
|
|
108
116
|
if (this.registeredActions[type]) {
|
|
109
117
|
log_error(`registerAction: action type: ${type} already registered`);
|
|
@@ -114,11 +122,48 @@ class ActionExecutor {
|
|
|
114
122
|
this.registeredActions[type] = handler;
|
|
115
123
|
log_debug(`registerAction: action type: ${type} registered`);
|
|
116
124
|
|
|
125
|
+
let unregisterState = () => {};
|
|
126
|
+
|
|
127
|
+
if (stateResolver) {
|
|
128
|
+
unregisterState = this.registerActionState(type, stateResolver);
|
|
129
|
+
}
|
|
130
|
+
|
|
117
131
|
return () => {
|
|
118
132
|
this.unregisterAction(type);
|
|
133
|
+
unregisterState();
|
|
119
134
|
log_debug(`registerAction: action type: ${type} un-registered`);
|
|
120
135
|
};
|
|
121
136
|
};
|
|
137
|
+
|
|
138
|
+
private actionStates: Record<string, (entry?: ZappEntry) => any> = {};
|
|
139
|
+
|
|
140
|
+
registerActionState = (
|
|
141
|
+
type: string,
|
|
142
|
+
stateResolver: (entry?: ZappEntry) => any
|
|
143
|
+
) => {
|
|
144
|
+
if (this.actionStates[type]) {
|
|
145
|
+
log_error(
|
|
146
|
+
`registerActionState: action type state: ${type} already registered`
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
return () => {};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
this.actionStates[type] = stateResolver;
|
|
153
|
+
log_debug(`registerActionState: action type state: ${type} registered`);
|
|
154
|
+
|
|
155
|
+
return () => {
|
|
156
|
+
delete this.actionStates[type];
|
|
157
|
+
|
|
158
|
+
log_debug(
|
|
159
|
+
`registerActionState: action type state: ${type} un-registered`
|
|
160
|
+
);
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
getActionState = (type: string, entry?: ZappEntry) => {
|
|
165
|
+
return this.actionStates[type]?.(entry);
|
|
166
|
+
};
|
|
122
167
|
}
|
|
123
168
|
|
|
124
169
|
export const actionExecutor = new ActionExecutor();
|
|
@@ -5,39 +5,41 @@ import {
|
|
|
5
5
|
actionExecutor as _actionExecutor,
|
|
6
6
|
ActionResult,
|
|
7
7
|
} from "./ActionExecutor";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
batchSave,
|
|
11
|
-
} from "../zappFrameworkUtils/localStorageHelper";
|
|
8
|
+
import { ActionExecutionContext, ActionHandler } from "./types";
|
|
9
|
+
import { batchRemoveAllFromNamespaceForStorage } from "../zappFrameworkUtils/localStorageHelper";
|
|
12
10
|
import { sessionStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/SessionStorage";
|
|
13
11
|
|
|
14
|
-
import
|
|
15
|
-
import { QUICK_BRICK_EVENTS } from "@applicaster/zapp-react-native-bridge/QuickBrick";
|
|
16
|
-
import { showConfirmationDialog } from "../alertUtils";
|
|
17
|
-
import { createCloudEvent, sendCloudEvent } from "../cloudEventsUtils";
|
|
18
|
-
import { createLogger } from "../logger";
|
|
19
|
-
import { ACTIVE_LAYOUT_ID_STORAGE_KEY } from "@applicaster/quick-brick-core/App/remoteContextReloader/consts";
|
|
20
|
-
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
21
|
-
import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes";
|
|
22
|
-
import {
|
|
23
|
-
EntryResolver,
|
|
24
|
-
resolveObjectValues,
|
|
25
|
-
} from "../appUtils/contextKeysManager/contextResolver";
|
|
26
|
-
import { useNavigation, useRivers } from "../reactHooks";
|
|
27
|
-
import {
|
|
28
|
-
getInflatedDataSourceUrl,
|
|
29
|
-
getSearchContext,
|
|
30
|
-
} from "../reactHooks/feed/useInflatedUrl";
|
|
31
|
-
|
|
12
|
+
import { useNavigation, usePresentScreen, useRivers } from "../reactHooks";
|
|
32
13
|
import { useContentTypes } from "@applicaster/zapp-react-native-redux/hooks";
|
|
33
14
|
import { useSubscriberFor } from "../reactHooks/useSubscriberFor";
|
|
34
15
|
import { APP_EVENTS } from "../appUtils/events";
|
|
35
|
-
import {
|
|
36
|
-
localStorageToggleFlag,
|
|
37
|
-
sessionStorageToggleFlag,
|
|
38
|
-
} from "./StorageActions";
|
|
16
|
+
import { createLogger } from "../logger";
|
|
39
17
|
|
|
40
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
appRestartAction,
|
|
20
|
+
confirmDialogAction,
|
|
21
|
+
showAlertAction,
|
|
22
|
+
createGoBackAction,
|
|
23
|
+
createGoHomeAction,
|
|
24
|
+
createNavigateToScreenAction,
|
|
25
|
+
dismissBottomSheetAction,
|
|
26
|
+
localStorageRemoveAction,
|
|
27
|
+
localStorageSetAction,
|
|
28
|
+
localStorageToggleFlagAction,
|
|
29
|
+
createPresentScreenAction,
|
|
30
|
+
openBottomSheetAction,
|
|
31
|
+
refreshComponentAction,
|
|
32
|
+
screenSetVariableAction,
|
|
33
|
+
screenToggleFlagAction,
|
|
34
|
+
sendCloudEventAction,
|
|
35
|
+
sessionStorageRemoveAction,
|
|
36
|
+
sessionStorageSetAction,
|
|
37
|
+
sessionStorageToggleFlagAction,
|
|
38
|
+
switchLayoutAction,
|
|
39
|
+
showToastAction,
|
|
40
|
+
} from "./actions";
|
|
41
|
+
|
|
42
|
+
import { ACTION_TYPES } from "./consts";
|
|
41
43
|
|
|
42
44
|
export const { log_error, log_info, log_debug } = createLogger({
|
|
43
45
|
subsystem: "ActionExecutorContext",
|
|
@@ -45,25 +47,28 @@ export const { log_error, log_info, log_debug } = createLogger({
|
|
|
45
47
|
});
|
|
46
48
|
|
|
47
49
|
export type ActionExecutorContextType = {
|
|
48
|
-
registerAction: (
|
|
50
|
+
registerAction: <TOptions = Record<string, any>>(
|
|
49
51
|
type: string,
|
|
50
|
-
handler:
|
|
51
|
-
|
|
52
|
-
context?: Record<string, any>
|
|
53
|
-
) => Promise<ActionResult>
|
|
52
|
+
handler: ActionHandler<TOptions>,
|
|
53
|
+
stateResolver?: (entry?: ZappEntry) => any
|
|
54
54
|
) => void;
|
|
55
55
|
unregisterAction: (type: string) => void;
|
|
56
|
+
registerActionState: (
|
|
57
|
+
type: string,
|
|
58
|
+
stateResolver: (entry?: ZappEntry) => any
|
|
59
|
+
) => () => void;
|
|
60
|
+
getActionState: (type: string, entry?: ZappEntry) => any;
|
|
56
61
|
handleAction: (
|
|
57
62
|
action: ActionType,
|
|
58
|
-
context?:
|
|
63
|
+
context?: ActionExecutionContext
|
|
59
64
|
) => Promise<ActionResult>;
|
|
60
65
|
handleActions: (
|
|
61
66
|
actions: ActionType[],
|
|
62
|
-
context
|
|
67
|
+
context?: ActionExecutionContext
|
|
63
68
|
) => Promise<ActionResult>;
|
|
64
69
|
handleEntryActions: (
|
|
65
70
|
entry: ZappEntry,
|
|
66
|
-
context?:
|
|
71
|
+
context?: ActionExecutionContext
|
|
67
72
|
) => Promise<ActionResult>;
|
|
68
73
|
};
|
|
69
74
|
|
|
@@ -71,235 +76,79 @@ type Props = {
|
|
|
71
76
|
children: React.ReactNode;
|
|
72
77
|
};
|
|
73
78
|
|
|
74
|
-
function findParentComponent(
|
|
75
|
-
childId: string,
|
|
76
|
-
parent: ZappRiver | ZappUIComponent
|
|
77
|
-
): ZappRiver | ZappUIComponent | null {
|
|
78
|
-
for (const child of parent.ui_components) {
|
|
79
|
-
if (child.id === childId) return parent;
|
|
80
|
-
|
|
81
|
-
if (child.ui_components) {
|
|
82
|
-
const found = findParentComponent(childId, child);
|
|
83
|
-
if (found) return found;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
79
|
const prepareDefaultActions = (actionExecutor) => {
|
|
91
|
-
actionExecutor.registerAction("localStorageSet", async (action) => {
|
|
92
|
-
const namespaces = action.options.content;
|
|
93
|
-
await batchSave(namespaces, localStorage);
|
|
94
|
-
// TODO: Add support for ownershipKey and ownershipNamespace
|
|
95
|
-
|
|
96
|
-
return ActionResult.Success;
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
actionExecutor.registerAction("sessionStorageSet", async (action) => {
|
|
100
|
-
const namespaces = action.options.content;
|
|
101
|
-
await batchSave(namespaces, sessionStorage);
|
|
102
|
-
// TODO: Add support for ownershipKey and ownershipNamespace
|
|
103
|
-
|
|
104
|
-
return ActionResult.Success;
|
|
105
|
-
});
|
|
106
|
-
|
|
107
80
|
actionExecutor.registerAction(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const dispatch = appStore.getDispatch();
|
|
111
|
-
|
|
112
|
-
const parentComponent = findParentComponent(
|
|
113
|
-
context?.component?.id,
|
|
114
|
-
context?.screenData
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
const componentSource = context?.component?.data?.source;
|
|
118
|
-
|
|
119
|
-
const componentData = componentSource
|
|
120
|
-
? context.component.data
|
|
121
|
-
: parentComponent?.data;
|
|
122
|
-
|
|
123
|
-
const source = componentData?.source;
|
|
124
|
-
const mapping = componentData?.mapping;
|
|
125
|
-
|
|
126
|
-
let dataSource = source;
|
|
127
|
-
|
|
128
|
-
if (source && mapping) {
|
|
129
|
-
dataSource =
|
|
130
|
-
getInflatedDataSourceUrl({
|
|
131
|
-
source,
|
|
132
|
-
contexts: {
|
|
133
|
-
entry: context?.screenEntry,
|
|
134
|
-
screen: context?.screenData,
|
|
135
|
-
search: getSearchContext(null, mapping),
|
|
136
|
-
},
|
|
137
|
-
mapping,
|
|
138
|
-
}) || source;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
log_info(`handleAction: refreshComponent for dataSource:${dataSource}`, {
|
|
142
|
-
source,
|
|
143
|
-
inflatedUrl: dataSource,
|
|
144
|
-
mapping,
|
|
145
|
-
entryContextId: context?.entryContext?.id,
|
|
146
|
-
entryId: context?.entry?.id,
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
// TODO: In theory we should wait callback to complete, before completing the action, but now it's not needed
|
|
150
|
-
// TODO: handle focused item removal
|
|
151
|
-
dispatch(
|
|
152
|
-
loadPipesData(dataSource, {
|
|
153
|
-
silentRefresh: false,
|
|
154
|
-
clearCache: true,
|
|
155
|
-
riverId: context?.screenData?.id,
|
|
156
|
-
})
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
return ActionResult.Success;
|
|
160
|
-
}
|
|
81
|
+
ACTION_TYPES.LOCAL_STORAGE_SET,
|
|
82
|
+
localStorageSetAction
|
|
161
83
|
);
|
|
162
84
|
|
|
163
|
-
actionExecutor.registerAction(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
ACTIVE_LAYOUT_ID_STORAGE_KEY,
|
|
168
|
-
action.options.layoutId
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
QuickBrickManager.sendQuickBrickEvent(QUICK_BRICK_EVENTS.FORCE_APP_RELOAD);
|
|
172
|
-
|
|
173
|
-
return ActionResult.Success;
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
actionExecutor.registerAction("appRestart", async () => {
|
|
177
|
-
log_info(`handleAction: ${QUICK_BRICK_EVENTS.FORCE_APP_RELOAD} event`);
|
|
178
|
-
|
|
179
|
-
QuickBrickManager.sendQuickBrickEvent(QUICK_BRICK_EVENTS.FORCE_APP_RELOAD);
|
|
180
|
-
|
|
181
|
-
return ActionResult.Success;
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
actionExecutor.registerAction("confirmDialog", async (action) => {
|
|
185
|
-
log_info("handleAction: confirmDialog event");
|
|
186
|
-
|
|
187
|
-
return new Promise<ActionResult>((resolve) => {
|
|
188
|
-
showConfirmationDialog({
|
|
189
|
-
...action.options,
|
|
190
|
-
confirmCompletion: () => resolve(ActionResult.Success),
|
|
191
|
-
cancelCompletion: () => resolve(ActionResult.Cancel),
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
actionExecutor.registerAction("sendCloudEvent", async (action, context) => {
|
|
197
|
-
try {
|
|
198
|
-
const options = action.options;
|
|
199
|
-
|
|
200
|
-
const entry = context?.entry || {};
|
|
201
|
-
const entryResolver = new EntryResolver(entry);
|
|
202
|
-
const screenData = context?.screenStateStore?.getState().data || {};
|
|
203
|
-
const screenResolver = new EntryResolver(screenData || {});
|
|
204
|
-
|
|
205
|
-
const data =
|
|
206
|
-
options?.data && options.inflateData
|
|
207
|
-
? await resolveObjectValues(options.data, {
|
|
208
|
-
entry: entryResolver,
|
|
209
|
-
screen: screenResolver,
|
|
210
|
-
})
|
|
211
|
-
: options?.data || entry;
|
|
85
|
+
actionExecutor.registerAction(
|
|
86
|
+
ACTION_TYPES.SESSION_STORAGE_SET,
|
|
87
|
+
sessionStorageSetAction
|
|
88
|
+
);
|
|
212
89
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
});
|
|
90
|
+
actionExecutor.registerAction(
|
|
91
|
+
ACTION_TYPES.LOCAL_STORAGE_REMOVE,
|
|
92
|
+
localStorageRemoveAction
|
|
93
|
+
);
|
|
218
94
|
|
|
219
|
-
|
|
95
|
+
actionExecutor.registerAction(
|
|
96
|
+
ACTION_TYPES.SESSION_STORAGE_REMOVE,
|
|
97
|
+
sessionStorageRemoveAction
|
|
98
|
+
);
|
|
220
99
|
|
|
221
|
-
|
|
100
|
+
actionExecutor.registerAction(
|
|
101
|
+
ACTION_TYPES.REFRESH_COMPONENT,
|
|
102
|
+
refreshComponentAction
|
|
103
|
+
);
|
|
222
104
|
|
|
223
|
-
|
|
224
|
-
log_error("sendCloudEvent: error sending cloud event", { error });
|
|
105
|
+
actionExecutor.registerAction(ACTION_TYPES.SWITCH_LAYOUT, switchLayoutAction);
|
|
225
106
|
|
|
226
|
-
|
|
227
|
-
}
|
|
107
|
+
actionExecutor.registerAction(ACTION_TYPES.APP_RESTART, appRestartAction);
|
|
228
108
|
|
|
229
|
-
|
|
230
|
-
|
|
109
|
+
actionExecutor.registerAction(
|
|
110
|
+
ACTION_TYPES.CONFIRM_DIALOG,
|
|
111
|
+
confirmDialogAction
|
|
112
|
+
);
|
|
231
113
|
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
} catch (error) {
|
|
235
|
-
log_error("sendCloudEvent: error sending cloud event", {
|
|
236
|
-
action,
|
|
237
|
-
error,
|
|
238
|
-
});
|
|
239
|
-
}
|
|
114
|
+
actionExecutor.registerAction(ACTION_TYPES.SHOW_ALERT, showAlertAction);
|
|
240
115
|
|
|
241
|
-
|
|
242
|
-
|
|
116
|
+
actionExecutor.registerAction(
|
|
117
|
+
ACTION_TYPES.SEND_CLOUD_EVENT,
|
|
118
|
+
sendCloudEventAction
|
|
119
|
+
);
|
|
243
120
|
|
|
244
121
|
actionExecutor.registerAction(
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
action: ActionType,
|
|
248
|
-
context?: Record<string, any>
|
|
249
|
-
): Promise<ActionResult> => {
|
|
250
|
-
return await sessionStorageToggleFlag(context, action);
|
|
251
|
-
}
|
|
122
|
+
ACTION_TYPES.SESSION_STORAGE_TOGGLE_FLAG,
|
|
123
|
+
sessionStorageToggleFlagAction
|
|
252
124
|
);
|
|
253
125
|
|
|
254
126
|
actionExecutor.registerAction(
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
action: ActionType,
|
|
258
|
-
context?: Record<string, any>
|
|
259
|
-
): Promise<ActionResult> => {
|
|
260
|
-
return await localStorageToggleFlag(context, action);
|
|
261
|
-
}
|
|
127
|
+
ACTION_TYPES.LOCAL_STORAGE_TOGGLE_FLAG,
|
|
128
|
+
localStorageToggleFlagAction
|
|
262
129
|
);
|
|
263
130
|
|
|
264
131
|
actionExecutor.registerAction(
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
context?: Record<string, any>
|
|
269
|
-
): Promise<ActionResult> => {
|
|
270
|
-
const route = context?.screenRoute;
|
|
271
|
-
const screenStateStore = context?.screenStateStore;
|
|
272
|
-
|
|
273
|
-
await screenSetVariable(
|
|
274
|
-
route,
|
|
275
|
-
screenStateStore,
|
|
276
|
-
{ entry: context?.entry, options: action.options },
|
|
277
|
-
action
|
|
278
|
-
);
|
|
132
|
+
ACTION_TYPES.SCREEN_SET_VARIABLE,
|
|
133
|
+
screenSetVariableAction
|
|
134
|
+
);
|
|
279
135
|
|
|
280
|
-
|
|
281
|
-
|
|
136
|
+
actionExecutor.registerAction(
|
|
137
|
+
ACTION_TYPES.SCREEN_TOGGLE_FLAG,
|
|
138
|
+
screenToggleFlagAction
|
|
282
139
|
);
|
|
283
140
|
|
|
284
141
|
actionExecutor.registerAction(
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
context?: Record<string, any>
|
|
289
|
-
): Promise<ActionResult> => {
|
|
290
|
-
const screenRoute = context?.screenRoute;
|
|
291
|
-
const screenStateStore = context?.screenStateStore;
|
|
292
|
-
|
|
293
|
-
await screenToggleFlag(
|
|
294
|
-
screenRoute,
|
|
295
|
-
screenStateStore,
|
|
296
|
-
{ entry: context?.entry, options: action.options },
|
|
297
|
-
action
|
|
298
|
-
);
|
|
142
|
+
ACTION_TYPES.OPEN_BOTTOM_SHEET,
|
|
143
|
+
openBottomSheetAction
|
|
144
|
+
);
|
|
299
145
|
|
|
300
|
-
|
|
301
|
-
|
|
146
|
+
actionExecutor.registerAction(
|
|
147
|
+
ACTION_TYPES.DISMISS_BOTTOM_SHEET,
|
|
148
|
+
dismissBottomSheetAction
|
|
302
149
|
);
|
|
150
|
+
|
|
151
|
+
actionExecutor.registerAction(ACTION_TYPES.SHOW_TOAST, showToastAction);
|
|
303
152
|
};
|
|
304
153
|
|
|
305
154
|
export const ActionExecutorContext =
|
|
@@ -312,11 +161,14 @@ export function withActionExecutor(Component) {
|
|
|
312
161
|
const navigator = useNavigation();
|
|
313
162
|
const rivers = useRivers();
|
|
314
163
|
const contentTypes = useContentTypes();
|
|
164
|
+
const presentScreen = usePresentScreen();
|
|
315
165
|
|
|
316
166
|
const handlers = useMemo(() => {
|
|
317
167
|
return {
|
|
318
168
|
unregisterAction: _actionExecutor.unregisterAction,
|
|
319
169
|
registerAction: _actionExecutor.registerAction,
|
|
170
|
+
registerActionState: _actionExecutor.registerActionState,
|
|
171
|
+
getActionState: _actionExecutor.getActionState,
|
|
320
172
|
handleAction: _actionExecutor.handleAction,
|
|
321
173
|
handleActions: _actionExecutor.handleActions,
|
|
322
174
|
handleEntryActions: _actionExecutor.handleEntryActions,
|
|
@@ -339,85 +191,31 @@ export function withActionExecutor(Component) {
|
|
|
339
191
|
|
|
340
192
|
useEffect(() => {
|
|
341
193
|
return _actionExecutor.registerAction(
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (!screenType) {
|
|
348
|
-
log_error("navigateToScreen: typeMapping option is missing");
|
|
349
|
-
|
|
350
|
-
return ActionResult.Error;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
const navigationAction = action.options?.navigationAction;
|
|
354
|
-
const entrySource = action.options?.entry;
|
|
355
|
-
|
|
356
|
-
const entry = entrySource
|
|
357
|
-
? entrySource === "@{entry/}"
|
|
358
|
-
? context?.entry
|
|
359
|
-
: entrySource
|
|
360
|
-
: null;
|
|
361
|
-
|
|
362
|
-
if (entry) {
|
|
363
|
-
if (typeof entry !== "object") {
|
|
364
|
-
log_error(
|
|
365
|
-
`navigateToScreen: entry option is not an object, entry: ${entry}`
|
|
366
|
-
);
|
|
367
|
-
|
|
368
|
-
return ActionResult.Error;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
log_info(
|
|
372
|
-
`navigateToScreen: navigating to screen type: ${screenType} with entry id: ${entry.id}`
|
|
373
|
-
);
|
|
374
|
-
|
|
375
|
-
const overriddenEntry = {
|
|
376
|
-
...entry,
|
|
377
|
-
type: {
|
|
378
|
-
value: screenType,
|
|
379
|
-
},
|
|
380
|
-
};
|
|
381
|
-
|
|
382
|
-
if (navigationAction === "push") {
|
|
383
|
-
navigator.push(overriddenEntry);
|
|
384
|
-
} else {
|
|
385
|
-
navigator.replace(overriddenEntry);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
return ActionResult.Success;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
const screenId = contentTypes?.[screenType]?.screen_id || null;
|
|
392
|
-
|
|
393
|
-
if (!screenId) {
|
|
394
|
-
log_error(
|
|
395
|
-
`navigateToScreen: can not resolve screen type mapping: ${screenType}`
|
|
396
|
-
);
|
|
397
|
-
|
|
398
|
-
return ActionResult.Error;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
const river = rivers[screenId];
|
|
402
|
-
|
|
403
|
-
if (!river) {
|
|
404
|
-
log_error("navigateToScreen: can not resolve river");
|
|
405
|
-
|
|
406
|
-
return ActionResult.Error;
|
|
407
|
-
}
|
|
194
|
+
ACTION_TYPES.NAVIGATE_TO_SCREEN,
|
|
195
|
+
createNavigateToScreenAction(navigator, rivers, contentTypes)
|
|
196
|
+
);
|
|
197
|
+
}, [navigator, rivers, contentTypes]);
|
|
408
198
|
|
|
409
|
-
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
return _actionExecutor.registerAction(
|
|
201
|
+
ACTION_TYPES.PRESENT_SCREEN,
|
|
202
|
+
createPresentScreenAction(presentScreen)
|
|
203
|
+
);
|
|
204
|
+
}, [presentScreen]);
|
|
410
205
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
return _actionExecutor.registerAction(
|
|
208
|
+
ACTION_TYPES.GO_BACK,
|
|
209
|
+
createGoBackAction(navigator)
|
|
210
|
+
);
|
|
211
|
+
}, [navigator]);
|
|
416
212
|
|
|
417
|
-
|
|
418
|
-
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
return _actionExecutor.registerAction(
|
|
215
|
+
ACTION_TYPES.GO_HOME,
|
|
216
|
+
createGoHomeAction(navigator)
|
|
419
217
|
);
|
|
420
|
-
}, [navigator
|
|
218
|
+
}, [navigator]);
|
|
421
219
|
|
|
422
220
|
return (
|
|
423
221
|
<ActionExecutorContext.Provider value={handlers}>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ActionExecutor, ActionResult } from "../ActionExecutor";
|
|
2
|
+
|
|
3
|
+
jest.mock("../../logger", () => ({
|
|
4
|
+
createLogger: () => ({
|
|
5
|
+
log_error: jest.fn(),
|
|
6
|
+
log_debug: jest.fn(),
|
|
7
|
+
log_info: jest.fn(),
|
|
8
|
+
}),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
describe("handleActions", () => {
|
|
12
|
+
const executor = () => {
|
|
13
|
+
const ran: string[] = [];
|
|
14
|
+
const actionExecutor = new ActionExecutor();
|
|
15
|
+
|
|
16
|
+
const register = (type: string, result?: ActionResult) =>
|
|
17
|
+
actionExecutor.registerAction(type, async () => {
|
|
18
|
+
ran.push(type);
|
|
19
|
+
|
|
20
|
+
return result as ActionResult;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
register("ok", ActionResult.Success);
|
|
24
|
+
register("fails", ActionResult.Error);
|
|
25
|
+
register("cancels", ActionResult.Cancel);
|
|
26
|
+
register("recover", ActionResult.Success);
|
|
27
|
+
register("after", ActionResult.Success);
|
|
28
|
+
|
|
29
|
+
return { actionExecutor, ran };
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Everything after a failed step was written for a state that never
|
|
33
|
+
// happened: the event was refused, the screen never opened.
|
|
34
|
+
it("stops when an action fails", async () => {
|
|
35
|
+
const { actionExecutor, ran } = executor();
|
|
36
|
+
|
|
37
|
+
const result = await actionExecutor.handleActions([
|
|
38
|
+
{ type: "fails" },
|
|
39
|
+
{ type: "after" },
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
expect(ran).toEqual(["fails"]);
|
|
43
|
+
expect(result).toBe(ActionResult.Error);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("still stops on a cancel", async () => {
|
|
47
|
+
const { actionExecutor, ran } = executor();
|
|
48
|
+
|
|
49
|
+
const result = await actionExecutor.handleActions([
|
|
50
|
+
{ type: "cancels" },
|
|
51
|
+
{ type: "after" },
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
expect(ran).toEqual(["cancels"]);
|
|
55
|
+
expect(result).toBe(ActionResult.Cancel);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// An action nobody registered never ran, which is a misconfigured feed
|
|
59
|
+
// rather than a step that failed. It is stepped over, as it always was.
|
|
60
|
+
it("steps over an action with no handler", async () => {
|
|
61
|
+
const { actionExecutor, ran } = executor();
|
|
62
|
+
|
|
63
|
+
const result = await actionExecutor.handleActions([
|
|
64
|
+
{ type: "nobody-registered-this" },
|
|
65
|
+
{ type: "after" },
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
expect(ran).toEqual(["after"]);
|
|
69
|
+
expect(result).toBe(ActionResult.Success);
|
|
70
|
+
});
|
|
71
|
+
});
|