@applicaster/zapp-react-native-ui-components 15.1.5 → 15.1.6-alpha.2015591222

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 (24) hide show
  1. package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +33 -69
  2. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +25 -47
  3. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +122 -39
  4. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +671 -0
  5. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +299 -131
  6. package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +86 -0
  7. package/Components/ModalComponent/AudioPlayer/Components/index.ts +8 -4
  8. package/Components/ModalComponent/BottomSheetModalContent.tsx +290 -55
  9. package/Components/ModalComponent/Button/index.tsx +5 -3
  10. package/Components/ModalComponent/Header/utils.ts +1 -1
  11. package/Components/ModalComponent/SortableList.tsx +142 -0
  12. package/Components/ModalComponent/TextInputContent.tsx +100 -0
  13. package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +183 -0
  14. package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +128 -0
  15. package/Components/ModalComponent/__tests__/showTextInput.test.ts +95 -0
  16. package/Components/ModalComponent/index.tsx +11 -0
  17. package/Components/ModalComponent/presets/__tests__/getCell.test.ts +66 -0
  18. package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +74 -0
  19. package/Components/ModalComponent/presets/index.ts +27 -0
  20. package/Components/ModalComponent/presets/selectableCell.tsx +27 -0
  21. package/Components/ModalComponent/presets/standardCell.tsx +41 -0
  22. package/Components/ModalComponent/presets/types.ts +35 -0
  23. package/Components/ModalComponent/showTextInput.ts +43 -0
  24. package/package.json +6 -5
@@ -1,17 +1,46 @@
1
1
  import React, { useCallback, useEffect, useMemo, useRef } from "react";
2
- import { View, ScrollView } from "react-native";
2
+ import {
3
+ ActivityIndicator,
4
+ ScrollView,
5
+ StyleSheet,
6
+ View,
7
+ Text,
8
+ } from "react-native";
3
9
  import { Button } from "./Button";
4
10
  import { ItemIconProps } from "./Button/ItemIcon";
5
11
  import { ItemProps } from "./Button/Item";
6
12
  import { ItemLabelProps } from "./Button/ItemLabel";
7
13
  import { getItemIconProps, getItemLabelProps, getItemProps } from "./utils";
8
14
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
15
+ import {
16
+ ContentViewModel,
17
+ useBottomSheetContent,
18
+ } from "@applicaster/zapp-react-native-utils/modalState";
19
+ import { getCell } from "./presets";
9
20
  import type { PluginConfiguration } from "./";
10
-
11
21
  import { ModalHeader as DefaultModalHeader } from "./Header";
12
22
  import { useSafeAreaInsets } from "react-native-safe-area-context";
23
+ import { SortableList } from "./SortableList";
24
+ import {
25
+ NowPlayingHeaderSection,
26
+ AudioPlayerButton,
27
+ VARIANT_WITH_ICON,
28
+ } from "./AudioPlayer/Components";
13
29
  import { BottomSheetDragArea } from "./BottomSheetDragArea";
14
30
 
31
+ const styles = StyleSheet.create({
32
+ centered: {
33
+ justifyContent: "center",
34
+ alignItems: "center",
35
+ padding: 16,
36
+ },
37
+ errorText: {
38
+ color: "#ff453a",
39
+ fontSize: 14,
40
+ textAlign: "center",
41
+ },
42
+ });
43
+
15
44
  type ModalComponentProps = {
16
45
  items: any[];
17
46
  current_selection?: any;
@@ -23,12 +52,6 @@ type ModalComponentProps = {
23
52
  maxHeight?: number;
24
53
  dismiss: () => void;
25
54
  headerComponent?: React.ComponentType;
26
-
27
- scrollViewContent?: {
28
- renderItem: (item: any, index: number) => React.ReactNode;
29
- items: any[];
30
- } | null;
31
-
32
55
  buttonComponent?: React.ComponentType;
33
56
  iconProps?: ItemIconProps | ((theme: PluginConfiguration) => ItemIconProps);
34
57
  itemProps?:
@@ -37,13 +60,17 @@ type ModalComponentProps = {
37
60
  labelProps?:
38
61
  | ItemLabelProps
39
62
  | ((theme: PluginConfiguration, width: number) => ItemLabelProps);
40
- getSelectedItemIcon: (
63
+ getSelectedItemIcon?: (
41
64
  theme: PluginConfiguration
42
65
  ) => ItemIconProps["asset"] | null;
43
- getDefaultItemIcon: (
66
+ getDefaultItemIcon?: (
44
67
  theme: PluginConfiguration
45
68
  ) => ItemIconProps["asset"] | null;
46
69
  iconPlacement?: "left" | "right";
70
+ viewModel?: ContentViewModel;
71
+ /** Optional body that replaces the items list (header stays DefaultModalHeader). */
72
+ contentComponent?: React.ComponentType<any>;
73
+ contentComponentProps?: Record<string, any>;
47
74
  };
48
75
 
49
76
  export function BottomSheetModalContent(props: ModalComponentProps) {
@@ -55,15 +82,16 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
55
82
  dismiss,
56
83
  summary,
57
84
  title,
58
- headerComponent = DefaultModalHeader,
59
- scrollViewContent = null,
60
-
85
+ headerComponent,
86
+ viewModel = null,
61
87
  buttonComponent: ButtonComponent = Button,
62
88
  getSelectedItemIcon = ({
63
89
  modal_bottom_sheet_item_selected_icon,
64
90
  }: BaseThemePropertiesMobile) => modal_bottom_sheet_item_selected_icon,
65
91
  getDefaultItemIcon = () => null,
66
92
  iconPlacement,
93
+ contentComponent: ContentComponent,
94
+ contentComponentProps = {},
67
95
  } = props;
68
96
 
69
97
  const route = useRef(currentRoute);
@@ -71,7 +99,29 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
71
99
  const paddingTop = Number(theme.modal_bottom_sheet_padding_top);
72
100
  const paddingBottom = Number(theme.modal_bottom_sheet_padding_bottom);
73
101
 
74
- const Header = headerComponent;
102
+ const {
103
+ items: fetchedItems,
104
+ showCentralLoading,
105
+ error,
106
+ handleItemPress,
107
+ role,
108
+ behavior,
109
+ editableCollection,
110
+ } = useBottomSheetContent({
111
+ onPress,
112
+ viewModel,
113
+ });
114
+
115
+ const itemsToRender = viewModel ? fetchedItems : items;
116
+ const operations = editableCollection?.operations || [];
117
+ const isSortable = operations.includes("reorder");
118
+
119
+ const cell = getCell(
120
+ viewModel ? role : undefined,
121
+ viewModel ? behavior : undefined
122
+ );
123
+
124
+ const Header = headerComponent ?? DefaultModalHeader;
75
125
 
76
126
  useEffect(() => {
77
127
  if (currentRoute !== route.current) {
@@ -91,7 +141,7 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
91
141
  return getItemLabelProps(theme, props.width, props.labelProps);
92
142
  }, [theme, props.width, props.labelProps]);
93
143
 
94
- const handlePress = useCallback(
144
+ const handleStaticPress = useCallback(
95
145
  (item: any) => {
96
146
  onPress(item);
97
147
  dismiss();
@@ -99,58 +149,243 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
99
149
  [onPress, dismiss]
100
150
  );
101
151
 
152
+ const onItemPress = viewModel ? handleItemPress : handleStaticPress;
153
+
154
+ const handleDragEnd = useCallback(
155
+ (params: any) => {
156
+ const from = params?.fromIndex ?? params?.from;
157
+ const to = params?.toIndex ?? params?.to;
158
+
159
+ const reorderedItemIds = Array.isArray(params?.data)
160
+ ? params.data
161
+ .filter((item: any) => !item?.isHeader)
162
+ .map((item: any) => item?.id)
163
+ .filter(Boolean)
164
+ : undefined;
165
+
166
+ console.log("[BottomSheetModalContent] handleDragEnd:", {
167
+ from,
168
+ to,
169
+ reorderedItemIds,
170
+ hasEditableCollection: !!editableCollection,
171
+ hasMove: !!editableCollection?.move,
172
+ });
173
+
174
+ if (
175
+ (reorderedItemIds?.length ||
176
+ (typeof from === "number" &&
177
+ typeof to === "number" &&
178
+ from !== to)) &&
179
+ editableCollection?.move
180
+ ) {
181
+ editableCollection.move(reorderedItemIds || []);
182
+ }
183
+ },
184
+ [editableCollection]
185
+ );
186
+
187
+ const renderContext = useMemo(
188
+ () => ({
189
+ theme,
190
+ ButtonComponent,
191
+ current_selection,
192
+ iconBaseProps,
193
+ itemBaseProps,
194
+ labelBaseProps,
195
+ getSelectedItemIcon,
196
+ getDefaultItemIcon,
197
+ iconPlacement,
198
+ editableCollection,
199
+ behavior,
200
+ }),
201
+ [
202
+ theme,
203
+ ButtonComponent,
204
+ current_selection,
205
+ iconBaseProps,
206
+ itemBaseProps,
207
+ labelBaseProps,
208
+ getSelectedItemIcon,
209
+ getDefaultItemIcon,
210
+ iconPlacement,
211
+ editableCollection,
212
+ behavior,
213
+ ]
214
+ );
215
+
102
216
  const bottomInset = useSafeAreaInsets().bottom;
103
217
 
104
- return (
105
- <View
106
- style={{
107
- maxHeight: props.maxHeight,
108
- paddingTop: paddingTop,
109
- }}
110
- >
111
- {Header ? (
112
- <BottomSheetDragArea>
113
- <Header
218
+ const hasNowPlaying = operations.includes("nowPlaying");
219
+ const hasClearAll = operations.includes("clearAll");
220
+ const hasAdd = operations.includes("add");
221
+
222
+ const handleDeleteCollection = useCallback(() => {
223
+ editableCollection?.deleteCollection?.();
224
+ }, [editableCollection]);
225
+
226
+ const handleCreatePlaylist = useCallback(() => {
227
+ editableCollection?.add?.({ title: "Create New Playlist" });
228
+ }, [editableCollection]);
229
+
230
+ const headers = useMemo(() => {
231
+ const list: Array<{ index: number; component: () => React.ReactElement }> =
232
+ [];
233
+
234
+ if (hasNowPlaying) {
235
+ list.push({
236
+ index: list.length,
237
+ component: () => (
238
+ <NowPlayingHeaderSection
239
+ onClear={hasClearAll ? handleDeleteCollection : undefined}
240
+ />
241
+ ),
242
+ });
243
+ }
244
+
245
+ if (hasAdd) {
246
+ list.push({
247
+ index: list.length,
248
+ component: () => (
249
+ <View
250
+ style={{ paddingHorizontal: 20, paddingTop: 8, paddingBottom: 12 }}
251
+ >
252
+ <AudioPlayerButton
253
+ configuration={VARIANT_WITH_ICON}
254
+ data={{ title: "Create New Playlist" }}
255
+ onPress={handleCreatePlaylist}
256
+ />
257
+ </View>
258
+ ),
259
+ });
260
+ }
261
+
262
+ return list.length > 0 ? list : undefined;
263
+ }, [
264
+ hasNowPlaying,
265
+ hasClearAll,
266
+ handleDeleteCollection,
267
+ hasAdd,
268
+ handleCreatePlaylist,
269
+ ]);
270
+
271
+ const renderBody = () => {
272
+ if (ContentComponent) {
273
+ return (
274
+ <ScrollView
275
+ contentContainerStyle={{
276
+ paddingBottom: paddingBottom + bottomInset,
277
+ }}
278
+ >
279
+ <ContentComponent
280
+ {...contentComponentProps}
114
281
  dismiss={dismiss}
115
- configuration={theme}
116
- summary={summary}
117
- title={title}
282
+ width={props.width}
283
+ maxHeight={props.maxHeight}
118
284
  />
119
- </BottomSheetDragArea>
120
- ) : null}
285
+ </ScrollView>
286
+ );
287
+ }
288
+
289
+ if (showCentralLoading) {
290
+ return (
291
+ <View
292
+ style={[
293
+ styles.centered,
294
+ { backgroundColor: theme.modal_bottom_sheet_background_color },
295
+ ]}
296
+ >
297
+ <ActivityIndicator color="#aaaaaa" />
298
+ </View>
299
+ );
300
+ }
301
+
302
+ if (error) {
303
+ return (
304
+ <View style={styles.centered}>
305
+ <Text style={styles.errorText}>
306
+ {typeof error === "string" ? error : error.message}
307
+ </Text>
308
+ </View>
309
+ );
310
+ }
311
+
312
+ if (isSortable) {
313
+ return (
314
+ <SortableList
315
+ sortableData={itemsToRender}
316
+ headers={headers}
317
+ scrollViewStyle={{
318
+ flexShrink: 1,
319
+ }}
320
+ contentContainerStyle={{
321
+ paddingBottom: paddingBottom + bottomInset,
322
+ }}
323
+ onDragEnd={handleDragEnd}
324
+ renderItem={({ item, index, renderHandle }) =>
325
+ cell.renderItem({
326
+ item,
327
+ index,
328
+ onPress: onItemPress,
329
+ context: renderContext,
330
+ renderHandle,
331
+ })
332
+ }
333
+ />
334
+ );
335
+ }
336
+
337
+ return (
121
338
  <ScrollView
122
339
  overScrollMode="never"
123
340
  bounces={false}
124
- showsVerticalScrollIndicator={false}
125
341
  contentContainerStyle={{
126
342
  paddingBottom: paddingBottom + bottomInset,
127
343
  }}
128
344
  >
129
- {scrollViewContent
130
- ? scrollViewContent.items.map((item, index) =>
131
- scrollViewContent.renderItem(item, index)
132
- )
133
- : null}
134
- {!scrollViewContent &&
135
- items.map((item, index) =>
136
- item ? (
137
- <ButtonComponent
138
- key={index}
139
- configuration={theme}
140
- selectedItem={current_selection}
141
- item={item}
142
- onPress={handlePress}
143
- label={theme[item?.label] ?? item?.label}
144
- iconBaseProps={iconBaseProps}
145
- itemBaseProps={itemBaseProps}
146
- labelBaseProps={labelBaseProps}
147
- selectedItemIcon={getSelectedItemIcon(theme)}
148
- defaultItemIcon={getDefaultItemIcon(theme)}
149
- iconPlacement={iconPlacement}
150
- />
151
- ) : null
152
- )}
345
+ {hasNowPlaying ? (
346
+ <NowPlayingHeaderSection
347
+ onClear={hasClearAll ? handleDeleteCollection : undefined}
348
+ />
349
+ ) : null}
350
+ {hasAdd ? (
351
+ <View
352
+ style={{ paddingHorizontal: 20, paddingTop: 8, paddingBottom: 12 }}
353
+ >
354
+ <AudioPlayerButton
355
+ configuration={VARIANT_WITH_ICON}
356
+ data={{ title: "Create New Playlist" }}
357
+ onPress={handleCreatePlaylist}
358
+ />
359
+ </View>
360
+ ) : null}
361
+ {(itemsToRender || []).map((item, index) =>
362
+ cell.renderItem({
363
+ item,
364
+ index,
365
+ onPress: onItemPress,
366
+ context: renderContext,
367
+ })
368
+ )}
153
369
  </ScrollView>
370
+ );
371
+ };
372
+
373
+ return (
374
+ <View
375
+ style={{
376
+ maxHeight: props.maxHeight,
377
+ paddingTop: paddingTop,
378
+ }}
379
+ >
380
+ <BottomSheetDragArea>
381
+ <Header
382
+ dismiss={dismiss}
383
+ configuration={theme}
384
+ summary={summary}
385
+ title={title}
386
+ />
387
+ </BottomSheetDragArea>
388
+ {renderBody()}
154
389
  </View>
155
390
  );
156
391
  }
@@ -43,11 +43,13 @@ export function Button({
43
43
  }: ButtonProps) {
44
44
  const [focused, setFocused] = useState(false);
45
45
 
46
- const selected = selectedItem && item.value === selectedItem?.value;
46
+ const selected = selectedItem && item?.value === selectedItem?.value;
47
47
 
48
48
  const itemIconPropsAssets = useMemo<ItemIconProps["asset"]>(
49
- () => configuration[item.asset] ?? item.asset ?? defaultItemIcon,
50
- [item.asset, defaultItemIcon]
49
+ () =>
50
+ (item ? (configuration[item.asset] ?? item.asset) : undefined) ??
51
+ defaultItemIcon,
52
+ [item?.asset, defaultItemIcon]
51
53
  );
52
54
 
53
55
  const selectedItemIconPropsAssets = useMemo<ItemIconProps["asset"]>(
@@ -18,7 +18,7 @@ export function getCloseButtonProps(config: PluginConfiguration) {
18
18
  return {
19
19
  asset: modal_bottom_sheet_close_asset,
20
20
  assetFocused: modal_bottom_sheet_close_focus_asset,
21
- enabled: modal_bottom_sheet_close_switch,
21
+ enabled: modal_bottom_sheet_close_switch !== false,
22
22
  width: modal_bottom_sheet_close_width,
23
23
  height: modal_bottom_sheet_close_height,
24
24
  marginTop: modal_bottom_sheet_close_margin_top,
@@ -0,0 +1,142 @@
1
+ import React, { useCallback, useEffect, useState } from "react";
2
+ import Sortable from "react-native-sortables";
3
+ import { Text, View, ViewStyle } from "react-native";
4
+ import Animated, { useAnimatedRef } from "react-native-reanimated";
5
+
6
+ const DEFAULT_HEADERS: any[] = [];
7
+ const DEFAULT_SORTABLE_DATA: any[] = [];
8
+
9
+ const renderHeaderComponent = (component: any) => {
10
+ if (!component) return null;
11
+ if (React.isValidElement(component)) return component;
12
+
13
+ if (typeof component === "function") {
14
+ const HeaderComponent = component;
15
+
16
+ return <HeaderComponent />;
17
+ }
18
+
19
+ return null;
20
+ };
21
+
22
+ const SortableListHeader = ({
23
+ title,
24
+ style,
25
+ }: {
26
+ title?: string;
27
+ style?: ViewStyle;
28
+ }) => {
29
+ const displayTitle = typeof title === "string" ? title : String(title ?? "");
30
+
31
+ return (
32
+ <View style={style}>
33
+ {displayTitle ? <Text>{displayTitle}</Text> : null}
34
+ </View>
35
+ );
36
+ };
37
+
38
+ export type SortableListProps = {
39
+ scrollViewStyle?: ViewStyle;
40
+ contentContainerStyle?: ViewStyle;
41
+ itemStyle?: ViewStyle;
42
+ headerStyle?: ViewStyle;
43
+ columns?: number;
44
+ rowGap?: number;
45
+ columnGap?: number;
46
+ spacing?: number;
47
+ keyExtractor?: (item: any, index?: number) => string;
48
+ renderItem: ({
49
+ item,
50
+ index,
51
+ renderHandle,
52
+ }: {
53
+ item: any;
54
+ index: number;
55
+ renderHandle: (children: React.ReactElement) => React.ReactElement;
56
+ }) => React.ReactNode;
57
+ sortableData?: any[];
58
+ headers?: { index: number; title?: string; component: any }[];
59
+ onDragEnd?: (params: {
60
+ fromIndex?: number;
61
+ toIndex?: number;
62
+ from?: number;
63
+ to?: number;
64
+ data: any[];
65
+ }) => void;
66
+ };
67
+
68
+ export const SortableList = ({
69
+ scrollViewStyle,
70
+ contentContainerStyle,
71
+ itemStyle,
72
+ headerStyle,
73
+ columns = 1,
74
+ rowGap = 0,
75
+ columnGap = 0,
76
+ keyExtractor = (item, index) =>
77
+ `sortable-item-${item?.id ?? item?.title ?? index}`,
78
+ renderItem,
79
+ sortableData = DEFAULT_SORTABLE_DATA,
80
+ headers = DEFAULT_HEADERS,
81
+ onDragEnd,
82
+ }: SortableListProps) => {
83
+ const scrollableRef = useAnimatedRef<Animated.ScrollView>();
84
+
85
+ const [localData, setLocalData] = useState(sortableData);
86
+
87
+ useEffect(() => {
88
+ setLocalData(sortableData);
89
+ }, [sortableData]);
90
+
91
+ const handleDragEnd = useCallback(
92
+ (params: any) => {
93
+ if (params?.data) {
94
+ setLocalData(params.data);
95
+ }
96
+
97
+ if (onDragEnd) {
98
+ onDragEnd(params);
99
+ }
100
+ },
101
+ [onDragEnd]
102
+ );
103
+
104
+ return (
105
+ <Animated.ScrollView
106
+ ref={scrollableRef}
107
+ style={scrollViewStyle}
108
+ contentContainerStyle={contentContainerStyle}
109
+ >
110
+ {(headers || []).map((header, idx) => (
111
+ <View key={`static-header-${idx}`} style={headerStyle}>
112
+ {header.title ? (
113
+ <SortableListHeader title={header.title} />
114
+ ) : (
115
+ renderHeaderComponent(header.component)
116
+ )}
117
+ </View>
118
+ ))}
119
+ <Sortable.Grid
120
+ scrollableRef={scrollableRef}
121
+ customHandle
122
+ columns={columns}
123
+ rowGap={rowGap}
124
+ columnGap={columnGap}
125
+ data={localData}
126
+ keyExtractor={keyExtractor}
127
+ onDragEnd={handleDragEnd}
128
+ renderItem={({ item, index }) => (
129
+ <View key={"sortable-item-" + index} style={itemStyle}>
130
+ {renderItem({
131
+ item,
132
+ index,
133
+ renderHandle: (children) => (
134
+ <Sortable.Handle>{children}</Sortable.Handle>
135
+ ),
136
+ })}
137
+ </View>
138
+ )}
139
+ />
140
+ </Animated.ScrollView>
141
+ );
142
+ };
@@ -0,0 +1,100 @@
1
+ import React, { useCallback, useState } from "react";
2
+ import { StyleSheet, View } from "react-native";
3
+ import { actionExecutor } from "@applicaster/zapp-react-native-utils/actionsExecutor/ActionExecutor";
4
+ import {
5
+ AudioPlayerInput,
6
+ VARIANT_NAME_PLAYLIST_INPUT,
7
+ } from "./AudioPlayer/Components/Input";
8
+ import {
9
+ AudioPlayerButton,
10
+ VARIANT_TEXT_ONLY,
11
+ } from "./AudioPlayer/Components/Button";
12
+
13
+ export type TextInputContentProps = {
14
+ /** Injected by the sheet host; unused for layout (header is owned by ModalComponent). */
15
+ dismiss?: () => void;
16
+ width?: number;
17
+ maxHeight?: number;
18
+ currentRoute?: boolean;
19
+ inputLabel?: string;
20
+ defaultValue?: string;
21
+ buttonLabel: string;
22
+ actions: ActionType[];
23
+ };
24
+
25
+ const styles = StyleSheet.create({
26
+ container: {
27
+ paddingHorizontal: 20,
28
+ paddingBottom: 8,
29
+ },
30
+ inputWrap: {
31
+ marginTop: 8,
32
+ marginBottom: 24,
33
+ },
34
+ });
35
+
36
+ /**
37
+ * Modal body only — input + submit.
38
+ * Standard dismiss header comes from BottomSheetModalContent / ModalHeader.
39
+ */
40
+ export function TextInputContent(props: TextInputContentProps) {
41
+ const { inputLabel, defaultValue = "", buttonLabel, actions } = props;
42
+
43
+ const [textValue, setTextValue] = useState(defaultValue);
44
+ const [submitting, setSubmitting] = useState(false);
45
+
46
+ const onSubmit = useCallback(async () => {
47
+ if (submitting) {
48
+ return;
49
+ }
50
+
51
+ setSubmitting(true);
52
+
53
+ try {
54
+ const updatedActions = (actions || []).map((item) => {
55
+ if (item.type === "sendCloudEvent" || item.options?.data) {
56
+ return {
57
+ ...item,
58
+ options: {
59
+ ...item.options,
60
+ data: {
61
+ ...(item.options?.data || {}),
62
+ name: textValue,
63
+ },
64
+ },
65
+ };
66
+ }
67
+
68
+ return item;
69
+ });
70
+
71
+ await actionExecutor.handleActions(updatedActions);
72
+ await actionExecutor.handleAction({ type: "dismissBottomSheet" });
73
+ } finally {
74
+ setSubmitting(false);
75
+ }
76
+ }, [submitting, actions, textValue]);
77
+
78
+ return (
79
+ <View style={styles.container}>
80
+ <View style={styles.inputWrap}>
81
+ <AudioPlayerInput
82
+ configuration={VARIANT_NAME_PLAYLIST_INPUT}
83
+ data={{
84
+ placeholder: inputLabel || "",
85
+ label: inputLabel,
86
+ }}
87
+ value={textValue}
88
+ onChangeText={setTextValue}
89
+ onClearPress={() => setTextValue("")}
90
+ />
91
+ </View>
92
+ <AudioPlayerButton
93
+ configuration={VARIANT_TEXT_ONLY}
94
+ data={{ title: buttonLabel }}
95
+ onPress={onSubmit}
96
+ disabled={submitting}
97
+ />
98
+ </View>
99
+ );
100
+ }