@applicaster/zapp-react-native-utils 16.0.0-rc.56 → 16.0.0-rc.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/actionsExecutor/ActionExecutor.ts +40 -2
  2. package/actionsExecutor/ActionExecutorContext.tsx +70 -16
  3. package/actionsExecutor/actions/__tests__/dismissBottomSheet.test.ts +22 -0
  4. package/actionsExecutor/actions/__tests__/loadItemsFromSource.test.ts +88 -0
  5. package/actionsExecutor/actions/__tests__/openBottomSheet.customContent.test.ts +76 -0
  6. package/actionsExecutor/actions/__tests__/openBottomSheet.inlineItems.test.ts +130 -0
  7. package/actionsExecutor/actions/dismissBottomSheet.ts +22 -0
  8. package/actionsExecutor/actions/index.ts +4 -0
  9. package/actionsExecutor/actions/openBottomSheet.ts +322 -0
  10. package/actionsExecutor/actions/refreshComponent.ts +6 -0
  11. package/actionsExecutor/actions/sendCloudEvent.ts +6 -1
  12. package/actionsExecutor/consts.ts +23 -0
  13. package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +5 -2
  14. package/appUtils/playerManager/OverlayObserver/utils.ts +46 -20
  15. package/arrayUtils/__tests__/reorderByIds.test.ts +50 -0
  16. package/arrayUtils/index.ts +79 -0
  17. package/configurationUtils/__tests__/modalBlocksParser.test.ts +107 -0
  18. package/configurationUtils/manifestKeyParser.ts +38 -9
  19. package/configurationUtils/modalBlocksParser.ts +178 -0
  20. package/manifestUtils/index.js +3 -0
  21. package/manifestUtils/modalBlocks.js +304 -0
  22. package/modalState/ContentViewModel.ts +113 -0
  23. package/modalState/EditableCollection.ts +17 -0
  24. package/modalState/EditableCollectionRegistry.ts +51 -0
  25. package/modalState/ModalOrchestrator.ts +201 -0
  26. package/modalState/RemoteEditableCollection.ts +227 -0
  27. package/modalState/__tests__/ContentViewModel.editable.test.ts +69 -0
  28. package/modalState/__tests__/ContentViewModel.test.ts +165 -0
  29. package/modalState/__tests__/EditableCollectionRegistry.test.ts +112 -0
  30. package/modalState/__tests__/ModalOrchestrator.phase3.test.ts +55 -0
  31. package/modalState/__tests__/RemoteEditableCollection.test.ts +326 -0
  32. package/modalState/__tests__/useBottomSheetContent.test.ts +349 -0
  33. package/modalState/index.ts +30 -83
  34. package/modalState/store.ts +102 -0
  35. package/modalState/types.ts +137 -0
  36. package/modalState/useBottomSheetContent.ts +102 -0
  37. package/package.json +2 -2
  38. package/reactHooks/index.ts +2 -0
  39. package/reactHooks/usePluginConfiguration.ts +4 -1
  40. package/reactHooks/utils/__tests__/index.test.js +22 -2
  41. package/reactHooks/utils/index.ts +11 -1
  42. /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();
@@ -19,9 +19,11 @@ import {
19
19
  appRestartAction,
20
20
  confirmDialogAction,
21
21
  createNavigateToScreenAction,
22
+ dismissBottomSheetAction,
22
23
  localStorageRemoveAction,
23
24
  localStorageSetAction,
24
25
  localStorageToggleFlagAction,
26
+ openBottomSheetAction,
25
27
  refreshComponentAction,
26
28
  screenSetVariableAction,
27
29
  screenToggleFlagAction,
@@ -33,6 +35,8 @@ import {
33
35
  showToastAction,
34
36
  } from "./actions";
35
37
 
38
+ import { ACTION_TYPES } from "./consts";
39
+
36
40
  export const { log_error, log_info, log_debug } = createLogger({
37
41
  subsystem: "ActionExecutorContext",
38
42
  category: "General",
@@ -41,9 +45,15 @@ export const { log_error, log_info, log_debug } = createLogger({
41
45
  export type ActionExecutorContextType = {
42
46
  registerAction: <TOptions = Record<string, any>>(
43
47
  type: string,
44
- handler: ActionHandler<TOptions>
48
+ handler: ActionHandler<TOptions>,
49
+ stateResolver?: (entry?: ZappEntry) => any
45
50
  ) => void;
46
51
  unregisterAction: (type: string) => void;
52
+ registerActionState: (
53
+ type: string,
54
+ stateResolver: (entry?: ZappEntry) => any
55
+ ) => () => void;
56
+ getActionState: (type: string, entry?: ZappEntry) => any;
47
57
  handleAction: (
48
58
  action: ActionType,
49
59
  context?: ActionExecutionContext
@@ -63,34 +73,76 @@ type Props = {
63
73
  };
64
74
 
65
75
  const prepareDefaultActions = (actionExecutor) => {
66
- actionExecutor.registerAction("localStorageSet", localStorageSetAction);
67
- actionExecutor.registerAction("sessionStorageSet", sessionStorageSetAction);
68
- actionExecutor.registerAction("localStorageRemove", localStorageRemoveAction);
76
+ actionExecutor.registerAction(
77
+ ACTION_TYPES.LOCAL_STORAGE_SET,
78
+ localStorageSetAction
79
+ );
80
+
81
+ actionExecutor.registerAction(
82
+ ACTION_TYPES.SESSION_STORAGE_SET,
83
+ sessionStorageSetAction
84
+ );
85
+
86
+ actionExecutor.registerAction(
87
+ ACTION_TYPES.LOCAL_STORAGE_REMOVE,
88
+ localStorageRemoveAction
89
+ );
69
90
 
70
91
  actionExecutor.registerAction(
71
- "sessionStorageRemove",
92
+ ACTION_TYPES.SESSION_STORAGE_REMOVE,
72
93
  sessionStorageRemoveAction
73
94
  );
74
95
 
75
- actionExecutor.registerAction("refreshComponent", refreshComponentAction);
76
- actionExecutor.registerAction("switchLayout", switchLayoutAction);
77
- actionExecutor.registerAction("appRestart", appRestartAction);
78
- actionExecutor.registerAction("confirmDialog", confirmDialogAction);
79
- actionExecutor.registerAction("sendCloudEvent", sendCloudEventAction);
96
+ actionExecutor.registerAction(
97
+ ACTION_TYPES.REFRESH_COMPONENT,
98
+ refreshComponentAction
99
+ );
100
+
101
+ actionExecutor.registerAction(ACTION_TYPES.SWITCH_LAYOUT, switchLayoutAction);
102
+
103
+ actionExecutor.registerAction(ACTION_TYPES.APP_RESTART, appRestartAction);
104
+
105
+ actionExecutor.registerAction(
106
+ ACTION_TYPES.CONFIRM_DIALOG,
107
+ confirmDialogAction
108
+ );
109
+
110
+ actionExecutor.registerAction(
111
+ ACTION_TYPES.SEND_CLOUD_EVENT,
112
+ sendCloudEventAction
113
+ );
80
114
 
81
115
  actionExecutor.registerAction(
82
- "sessionStorageToggleFlag",
116
+ ACTION_TYPES.SESSION_STORAGE_TOGGLE_FLAG,
83
117
  sessionStorageToggleFlagAction
84
118
  );
85
119
 
86
120
  actionExecutor.registerAction(
87
- "localStorageToggleFlag",
121
+ ACTION_TYPES.LOCAL_STORAGE_TOGGLE_FLAG,
88
122
  localStorageToggleFlagAction
89
123
  );
90
124
 
91
- actionExecutor.registerAction("screenSetVariable", screenSetVariableAction);
92
- actionExecutor.registerAction("screenToggleFlag", screenToggleFlagAction);
93
- actionExecutor.registerAction("showToast", showToastAction);
125
+ actionExecutor.registerAction(
126
+ ACTION_TYPES.SCREEN_SET_VARIABLE,
127
+ screenSetVariableAction
128
+ );
129
+
130
+ actionExecutor.registerAction(
131
+ ACTION_TYPES.SCREEN_TOGGLE_FLAG,
132
+ screenToggleFlagAction
133
+ );
134
+
135
+ actionExecutor.registerAction(
136
+ ACTION_TYPES.OPEN_BOTTOM_SHEET,
137
+ openBottomSheetAction
138
+ );
139
+
140
+ actionExecutor.registerAction(
141
+ ACTION_TYPES.DISMISS_BOTTOM_SHEET,
142
+ dismissBottomSheetAction
143
+ );
144
+
145
+ actionExecutor.registerAction(ACTION_TYPES.SHOW_TOAST, showToastAction);
94
146
  };
95
147
 
96
148
  export const ActionExecutorContext =
@@ -108,6 +160,8 @@ export function withActionExecutor(Component) {
108
160
  return {
109
161
  unregisterAction: _actionExecutor.unregisterAction,
110
162
  registerAction: _actionExecutor.registerAction,
163
+ registerActionState: _actionExecutor.registerActionState,
164
+ getActionState: _actionExecutor.getActionState,
111
165
  handleAction: _actionExecutor.handleAction,
112
166
  handleActions: _actionExecutor.handleActions,
113
167
  handleEntryActions: _actionExecutor.handleEntryActions,
@@ -130,7 +184,7 @@ export function withActionExecutor(Component) {
130
184
 
131
185
  useEffect(() => {
132
186
  return _actionExecutor.registerAction(
133
- "navigateToScreen",
187
+ ACTION_TYPES.NAVIGATE_TO_SCREEN,
134
188
  createNavigateToScreenAction(navigator, rivers, contentTypes)
135
189
  );
136
190
  }, [navigator, rivers, contentTypes]);
@@ -0,0 +1,22 @@
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
+ }));
8
+
9
+ describe("dismissBottomSheetAction", () => {
10
+ beforeEach(() => {
11
+ jest.clearAllMocks();
12
+ });
13
+
14
+ it("calls dismissModal and returns ActionResult.Success", async () => {
15
+ const result = await dismissBottomSheetAction({
16
+ type: "dismissBottomSheet",
17
+ });
18
+
19
+ expect(dismissModal).toHaveBeenCalledTimes(1);
20
+ expect(result).toBe(ActionResult.Success);
21
+ });
22
+ });
@@ -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,76 @@
1
+ import { openBottomSheetAction } from "../openBottomSheet";
2
+ import { ActionResult } from "../../ActionExecutor";
3
+ import { openBottomSheetModal } from "../../../modalState/store";
4
+ import { modalOrchestrator } from "../../../modalState/ModalOrchestrator";
5
+
6
+ jest.mock("../../../modalState/store", () => ({
7
+ openBottomSheetModal: jest.fn(),
8
+ }));
9
+
10
+ jest.mock("../../../modalState/ModalOrchestrator", () => ({
11
+ modalOrchestrator: {
12
+ open: jest.fn(),
13
+ },
14
+ isPresentSubMenuAction: jest.requireActual("../../../modalState/types")
15
+ .isPresentSubMenuAction,
16
+ }));
17
+
18
+ describe("openBottomSheetAction custom content", () => {
19
+ beforeEach(() => {
20
+ jest.clearAllMocks();
21
+ });
22
+
23
+ it("opens default sheet with contentComponent body props", async () => {
24
+ const CustomContent = function CustomContent() {
25
+ return null;
26
+ };
27
+
28
+ const result = await openBottomSheetAction({
29
+ type: "openBottomSheet",
30
+ options: {
31
+ header: { title: "Create Playlist" },
32
+ content: {
33
+ component: CustomContent,
34
+ props: {
35
+ inputLabel: "Name your playlist",
36
+ buttonLabel: "Create",
37
+ defaultValue: "",
38
+ submitAction: { type: "sendCloudEvent", options: {} },
39
+ },
40
+ },
41
+ },
42
+ } as any);
43
+
44
+ expect(result).toBe(ActionResult.Success);
45
+ expect(openBottomSheetModal).toHaveBeenCalledTimes(1);
46
+ expect(modalOrchestrator.open).not.toHaveBeenCalled();
47
+
48
+ const args = (openBottomSheetModal as jest.Mock).mock.calls[0][0];
49
+ // Default ModalComponent provides the standard header
50
+ expect(args.ModalBottomSheetContent).toBeUndefined();
51
+
52
+ expect(args.modalBottomSheetContentProps).toMatchObject({
53
+ title: "Create Playlist",
54
+ contentComponent: CustomContent,
55
+ contentComponentProps: {
56
+ inputLabel: "Name your playlist",
57
+ buttonLabel: "Create",
58
+ defaultValue: "",
59
+ },
60
+ });
61
+ });
62
+
63
+ it("returns Error when neither list data nor content.component is provided", async () => {
64
+ const result = await openBottomSheetAction({
65
+ type: "openBottomSheet",
66
+ options: {
67
+ header: { title: "Empty" },
68
+ content: {},
69
+ },
70
+ } as any);
71
+
72
+ expect(result).toBe(ActionResult.Error);
73
+ expect(openBottomSheetModal).not.toHaveBeenCalled();
74
+ expect(modalOrchestrator.open).not.toHaveBeenCalled();
75
+ });
76
+ });
@@ -0,0 +1,130 @@
1
+ import { openBottomSheetAction } from "../openBottomSheet";
2
+ import { ActionResult } from "../../ActionExecutor";
3
+ import { modalOrchestrator } from "../../../modalState/ModalOrchestrator";
4
+
5
+ jest.mock("../../../modalState/store", () => ({
6
+ openBottomSheetModal: jest.fn(),
7
+ }));
8
+
9
+ jest.mock("../../../modalState/ModalOrchestrator", () => ({
10
+ modalOrchestrator: {
11
+ open: jest.fn(),
12
+ replace: jest.fn(),
13
+ stackSize: 0,
14
+ },
15
+ isPresentSubMenuAction: jest.requireActual("../../../modalState/types")
16
+ .isPresentSubMenuAction,
17
+ }));
18
+
19
+ describe("openBottomSheetAction inline items", () => {
20
+ const HeaderComponent = function HeaderComponent() {
21
+ return null;
22
+ };
23
+
24
+ beforeEach(() => {
25
+ jest.clearAllMocks();
26
+ (modalOrchestrator as any).stackSize = 0;
27
+ });
28
+
29
+ const speedSheetAction = {
30
+ type: "openBottomSheet",
31
+ options: {
32
+ header: { title: "Playback Speed" },
33
+ content: {
34
+ role: "collection_selector",
35
+ behavior: {
36
+ selectMode: "single",
37
+ currentSelection: ["1"],
38
+ },
39
+ items: [
40
+ {
41
+ id: "0.8",
42
+ title: "0.8x",
43
+ extensions: {
44
+ tap_actions: {
45
+ actions: [
46
+ { type: "setPlaybackSpeed", options: { value: 0.8 } },
47
+ ],
48
+ },
49
+ },
50
+ },
51
+ {
52
+ id: "1",
53
+ title: "1x",
54
+ extensions: {
55
+ tap_actions: {
56
+ actions: [{ type: "setPlaybackSpeed", options: { value: 1 } }],
57
+ },
58
+ },
59
+ },
60
+ ],
61
+ },
62
+ },
63
+ } as any;
64
+
65
+ it("opens orchestrator with inline items and marks current selection", async () => {
66
+ const result = await openBottomSheetAction(speedSheetAction);
67
+
68
+ expect(result).toBe(ActionResult.Success);
69
+ expect(modalOrchestrator.open).toHaveBeenCalledTimes(1);
70
+ expect(modalOrchestrator.replace).not.toHaveBeenCalled();
71
+
72
+ const [menu] = (modalOrchestrator.open as jest.Mock).mock.calls[0];
73
+
74
+ expect(menu.content.items[0].isSelected).toBe(false);
75
+
76
+ expect(menu.content.items[1]).toMatchObject({
77
+ id: "1",
78
+ title: "1x",
79
+ isSelected: true,
80
+ action: { type: "setPlaybackSpeed", options: { value: 1 } },
81
+ });
82
+ });
83
+
84
+ it("pushes a new sheet by default even when another sheet is already open", async () => {
85
+ (modalOrchestrator as any).stackSize = 1;
86
+
87
+ await openBottomSheetAction(speedSheetAction);
88
+
89
+ expect(modalOrchestrator.open).toHaveBeenCalledTimes(1);
90
+ expect(modalOrchestrator.replace).not.toHaveBeenCalled();
91
+ });
92
+
93
+ it("replaces the current sheet when options.replace is true", async () => {
94
+ (modalOrchestrator as any).stackSize = 1;
95
+
96
+ await openBottomSheetAction({
97
+ ...speedSheetAction,
98
+ options: { ...speedSheetAction.options, replace: true },
99
+ });
100
+
101
+ expect(modalOrchestrator.replace).toHaveBeenCalledTimes(1);
102
+ expect(modalOrchestrator.open).not.toHaveBeenCalled();
103
+ });
104
+
105
+ it("forwards a custom header component and its props onto the menu", async () => {
106
+ await openBottomSheetAction({
107
+ type: "openBottomSheet",
108
+ options: {
109
+ header: {
110
+ title: "Sleep Timer",
111
+ component: HeaderComponent,
112
+ componentProps: { timerDate: 123 },
113
+ },
114
+ content: {
115
+ items: [{ id: "5", title: "5 minutes" }],
116
+ },
117
+ },
118
+ } as any);
119
+
120
+ const [menu] = (modalOrchestrator.open as jest.Mock).mock.calls[0];
121
+
122
+ expect(menu.header).toEqual({
123
+ title: "Sleep Timer",
124
+ subtitle: undefined,
125
+ icon: undefined,
126
+ component: HeaderComponent,
127
+ componentProps: { timerDate: 123 },
128
+ });
129
+ });
130
+ });
@@ -0,0 +1,22 @@
1
+ /// <reference types="@applicaster/applicaster-types" />
2
+ import { dismissModal } from "../../modalState";
3
+ import { ActionResult } from "../ActionExecutor";
4
+ import { ActionHandler } from "../types";
5
+ import { createLogger } from "../../logger";
6
+
7
+ const { log_info } = createLogger({
8
+ subsystem: "ActionExecutorContext",
9
+ category: "General",
10
+ });
11
+
12
+ /**
13
+ * Dismisses the active bottom sheet / modal.
14
+ */
15
+ export const dismissBottomSheetAction: ActionHandler<
16
+ any
17
+ > = async (): Promise<ActionResult> => {
18
+ log_info("handleAction: dismissBottomSheet");
19
+ dismissModal();
20
+
21
+ return ActionResult.Success;
22
+ };
@@ -28,3 +28,7 @@ export { screenToggleFlagAction } from "./screenToggleFlag";
28
28
  export { createNavigateToScreenAction } from "./navigateToScreen";
29
29
 
30
30
  export { showToastAction } from "./showToast";
31
+
32
+ export { openBottomSheetAction } from "./openBottomSheet";
33
+
34
+ export { dismissBottomSheetAction } from "./dismissBottomSheet";