@applicaster/zapp-react-native-utils 16.0.0-alpha.1263110541 → 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.
- package/actionUtils/index.ts +3 -1
- package/actionsExecutor/ActionExecutor.ts +10 -1
- package/actionsExecutor/ActionExecutorContext.tsx +13 -1
- package/actionsExecutor/__tests__/ActionExecutor.test.ts +71 -0
- package/actionsExecutor/__tests__/StorageActions.test.ts +98 -0
- package/actionsExecutor/__tests__/feedDecorator.test.ts +30 -0
- package/actionsExecutor/__tests__/screenToggleFlag.test.ts +90 -0
- package/actionsExecutor/actions/__tests__/dismissBottomSheet.test.ts +3 -0
- package/actionsExecutor/actions/__tests__/navigateToScreen.test.ts +186 -5
- package/actionsExecutor/actions/__tests__/openBottomSheet.customContent.test.ts +24 -0
- package/actionsExecutor/actions/__tests__/openBottomSheet.inlineItems.test.ts +273 -1
- 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 +2 -2
- package/actionsExecutor/actions/dismissBottomSheet.ts +2 -1
- package/actionsExecutor/actions/index.ts +4 -0
- package/actionsExecutor/actions/navigateToScreen.ts +33 -43
- package/actionsExecutor/actions/openBottomSheet.ts +36 -16
- package/actionsExecutor/actions/presentScreen.ts +78 -0
- package/actionsExecutor/actions/screenToggleFlag.ts +1 -3
- package/actionsExecutor/actions/showAlert.ts +51 -0
- package/actionsExecutor/actions/showToast.ts +2 -2
- package/actionsExecutor/consts.ts +2 -0
- package/actionsExecutor/feedDecorator.ts +6 -0
- package/actionsExecutor/types.ts +7 -0
- package/appUtils/contextKeysManager/__tests__/getKey/failure.test.ts +1 -22
- package/appUtils/contextKeysManager/__tests__/getKey/success/fullSearch.test.ts +1 -25
- package/appUtils/contextKeysManager/__tests__/getKey/success/searchInSecure.test.ts +1 -7
- package/appUtils/contextKeysManager/__tests__/removeKey/failure.test.ts +0 -25
- package/appUtils/contextKeysManager/__tests__/removeKey/success.test.ts +0 -6
- package/appUtils/contextKeysManager/__tests__/setKey/failure/failedToSaveInStorage.test.ts +0 -40
- package/appUtils/contextKeysManager/__tests__/setKey/{success/saveItemAndReference.test.ts → success.test.ts} +2 -42
- package/appUtils/contextKeysManager/index.ts +28 -129
- package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +4 -11
- package/appUtils/playerManager/OverlayObserver/__tests__/getUpcomingQueue.test.ts +67 -0
- package/appUtils/playerManager/OverlayObserver/__tests__/utils.test.ts +9 -2
- package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.ts +15 -2
- package/appUtils/playerManager/OverlayObserver/utils.ts +2 -5
- package/appUtils/playerManager/__tests__/playerDiagnostics.test.ts +62 -0
- package/appUtils/playerManager/__tests__/playerNative.test.ts +76 -0
- package/appUtils/playerManager/__tests__/playerNativeCommand.test.ts +62 -0
- package/appUtils/playerManager/__tests__/playerToolbox.test.ts +281 -0
- package/appUtils/playerManager/__tests__/usePlayerControllerSetup.test.tsx +62 -0
- package/appUtils/playerManager/const.ts +13 -0
- package/appUtils/playerManager/index.ts +7 -0
- package/appUtils/playerManager/player.ts +25 -0
- package/appUtils/playerManager/playerNative.ts +22 -0
- package/appUtils/playerManager/playerNativeCommand.ts +3 -6
- package/appUtils/playerManager/playerToolbox.ts +196 -0
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +6 -0
- package/arrayUtils/__tests__/reorderByIds.test.ts +13 -0
- package/arrayUtils/index.ts +26 -43
- package/cellUtils/__tests__/getButtonsEnabled.test.ts +28 -0
- package/cellUtils/__tests__/getCellNavigationEnabled.test.ts +36 -0
- package/cellUtils/__tests__/getIsCellNavigationEnabled.test.ts +35 -0
- package/cellUtils/__tests__/isCellNavigationEnabled.test.ts +21 -0
- package/cellUtils/index.ts +40 -0
- package/configurationUtils/__tests__/manifestKeyParser.test.ts +21 -0
- package/configurationUtils/__tests__/modalBlocksParser.test.ts +297 -1
- package/configurationUtils/manifestKeyParser.ts +12 -1
- package/configurationUtils/modalBlocksParser.ts +78 -4
- package/entryActions/__tests__/buildEntryActions.test.ts +97 -18
- package/entryActions/aliasPresentation.ts +26 -42
- package/entryActions/buildEntryActions.ts +1 -0
- package/entryActions/index.ts +0 -2
- package/focusManager/FocusManager.ts +26 -0
- package/focusManager/__tests__/FocusManager.test.ts +9 -0
- package/localizationUtils/index.ts +12 -6
- package/logger/__tests__/xrayToolbox.test.ts +69 -0
- package/logger/xrayToolbox.ts +67 -0
- package/manifestUtils/defaultManifestConfigurations/generalContent.js +6 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +8 -0
- package/manifestUtils/keys.js +7 -2
- package/manifestUtils/mobileAction/container/__tests__/mobileActionButtonsContainer.test.js +21 -0
- package/manifestUtils/mobileAction/groups/__tests__/buildMobileActionButtonGroups.test.js +1 -1
- package/manifestUtils/mobileAction/groups/defaults.js +2 -1
- package/modalState/ModalOrchestrator.ts +23 -9
- package/modalState/RemoteEditableCollection.ts +9 -1
- package/modalState/__tests__/ModalOrchestrator.phase3.test.ts +39 -0
- package/modalState/__tests__/RemoteEditableCollection.test.ts +90 -1
- package/modalState/__tests__/index.test.ts +30 -1
- package/modalState/__tests__/useModalStoreState.test.ts +142 -0
- package/modalState/index.ts +8 -1
- package/modalState/store.ts +9 -2
- package/modalState/types.ts +42 -2
- package/modalState/useBottomSheetContent.ts +24 -17
- package/navigationUtils/xrayNavigator.ts +12 -0
- package/package.json +3 -2
- package/pluginUtils/__tests__/pluginUtils.test.js +32 -0
- package/pluginUtils/index.ts +29 -0
- package/quickBrickAssets/__tests__/index.test.ts +18 -0
- package/quickBrickAssets/index.ts +35 -0
- package/reactHooks/__tests__/getPluginConfiguration.test.ts +82 -0
- package/reactHooks/actions/index.ts +29 -23
- package/reactHooks/cell-click/__tests__/index.test.js +131 -1
- package/reactHooks/cell-click/index.ts +30 -15
- package/reactHooks/connection/index.tsx +8 -5
- package/reactHooks/feed/__tests__/feedInvalidation.test.tsx +338 -0
- package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +8 -2
- package/reactHooks/feed/__tests__/useMarkPipesDataStale.test.tsx +243 -0
- package/reactHooks/feed/useFeedLoader.tsx +28 -1
- package/reactHooks/feed/useLoadPipesDataDispatch.ts +12 -11
- package/reactHooks/feed/useMarkPipesDataStale.ts +68 -23
- package/reactHooks/navigation/__tests__/index.test.tsx +176 -1
- 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 +22 -5
- package/reactHooks/usePluginConfiguration.ts +84 -46
- package/riverComponetsMeasurementProvider/index.tsx +3 -1
- package/uiActionsRegistrator/__tests__/subscribe.test.ts +26 -0
- package/uiActionsRegistrator/registry.ts +16 -0
- package/appUtils/contextKeysManager/__tests__/getKey/success/byReference.test.ts +0 -56
- package/appUtils/contextKeysManager/__tests__/getKeyByReference/failure.test.ts +0 -265
- package/appUtils/contextKeysManager/__tests__/getKeyByReference/success.test.ts +0 -303
- package/appUtils/contextKeysManager/__tests__/removeReferenceForKey/failure.test.ts +0 -82
- package/appUtils/contextKeysManager/__tests__/removeReferenceForKey/success.test.ts +0 -41
- package/appUtils/contextKeysManager/__tests__/setKey/success/saveItemButNotReference.test.ts +0 -208
- package/appUtils/contextKeysManager/__tests__/setReferenceForKey/failure.test.ts +0 -93
- package/appUtils/contextKeysManager/__tests__/setReferenceForKey/success.test.ts +0 -45
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
import { openBottomSheetAction } from "../openBottomSheet";
|
|
2
|
-
import { ActionResult } from "../../ActionExecutor";
|
|
2
|
+
import { actionExecutor, ActionResult } from "../../ActionExecutor";
|
|
3
3
|
import { modalOrchestrator } from "../../../modalState/ModalOrchestrator";
|
|
4
4
|
|
|
5
|
+
jest.mock("@applicaster/zapp-pipes-v2-client", () => ({
|
|
6
|
+
PipesClientResponseHelper: jest.fn(() => ({ error: null })),
|
|
7
|
+
RequestBuilder: jest.fn(() => ({
|
|
8
|
+
setEntryContext: jest.fn().mockReturnThis(),
|
|
9
|
+
setScreenContext: jest.fn().mockReturnThis(),
|
|
10
|
+
setUrl: jest.fn().mockReturnThis(),
|
|
11
|
+
buildAxiosRequest: jest.fn().mockResolvedValue(undefined),
|
|
12
|
+
call: jest.fn().mockResolvedValue({
|
|
13
|
+
response: {
|
|
14
|
+
entry: [],
|
|
15
|
+
extensions: { dynamic_collection_options: { operations: "add" } },
|
|
16
|
+
},
|
|
17
|
+
}),
|
|
18
|
+
})),
|
|
19
|
+
}));
|
|
20
|
+
|
|
5
21
|
jest.mock("../../../modalState/store", () => ({
|
|
6
22
|
openBottomSheetModal: jest.fn(),
|
|
7
23
|
}));
|
|
@@ -77,6 +93,150 @@ describe("openBottomSheetAction inline items", () => {
|
|
|
77
93
|
});
|
|
78
94
|
});
|
|
79
95
|
|
|
96
|
+
it("opens an empty inline collection when dynamic collection options are present", async () => {
|
|
97
|
+
const result = await openBottomSheetAction({
|
|
98
|
+
type: "openBottomSheet",
|
|
99
|
+
options: {
|
|
100
|
+
header: { title: "Queue" },
|
|
101
|
+
content: {
|
|
102
|
+
items: [],
|
|
103
|
+
emptyMessage: "Queued items will be added here",
|
|
104
|
+
dynamicCollection: {
|
|
105
|
+
operations: ["remove"],
|
|
106
|
+
provider: "@{provider/queue}",
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
} as any);
|
|
111
|
+
|
|
112
|
+
expect(result).toBe(ActionResult.Success);
|
|
113
|
+
|
|
114
|
+
const [menu] = (modalOrchestrator.open as jest.Mock).mock.calls[0];
|
|
115
|
+
|
|
116
|
+
expect(menu.content.items).toEqual([]);
|
|
117
|
+
expect(menu.content.emptyMessage).toBe("Queued items will be added here");
|
|
118
|
+
|
|
119
|
+
expect(menu.content.dynamicCollection).toMatchObject({
|
|
120
|
+
operations: ["remove"],
|
|
121
|
+
provider: "@{provider/queue}",
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("accepts empty inline items when dynamic_collection_options live on extensions", async () => {
|
|
126
|
+
const result = await openBottomSheetAction({
|
|
127
|
+
type: "openBottomSheet",
|
|
128
|
+
options: {
|
|
129
|
+
content: {
|
|
130
|
+
items: [],
|
|
131
|
+
extensions: {
|
|
132
|
+
dynamic_collection_options: {
|
|
133
|
+
operations: "remove,reorder",
|
|
134
|
+
provider: "@{provider/offline}",
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
} as any);
|
|
140
|
+
|
|
141
|
+
expect(result).toBe(ActionResult.Success);
|
|
142
|
+
|
|
143
|
+
const [menu] = (modalOrchestrator.open as jest.Mock).mock.calls[0];
|
|
144
|
+
|
|
145
|
+
expect(menu.content.items).toEqual([]);
|
|
146
|
+
|
|
147
|
+
expect(menu.content.dynamicCollection).toMatchObject({
|
|
148
|
+
operations: ["remove", "reorder"],
|
|
149
|
+
provider: "@{provider/offline}",
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("returns an error for an empty items array with no source", async () => {
|
|
154
|
+
const result = await openBottomSheetAction({
|
|
155
|
+
type: "openBottomSheet",
|
|
156
|
+
options: {
|
|
157
|
+
content: { items: [] },
|
|
158
|
+
},
|
|
159
|
+
} as any);
|
|
160
|
+
|
|
161
|
+
expect(result).toBe(ActionResult.Error);
|
|
162
|
+
expect(modalOrchestrator.open).not.toHaveBeenCalled();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("forwards content.emptyMessage onto the menu", async () => {
|
|
166
|
+
const result = await openBottomSheetAction({
|
|
167
|
+
type: "openBottomSheet",
|
|
168
|
+
options: {
|
|
169
|
+
header: { title: "Queue" },
|
|
170
|
+
content: {
|
|
171
|
+
items: [{ id: "t1", title: "Track 1" }],
|
|
172
|
+
emptyMessage: "Queued items will be added here",
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
} as any);
|
|
176
|
+
|
|
177
|
+
expect(result).toBe(ActionResult.Success);
|
|
178
|
+
|
|
179
|
+
const [menu] = (modalOrchestrator.open as jest.Mock).mock.calls[0];
|
|
180
|
+
|
|
181
|
+
expect(menu.content.emptyMessage).toBe("Queued items will be added here");
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("forwards content.operationButtons onto the menu", async () => {
|
|
185
|
+
const result = await openBottomSheetAction({
|
|
186
|
+
type: "openBottomSheet",
|
|
187
|
+
options: {
|
|
188
|
+
header: { title: "Playlists" },
|
|
189
|
+
content: {
|
|
190
|
+
items: [{ id: "p1", title: "Playlist 1" }],
|
|
191
|
+
operationButtons: {
|
|
192
|
+
add: {
|
|
193
|
+
title: "Create New Playlist",
|
|
194
|
+
iconURL: "https://example.com/add.png",
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
} as any);
|
|
200
|
+
|
|
201
|
+
expect(result).toBe(ActionResult.Success);
|
|
202
|
+
|
|
203
|
+
const [menu] = (modalOrchestrator.open as jest.Mock).mock.calls[0];
|
|
204
|
+
|
|
205
|
+
expect(menu.content.operationButtons).toEqual({
|
|
206
|
+
add: {
|
|
207
|
+
title: "Create New Playlist",
|
|
208
|
+
iconURL: "https://example.com/add.png",
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("preserves Add presentation when loading itemsUrl content", async () => {
|
|
214
|
+
const result = await openBottomSheetAction({
|
|
215
|
+
type: "openBottomSheet",
|
|
216
|
+
options: {
|
|
217
|
+
content: {
|
|
218
|
+
itemsUrl: "https://example.com/collections",
|
|
219
|
+
operationButtons: {
|
|
220
|
+
add: {
|
|
221
|
+
title: "Create Collection",
|
|
222
|
+
iconURL: "file:///bundled-add.png",
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
expect(result).toBe(ActionResult.Success);
|
|
230
|
+
const [menu] = (modalOrchestrator.open as jest.Mock).mock.calls[0];
|
|
231
|
+
|
|
232
|
+
expect(menu.content).toMatchObject({
|
|
233
|
+
operationButtons: {
|
|
234
|
+
add: { title: "Create Collection", iconURL: "file:///bundled-add.png" },
|
|
235
|
+
},
|
|
236
|
+
dynamicCollection: { operations: ["add"] },
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
80
240
|
it("pushes a new sheet by default even when another sheet is already open", async () => {
|
|
81
241
|
(modalOrchestrator as any).stackSize = 1;
|
|
82
242
|
|
|
@@ -97,4 +257,116 @@ describe("openBottomSheetAction inline items", () => {
|
|
|
97
257
|
expect(modalOrchestrator.replace).toHaveBeenCalledTimes(1);
|
|
98
258
|
expect(modalOrchestrator.open).not.toHaveBeenCalled();
|
|
99
259
|
});
|
|
260
|
+
|
|
261
|
+
it("executes all actions in tap_actions when onItemPress is called", async () => {
|
|
262
|
+
const multiActionSheet = {
|
|
263
|
+
type: "openBottomSheet",
|
|
264
|
+
options: {
|
|
265
|
+
header: { title: "Target Playlists" },
|
|
266
|
+
content: {
|
|
267
|
+
role: "collection_selector",
|
|
268
|
+
behavior: { selectMode: "none", currentSelection: [] },
|
|
269
|
+
items: [
|
|
270
|
+
{
|
|
271
|
+
id: "playlist-1",
|
|
272
|
+
title: "My Playlist",
|
|
273
|
+
extensions: {
|
|
274
|
+
tap_actions: {
|
|
275
|
+
actions: [
|
|
276
|
+
{
|
|
277
|
+
type: "sendCloudEvent",
|
|
278
|
+
options: { subject: "add_collection_to_collection" },
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
type: "showToast",
|
|
282
|
+
options: { message: "Added to playlist" },
|
|
283
|
+
},
|
|
284
|
+
{ type: "dismissBottomSheet", options: {} },
|
|
285
|
+
],
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
} as any;
|
|
293
|
+
|
|
294
|
+
await openBottomSheetAction(multiActionSheet);
|
|
295
|
+
|
|
296
|
+
const [menu, onItemPress] = (modalOrchestrator.open as jest.Mock).mock
|
|
297
|
+
.calls[0];
|
|
298
|
+
|
|
299
|
+
expect(menu.content.items[0].actions).toHaveLength(3);
|
|
300
|
+
|
|
301
|
+
const handleActionsSpy = jest
|
|
302
|
+
.spyOn(actionExecutor, "handleActions")
|
|
303
|
+
.mockResolvedValue(ActionResult.Success);
|
|
304
|
+
|
|
305
|
+
await onItemPress(menu.content.items[0]);
|
|
306
|
+
|
|
307
|
+
expect(handleActionsSpy).toHaveBeenCalledWith(
|
|
308
|
+
[
|
|
309
|
+
{
|
|
310
|
+
type: "sendCloudEvent",
|
|
311
|
+
options: { subject: "add_collection_to_collection" },
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
type: "showToast",
|
|
315
|
+
options: { message: "Added to playlist" },
|
|
316
|
+
},
|
|
317
|
+
{ type: "dismissBottomSheet", options: {} },
|
|
318
|
+
],
|
|
319
|
+
expect.objectContaining({
|
|
320
|
+
entry: menu.content.items[0],
|
|
321
|
+
})
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
handleActionsSpy.mockRestore();
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it("executes preceding actions and excludes presentSubMenu from actionExecutor", async () => {
|
|
328
|
+
const subMenuSheet = {
|
|
329
|
+
type: "openBottomSheet",
|
|
330
|
+
options: {
|
|
331
|
+
header: { title: "Menu" },
|
|
332
|
+
content: {
|
|
333
|
+
items: [
|
|
334
|
+
{
|
|
335
|
+
id: "item-with-submenu",
|
|
336
|
+
title: "More Options",
|
|
337
|
+
extensions: {
|
|
338
|
+
tap_actions: {
|
|
339
|
+
actions: [
|
|
340
|
+
{ type: "sendAnalytics", options: { event: "click_more" } },
|
|
341
|
+
{
|
|
342
|
+
type: "presentSubMenu",
|
|
343
|
+
menu: { content: { title: "Sub Options", items: [] } },
|
|
344
|
+
},
|
|
345
|
+
],
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
],
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
} as any;
|
|
353
|
+
|
|
354
|
+
await openBottomSheetAction(subMenuSheet);
|
|
355
|
+
|
|
356
|
+
const [menu, onItemPress] = (modalOrchestrator.open as jest.Mock).mock
|
|
357
|
+
.calls[0];
|
|
358
|
+
|
|
359
|
+
const handleActionsSpy = jest
|
|
360
|
+
.spyOn(actionExecutor, "handleActions")
|
|
361
|
+
.mockResolvedValue(ActionResult.Success);
|
|
362
|
+
|
|
363
|
+
await onItemPress(menu.content.items[0]);
|
|
364
|
+
|
|
365
|
+
expect(handleActionsSpy).toHaveBeenCalledWith(
|
|
366
|
+
[{ type: "sendAnalytics", options: { event: "click_more" } }],
|
|
367
|
+
expect.objectContaining({ entry: menu.content.items[0] })
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
handleActionsSpy.mockRestore();
|
|
371
|
+
});
|
|
100
372
|
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { createPresentScreenAction } from "../presentScreen";
|
|
2
|
+
import { ActionResult } from "../../ActionExecutor";
|
|
3
|
+
|
|
4
|
+
const contextEntry = { id: "context-entry" } as any;
|
|
5
|
+
|
|
6
|
+
const succeeded = { success: true, cancelled: false };
|
|
7
|
+
|
|
8
|
+
const cancelled = { success: false, cancelled: true };
|
|
9
|
+
|
|
10
|
+
describe("presentScreenAction", () => {
|
|
11
|
+
let presentScreen: jest.Mock;
|
|
12
|
+
let presentScreenAction: ReturnType<typeof createPresentScreenAction>;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
presentScreen = jest.fn().mockResolvedValue(succeeded);
|
|
16
|
+
presentScreenAction = createPresentScreenAction(presentScreen);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("presents the screen named by typeMapping", async () => {
|
|
20
|
+
await presentScreenAction({
|
|
21
|
+
type: "presentScreen",
|
|
22
|
+
options: { typeMapping: "parent_lock" },
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
expect(presentScreen).toHaveBeenCalledWith({
|
|
26
|
+
typeMapping: "parent_lock",
|
|
27
|
+
entry: undefined,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("fails without presenting anything when typeMapping is missing", async () => {
|
|
32
|
+
const result = await presentScreenAction({
|
|
33
|
+
type: "presentScreen",
|
|
34
|
+
options: {},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(result).toBe(ActionResult.Error);
|
|
38
|
+
expect(presentScreen).not.toHaveBeenCalled();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("resolves the '@{entry/}' token to the entry from the execution context", async () => {
|
|
42
|
+
await presentScreenAction(
|
|
43
|
+
{
|
|
44
|
+
type: "presentScreen",
|
|
45
|
+
options: { typeMapping: "parent_lock", entry: "@{entry/}" },
|
|
46
|
+
},
|
|
47
|
+
{ entry: contextEntry }
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
expect(presentScreen).toHaveBeenCalledWith({
|
|
51
|
+
typeMapping: "parent_lock",
|
|
52
|
+
entry: contextEntry,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("presents an entry supplied inline in the options", async () => {
|
|
57
|
+
const inlineEntry = { id: "inline-entry" } as any;
|
|
58
|
+
|
|
59
|
+
await presentScreenAction({
|
|
60
|
+
type: "presentScreen",
|
|
61
|
+
options: { typeMapping: "parent_lock", entry: inlineEntry },
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
expect(presentScreen).toHaveBeenCalledWith({
|
|
65
|
+
typeMapping: "parent_lock",
|
|
66
|
+
entry: inlineEntry,
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("fails without presenting anything when the entry option is not an object", async () => {
|
|
71
|
+
const result = await presentScreenAction({
|
|
72
|
+
type: "presentScreen",
|
|
73
|
+
options: { typeMapping: "parent_lock", entry: "not-an-entry" as any },
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
expect(result).toBe(ActionResult.Error);
|
|
77
|
+
expect(presentScreen).not.toHaveBeenCalled();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("succeeds when the presented screen reports success", async () => {
|
|
81
|
+
presentScreen.mockResolvedValue(succeeded);
|
|
82
|
+
|
|
83
|
+
const result = await presentScreenAction({
|
|
84
|
+
type: "presentScreen",
|
|
85
|
+
options: { typeMapping: "parent_lock" },
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(result).toBe(ActionResult.Success);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("cancels the action chain when the presented screen is cancelled", async () => {
|
|
92
|
+
presentScreen.mockResolvedValue(cancelled);
|
|
93
|
+
|
|
94
|
+
const result = await presentScreenAction({
|
|
95
|
+
type: "presentScreen",
|
|
96
|
+
options: { typeMapping: "parent_lock" },
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(result).toBe(ActionResult.Cancel);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("cancels the action chain when the presented screen reports failure", async () => {
|
|
103
|
+
presentScreen.mockResolvedValue({ success: false, cancelled: false });
|
|
104
|
+
|
|
105
|
+
const result = await presentScreenAction({
|
|
106
|
+
type: "presentScreen",
|
|
107
|
+
options: { typeMapping: "parent_lock" },
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(result).toBe(ActionResult.Cancel);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("errors when the screen could not be presented at all", async () => {
|
|
114
|
+
presentScreen.mockResolvedValue({
|
|
115
|
+
success: false,
|
|
116
|
+
cancelled: false,
|
|
117
|
+
error: "can not resolve screen type mapping: parent_lock",
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const result = await presentScreenAction({
|
|
121
|
+
type: "presentScreen",
|
|
122
|
+
options: { typeMapping: "parent_lock" },
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
expect(result).toBe(ActionResult.Error);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { showAlertAction } from "../showAlert";
|
|
2
|
+
import { ActionResult } from "../../ActionExecutor";
|
|
3
|
+
import { showAlertDialog } from "../../../alertUtils";
|
|
4
|
+
|
|
5
|
+
jest.mock("../../../alertUtils", () => ({
|
|
6
|
+
showAlertDialog: jest.fn(),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
const dialog = showAlertDialog as jest.Mock;
|
|
10
|
+
|
|
11
|
+
const dismiss = () => dialog.mock.calls[0][0].completion();
|
|
12
|
+
|
|
13
|
+
describe("showAlert", () => {
|
|
14
|
+
beforeEach(() => dialog.mockReset());
|
|
15
|
+
|
|
16
|
+
it("shows the notice and resolves once it is dismissed", async () => {
|
|
17
|
+
const result = showAlertAction({
|
|
18
|
+
type: "showAlert",
|
|
19
|
+
options: { title: "Locked", message: "Ask the account owner." },
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
expect(dialog).toHaveBeenCalledWith(
|
|
23
|
+
expect.objectContaining({
|
|
24
|
+
title: "Locked",
|
|
25
|
+
message: "Ask the account owner.",
|
|
26
|
+
okButtonText: "OK",
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
dismiss();
|
|
31
|
+
|
|
32
|
+
// Dismissing a notice is not a cancellation: the chain carries on.
|
|
33
|
+
await expect(result).resolves.toBe(ActionResult.Success);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("takes the dismiss label from the action", async () => {
|
|
37
|
+
const result = showAlertAction({
|
|
38
|
+
type: "showAlert",
|
|
39
|
+
options: { title: "Locked", okButtonText: "Got it" },
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(dialog).toHaveBeenCalledWith(
|
|
43
|
+
expect.objectContaining({ okButtonText: "Got it" })
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
dismiss();
|
|
47
|
+
await result;
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -31,7 +31,7 @@ describe("showToastAction", () => {
|
|
|
31
31
|
message: "Added to queue",
|
|
32
32
|
extraMessage: "Extra info",
|
|
33
33
|
style: undefined,
|
|
34
|
-
timeout:
|
|
34
|
+
timeout: 5000,
|
|
35
35
|
source: "confirmation",
|
|
36
36
|
},
|
|
37
37
|
]);
|
|
@@ -78,7 +78,7 @@ describe("showToastAction", () => {
|
|
|
78
78
|
message: undefined,
|
|
79
79
|
extraMessage: undefined,
|
|
80
80
|
style: undefined,
|
|
81
|
-
timeout:
|
|
81
|
+
timeout: 5000,
|
|
82
82
|
source: "confirmation",
|
|
83
83
|
},
|
|
84
84
|
]);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="@applicaster/applicaster-types" />
|
|
2
|
-
import { dismissModal } from "../../modalState";
|
|
2
|
+
import { dismissModal, modalOrchestrator } from "../../modalState";
|
|
3
3
|
import { ActionResult } from "../ActionExecutor";
|
|
4
4
|
import { ActionHandler } from "../types";
|
|
5
5
|
import { createLogger } from "../../logger";
|
|
@@ -16,6 +16,7 @@ export const dismissBottomSheetAction: ActionHandler<
|
|
|
16
16
|
any
|
|
17
17
|
> = async (): Promise<ActionResult> => {
|
|
18
18
|
log_info("handleAction: dismissBottomSheet");
|
|
19
|
+
modalOrchestrator.close();
|
|
19
20
|
dismissModal();
|
|
20
21
|
|
|
21
22
|
return ActionResult.Success;
|
|
@@ -15,6 +15,8 @@ export { appRestartAction } from "./appRestart";
|
|
|
15
15
|
|
|
16
16
|
export { confirmDialogAction } from "./confirmDialog";
|
|
17
17
|
|
|
18
|
+
export { showAlertAction } from "./showAlert";
|
|
19
|
+
|
|
18
20
|
export { sendCloudEventAction } from "./sendCloudEvent";
|
|
19
21
|
|
|
20
22
|
export { sessionStorageToggleFlagAction } from "./sessionStorageToggleFlag";
|
|
@@ -27,6 +29,8 @@ export { screenToggleFlagAction } from "./screenToggleFlag";
|
|
|
27
29
|
|
|
28
30
|
export { createNavigateToScreenAction } from "./navigateToScreen";
|
|
29
31
|
|
|
32
|
+
export { createPresentScreenAction } from "./presentScreen";
|
|
33
|
+
|
|
30
34
|
export { createGoBackAction } from "./goBack";
|
|
31
35
|
|
|
32
36
|
export { createGoHomeAction } from "./goHome";
|
|
@@ -13,7 +13,7 @@ const { log_info, log_error } = createLogger({
|
|
|
13
13
|
*/
|
|
14
14
|
export interface NavigateToScreenOptions {
|
|
15
15
|
/** The screen type to navigate to (from content types mapping) */
|
|
16
|
-
typeMapping
|
|
16
|
+
typeMapping?: string;
|
|
17
17
|
|
|
18
18
|
/** Navigation action: push adds to history, replace replaces current screen */
|
|
19
19
|
navigationAction?: "push" | "replace";
|
|
@@ -58,14 +58,6 @@ export function createNavigateToScreenAction(
|
|
|
58
58
|
action,
|
|
59
59
|
context?: ActionExecutionContext
|
|
60
60
|
): Promise<ActionResult> {
|
|
61
|
-
const screenType = action.options?.typeMapping;
|
|
62
|
-
|
|
63
|
-
if (!screenType) {
|
|
64
|
-
log_error("navigateToScreen: typeMapping option is missing");
|
|
65
|
-
|
|
66
|
-
return ActionResult.Error;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
61
|
const navigationAction = action.options?.navigationAction;
|
|
70
62
|
const entrySource = action.options?.entry;
|
|
71
63
|
const navigationOptions = action.options?.navigationOptions;
|
|
@@ -76,62 +68,60 @@ export function createNavigateToScreenAction(
|
|
|
76
68
|
: entrySource
|
|
77
69
|
: null;
|
|
78
70
|
|
|
79
|
-
|
|
80
|
-
if (typeof entry !== "object") {
|
|
81
|
-
log_error(
|
|
82
|
-
`navigateToScreen: entry option is not an object, entry: ${entry}`
|
|
83
|
-
);
|
|
71
|
+
const screenType = action.options?.typeMapping || entry?.type?.value;
|
|
84
72
|
|
|
85
|
-
|
|
86
|
-
|
|
73
|
+
const navigate =
|
|
74
|
+
navigationAction === "push" ? navigator.push : navigator.replace;
|
|
87
75
|
|
|
76
|
+
let navigatingEntry: ZappEntry | ZappRiver = entry;
|
|
77
|
+
|
|
78
|
+
if (entry && screenType) {
|
|
88
79
|
log_info(
|
|
89
80
|
`navigateToScreen: navigating to screen type: ${screenType} with entry id: ${entry.id}`
|
|
90
81
|
);
|
|
91
82
|
|
|
92
|
-
|
|
83
|
+
navigatingEntry = {
|
|
93
84
|
...entry,
|
|
94
|
-
type: {
|
|
95
|
-
value: screenType,
|
|
96
|
-
},
|
|
85
|
+
type: { value: screenType },
|
|
97
86
|
};
|
|
87
|
+
} else {
|
|
88
|
+
if (!screenType) {
|
|
89
|
+
log_error(
|
|
90
|
+
"navigateToScreen: options.typeMapping or entry?.type?.value is not provided"
|
|
91
|
+
);
|
|
98
92
|
|
|
99
|
-
|
|
100
|
-
navigator.push(overriddenEntry, navigationOptions);
|
|
101
|
-
} else {
|
|
102
|
-
// Use replaceTop for entries since replace() only accepts ZappRiver
|
|
103
|
-
navigator.replaceTop(overriddenEntry, navigationOptions);
|
|
93
|
+
return ActionResult.Error;
|
|
104
94
|
}
|
|
105
95
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const screenId = contentTypes?.[screenType]?.screen_id || null;
|
|
96
|
+
const screenId = contentTypes?.[screenType]?.screen_id || null;
|
|
97
|
+
const river = rivers?.[screenId];
|
|
110
98
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
99
|
+
if (!screenId) {
|
|
100
|
+
log_error(
|
|
101
|
+
`navigateToScreen: can not resolve screen type mapping: ${screenType}`
|
|
102
|
+
);
|
|
115
103
|
|
|
116
|
-
|
|
117
|
-
|
|
104
|
+
return ActionResult.Error;
|
|
105
|
+
}
|
|
118
106
|
|
|
119
|
-
|
|
107
|
+
if (!river) {
|
|
108
|
+
log_error("navigateToScreen: can not resolve river");
|
|
120
109
|
|
|
121
|
-
|
|
122
|
-
|
|
110
|
+
return ActionResult.Error;
|
|
111
|
+
}
|
|
123
112
|
|
|
124
|
-
|
|
113
|
+
navigatingEntry = river;
|
|
125
114
|
}
|
|
126
115
|
|
|
127
116
|
context?.callback?.({ success: false, error: null, abort: true });
|
|
128
117
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
navigator.replace(river, navigationOptions);
|
|
118
|
+
// The destination screen resolves `@{entry/...}` from the entry context
|
|
119
|
+
if (entry) {
|
|
120
|
+
context?.setEntryContext?.(entry);
|
|
133
121
|
}
|
|
134
122
|
|
|
123
|
+
navigate(navigatingEntry, navigationOptions);
|
|
124
|
+
|
|
135
125
|
return ActionResult.Success;
|
|
136
126
|
};
|
|
137
127
|
}
|