[V0.1 WORKING] Added chat, profile, & calendar screen implementations.
This commit is contained in:
@@ -1,19 +1,192 @@
|
||||
// src/screens/DashboardScreen.tsx
|
||||
import React from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import { Text, useTheme } from 'react-native-paper';
|
||||
// src/screens/ChatScreen.tsx
|
||||
import React, { useState, useCallback, useRef } from 'react';
|
||||
import { View, StyleSheet, FlatList, KeyboardAvoidingView, Platform, TextInput as RNTextInput, NativeSyntheticEvent, TextInputKeyPressEventData } from 'react-native';
|
||||
import { Text, useTheme, TextInput, Button, IconButton, PaperProvider } from 'react-native-paper';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import apiClient from '../api/client'; // Import the apiClient
|
||||
|
||||
const DashboardScreen = () => {
|
||||
// Define the structure for a message
|
||||
interface Message {
|
||||
id: string;
|
||||
text: string;
|
||||
sender: 'user' | 'ai';
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
const ChatScreen = () => {
|
||||
const theme = useTheme();
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
const [inputText, setInputText] = useState('');
|
||||
const [isLoading, setIsLoading] = useState(false); // To show activity indicator while AI responds
|
||||
const flatListRef = useRef<FlatList>(null);
|
||||
|
||||
// Function to handle sending a message
|
||||
const handleSend = useCallback(async () => {
|
||||
const trimmedText = inputText.trim();
|
||||
if (!trimmedText) return; // Don't send empty messages
|
||||
|
||||
const userMessage: Message = {
|
||||
id: Date.now().toString() + '-user',
|
||||
text: trimmedText,
|
||||
sender: 'user',
|
||||
timestamp: new Date(),
|
||||
};
|
||||
|
||||
setMessages(prevMessages => [...prevMessages, userMessage]);
|
||||
setInputText('');
|
||||
setIsLoading(true);
|
||||
|
||||
// Scroll to bottom after sending user message
|
||||
setTimeout(() => flatListRef.current?.scrollToEnd({ animated: true }), 100);
|
||||
|
||||
// --- Call Backend API ---
|
||||
try {
|
||||
console.log(`[ChatScreen] Sending to /nlp/process-command: ${trimmedText}`);
|
||||
const response = await apiClient.post<{ response: string }>('/nlp/process-command', { user_input: trimmedText });
|
||||
console.log("[ChatScreen] Received response:", response.data);
|
||||
|
||||
const aiResponse: Message = {
|
||||
id: Date.now().toString() + '-ai',
|
||||
// Assuming the backend returns the response text in a 'response' field
|
||||
text: response.data.response || "Sorry, I didn't get a valid response.",
|
||||
sender: 'ai',
|
||||
timestamp: new Date(),
|
||||
};
|
||||
setMessages(prevMessages => [...prevMessages, aiResponse]);
|
||||
} catch (error: any) {
|
||||
console.error("Failed to get AI response:", error.response?.data || error.message || error);
|
||||
const errorResponse: Message = {
|
||||
id: Date.now().toString() + '-error',
|
||||
text: 'Sorry, I encountered an error trying to reach MAIA.',
|
||||
sender: 'ai',
|
||||
timestamp: new Date(),
|
||||
};
|
||||
setMessages(prevMessages => [...prevMessages, errorResponse]);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
// Scroll to bottom after receiving AI message
|
||||
setTimeout(() => flatListRef.current?.scrollToEnd({ animated: true }), 100);
|
||||
}
|
||||
// --- End API Call ---
|
||||
|
||||
}, [inputText]); // Keep inputText as dependency
|
||||
|
||||
const handleKeyPress = (e: NativeSyntheticEvent<TextInputKeyPressEventData>) => {
|
||||
if (e.nativeEvent.key === 'Enter' && !(e.nativeEvent as any).shiftKey) {
|
||||
e.preventDefault(); // Prevent new line
|
||||
handleSend();
|
||||
}
|
||||
};
|
||||
|
||||
// Render individual message item
|
||||
const renderMessage = ({ item }: { item: Message }) => {
|
||||
const isUser = item.sender === 'user';
|
||||
return (
|
||||
<View style={[styles.messageBubble, isUser ? styles.userBubble : styles.aiBubble]}>
|
||||
<Text style={{ color: isUser ? theme.colors.onPrimary : theme.colors.onSurface }}>
|
||||
{item.text}
|
||||
</Text>
|
||||
{/* Optional: Add timestamp */}
|
||||
{/* <Text style={[styles.timestamp, { color: isUser ? theme.colors.onPrimary : theme.colors.onSurfaceVariant }]}>
|
||||
{item.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
||||
</Text> */}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: { flex: 1, alignItems: 'center', justifyContent: 'center', padding: 16, backgroundColor: theme.colors.background },
|
||||
text: { fontSize: 20, color: theme.colors.text }
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.colors.background,
|
||||
},
|
||||
listContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
messageList: {
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 10,
|
||||
},
|
||||
inputContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
padding: 8,
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
borderTopColor: theme.colors.outlineVariant,
|
||||
backgroundColor: theme.colors.elevation.level2, // Slightly elevated background
|
||||
},
|
||||
textInput: {
|
||||
flex: 1,
|
||||
marginRight: 8,
|
||||
backgroundColor: theme.colors.surface, // Use surface color for input background
|
||||
},
|
||||
messageBubble: {
|
||||
maxWidth: '80%',
|
||||
padding: 10,
|
||||
borderRadius: 15,
|
||||
marginBottom: 10,
|
||||
},
|
||||
userBubble: {
|
||||
alignSelf: 'flex-end',
|
||||
backgroundColor: theme.colors.primary,
|
||||
borderBottomRightRadius: 5,
|
||||
},
|
||||
aiBubble: {
|
||||
alignSelf: 'flex-start',
|
||||
backgroundColor: theme.colors.surfaceVariant,
|
||||
borderBottomLeftRadius: 5,
|
||||
},
|
||||
timestamp: {
|
||||
fontSize: 10,
|
||||
marginTop: 4,
|
||||
alignSelf: 'flex-end',
|
||||
opacity: 0.7,
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.text}>Chat</Text>
|
||||
</View>
|
||||
<SafeAreaView style={styles.container} edges={['bottom', 'left', 'right']}>
|
||||
<KeyboardAvoidingView
|
||||
style={{ flex: 1 }}
|
||||
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
||||
keyboardVerticalOffset={Platform.OS === "ios" ? 90 : 0} // Adjust as needed
|
||||
>
|
||||
<View style={styles.listContainer}>
|
||||
<FlatList
|
||||
ref={flatListRef}
|
||||
data={messages}
|
||||
renderItem={renderMessage}
|
||||
keyExtractor={(item) => item.id}
|
||||
contentContainerStyle={styles.messageList}
|
||||
onContentSizeChange={() => flatListRef.current?.scrollToEnd({ animated: false })} // Scroll on initial load/size change
|
||||
onLayout={() => flatListRef.current?.scrollToEnd({ animated: false })} // Scroll on layout change
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={styles.inputContainer}>
|
||||
<TextInput
|
||||
style={styles.textInput}
|
||||
value={inputText}
|
||||
onChangeText={setInputText}
|
||||
placeholder="Type your message..."
|
||||
mode="outlined" // Or "flat"
|
||||
multiline
|
||||
onKeyPress={handleKeyPress}
|
||||
blurOnSubmit={false}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<IconButton
|
||||
icon="send"
|
||||
size={24}
|
||||
onPress={handleSend}
|
||||
disabled={!inputText.trim() || isLoading}
|
||||
mode="contained"
|
||||
iconColor={theme.colors.onPrimary}
|
||||
containerColor={theme.colors.primary}
|
||||
/>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardScreen;
|
||||
export default ChatScreen;
|
||||
Reference in New Issue
Block a user