react-native-srschat 0.1.27 → 0.1.29

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 (75) hide show
  1. package/README.md +215 -0
  2. package/lib/commonjs/assets/chat-icon-mobile.svg +1 -0
  3. package/lib/commonjs/assets/posiden.svg +51 -0
  4. package/lib/commonjs/components/PoseidonLogo.js +127 -0
  5. package/lib/commonjs/components/PoseidonLogo.js.map +1 -0
  6. package/lib/commonjs/components/header.js +6 -6
  7. package/lib/commonjs/components/header.js.map +1 -1
  8. package/lib/commonjs/components/input.js +20 -1
  9. package/lib/commonjs/components/input.js.map +1 -1
  10. package/lib/commonjs/components/productCard.js +164 -37
  11. package/lib/commonjs/components/productCard.js.map +1 -1
  12. package/lib/commonjs/components/voice.js +88 -17
  13. package/lib/commonjs/components/voice.js.map +1 -1
  14. package/lib/commonjs/components/welcomeInput.js +20 -1
  15. package/lib/commonjs/components/welcomeInput.js.map +1 -1
  16. package/lib/commonjs/layout/icon.js +11 -7
  17. package/lib/commonjs/layout/icon.js.map +1 -1
  18. package/lib/commonjs/layout/layout.js +10 -6
  19. package/lib/commonjs/layout/layout.js.map +1 -1
  20. package/lib/commonjs/layout/welcome.js +5 -6
  21. package/lib/commonjs/layout/welcome.js.map +1 -1
  22. package/lib/commonjs/layout/window.js +2 -1
  23. package/lib/commonjs/layout/window.js.map +1 -1
  24. package/lib/commonjs/utils/audioRecorder.js +58 -7
  25. package/lib/commonjs/utils/audioRecorder.js.map +1 -1
  26. package/lib/module/assets/chat-icon-mobile.svg +1 -0
  27. package/lib/module/assets/posiden.svg +51 -0
  28. package/lib/module/components/PoseidonLogo.js +117 -0
  29. package/lib/module/components/PoseidonLogo.js.map +1 -0
  30. package/lib/module/components/header.js +6 -6
  31. package/lib/module/components/header.js.map +1 -1
  32. package/lib/module/components/input.js +20 -1
  33. package/lib/module/components/input.js.map +1 -1
  34. package/lib/module/components/productCard.js +165 -38
  35. package/lib/module/components/productCard.js.map +1 -1
  36. package/lib/module/components/voice.js +90 -19
  37. package/lib/module/components/voice.js.map +1 -1
  38. package/lib/module/components/welcomeInput.js +20 -1
  39. package/lib/module/components/welcomeInput.js.map +1 -1
  40. package/lib/module/layout/icon.js +11 -7
  41. package/lib/module/layout/icon.js.map +1 -1
  42. package/lib/module/layout/layout.js +10 -6
  43. package/lib/module/layout/layout.js.map +1 -1
  44. package/lib/module/layout/welcome.js +5 -6
  45. package/lib/module/layout/welcome.js.map +1 -1
  46. package/lib/module/layout/window.js +2 -1
  47. package/lib/module/layout/window.js.map +1 -1
  48. package/lib/module/utils/audioRecorder.js +56 -7
  49. package/lib/module/utils/audioRecorder.js.map +1 -1
  50. package/lib/typescript/components/PoseidonLogo.d.ts +7 -0
  51. package/lib/typescript/components/PoseidonLogo.d.ts.map +1 -0
  52. package/lib/typescript/components/header.d.ts.map +1 -1
  53. package/lib/typescript/components/input.d.ts.map +1 -1
  54. package/lib/typescript/components/productCard.d.ts.map +1 -1
  55. package/lib/typescript/components/voice.d.ts.map +1 -1
  56. package/lib/typescript/components/welcomeInput.d.ts.map +1 -1
  57. package/lib/typescript/layout/icon.d.ts.map +1 -1
  58. package/lib/typescript/layout/layout.d.ts.map +1 -1
  59. package/lib/typescript/layout/welcome.d.ts.map +1 -1
  60. package/lib/typescript/utils/audioRecorder.d.ts +2 -0
  61. package/lib/typescript/utils/audioRecorder.d.ts.map +1 -1
  62. package/package.json +5 -3
  63. package/src/assets/chat-icon-mobile.svg +1 -0
  64. package/src/assets/posiden.svg +51 -0
  65. package/src/components/PoseidonLogo.js +55 -0
  66. package/src/components/header.js +3 -2
  67. package/src/components/input.js +20 -1
  68. package/src/components/productCard.js +205 -60
  69. package/src/components/voice.js +107 -23
  70. package/src/components/welcomeInput.js +20 -1
  71. package/src/layout/icon.js +5 -3
  72. package/src/layout/layout.js +12 -8
  73. package/src/layout/welcome.js +2 -5
  74. package/src/layout/window.js +2 -1
  75. package/src/utils/audioRecorder.js +60 -13
@@ -1,5 +1,5 @@
1
1
  import React, { useState, useContext, useEffect } from "react";
2
- import { View, Text, Image, TouchableOpacity, TextInput, StyleSheet, Platform, Keyboard } from "react-native";
2
+ import { View, Text, Image, TouchableOpacity, TextInput, StyleSheet, Platform, Keyboard, ActionSheetIOS } from "react-native";
3
3
  import { AppContext } from "../contexts/AppContext";
4
4
  import Ionicons from 'react-native-vector-icons/Ionicons';
5
5
 
@@ -24,7 +24,8 @@ export const ProductCard = ({ prod, onFocusQuantityInput }) => {
24
24
  const grossPrice = uomInfo.gross_price || 0;
25
25
  const netPrice = uomInfo.net_price || 0;
26
26
  const isOnSale = uomInfo.is_on_sale || false;
27
- const discounts = uomInfo.discounts || []
27
+ const discounts = uomInfo.discounts || [];
28
+
28
29
 
29
30
  const maxQuantity = Math.floor(prod.inventory_info?.info_by_uom?.EA?.quantity_available || 0);
30
31
  const valid = prod.inventory_info?.is_valid || false;
@@ -64,9 +65,21 @@ export const ProductCard = ({ prod, onFocusQuantityInput }) => {
64
65
  }
65
66
  };
66
67
 
68
+ const incrementQuantity = () => {
69
+ if (quantity < maxQuantity) {
70
+ setQuantity(prevQuantity => prevQuantity + 1);
71
+ }
72
+ };
73
+
74
+ const decrementQuantity = () => {
75
+ if (quantity > 0) {
76
+ setQuantity(prevQuantity => prevQuantity - 1);
77
+ }
78
+ };
79
+
67
80
  const handleAddToCart = () => {
68
81
  onAddToCartClick({
69
- selectedUom: "EA",
82
+ selectedUom: selectedUom,
70
83
  quantity: quantity,
71
84
  product: prod
72
85
  });
@@ -81,6 +94,27 @@ export const ProductCard = ({ prod, onFocusQuantityInput }) => {
81
94
  onProductCardClick(prodWithSku);
82
95
  };
83
96
 
97
+ const handleUomChange = (value) => {
98
+ setSelectedUom(value);
99
+ };
100
+
101
+ const openUomPicker = () => {
102
+ if (Platform.OS === 'ios') {
103
+ ActionSheetIOS.showActionSheetWithOptions(
104
+ {
105
+ options: [...uoms, 'Cancel'],
106
+ cancelButtonIndex: uoms.length,
107
+ title: 'Select Unit',
108
+ },
109
+ (buttonIndex) => {
110
+ if (buttonIndex !== uoms.length) {
111
+ handleUomChange(uoms[buttonIndex]);
112
+ }
113
+ }
114
+ );
115
+ }
116
+ };
117
+
84
118
  return (
85
119
  <View style={styles.card}>
86
120
  {/* Two-column layout */}
@@ -88,6 +122,11 @@ export const ProductCard = ({ prod, onFocusQuantityInput }) => {
88
122
  {/* Left Column (Image, Price, Availability) */}
89
123
  <View style={styles.leftColumn}>
90
124
  <TouchableOpacity onPress={handleProductClick}>
125
+ {isOnSale && (
126
+ <View style={styles.saleTag}>
127
+ <Text style={styles.saleTagText}>SALE</Text>
128
+ </View>
129
+ )}
91
130
  <Image source={{ uri: prod.product_details.image_url }} style={styles.image} />
92
131
  </TouchableOpacity>
93
132
  {valid &&
@@ -123,43 +162,89 @@ export const ProductCard = ({ prod, onFocusQuantityInput }) => {
123
162
  <Text style={styles.boldText}>Part # </Text>{prod.part_number}
124
163
  </Text>
125
164
 
126
- {/* Bottom Row: Quantity Input & Add to Cart Button */}
127
- <View style={styles.bottomRow}>
128
- <TextInput
129
- value={quantity.toString()}
130
- onChangeText={handleQuantityChange}
131
- keyboardType="numeric"
132
- style={[
133
- styles.quantityInput,
134
- {
135
- height: 40,
136
- minHeight: 40,
137
- maxHeight: 40
138
- }
139
- ]}
140
- editable={valid && inStock}
141
- underlineColorAndroid="transparent"
142
- autoCorrect={false}
143
- autoCapitalize="none"
144
- caretHidden={false}
145
- contextMenuHidden={true}
146
- disableFullscreenUI={true}
147
- defaultValue="1"
148
- />
165
+ {/* Display discounts if available */}
166
+ {discounts.length > 0 && (
167
+ <View style={styles.discountContainer}>
168
+ {discounts.map((discount, index) => (
169
+ <Text key={index} style={styles.discountText}>{discount}</Text>
170
+ ))}
171
+ </View>
172
+ )}
149
173
 
150
- <TouchableOpacity
151
- onPress={handleAddToCart}
152
- disabled={!valid || !inStock || quantity === 0}
153
- style={[
154
- styles.addToCartButton,
155
- valid && inStock && quantity > 0 ? styles.activeButton : styles.disabledButton
156
- ]}
157
- >
158
- <Text style={[styles.buttonText, valid && inStock && quantity > 0 ? styles.activeText : styles.disabledText]}>
159
- Add to Cart
160
- </Text>
161
- </TouchableOpacity>
174
+ {/* Input Section with UOM Selector and Quantity */}
175
+ <View style={styles.inputRow}>
176
+ {uoms.length > 1 ? (
177
+ <View style={styles.uomSelectorContainer}>
178
+ <Text style={styles.inputLabel}>Unit</Text>
179
+ <TouchableOpacity
180
+ style={styles.dropdownButton}
181
+ onPress={openUomPicker}
182
+ disabled={!valid}
183
+ >
184
+ <Text style={styles.dropdownButtonText}>{selectedUom}</Text>
185
+ <Ionicons name="chevron-down" size={16} color="rgba(0, 0, 0, 0.6)" />
186
+ </TouchableOpacity>
187
+ </View>
188
+ ) : null}
189
+
190
+ <View style={styles.quantityContainer}>
191
+ <Text style={styles.inputLabel}>Qty</Text>
192
+ <View style={styles.quantityInputContainer}>
193
+ <TouchableOpacity
194
+ style={styles.quantityButton}
195
+ onPress={decrementQuantity}
196
+ disabled={!valid || quantity <= 0}
197
+ >
198
+ <Ionicons
199
+ name="remove"
200
+ size={18}
201
+ color={valid && quantity > 0 ? "#367cb6" : "rgba(0, 0, 0, 0.23)"}
202
+ />
203
+ </TouchableOpacity>
204
+
205
+ <TextInput
206
+ value={quantity.toString()}
207
+ onChangeText={handleQuantityChange}
208
+ keyboardType="numeric"
209
+ style={styles.quantityInput}
210
+ editable={valid && inStock}
211
+ underlineColorAndroid="transparent"
212
+ autoCorrect={false}
213
+ autoCapitalize="none"
214
+ caretHidden={false}
215
+ contextMenuHidden={true}
216
+ disableFullscreenUI={true}
217
+ defaultValue="1"
218
+ />
219
+
220
+ <TouchableOpacity
221
+ style={styles.quantityButton}
222
+ onPress={incrementQuantity}
223
+ disabled={!valid || quantity >= maxQuantity}
224
+ >
225
+ <Ionicons
226
+ name="add"
227
+ size={18}
228
+ color={valid && quantity < maxQuantity ? "#367cb6" : "rgba(0, 0, 0, 0.23)"}
229
+ />
230
+ </TouchableOpacity>
231
+ </View>
232
+ </View>
162
233
  </View>
234
+
235
+ {/* Add to Cart Button on a new line */}
236
+ <TouchableOpacity
237
+ onPress={handleAddToCart}
238
+ disabled={!valid || !inStock || quantity === 0}
239
+ style={[
240
+ styles.addToCartButton,
241
+ valid && inStock && quantity > 0 ? styles.activeButton : styles.disabledButton
242
+ ]}
243
+ >
244
+ <Text style={[styles.buttonText, valid && inStock && quantity > 0 ? styles.activeText : styles.disabledText]}>
245
+ Add to Cart
246
+ </Text>
247
+ </TouchableOpacity>
163
248
  </View>
164
249
  </View>
165
250
  </View>
@@ -170,7 +255,6 @@ const styles = StyleSheet.create({
170
255
  card: {
171
256
  backgroundColor: "#fff",
172
257
  width: '100%',
173
- padding: 10,
174
258
  padding: 12,
175
259
  paddingHorizontal: 16,
176
260
  borderRadius: 12,
@@ -183,6 +267,7 @@ const styles = StyleSheet.create({
183
267
  leftColumn: {
184
268
  width: "25%",
185
269
  alignItems: "center",
270
+ position: "relative",
186
271
  },
187
272
  rightColumn: {
188
273
  width: "75%",
@@ -194,24 +279,31 @@ const styles = StyleSheet.create({
194
279
  resizeMode: "contain",
195
280
  marginBottom: 5,
196
281
  },
282
+ saleTag: {
283
+ position: "absolute",
284
+ top: 0,
285
+ left: 0,
286
+ backgroundColor: "red",
287
+ borderRadius: 4,
288
+ paddingVertical: 2,
289
+ paddingHorizontal: 5,
290
+ zIndex: 1,
291
+ },
292
+ saleTagText: {
293
+ color: "#fff",
294
+ fontSize: 10,
295
+ fontWeight: "bold",
296
+ },
197
297
  price: {
198
298
  fontSize: 14,
199
299
  fontWeight: "bold",
200
300
  textAlign: "center",
201
301
  },
202
- saleTag: {
203
- backgroundColor: "#ff4757",
204
- color: "#fff",
205
- fontSize: 12,
206
- fontWeight: "bold",
207
- paddingVertical: 2,
208
- paddingHorizontal: 5,
209
- borderRadius: 4,
210
- marginBottom: 5,
211
- },
212
302
  priceContainer: {
213
303
  flexDirection: "row",
214
304
  alignItems: "center",
305
+ flexWrap: "wrap",
306
+ justifyContent: "center",
215
307
  },
216
308
  originalPrice: {
217
309
  fontSize: 14,
@@ -222,7 +314,7 @@ const styles = StyleSheet.create({
222
314
  salePrice: {
223
315
  fontSize: 16,
224
316
  fontWeight: "bold",
225
- color: "#ff4757",
317
+ color: "red",
226
318
  },
227
319
  perUnit: {
228
320
  fontSize: 12,
@@ -250,29 +342,82 @@ const styles = StyleSheet.create({
250
342
  boldText: {
251
343
  fontWeight: "bold",
252
344
  },
253
- bottomRow: {
345
+ discountContainer: {
346
+ marginTop: 5,
347
+ marginBottom: 5,
348
+ },
349
+ discountText: {
350
+ fontSize: 13,
351
+ color: "#e41e31",
352
+ fontWeight: "500",
353
+ // backgroundColor: "#f0f7ff",
354
+ // borderWidth: 1,
355
+ // borderColor: "#bae0ff",
356
+ // borderRadius: 4,
357
+ // padding: 3,
358
+ marginTop: 3,
359
+ },
360
+ inputRow: {
254
361
  flexDirection: "row",
255
- alignItems: "center",
362
+ alignItems: "flex-end",
256
363
  marginTop: 10,
364
+ marginBottom: 10,
257
365
  },
258
- quantityInput: {
259
- width: 60,
260
- height: Platform.OS === 'ios' ? 40 : 35,
366
+ uomSelectorContainer: {
367
+ marginRight: 10,
368
+ },
369
+ inputLabel: {
370
+ fontSize: 12,
371
+ color: "rgba(0, 0, 0, 0.6)",
372
+ marginBottom: 4,
373
+ },
374
+ dropdownButton: {
375
+ flexDirection: 'row',
376
+ justifyContent: 'space-between',
377
+ alignItems: 'center',
261
378
  borderWidth: 1,
262
- borderColor: "rgba(0, 0, 0, 0.23)",
379
+ borderColor: 'rgba(0, 0, 0, 0.23)',
263
380
  borderRadius: 4,
264
381
  paddingHorizontal: 10,
382
+ height: 40,
383
+ minWidth: 80,
384
+ },
385
+ dropdownButtonText: {
386
+ fontSize: 14,
387
+ color: '#555',
388
+ marginRight: 8,
389
+ },
390
+ quantityContainer: {
391
+ flex: 1,
392
+ },
393
+ quantityInputContainer: {
394
+ flexDirection: "row",
395
+ alignItems: "center",
396
+ height: 40,
397
+ borderWidth: 1,
398
+ borderColor: "rgba(0, 0, 0, 0.23)",
399
+ borderRadius: 4,
400
+ },
401
+ quantityButton: {
402
+ width: 40,
403
+ height: 40,
404
+ justifyContent: "center",
405
+ alignItems: "center",
406
+ },
407
+ quantityInput: {
408
+ flex: 1,
409
+ height: 40,
265
410
  textAlign: "center",
266
- marginRight: 10,
267
- lineHeight: Platform.OS === 'ios' ? undefined : 24,
268
- includeFontPadding: false,
411
+ paddingHorizontal: 5,
412
+ fontSize: 14,
413
+ minWidth: 40,
269
414
  },
270
415
  addToCartButton: {
271
- flex: 1,
272
416
  height: 40,
273
417
  justifyContent: "center",
274
418
  alignItems: "center",
275
419
  borderRadius: 4,
420
+ marginTop: 5,
276
421
  },
277
422
  activeButton: {
278
423
  backgroundColor: "#367cb6",
@@ -1,8 +1,9 @@
1
1
  // VoiceButton.js
2
2
 
3
3
  import React, { useState, useContext, useEffect } from 'react';
4
- import { TouchableOpacity, ActivityIndicator, StyleSheet, Alert } from 'react-native';
4
+ import { TouchableOpacity, ActivityIndicator, StyleSheet, Alert, Linking, Platform } from 'react-native';
5
5
  import Ionicons from 'react-native-vector-icons/Ionicons';
6
+ import useAsyncStorage from '../hooks/useAsyncStorage';
6
7
 
7
8
  import {
8
9
  startRecording,
@@ -10,60 +11,129 @@ import {
10
11
  cancelRecording,
11
12
  requestAudioPermission,
12
13
  cleanup,
13
- initVoice
14
+ initVoice,
15
+ resetStoredPermission,
16
+ setPermissionStatusHandlers
14
17
  } from '../utils/audioRecorder';
15
18
  import { AppContext } from '../contexts/AppContext';
16
19
 
20
+ const PERMISSION_STORAGE_KEY = '@voice_permission_status';
21
+
17
22
  export const VoiceButton = () => {
18
23
  const { handleVoiceSend } = useContext(AppContext);
19
24
  const [isListening, setIsListening] = useState(false);
20
25
  const [loading, setLoading] = useState(false);
26
+ const [permissionChecked, setPermissionChecked] = useState(false);
27
+ const [hasPermission, setHasPermission] = useState(null);
28
+
29
+ // Use your custom AsyncStorage hook
30
+ const [permissionStatus, setPermissionStatus] = useAsyncStorage(PERMISSION_STORAGE_KEY, null);
21
31
 
22
32
  useEffect(() => {
33
+ // Register our permission handlers
34
+ setPermissionStatusHandlers(() => permissionStatus, setPermissionStatus);
35
+
23
36
  const setupVoice = async () => {
24
- const initialized = await initVoice((result, error) => {
25
- if (error) {
26
- Alert.alert('Error', error);
27
- setIsListening(false);
28
- setLoading(false);
37
+ try {
38
+ // Check stored permission first
39
+ if (permissionStatus === 'denied') {
40
+ // We already know permission was denied, don't show alert again
41
+ setHasPermission(false);
42
+ setPermissionChecked(true);
29
43
  return;
30
44
  }
31
- if (result) {
32
- handleVoiceSend(null, result);
33
- setIsListening(false);
34
- setLoading(false);
35
- }
36
- });
45
+
46
+ // Only request permission if not already denied
47
+ const permissionResult = await requestAudioPermission();
48
+ setHasPermission(permissionResult);
49
+
50
+ if (permissionResult) {
51
+ const initialized = await initVoice((result, error) => {
52
+ if (error) {
53
+ // Don't show alert for permission errors since we handle that elsewhere
54
+ if (!error.includes('permission')) {
55
+ Alert.alert('Error', error);
56
+ }
57
+ setIsListening(false);
58
+ setLoading(false);
59
+ return;
60
+ }
61
+ if (result) {
62
+ handleVoiceSend(null, result);
63
+ }
64
+ // Always reset states when the recognition ends
65
+ setIsListening(false);
66
+ setLoading(false);
67
+ });
37
68
 
38
- if (!initialized) {
39
- Alert.alert(
40
- 'Error',
41
- 'Speech recognition is not available on this device'
42
- );
69
+ if (!initialized) {
70
+ // Only show this alert once per session
71
+ if (!permissionChecked) {
72
+ Alert.alert(
73
+ 'Error',
74
+ 'Speech recognition is not available on this device'
75
+ );
76
+ }
77
+ }
78
+ }
79
+ } catch (error) {
80
+ console.error('Error in setupVoice:', error);
81
+ } finally {
82
+ setPermissionChecked(true);
43
83
  }
44
84
  };
45
85
 
46
- setupVoice();
86
+ if (!permissionChecked) {
87
+ setupVoice();
88
+ }
47
89
 
48
90
  return () => {
49
91
  cleanup();
50
92
  };
51
- }, []);
93
+ }, [permissionStatus, permissionChecked]);
52
94
 
53
95
  const toggleRecording = async () => {
54
96
  try {
55
97
  if (!isListening) {
56
- const hasPermission = await requestAudioPermission();
57
- if (!hasPermission) {
58
- Alert.alert('Permission Denied', 'Microphone permission is required for voice recognition');
98
+ // If we already know we don't have permission, show a better message
99
+ if (hasPermission === false) {
100
+ Alert.alert(
101
+ 'Permission Required',
102
+ 'Voice recognition requires microphone permission. Would you like to update your settings?',
103
+ [
104
+ {
105
+ text: 'Cancel',
106
+ style: 'cancel'
107
+ },
108
+ {
109
+ text: 'Settings',
110
+ onPress: () => {
111
+ // Reset stored permission so we can check again
112
+ setPermissionStatus(null);
113
+ // Open device settings
114
+ openAppSettings();
115
+ // Reset our permission check
116
+ setPermissionChecked(false);
117
+ }
118
+ }
119
+ ]
120
+ );
59
121
  return;
60
122
  }
61
123
 
62
124
  setLoading(true);
125
+ const checkPermission = await requestAudioPermission();
126
+ if (!checkPermission) {
127
+ setHasPermission(false);
128
+ setLoading(false);
129
+ return;
130
+ }
131
+
63
132
  const started = await startRecording();
64
133
  if (started) {
65
134
  setIsListening(true);
66
135
  } else {
136
+ // Only show error if not permission related
67
137
  Alert.alert('Error', 'Failed to start voice recognition');
68
138
  }
69
139
  } else {
@@ -78,7 +148,21 @@ export const VoiceButton = () => {
78
148
  await cleanup();
79
149
  } finally {
80
150
  setLoading(false);
151
+ }
152
+ };
81
153
 
154
+ const openAppSettings = async () => {
155
+ try {
156
+ if (Platform.OS === 'ios') {
157
+ // For iOS
158
+ await Linking.openURL('app-settings://');
159
+ } else {
160
+ // For Android
161
+ await Linking.openSettings();
162
+ }
163
+ } catch (error) {
164
+ console.error('Cannot open settings', error);
165
+ Alert.alert('Error', 'Unable to open settings. Please open settings manually.');
82
166
  }
83
167
  };
84
168
 
@@ -18,6 +18,20 @@ export const WelcomeInput = ({ onProductCardClick, onAddToCartClick }) => {
18
18
 
19
19
  const { data, handleSend, input, setInput, showModal, theme } = useContext(AppContext);
20
20
 
21
+ const handleKeyPress = ({ nativeEvent }) => {
22
+ if (nativeEvent.key === 'return' && !nativeEvent.shiftKey) {
23
+ nativeEvent.preventDefault && nativeEvent.preventDefault();
24
+ handleSend(input);
25
+ return;
26
+ }
27
+ };
28
+
29
+ const onSubmitEditing = () => {
30
+ if (input.trim()) {
31
+ handleSend(input);
32
+ }
33
+ };
34
+
21
35
  return (
22
36
  <View style={styles.inputContainer}>
23
37
  <TextInput
@@ -26,7 +40,12 @@ export const WelcomeInput = ({ onProductCardClick, onAddToCartClick }) => {
26
40
  onChangeText={setInput}
27
41
  placeholder="Ask a question..."
28
42
  placeholderTextColor="#999"
29
- multiline
43
+ multiline={false}
44
+ returnKeyType="send"
45
+ enablesReturnKeyAutomatically={true}
46
+ onKeyPress={handleKeyPress}
47
+ onSubmitEditing={onSubmitEditing}
48
+ blurOnSubmit={false}
30
49
  />
31
50
  <VoiceButton/>
32
51
  <TouchableOpacity
@@ -2,13 +2,14 @@ import React, { useContext } from 'react';
2
2
  import { TouchableOpacity, View, StyleSheet, Text } from 'react-native';
3
3
  import { AppContext } from '../contexts/AppContext';
4
4
  import Ionicons from 'react-native-vector-icons/Ionicons';
5
+ import Svg, { Path } from 'react-native-svg';
5
6
 
6
7
  export const ChatIcon = () => {
7
8
  const { setShowModal, messages, maintenance, disclaimer, uiConfig } = useContext(AppContext);
8
9
 
9
10
  // Determine position from uiConfig or default
10
11
  const iconPosition = {
11
- bottom: uiConfig?.iconPosition?.bottom ?? 80,
12
+ top: uiConfig?.iconPosition?.top ?? 50,
12
13
  right: uiConfig?.iconPosition?.right ?? 20,
13
14
  };
14
15
 
@@ -36,7 +37,9 @@ export const ChatIcon = () => {
36
37
  return (
37
38
  <TouchableOpacity style={containerStyle} onPress={handleClick} activeOpacity={0.7}>
38
39
  <View style={styles.iconContent}>
39
- <Ionicons name="chatbubble-ellipses" size={28} color="white" />
40
+ <Svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" fill="#ffffff" viewBox="0 0 256 256">
41
+ <Path d="M132,24A100.11,100.11,0,0,0,32,124v84.33A15.69,15.69,0,0,0,47.67,224H132a100,100,0,0,0,0-200Zm28,128H96a8,8,0,0,1,0-16h64a8,8,0,0,1,0,16Zm0-32H96a8,8,0,0,1,0-16h64a8,8,0,0,1,0,16Z" />
42
+ </Svg>
40
43
  {iconType === 'tab' && (
41
44
  <Text style={styles.tabText}>Chat with Heritage</Text>
42
45
  )}
@@ -52,7 +55,6 @@ const styles = StyleSheet.create({
52
55
  width: 60,
53
56
  height: 60,
54
57
  borderRadius: 30,
55
- backgroundColor: '#FFA500',
56
58
  alignItems: 'center',
57
59
  justifyContent: 'center',
58
60
  zIndex: 10,
@@ -64,15 +64,19 @@ export const Layout = () => {
64
64
  // uiConfig.showIcon, uiConfig.toggleChat
65
65
 
66
66
  useEffect(() => {
67
- if (!disclaimer) {
68
- setShowModal("Form");
69
- } else {
70
- if (messages.length > 1 || maintenance) {
71
- setShowModal("ChatWindow");
72
- }
73
- else {
74
- setShowModal("Welcome")
67
+ if (uiConfig.toggleChat) {
68
+ if (!disclaimer) {
69
+ setShowModal("Form");
70
+ } else {
71
+ if (messages.length > 1 || maintenance) {
72
+ setShowModal("ChatWindow");
73
+ }
74
+ else {
75
+ setShowModal("Welcome")
76
+ }
75
77
  }
78
+ } else {
79
+ setShowModal("Icon"); // Default to Icon/bubble state
76
80
  }
77
81
  }, [uiConfig.toggleChat]);
78
82
 
@@ -6,6 +6,7 @@ import { AppContext } from '../contexts/AppContext';
6
6
  import { WelcomeInput } from '../components/welcomeInput';
7
7
  import ButtonComponent from '../components/welcomeButton';
8
8
  import { Testing } from '../components/testing';
9
+ import { PoseidonLogo } from '../components/PoseidonLogo';
9
10
 
10
11
  export const Welcome = ({ panHandlers }) => {
11
12
  const { setShowModal, uiConfig, onProductCardClick, onAddToCartClick } = useContext(AppContext);
@@ -25,11 +26,7 @@ export const Welcome = ({ panHandlers }) => {
25
26
  {/* Top section */}
26
27
  <View style={styles.topContainer}>
27
28
  <View style={styles.topHeader}>
28
- <Image
29
- source={require('../assets/heritage.png')}
30
- style={[styles.logo, { tintColor: "white" }]}
31
- resizeMode="contain"
32
- />
29
+ <PoseidonLogo width={150} height={35} color="white" />
33
30
  <TouchableOpacity onPress={() => handleClick()} style={styles.collapseButton}>
34
31
  <Ionicons name="chevron-down" size={24} color="white" />
35
32
  </TouchableOpacity>
@@ -180,7 +180,8 @@ const styles = StyleSheet.create({
180
180
  userMessage: {
181
181
  alignSelf: 'flex-end',
182
182
  backgroundColor: "#003764",
183
- color: "#ffffff"
183
+ color: "#ffffff",
184
+ borderTopRightRadius: 0,
184
185
  },
185
186
  aiMessage: {
186
187
  alignSelf: 'flex-start',