react-native-srschat 0.1.5 → 0.1.8

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 (61) hide show
  1. package/lib/commonjs/assets/heritage.png +0 -0
  2. package/lib/commonjs/components/header.js +105 -0
  3. package/lib/commonjs/components/header.js.map +1 -0
  4. package/lib/commonjs/components/testing.js +58 -0
  5. package/lib/commonjs/components/testing.js.map +1 -0
  6. package/lib/commonjs/contexts/AppContext.js +99 -0
  7. package/lib/commonjs/contexts/AppContext.js.map +1 -0
  8. package/lib/commonjs/hooks/Stream.js +202 -0
  9. package/lib/commonjs/hooks/Stream.js.map +1 -0
  10. package/lib/commonjs/index.js +14 -154
  11. package/lib/commonjs/index.js.map +1 -1
  12. package/lib/commonjs/layout/chatIcon.js +53 -0
  13. package/lib/commonjs/layout/chatIcon.js.map +1 -0
  14. package/lib/commonjs/layout/chatWindow.js +208 -0
  15. package/lib/commonjs/layout/chatWindow.js.map +1 -0
  16. package/lib/commonjs/layout/layout.js +35 -0
  17. package/lib/commonjs/layout/layout.js.map +1 -0
  18. package/lib/module/assets/heritage.png +0 -0
  19. package/lib/module/components/header.js +96 -0
  20. package/lib/module/components/header.js.map +1 -0
  21. package/lib/module/components/testing.js +50 -0
  22. package/lib/module/components/testing.js.map +1 -0
  23. package/lib/module/contexts/AppContext.js +90 -0
  24. package/lib/module/contexts/AppContext.js.map +1 -0
  25. package/lib/module/hooks/Stream.js +194 -0
  26. package/lib/module/hooks/Stream.js.map +1 -0
  27. package/lib/module/index.js +13 -154
  28. package/lib/module/index.js.map +1 -1
  29. package/lib/module/layout/chatIcon.js +44 -0
  30. package/lib/module/layout/chatIcon.js.map +1 -0
  31. package/lib/module/layout/chatWindow.js +199 -0
  32. package/lib/module/layout/chatWindow.js.map +1 -0
  33. package/lib/module/layout/layout.js +26 -0
  34. package/lib/module/layout/layout.js.map +1 -0
  35. package/lib/typescript/components/header.d.ts +3 -0
  36. package/lib/typescript/components/header.d.ts.map +1 -0
  37. package/lib/typescript/components/testing.d.ts +6 -0
  38. package/lib/typescript/components/testing.d.ts.map +1 -0
  39. package/lib/typescript/contexts/AppContext.d.ts +6 -0
  40. package/lib/typescript/contexts/AppContext.d.ts.map +1 -0
  41. package/lib/typescript/hooks/Stream.d.ts +2 -0
  42. package/lib/typescript/hooks/Stream.d.ts.map +1 -0
  43. package/lib/typescript/index.d.ts +5 -11
  44. package/lib/typescript/index.d.ts.map +1 -1
  45. package/lib/typescript/layout/chatIcon.d.ts +3 -0
  46. package/lib/typescript/layout/chatIcon.d.ts.map +1 -0
  47. package/lib/typescript/layout/chatWindow.d.ts +6 -0
  48. package/lib/typescript/layout/chatWindow.d.ts.map +1 -0
  49. package/lib/typescript/layout/layout.d.ts +6 -0
  50. package/lib/typescript/layout/layout.d.ts.map +1 -0
  51. package/package.json +5 -3
  52. package/src/assets/heritage.png +0 -0
  53. package/src/components/header.js +89 -0
  54. package/src/components/testing.js +47 -0
  55. package/src/contexts/AppContext.js +83 -0
  56. package/src/hooks/Stream.js +198 -0
  57. package/src/index.js +18 -0
  58. package/src/layout/chatIcon.js +38 -0
  59. package/src/layout/chatWindow.js +200 -0
  60. package/src/layout/layout.js +23 -0
  61. package/src/index.tsx +0 -194
@@ -0,0 +1,198 @@
1
+ import React, { useState, useEffect, useRef, useContext } from 'react';
2
+ import { AppContext } from "../contexts/AppContext";
3
+
4
+ export function useWebSocketMessage() {
5
+ const { setIsComplete, startStreaming, setMessages, messages, setGhostMessage, isMobile,
6
+ setTypingIndicator, setStartStreaming, lastUserMessage, stopActivated, custEmail, custName, data,
7
+ setGhostCard, BASE_URL, setStopActivated, setLastMessageId } = useContext(AppContext);
8
+
9
+ const wsProtocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
10
+ const wsUrl = BASE_URL.replace(/^http(s)?:\/\//, '');
11
+ const ENDPOINT = '/send/event';
12
+
13
+ const payload = {
14
+ ...data,
15
+ user_query: lastUserMessage,
16
+ customer_name: data.customer_name
17
+ };
18
+
19
+ const wsRef = useRef(null);
20
+
21
+ useEffect(() => {
22
+ if (startStreaming) {
23
+ console.log(payload)
24
+ const socketUrl = `${wsProtocol}${wsUrl}${ENDPOINT}`;
25
+ const ws = new WebSocket(socketUrl);
26
+ wsRef.current = ws;
27
+
28
+ ws.onopen = () => {
29
+ console.log("WebSocket connection established.");
30
+ ws.send(JSON.stringify(payload));
31
+ };
32
+
33
+ ws.onmessage = (event) => {
34
+ const response = JSON.parse(event.data);
35
+ console.log(response)
36
+ switch (response.type) {
37
+ case 'middle_message':
38
+ const middleMessage = {
39
+ type: "middle",
40
+ text: response.message,
41
+ products: [],
42
+ product_cards: "False",
43
+ }
44
+ setMessages([...messages, middleMessage])
45
+ break;
46
+ case 'message':
47
+ if (response.product_cards == "False" || response.product_cards == false ) {
48
+ setGhostMessage(false);
49
+ setTypingIndicator(false)
50
+ } else {
51
+ setGhostMessage(false)
52
+ setGhostCard(true)
53
+ }
54
+ const newMessage = {
55
+ type: "ai",
56
+ message_id: response.message_id || '',
57
+ text: [response.message],
58
+ feedback: "True",
59
+ products: [],
60
+ product_cards: response.product_cards || "False",
61
+ resource: response?.resource_details?.[0]?.link ?? "",
62
+ resource_type: response?.resource_details?.[0]?.type ?? ""
63
+ };
64
+ setMessages((prevMessages) => [...prevMessages, newMessage]);
65
+ setLastMessageId(response.message_id)
66
+ break;
67
+ case 'chunk':
68
+ const newContent = response.chunk;
69
+ const newMessageId = response.message_id;
70
+ setMessages(prevMessages => {
71
+ const lastMessageIndex = prevMessages.length - 1;
72
+ if (prevMessages[lastMessageIndex] && prevMessages[lastMessageIndex].type === "ai") {
73
+ const updatedLastMessage = {
74
+ ...prevMessages[lastMessageIndex],
75
+ text: [prevMessages[lastMessageIndex].text[0] + newContent],
76
+ message_id: newMessageId || prevMessages[lastMessageIndex].message_id,
77
+ feedback: "True"
78
+ };
79
+ return [
80
+ ...prevMessages.slice(0, lastMessageIndex),
81
+ updatedLastMessage
82
+ ];
83
+ } else {
84
+ setGhostMessage(false);
85
+ return [...prevMessages, {
86
+ type: "ai",
87
+ text: [newContent],
88
+ message_id: newMessageId,
89
+ feedback: "True"
90
+ }];
91
+ }
92
+ });
93
+ if (newMessageId) {
94
+ setLastMessageId(newMessageId);
95
+ }
96
+ break;
97
+ case 'product_cards':
98
+ setMessages(prevMessages => {
99
+ const lastMessageIndex = prevMessages.length - 1;
100
+ if (prevMessages[lastMessageIndex] && prevMessages[lastMessageIndex].type === "ai") {
101
+ const updatedLastMessage = {
102
+ ...prevMessages[lastMessageIndex],
103
+ products: response.products || [],
104
+ product_cards: "True"
105
+ };
106
+ return [
107
+ ...prevMessages.slice(0, lastMessageIndex),
108
+ updatedLastMessage
109
+ ];
110
+ }
111
+ });
112
+ setGhostMessage(false);
113
+ setTypingIndicator(false)
114
+ setGhostCard(false)
115
+ break;
116
+ case 'product_document':
117
+ setMessages(prevMessages => {
118
+ const lastMessageIndex = prevMessages.length - 1;
119
+ if (prevMessages[lastMessageIndex] && prevMessages[lastMessageIndex].type === "ai") {
120
+ const updatedLastMessage = {
121
+ ...prevMessages[lastMessageIndex],
122
+ resource: response?.resource_details?.[0]?.link ?? "",
123
+ resource_type: response?.resource_details?.[0]?.type ?? ""
124
+ };
125
+ return [
126
+ ...prevMessages.slice(0, lastMessageIndex),
127
+ updatedLastMessage
128
+ ];
129
+ }
130
+ });
131
+ setGhostMessage(false);
132
+ setTypingIndicator(false)
133
+ setGhostCard(false)
134
+ break;
135
+ case 'suggested_questions':
136
+ /* const questions = {
137
+ type: "questions",
138
+ message_id: response.message_id || '',
139
+ suggested_questions: response.suggested_questions || [],
140
+ }; */
141
+ setMessages(prevMessages => {
142
+ const lastMessageIndex = prevMessages.length - 1;
143
+ if (prevMessages[lastMessageIndex] && prevMessages[lastMessageIndex].type === "ai") {
144
+ const updatedLastMessage = {
145
+ ...prevMessages[lastMessageIndex],
146
+ suggested_questions: true,
147
+ questions: response.suggested_questions
148
+ };
149
+ return [
150
+ ...prevMessages.slice(0, lastMessageIndex),
151
+ updatedLastMessage
152
+ ];
153
+ }
154
+ });
155
+ //setMessages((prevMessages) => [...prevMessages, questions]);
156
+ break;
157
+ case 'setComplete':
158
+ setIsComplete(true);
159
+ setTypingIndicator(false);
160
+ setStartStreaming(false)
161
+ setGhostCard(false)
162
+ break;
163
+ }
164
+ };
165
+
166
+ ws.onerror = (error) => {
167
+ setStartStreaming(false)
168
+ setGhostMessage(false);
169
+ setTypingIndicator(false)
170
+ setGhostCard(false)
171
+ };
172
+
173
+ ws.onclose = (event) => {
174
+ console.log("WebSocket connection closed:", event.reason);
175
+ setStartStreaming(false)
176
+ setGhostMessage(false);
177
+ setTypingIndicator(false)
178
+ setGhostCard(false)
179
+ };
180
+
181
+ return () => {
182
+ ws.close();
183
+ };
184
+ };
185
+ }, [startStreaming]);
186
+
187
+ useEffect(() => {
188
+ if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
189
+ wsRef.current.close();
190
+ setStartStreaming(false);
191
+ setGhostMessage(false)
192
+ setGhostCard(false)
193
+ setTypingIndicator(false)
194
+ setStopActivated(false)
195
+ }
196
+ }, [stopActivated, setStartStreaming]);
197
+
198
+ }
package/src/index.js ADDED
@@ -0,0 +1,18 @@
1
+ import React, { useState, useCallback } from 'react';
2
+ import { SafeAreaView, Text, StyleSheet, View, TextInput, ScrollView, KeyboardAvoidingView,
3
+ Platform, TouchableOpacity, RefreshControl, } from 'react-native';
4
+ import { Ionicons as Icon } from '@expo/vector-icons';
5
+ import { AppProvider } from './contexts/AppContext';
6
+ import { Layout } from './layout/layout'
7
+
8
+ export const Chat = ({data, onProductCardClick, onAddToCartClick }) => {
9
+
10
+ return (
11
+ <AppProvider>
12
+ <Layout onProductCardClick={onProductCardClick} onAddToCartClick={onAddToCartClick}/>
13
+ </AppProvider>
14
+ );
15
+ };
16
+
17
+ const styles = StyleSheet.create({
18
+ });
@@ -0,0 +1,38 @@
1
+ import React, { useContext } from 'react';
2
+ import { TouchableOpacity, View, StyleSheet } from 'react-native';
3
+ import { Ionicons as Icon } from '@expo/vector-icons';
4
+ import { AppContext } from '../contexts/AppContext';
5
+
6
+ export const ChatIcon = () => {
7
+ const { setShowModal } = useContext(AppContext);
8
+
9
+ return (
10
+ <TouchableOpacity style={styles.iconContainer} onPress={() => setShowModal("ChatWindow")}>
11
+ <View style={styles.icon}>
12
+ <Icon name="chatbubble-ellipses" size={28} color="white" />
13
+ </View>
14
+ </TouchableOpacity>
15
+ );
16
+ };
17
+
18
+ const styles = StyleSheet.create({
19
+ iconContainer: {
20
+ position: 'absolute',
21
+ bottom: 80,
22
+ right: 20,
23
+ zIndex: 10,
24
+ },
25
+ icon: {
26
+ backgroundColor: '#FFA500',
27
+ width: 60,
28
+ height: 60,
29
+ borderRadius: 30,
30
+ alignItems: 'center',
31
+ justifyContent: 'center',
32
+ shadowColor: '#000',
33
+ shadowOffset: { width: 0, height: 2 },
34
+ shadowOpacity: 0.2,
35
+ shadowRadius: 3,
36
+ elevation: 5,
37
+ },
38
+ });
@@ -0,0 +1,200 @@
1
+ import React, { useState, useCallback, useContext } from 'react';
2
+ import { SafeAreaView, Text, StyleSheet, View, TextInput, ScrollView, KeyboardAvoidingView,
3
+ Platform, TouchableOpacity, RefreshControl, } from 'react-native';
4
+ import { Ionicons as Icon } from '@expo/vector-icons';
5
+ import { Header } from '../components/header';
6
+ import { AppContext } from '../contexts/AppContext';
7
+ import { Testing } from '../components/testing';
8
+
9
+ const theme = {
10
+ primaryColor: '#003764',
11
+ backgroundColor: '#f6f6f6',
12
+ textColor: '#000000',
13
+ inputBackgroundColor: '#f6f6f6',
14
+ };
15
+
16
+ export const ChatWindow = ({ onProductCardClick, onAddToCartClick }) => {
17
+ const { handleSend, messages, setMessages, onSendMessage, input, setInput } = useContext(AppContext)
18
+
19
+ const [refreshing, setRefreshing] = useState(false);
20
+
21
+ const onRefresh = useCallback(async () => {
22
+ setRefreshing(true);
23
+ try {
24
+ const response = await onSendMessage("Hi, I'm refreshing the chat!");
25
+ setMessages((prev) => [...prev, { text: response, isUser: false }]);
26
+ } catch (error) {
27
+ console.error('Error refreshing chat:', error);
28
+ } finally {
29
+ setRefreshing(false);
30
+ }
31
+ }, []);
32
+
33
+ const getMessageStyle = (type) => [
34
+ styles.messageBubble,
35
+ type === "user" ? styles.userMessage : styles.aiMessage,
36
+ { backgroundColor: type === "user" ? theme.primaryColor : '#E8E8E8' },
37
+ ];
38
+
39
+ const getTextStyle = (type) => [
40
+ styles.messageText,
41
+ { color: type === "user" ? '#fff' : theme.textColor },
42
+ ];
43
+
44
+ return (
45
+ <SafeAreaView style={[styles.container, { backgroundColor: theme.backgroundColor }]}>
46
+ <View style={styles.chatWindow}>
47
+ <Header />
48
+
49
+ <KeyboardAvoidingView
50
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
51
+ style={styles.content}
52
+ keyboardVerticalOffset={Platform.OS === 'ios' ? 60 : 0}
53
+ >
54
+ <ScrollView
55
+ style={styles.messagesContainer}
56
+ contentContainerStyle={styles.messagesContent}
57
+ refreshControl={
58
+ <RefreshControl
59
+ refreshing={refreshing}
60
+ onRefresh={onRefresh}
61
+ tintColor={theme.primaryColor}
62
+ colors={[theme.primaryColor]}
63
+ progressBackgroundColor="#ffffff"
64
+ />
65
+ }
66
+ >
67
+ {messages.map((msg, i) => (
68
+ <View key={i} style={styles.messageWrapper}>
69
+ <View style={[getMessageStyle(msg.type), styles.messageShadow]}>
70
+ <Text style={getTextStyle(msg.type)}>{msg.text}</Text>
71
+ </View>
72
+ </View>
73
+ ))}
74
+ </ScrollView>
75
+
76
+ <Testing
77
+ onProductCardClick={onProductCardClick}
78
+ onAddToCartClick={onAddToCartClick}
79
+ />
80
+ <View style={styles.inputWrapper}>
81
+ <View style={styles.inputContainer}>
82
+ <TextInput
83
+ style={styles.input}
84
+ value={input}
85
+ onChangeText={setInput}
86
+ placeholder="Ask a question..."
87
+ placeholderTextColor="#999"
88
+ multiline
89
+ />
90
+ <TouchableOpacity style={styles.inputButton}>
91
+ <Icon name="mic-outline" size={24} color="#8E8E93" />
92
+ </TouchableOpacity>
93
+ <TouchableOpacity
94
+ style={[styles.sendButton, !input.trim() && styles.disabledButton]}
95
+ onPress={handleSend}
96
+ disabled={!input.trim()}
97
+ >
98
+ <Icon name="paper-plane-outline" size={24} color={input.trim() ? theme.primaryColor : '#8E8E93'} />
99
+ </TouchableOpacity>
100
+ </View>
101
+ </View>
102
+ </KeyboardAvoidingView>
103
+ </View>
104
+ </SafeAreaView>
105
+ );
106
+ };
107
+
108
+ const styles = StyleSheet.create({
109
+ container: {
110
+ flex: 1,
111
+ backgroundColor: '#FFFFFF',
112
+ },
113
+ chatWindow: {
114
+ zIndex: 1,
115
+ flex: 1,
116
+ marginTop: 40,
117
+ borderTopWidth: 1,
118
+ borderTopColor: '#DDD',
119
+ borderTopLeftRadius: 16,
120
+ borderTopRightRadius: 16,
121
+ overflow: 'hidden',
122
+ backgroundColor: theme.backgroundColor,
123
+ },
124
+ content: {
125
+ flex: 1,
126
+ },
127
+ messagesContainer: {
128
+ flex: 1,
129
+ },
130
+ messagesContent: {
131
+ padding: 16,
132
+ paddingBottom: 32,
133
+ },
134
+ messageWrapper: {
135
+ marginBottom: 16,
136
+ },
137
+ messageBubble: {
138
+ maxWidth: '90%',
139
+ padding: 12,
140
+ paddingHorizontal: 16,
141
+ borderRadius: 20,
142
+ },
143
+ messageShadow: {
144
+ shadowColor: '#000',
145
+ shadowOffset: { width: 0, height: 1 },
146
+ shadowOpacity: 0.08,
147
+ shadowRadius: 2,
148
+ elevation: 2,
149
+ },
150
+ userMessage: {
151
+ alignSelf: 'flex-end',
152
+ backgroundColor: theme.primaryColor,
153
+ },
154
+ aiMessage: {
155
+ alignSelf: 'flex-start',
156
+ backgroundColor: '#F2F2F7',
157
+ },
158
+ messageText: {
159
+ fontSize: 16,
160
+ lineHeight: 22,
161
+ },
162
+ inputWrapper: {
163
+ backgroundColor: '#f6f6f6',
164
+ paddingHorizontal: 8,
165
+ paddingVertical: 8,
166
+ borderTopWidth: 1,
167
+ borderTopColor: 'rgba(0, 0, 0, 0.1)',
168
+ },
169
+ inputContainer: {
170
+ flexDirection: 'row',
171
+ alignItems: 'center',
172
+ paddingHorizontal: 8,
173
+ paddingVertical: 8,
174
+ backgroundColor: '#FFFFFF',
175
+ borderRadius: 16,
176
+ margin: 8,
177
+ shadowColor: '#000',
178
+ shadowOffset: {
179
+ width: 0,
180
+ height: 2,
181
+ },
182
+ shadowOpacity: 0.1,
183
+ shadowRadius: 4,
184
+ elevation: 3,
185
+ },
186
+ input: {
187
+ flex: 1,
188
+ fontSize: 16,
189
+ paddingVertical: 8,
190
+ paddingHorizontal: 12,
191
+ color: '#000000',
192
+ },
193
+ sendButton: {
194
+ padding: 6,
195
+ marginLeft: 'auto',
196
+ },
197
+ disabledButton: {
198
+ opacity: 0.7,
199
+ },
200
+ });
@@ -0,0 +1,23 @@
1
+ import React, { useState, useCallback, useContext } from 'react';
2
+ import { SafeAreaView, Text, StyleSheet, View, TextInput, ScrollView, KeyboardAvoidingView,
3
+ Platform, TouchableOpacity, RefreshControl, } from 'react-native';
4
+ import { Ionicons as Icon } from '@expo/vector-icons';
5
+ import { ChatWindow } from './chatWindow';
6
+ import { AppContext } from '../contexts/AppContext';
7
+ import { ChatIcon } from './chatIcon';
8
+
9
+ export const Layout = ({ onProductCardClick, onAddToCartClick }) => {
10
+ const { showModal } = useContext(AppContext);
11
+
12
+ return (
13
+ <View style={styles.container}>
14
+ {showModal === "Icon" ? <ChatIcon /> : <ChatWindow onProductCardClick={onProductCardClick} onAddToCartClick={onAddToCartClick}/>}
15
+ </View>
16
+ );
17
+ };
18
+
19
+ const styles = StyleSheet.create({
20
+ container: {
21
+ flex: 1
22
+ }
23
+ });
package/src/index.tsx DELETED
@@ -1,194 +0,0 @@
1
- import React from 'react';
2
- import {
3
- SafeAreaView,
4
- Text,
5
- StyleSheet,
6
- View,
7
- TextInput,
8
- ScrollView,
9
- KeyboardAvoidingView,
10
- Platform,
11
- Button,
12
- } from 'react-native';
13
-
14
- interface ChatProps {
15
- onSendMessage: (message: string) => Promise<string>;
16
- placeholder?: string;
17
- theme?: {
18
- primaryColor?: string;
19
- backgroundColor?: string;
20
- textColor?: string;
21
- };
22
- }
23
-
24
- const defaultTheme = {
25
- primaryColor: '#007AFF',
26
- backgroundColor: '#FFFFFF',
27
- textColor: '#000000',
28
- };
29
-
30
- export const Chat: React.FC<ChatProps> = ({
31
- onSendMessage,
32
- theme = defaultTheme,
33
- }) => {
34
- const [message, setMessage] = React.useState('');
35
- const [messages, setMessages] = React.useState<Array<{text: string; isUser: boolean}>>([]);
36
-
37
- const handleSend = async () => {
38
- if (!message.trim()) return;
39
-
40
- const userMessage = message;
41
- setMessage('');
42
- setMessages(prev => [...prev, { text: userMessage, isUser: true }]);
43
-
44
- try {
45
- const response = await onSendMessage(userMessage);
46
- setMessages(prev => [...prev, { text: response, isUser: false }]);
47
- } catch (error) {
48
- console.error('Error sending message:', error);
49
- }
50
- };
51
-
52
- const containerStyle = [
53
- styles.container,
54
- {
55
- backgroundColor: theme.backgroundColor,
56
- },
57
- ];
58
-
59
- const getMessageStyle = (fromUser: boolean) => [
60
- styles.messageBubble,
61
- fromUser ? styles.userMessage : styles.botMessage,
62
- { backgroundColor: fromUser ? theme.primaryColor : '#E8E8E8' },
63
- ];
64
-
65
- const getTextStyle = (fromUser: boolean) => [
66
- styles.messageText,
67
- { color: fromUser ? '#fff' : theme.textColor },
68
- ];
69
-
70
- return (
71
- <SafeAreaView style={containerStyle} testID="chat-container">
72
- <KeyboardAvoidingView
73
- behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
74
- style={styles.content}
75
- >
76
- <ScrollView style={styles.messagesContainer}>
77
- {messages.map((msg, i) => (
78
- <View
79
- key={i}
80
- style={getMessageStyle(msg.isUser)}
81
- >
82
- <Text style={getTextStyle(msg.isUser)}>
83
- {msg.text}
84
- </Text>
85
- </View>
86
- ))}
87
- </ScrollView>
88
- <View style={styles.inputContainer}>
89
- <TextInput
90
- style={[styles.input, { color: theme.textColor }]}
91
- value={message}
92
- onChangeText={setMessage}
93
- placeholder="Type a message..."
94
- placeholderTextColor="#999"
95
- />
96
- <Button
97
- title="Send"
98
- onPress={handleSend}
99
- color={theme.primaryColor}
100
- />
101
- </View>
102
- </KeyboardAvoidingView>
103
- </SafeAreaView>
104
- );
105
- };
106
-
107
- const styles = StyleSheet.create({
108
- container: {
109
- flex: 1,
110
- backgroundColor: '#FFFFFF',
111
- },
112
- content: {
113
- flex: 1,
114
- backgroundColor: '#F8F9FA',
115
- },
116
- messagesContainer: {
117
- flex: 1,
118
- padding: 20,
119
- paddingTop: 30,
120
- },
121
- messageBubble: {
122
- maxWidth: '85%',
123
- padding: 16,
124
- borderRadius: 20,
125
- marginBottom: 16,
126
- backgroundColor: '#FFFFFF',
127
- shadowColor: '#000',
128
- shadowOffset: {
129
- width: 0,
130
- height: 2,
131
- },
132
- shadowOpacity: 0.05,
133
- shadowRadius: 8,
134
- elevation: 3,
135
- },
136
- userMessage: {
137
- alignSelf: 'flex-end',
138
- backgroundColor: '#FFFFFF',
139
- borderWidth: 1,
140
- borderColor: 'rgba(0, 0, 0, 0.04)',
141
- },
142
- botMessage: {
143
- alignSelf: 'flex-start',
144
- backgroundColor: '#FFFFFF',
145
- borderWidth: 1,
146
- borderColor: 'rgba(0, 0, 0, 0.04)',
147
- },
148
- messageText: {
149
- fontSize: 15,
150
- lineHeight: 22,
151
- fontWeight: '400',
152
- color: '#2C2C2E',
153
- letterSpacing: 0.2,
154
- },
155
- inputContainer: {
156
- flexDirection: 'row',
157
- alignItems: 'center',
158
- paddingHorizontal: 16,
159
- paddingVertical: 12,
160
- backgroundColor: '#FFFFFF',
161
- borderTopWidth: 1,
162
- borderTopColor: 'rgba(0, 0, 0, 0.06)',
163
- shadowColor: '#000',
164
- shadowOffset: {
165
- width: 0,
166
- height: -8,
167
- },
168
- shadowOpacity: 0.04,
169
- shadowRadius: 12,
170
- elevation: 8,
171
- marginTop: 'auto',
172
- },
173
- input: {
174
- flex: 1,
175
- backgroundColor: '#F8F9FA',
176
- borderRadius: 16,
177
- borderWidth: 1,
178
- borderColor: 'rgba(0, 0, 0, 0.06)',
179
- paddingHorizontal: 20,
180
- paddingVertical: 12,
181
- marginRight: 12,
182
- fontSize: 15,
183
- maxHeight: 120,
184
- color: '#2C2C2E',
185
- shadowColor: '#000',
186
- shadowOffset: {
187
- width: 0,
188
- height: 1,
189
- },
190
- shadowOpacity: 0.04,
191
- shadowRadius: 4,
192
- elevation: 2,
193
- },
194
- });