@applicaster/zapp-react-native-utils 16.0.0-alpha.6861239588 → 16.0.0-alpha.6918106967
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 +40 -2
- package/actionsExecutor/ActionExecutorContext.tsx +96 -17
- 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 +212 -0
- package/actionsExecutor/actions/__tests__/presentScreen.test.ts +127 -0
- package/actionsExecutor/actions/__tests__/showToast.test.ts +88 -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 +10 -0
- package/actionsExecutor/actions/navigateToScreen.ts +18 -4
- package/actionsExecutor/actions/openBottomSheet.ts +325 -0
- package/actionsExecutor/actions/presentScreen.ts +78 -0
- package/actionsExecutor/actions/refreshComponent.ts +6 -0
- package/actionsExecutor/actions/sendCloudEvent.ts +6 -1
- package/actionsExecutor/actions/showToast.ts +64 -8
- package/actionsExecutor/consts.ts +26 -0
- package/actionsExecutor/feedDecorator.ts +6 -6
- package/actionsExecutor/types.ts +1 -1
- package/analyticsUtils/PlayerAnalyticsManager.ts +1 -1
- package/appUtils/HooksManager/index.ts +9 -0
- package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +143 -53
- package/appUtils/playerManager/OverlayObserver/__tests__/liveContent.test.ts +205 -0
- package/appUtils/playerManager/OverlayObserver/__tests__/utils.test.ts +49 -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 +11 -0
- package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.web.ts +4 -0
- package/appUtils/playerManager/OverlayObserver/utils.ts +54 -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 +50 -0
- package/arrayUtils/__tests__/sample.test.ts +61 -0
- package/arrayUtils/index.ts +136 -20
- package/bottomSheet/__tests__/scrollContext.test.tsx +35 -0
- package/bottomSheet/index.ts +17 -0
- package/cellUtils/index.ts +30 -3
- package/configurationUtils/__tests__/manifestKeyParser.test.ts +21 -0
- package/configurationUtils/__tests__/modalBlocksParser.test.ts +260 -0
- package/configurationUtils/manifestKeyParser.ts +50 -10
- package/configurationUtils/modalBlocksParser.ts +296 -0
- package/entryActions/__tests__/buildEntryActions.test.ts +156 -0
- package/entryActions/aliasPresentation.ts +124 -0
- package/entryActions/buildEntryActions.ts +166 -0
- package/entryActions/index.ts +17 -0
- package/entryActions/types.ts +39 -0
- package/manifestUtils/__tests__/player.audioControls.test.js +158 -0
- package/manifestUtils/_internals/index.js +8 -3
- package/manifestUtils/defaultManifestConfigurations/player.js +349 -20
- package/manifestUtils/index.js +3 -0
- package/manifestUtils/modalBlocks.js +304 -0
- package/modalState/ContentViewModel.ts +113 -0
- package/modalState/EditableCollection.ts +17 -0
- package/modalState/EditableCollectionRegistry.ts +51 -0
- package/modalState/ModalOrchestrator.ts +211 -0
- package/modalState/RemoteEditableCollection.ts +227 -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 +47 -0
- package/modalState/__tests__/RemoteEditableCollection.test.ts +326 -0
- package/modalState/__tests__/index.test.ts +30 -1
- package/modalState/__tests__/useBottomSheetContent.test.ts +349 -0
- package/modalState/index.ts +35 -83
- package/modalState/store.ts +102 -0
- package/modalState/types.ts +159 -0
- package/modalState/useBottomSheetContent.ts +104 -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/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/reactHooks/actions/index.ts +67 -2
- package/reactHooks/feed/__tests__/useInflatedUrl.test.tsx +25 -0
- package/reactHooks/index.ts +2 -0
- package/reactHooks/layout/index.ts +1 -1
- package/reactHooks/navigation/__tests__/index.test.tsx +48 -0
- package/reactHooks/navigation/__tests__/usePresentScreen.test.ts +154 -0
- package/reactHooks/navigation/index.ts +2 -0
- package/reactHooks/navigation/usePresentScreen.ts +157 -0
- package/reactHooks/navigation/useRoute.ts +14 -1
- 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 +11 -1
- 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 -203
- 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/actions/__tests__/{index.test.js → index.test.tsx} +0 -0
|
@@ -12,7 +12,7 @@ const { log_error, log_debug, log_info } = createLogger({
|
|
|
12
12
|
category: "General",
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
class ActionExecutor {
|
|
15
|
+
export class ActionExecutor {
|
|
16
16
|
private registeredActions: Record<string, ActionHandler> = {};
|
|
17
17
|
|
|
18
18
|
// TODO: Remove context
|
|
@@ -101,7 +101,8 @@ class ActionExecutor {
|
|
|
101
101
|
|
|
102
102
|
registerAction = <TOptions = Record<string, any>>(
|
|
103
103
|
type: string,
|
|
104
|
-
handler: ActionHandler<TOptions
|
|
104
|
+
handler: ActionHandler<TOptions>,
|
|
105
|
+
stateResolver?: (entry?: ZappEntry) => any
|
|
105
106
|
) => {
|
|
106
107
|
if (this.registeredActions[type]) {
|
|
107
108
|
log_error(`registerAction: action type: ${type} already registered`);
|
|
@@ -112,11 +113,48 @@ class ActionExecutor {
|
|
|
112
113
|
this.registeredActions[type] = handler;
|
|
113
114
|
log_debug(`registerAction: action type: ${type} registered`);
|
|
114
115
|
|
|
116
|
+
let unregisterState = () => {};
|
|
117
|
+
|
|
118
|
+
if (stateResolver) {
|
|
119
|
+
unregisterState = this.registerActionState(type, stateResolver);
|
|
120
|
+
}
|
|
121
|
+
|
|
115
122
|
return () => {
|
|
116
123
|
this.unregisterAction(type);
|
|
124
|
+
unregisterState();
|
|
117
125
|
log_debug(`registerAction: action type: ${type} un-registered`);
|
|
118
126
|
};
|
|
119
127
|
};
|
|
128
|
+
|
|
129
|
+
private actionStates: Record<string, (entry?: ZappEntry) => any> = {};
|
|
130
|
+
|
|
131
|
+
registerActionState = (
|
|
132
|
+
type: string,
|
|
133
|
+
stateResolver: (entry?: ZappEntry) => any
|
|
134
|
+
) => {
|
|
135
|
+
if (this.actionStates[type]) {
|
|
136
|
+
log_error(
|
|
137
|
+
`registerActionState: action type state: ${type} already registered`
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
return () => {};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
this.actionStates[type] = stateResolver;
|
|
144
|
+
log_debug(`registerActionState: action type state: ${type} registered`);
|
|
145
|
+
|
|
146
|
+
return () => {
|
|
147
|
+
delete this.actionStates[type];
|
|
148
|
+
|
|
149
|
+
log_debug(
|
|
150
|
+
`registerActionState: action type state: ${type} un-registered`
|
|
151
|
+
);
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
getActionState = (type: string, entry?: ZappEntry) => {
|
|
156
|
+
return this.actionStates[type]?.(entry);
|
|
157
|
+
};
|
|
120
158
|
}
|
|
121
159
|
|
|
122
160
|
export const actionExecutor = new ActionExecutor();
|
|
@@ -9,7 +9,7 @@ import { ActionExecutionContext, ActionHandler } from "./types";
|
|
|
9
9
|
import { batchRemoveAllFromNamespaceForStorage } from "../zappFrameworkUtils/localStorageHelper";
|
|
10
10
|
import { sessionStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/SessionStorage";
|
|
11
11
|
|
|
12
|
-
import { useNavigation, useRivers } from "../reactHooks";
|
|
12
|
+
import { useNavigation, usePresentScreen, useRivers } from "../reactHooks";
|
|
13
13
|
import { useContentTypes } from "@applicaster/zapp-react-native-redux/hooks";
|
|
14
14
|
import { useSubscriberFor } from "../reactHooks/useSubscriberFor";
|
|
15
15
|
import { APP_EVENTS } from "../appUtils/events";
|
|
@@ -18,10 +18,15 @@ import { createLogger } from "../logger";
|
|
|
18
18
|
import {
|
|
19
19
|
appRestartAction,
|
|
20
20
|
confirmDialogAction,
|
|
21
|
+
createGoBackAction,
|
|
22
|
+
createGoHomeAction,
|
|
21
23
|
createNavigateToScreenAction,
|
|
24
|
+
dismissBottomSheetAction,
|
|
22
25
|
localStorageRemoveAction,
|
|
23
26
|
localStorageSetAction,
|
|
24
27
|
localStorageToggleFlagAction,
|
|
28
|
+
createPresentScreenAction,
|
|
29
|
+
openBottomSheetAction,
|
|
25
30
|
refreshComponentAction,
|
|
26
31
|
screenSetVariableAction,
|
|
27
32
|
screenToggleFlagAction,
|
|
@@ -33,6 +38,8 @@ import {
|
|
|
33
38
|
showToastAction,
|
|
34
39
|
} from "./actions";
|
|
35
40
|
|
|
41
|
+
import { ACTION_TYPES } from "./consts";
|
|
42
|
+
|
|
36
43
|
export const { log_error, log_info, log_debug } = createLogger({
|
|
37
44
|
subsystem: "ActionExecutorContext",
|
|
38
45
|
category: "General",
|
|
@@ -41,9 +48,15 @@ export const { log_error, log_info, log_debug } = createLogger({
|
|
|
41
48
|
export type ActionExecutorContextType = {
|
|
42
49
|
registerAction: <TOptions = Record<string, any>>(
|
|
43
50
|
type: string,
|
|
44
|
-
handler: ActionHandler<TOptions
|
|
51
|
+
handler: ActionHandler<TOptions>,
|
|
52
|
+
stateResolver?: (entry?: ZappEntry) => any
|
|
45
53
|
) => void;
|
|
46
54
|
unregisterAction: (type: string) => void;
|
|
55
|
+
registerActionState: (
|
|
56
|
+
type: string,
|
|
57
|
+
stateResolver: (entry?: ZappEntry) => any
|
|
58
|
+
) => () => void;
|
|
59
|
+
getActionState: (type: string, entry?: ZappEntry) => any;
|
|
47
60
|
handleAction: (
|
|
48
61
|
action: ActionType,
|
|
49
62
|
context?: ActionExecutionContext
|
|
@@ -63,34 +76,76 @@ type Props = {
|
|
|
63
76
|
};
|
|
64
77
|
|
|
65
78
|
const prepareDefaultActions = (actionExecutor) => {
|
|
66
|
-
actionExecutor.registerAction(
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
actionExecutor.registerAction(
|
|
80
|
+
ACTION_TYPES.LOCAL_STORAGE_SET,
|
|
81
|
+
localStorageSetAction
|
|
82
|
+
);
|
|
69
83
|
|
|
70
84
|
actionExecutor.registerAction(
|
|
71
|
-
|
|
85
|
+
ACTION_TYPES.SESSION_STORAGE_SET,
|
|
86
|
+
sessionStorageSetAction
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
actionExecutor.registerAction(
|
|
90
|
+
ACTION_TYPES.LOCAL_STORAGE_REMOVE,
|
|
91
|
+
localStorageRemoveAction
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
actionExecutor.registerAction(
|
|
95
|
+
ACTION_TYPES.SESSION_STORAGE_REMOVE,
|
|
72
96
|
sessionStorageRemoveAction
|
|
73
97
|
);
|
|
74
98
|
|
|
75
|
-
actionExecutor.registerAction(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
99
|
+
actionExecutor.registerAction(
|
|
100
|
+
ACTION_TYPES.REFRESH_COMPONENT,
|
|
101
|
+
refreshComponentAction
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
actionExecutor.registerAction(ACTION_TYPES.SWITCH_LAYOUT, switchLayoutAction);
|
|
105
|
+
|
|
106
|
+
actionExecutor.registerAction(ACTION_TYPES.APP_RESTART, appRestartAction);
|
|
107
|
+
|
|
108
|
+
actionExecutor.registerAction(
|
|
109
|
+
ACTION_TYPES.CONFIRM_DIALOG,
|
|
110
|
+
confirmDialogAction
|
|
111
|
+
);
|
|
80
112
|
|
|
81
113
|
actionExecutor.registerAction(
|
|
82
|
-
|
|
114
|
+
ACTION_TYPES.SEND_CLOUD_EVENT,
|
|
115
|
+
sendCloudEventAction
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
actionExecutor.registerAction(
|
|
119
|
+
ACTION_TYPES.SESSION_STORAGE_TOGGLE_FLAG,
|
|
83
120
|
sessionStorageToggleFlagAction
|
|
84
121
|
);
|
|
85
122
|
|
|
86
123
|
actionExecutor.registerAction(
|
|
87
|
-
|
|
124
|
+
ACTION_TYPES.LOCAL_STORAGE_TOGGLE_FLAG,
|
|
88
125
|
localStorageToggleFlagAction
|
|
89
126
|
);
|
|
90
127
|
|
|
91
|
-
actionExecutor.registerAction(
|
|
92
|
-
|
|
93
|
-
|
|
128
|
+
actionExecutor.registerAction(
|
|
129
|
+
ACTION_TYPES.SCREEN_SET_VARIABLE,
|
|
130
|
+
screenSetVariableAction
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
actionExecutor.registerAction(
|
|
134
|
+
ACTION_TYPES.SCREEN_TOGGLE_FLAG,
|
|
135
|
+
screenToggleFlagAction
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
actionExecutor.registerAction(
|
|
139
|
+
ACTION_TYPES.OPEN_BOTTOM_SHEET,
|
|
140
|
+
openBottomSheetAction
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
actionExecutor.registerAction(
|
|
144
|
+
ACTION_TYPES.DISMISS_BOTTOM_SHEET,
|
|
145
|
+
dismissBottomSheetAction
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
actionExecutor.registerAction(ACTION_TYPES.SHOW_TOAST, showToastAction);
|
|
94
149
|
};
|
|
95
150
|
|
|
96
151
|
export const ActionExecutorContext =
|
|
@@ -103,11 +158,14 @@ export function withActionExecutor(Component) {
|
|
|
103
158
|
const navigator = useNavigation();
|
|
104
159
|
const rivers = useRivers();
|
|
105
160
|
const contentTypes = useContentTypes();
|
|
161
|
+
const presentScreen = usePresentScreen();
|
|
106
162
|
|
|
107
163
|
const handlers = useMemo(() => {
|
|
108
164
|
return {
|
|
109
165
|
unregisterAction: _actionExecutor.unregisterAction,
|
|
110
166
|
registerAction: _actionExecutor.registerAction,
|
|
167
|
+
registerActionState: _actionExecutor.registerActionState,
|
|
168
|
+
getActionState: _actionExecutor.getActionState,
|
|
111
169
|
handleAction: _actionExecutor.handleAction,
|
|
112
170
|
handleActions: _actionExecutor.handleActions,
|
|
113
171
|
handleEntryActions: _actionExecutor.handleEntryActions,
|
|
@@ -130,11 +188,32 @@ export function withActionExecutor(Component) {
|
|
|
130
188
|
|
|
131
189
|
useEffect(() => {
|
|
132
190
|
return _actionExecutor.registerAction(
|
|
133
|
-
|
|
191
|
+
ACTION_TYPES.NAVIGATE_TO_SCREEN,
|
|
134
192
|
createNavigateToScreenAction(navigator, rivers, contentTypes)
|
|
135
193
|
);
|
|
136
194
|
}, [navigator, rivers, contentTypes]);
|
|
137
195
|
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
return _actionExecutor.registerAction(
|
|
198
|
+
ACTION_TYPES.PRESENT_SCREEN,
|
|
199
|
+
createPresentScreenAction(presentScreen)
|
|
200
|
+
);
|
|
201
|
+
}, [presentScreen]);
|
|
202
|
+
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
return _actionExecutor.registerAction(
|
|
205
|
+
ACTION_TYPES.GO_BACK,
|
|
206
|
+
createGoBackAction(navigator)
|
|
207
|
+
);
|
|
208
|
+
}, [navigator]);
|
|
209
|
+
|
|
210
|
+
useEffect(() => {
|
|
211
|
+
return _actionExecutor.registerAction(
|
|
212
|
+
ACTION_TYPES.GO_HOME,
|
|
213
|
+
createGoHomeAction(navigator)
|
|
214
|
+
);
|
|
215
|
+
}, [navigator]);
|
|
216
|
+
|
|
138
217
|
return (
|
|
139
218
|
<ActionExecutorContext.Provider value={handlers}>
|
|
140
219
|
<Component {...props} />
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { decorateFeed } from "../feedDecorator";
|
|
2
|
+
|
|
3
|
+
const preferenceFeed = (selectMode: string) =>
|
|
4
|
+
({
|
|
5
|
+
id: "agree-component",
|
|
6
|
+
title: "I agree to the terms",
|
|
7
|
+
type: { value: "feed" },
|
|
8
|
+
extensions: {
|
|
9
|
+
role: "preference_editor",
|
|
10
|
+
preference_editor_options: {
|
|
11
|
+
key: "agree",
|
|
12
|
+
scope: "screen",
|
|
13
|
+
select_mode: selectMode,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
entry: [
|
|
17
|
+
{
|
|
18
|
+
id: "I agree to the terms-agree",
|
|
19
|
+
title: "I agree to the terms",
|
|
20
|
+
type: { value: "action" },
|
|
21
|
+
extensions: { tag: "true" },
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
}) as unknown as ZappFeed;
|
|
25
|
+
|
|
26
|
+
describe("decorateFeed", () => {
|
|
27
|
+
it("decorates single select feeds with screenSetVariable", () => {
|
|
28
|
+
const decorated = decorateFeed(preferenceFeed("single"));
|
|
29
|
+
|
|
30
|
+
expect(decorated.extensions.behavior).toEqual(
|
|
31
|
+
expect.objectContaining({
|
|
32
|
+
select_mode: "single",
|
|
33
|
+
current_selection: "@{screen/agree}",
|
|
34
|
+
})
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
expect(decorated.entry[0].extensions.tap_actions.actions[0].type).toBe(
|
|
38
|
+
"screenSetVariable"
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("decorates multi select feeds with screenToggleFlag", () => {
|
|
43
|
+
const decorated = decorateFeed(preferenceFeed("multi"));
|
|
44
|
+
|
|
45
|
+
expect(decorated.extensions.behavior.select_mode).toBe("multi");
|
|
46
|
+
|
|
47
|
+
expect(decorated.entry[0].extensions.tap_actions.actions[0].type).toBe(
|
|
48
|
+
"screenToggleFlag"
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("leaves a feed without the preference_editor role untouched", () => {
|
|
53
|
+
const feed = preferenceFeed("single");
|
|
54
|
+
feed.extensions.role = undefined;
|
|
55
|
+
|
|
56
|
+
const decorated = decorateFeed(feed);
|
|
57
|
+
|
|
58
|
+
expect(decorated.extensions.behavior).toBeUndefined();
|
|
59
|
+
expect(decorated.entry[0].extensions.tap_actions).toBeUndefined();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { dismissBottomSheetAction } from "../dismissBottomSheet";
|
|
2
|
+
import { dismissModal } from "../../../modalState";
|
|
3
|
+
import { ActionResult } from "../../ActionExecutor";
|
|
4
|
+
|
|
5
|
+
jest.mock("../../../modalState", () => ({
|
|
6
|
+
dismissModal: jest.fn(),
|
|
7
|
+
modalOrchestrator: {
|
|
8
|
+
close: jest.fn(),
|
|
9
|
+
},
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
describe("dismissBottomSheetAction", () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("calls dismissModal and returns ActionResult.Success", async () => {
|
|
18
|
+
const result = await dismissBottomSheetAction({
|
|
19
|
+
type: "dismissBottomSheet",
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
expect(dismissModal).toHaveBeenCalledTimes(1);
|
|
23
|
+
expect(result).toBe(ActionResult.Success);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createGoBackAction } from "../goBack";
|
|
2
|
+
import { ActionResult } from "../../ActionExecutor";
|
|
3
|
+
|
|
4
|
+
function createNavigator(canGoBack = true) {
|
|
5
|
+
return {
|
|
6
|
+
goBack: jest.fn(),
|
|
7
|
+
canGoBack: jest.fn().mockReturnValue(canGoBack),
|
|
8
|
+
} as unknown as QuickBrickAppNavigator;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe("goBack", () => {
|
|
12
|
+
it("goes back with fallbackToHome enabled when no options are provided", async () => {
|
|
13
|
+
const navigator = createNavigator();
|
|
14
|
+
|
|
15
|
+
const result = await createGoBackAction(navigator)({ type: "goBack" });
|
|
16
|
+
|
|
17
|
+
expect(navigator.goBack).toHaveBeenCalledWith(true, false, false);
|
|
18
|
+
expect(result).toBe(ActionResult.Success);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("forwards backToTop", async () => {
|
|
22
|
+
const navigator = createNavigator();
|
|
23
|
+
|
|
24
|
+
const action = {
|
|
25
|
+
type: "goBack",
|
|
26
|
+
options: { backToTop: true },
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const result = await createGoBackAction(navigator)(action);
|
|
30
|
+
|
|
31
|
+
expect(navigator.goBack).toHaveBeenCalledWith(true, false, true);
|
|
32
|
+
expect(result).toBe(ActionResult.Success);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("goes back without falling back to home when the stack allows it", async () => {
|
|
36
|
+
const navigator = createNavigator(true);
|
|
37
|
+
|
|
38
|
+
const action = {
|
|
39
|
+
type: "goBack",
|
|
40
|
+
options: { fallbackToHome: false },
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const result = await createGoBackAction(navigator)(action);
|
|
44
|
+
|
|
45
|
+
expect(navigator.goBack).toHaveBeenCalledWith(false, false, false);
|
|
46
|
+
expect(result).toBe(ActionResult.Success);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("does nothing when there is nothing to go back to and fallbackToHome is off", async () => {
|
|
50
|
+
const navigator = createNavigator(false);
|
|
51
|
+
|
|
52
|
+
const action = {
|
|
53
|
+
type: "goBack",
|
|
54
|
+
options: { fallbackToHome: false },
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const result = await createGoBackAction(navigator)(action);
|
|
58
|
+
|
|
59
|
+
expect(navigator.goBack).not.toHaveBeenCalled();
|
|
60
|
+
expect(result).toBe(ActionResult.Error);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createGoHomeAction } from "../goHome";
|
|
2
|
+
import { ActionResult } from "../../ActionExecutor";
|
|
3
|
+
|
|
4
|
+
function createNavigator() {
|
|
5
|
+
return {
|
|
6
|
+
goHome: jest.fn(),
|
|
7
|
+
} as unknown as QuickBrickAppNavigator;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe("goHome", () => {
|
|
11
|
+
it("navigates to the home screen", async () => {
|
|
12
|
+
const navigator = createNavigator();
|
|
13
|
+
|
|
14
|
+
const result = await createGoHomeAction(navigator)({ type: "goHome" });
|
|
15
|
+
|
|
16
|
+
expect(navigator.goHome).toHaveBeenCalledWith();
|
|
17
|
+
expect(result).toBe(ActionResult.Success);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { loadItemsFromSource } from "../openBottomSheet";
|
|
2
|
+
|
|
3
|
+
const mockCall = jest.fn();
|
|
4
|
+
|
|
5
|
+
jest.mock("@applicaster/zapp-pipes-v2-client", () => ({
|
|
6
|
+
RequestBuilder: jest.fn().mockImplementation(() => ({
|
|
7
|
+
setEntryContext: jest.fn().mockReturnThis(),
|
|
8
|
+
setScreenContext: jest.fn().mockReturnThis(),
|
|
9
|
+
setUrl: jest.fn().mockReturnThis(),
|
|
10
|
+
buildAxiosRequest: jest.fn().mockResolvedValue(undefined),
|
|
11
|
+
call: (...args: any[]) => mockCall(...args),
|
|
12
|
+
})),
|
|
13
|
+
PipesClientResponseHelper: jest.fn().mockImplementation(() => ({
|
|
14
|
+
error: null,
|
|
15
|
+
})),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
describe("loadItemsFromSource", () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
jest.clearAllMocks();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("returns items plus role and behavior from feed.extensions", async () => {
|
|
24
|
+
mockCall.mockResolvedValueOnce({
|
|
25
|
+
response: {
|
|
26
|
+
entry: [{ id: "a", title: "Playlist A", media_group: [] }],
|
|
27
|
+
extensions: {
|
|
28
|
+
role: "collection_selector",
|
|
29
|
+
behavior: {
|
|
30
|
+
select_mode: "multi",
|
|
31
|
+
current_selection: ["a"],
|
|
32
|
+
selector: "extensions.tag",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const result = await loadItemsFromSource("https://example.com/feed");
|
|
39
|
+
|
|
40
|
+
expect(result.role).toBe("collection_selector");
|
|
41
|
+
|
|
42
|
+
expect(result.behavior).toEqual({
|
|
43
|
+
selectMode: "multi",
|
|
44
|
+
currentSelection: ["a"],
|
|
45
|
+
selector: "extensions.tag",
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(result.items).toHaveLength(1);
|
|
49
|
+
expect(result.items[0].isSelected).toBe(true);
|
|
50
|
+
expect(result.items[0].title).toBe("Playlist A");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("returns undefined role/behavior when feed has no extensions", async () => {
|
|
54
|
+
mockCall.mockResolvedValueOnce({
|
|
55
|
+
response: {
|
|
56
|
+
entry: [{ id: "1", title: "Plain", media_group: [] }],
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const result = await loadItemsFromSource("https://example.com/plain");
|
|
61
|
+
|
|
62
|
+
expect(result.role).toBeUndefined();
|
|
63
|
+
expect(result.behavior).toBeUndefined();
|
|
64
|
+
expect(result.items).toHaveLength(1);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("preserves events in dynamic_collection_options", async () => {
|
|
68
|
+
mockCall.mockResolvedValueOnce({
|
|
69
|
+
response: {
|
|
70
|
+
entry: [],
|
|
71
|
+
extensions: {
|
|
72
|
+
dynamic_collection_options: {
|
|
73
|
+
operations: "add,remove",
|
|
74
|
+
events: {
|
|
75
|
+
add: [{ type: "showTextInput", options: {} }],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const result = await loadItemsFromSource("https://example.com/feed");
|
|
83
|
+
|
|
84
|
+
expect(result.dynamicCollection?.events).toEqual({
|
|
85
|
+
add: [{ type: "showTextInput", options: {} }],
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { createNavigateToScreenAction } from "../navigateToScreen";
|
|
2
|
+
import { ActionResult } from "../../ActionExecutor";
|
|
3
|
+
|
|
4
|
+
const river = { id: "screen-id", type: "parent-lock" } as unknown as ZappRiver;
|
|
5
|
+
|
|
6
|
+
const rivers = { "screen-id": river } as Record<string, ZappRiver>;
|
|
7
|
+
|
|
8
|
+
const contentTypes = {
|
|
9
|
+
parentLock: { screen_id: "screen-id" },
|
|
10
|
+
} as unknown as ZappContentTypes;
|
|
11
|
+
|
|
12
|
+
function createNavigator() {
|
|
13
|
+
return {
|
|
14
|
+
push: jest.fn(),
|
|
15
|
+
replace: jest.fn(),
|
|
16
|
+
replaceTop: jest.fn(),
|
|
17
|
+
} as unknown as QuickBrickAppNavigator;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe("navigateToScreen navigationOptions", () => {
|
|
21
|
+
it("forwards navigationOptions to push when navigating to a screen", async () => {
|
|
22
|
+
const navigator = createNavigator();
|
|
23
|
+
|
|
24
|
+
const action = {
|
|
25
|
+
type: "navigateToScreen",
|
|
26
|
+
options: {
|
|
27
|
+
typeMapping: "parentLock",
|
|
28
|
+
navigationAction: "push",
|
|
29
|
+
navigationOptions: { pinFlow: "set-pin" },
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const result = await createNavigateToScreenAction(
|
|
34
|
+
navigator,
|
|
35
|
+
rivers,
|
|
36
|
+
contentTypes
|
|
37
|
+
)(action);
|
|
38
|
+
|
|
39
|
+
expect(navigator.push).toHaveBeenCalledWith(river, {
|
|
40
|
+
pinFlow: "set-pin",
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
expect(result).toBe(ActionResult.Success);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("forwards navigationOptions to replace when navigating to a screen", async () => {
|
|
47
|
+
const navigator = createNavigator();
|
|
48
|
+
|
|
49
|
+
const action = {
|
|
50
|
+
type: "navigateToScreen",
|
|
51
|
+
options: {
|
|
52
|
+
typeMapping: "parentLock",
|
|
53
|
+
navigationOptions: { pinFlow: "change-pin" },
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
await createNavigateToScreenAction(navigator, rivers, contentTypes)(action);
|
|
58
|
+
|
|
59
|
+
expect(navigator.replace).toHaveBeenCalledWith(river, {
|
|
60
|
+
pinFlow: "change-pin",
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("forwards navigationOptions to push when navigating with an entry", async () => {
|
|
65
|
+
const navigator = createNavigator();
|
|
66
|
+
const entry = { id: "entry-id" } as unknown as ZappEntry;
|
|
67
|
+
|
|
68
|
+
const action = {
|
|
69
|
+
type: "navigateToScreen",
|
|
70
|
+
options: {
|
|
71
|
+
typeMapping: "parentLock",
|
|
72
|
+
navigationAction: "push",
|
|
73
|
+
entry,
|
|
74
|
+
navigationOptions: { pinFlow: "set-pin" },
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
await createNavigateToScreenAction(navigator, rivers, contentTypes)(action);
|
|
79
|
+
|
|
80
|
+
expect(navigator.push).toHaveBeenCalledWith(
|
|
81
|
+
{ id: "entry-id", type: { value: "parentLock" } },
|
|
82
|
+
{ pinFlow: "set-pin" }
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Entries cannot go through replace(), which only accepts a river, so the
|
|
87
|
+
// non-push path falls back to replaceTop().
|
|
88
|
+
it("forwards navigationOptions to replaceTop when navigating with an entry", async () => {
|
|
89
|
+
const navigator = createNavigator();
|
|
90
|
+
const entry = { id: "entry-id" } as unknown as ZappEntry;
|
|
91
|
+
|
|
92
|
+
const action = {
|
|
93
|
+
type: "navigateToScreen",
|
|
94
|
+
options: {
|
|
95
|
+
typeMapping: "parentLock",
|
|
96
|
+
entry,
|
|
97
|
+
navigationOptions: { pinFlow: "change-pin" },
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
await createNavigateToScreenAction(navigator, rivers, contentTypes)(action);
|
|
102
|
+
|
|
103
|
+
expect(navigator.replaceTop).toHaveBeenCalledWith(
|
|
104
|
+
{ id: "entry-id", type: { value: "parentLock" } },
|
|
105
|
+
{ pinFlow: "change-pin" }
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
expect(navigator.replace).not.toHaveBeenCalled();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Regression guard: the no-options path must keep working unchanged.
|
|
112
|
+
it("still navigates when no navigationOptions are configured", async () => {
|
|
113
|
+
const navigator = createNavigator();
|
|
114
|
+
|
|
115
|
+
const action = {
|
|
116
|
+
type: "navigateToScreen",
|
|
117
|
+
options: {
|
|
118
|
+
typeMapping: "parentLock",
|
|
119
|
+
navigationAction: "push",
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const result = await createNavigateToScreenAction(
|
|
124
|
+
navigator,
|
|
125
|
+
rivers,
|
|
126
|
+
contentTypes
|
|
127
|
+
)(action);
|
|
128
|
+
|
|
129
|
+
expect(navigator.push).toHaveBeenCalledTimes(1);
|
|
130
|
+
expect((navigator.push as jest.Mock).mock.calls[0][0]).toBe(river);
|
|
131
|
+
expect(result).toBe(ActionResult.Success);
|
|
132
|
+
});
|
|
133
|
+
});
|