import React, {useState, useEffect} from 'react';
import {
View,
Text,
FlatList,
TextInput,
Button,
KeyboardAvoidingView,
Platform,
SafeAreaView,
StyleSheet,
TouchableOpacity,
StatusBar,
Image,
} from 'react-native';
import database from '@react-native-firebase/database';
import {Picker} from '@react-native-picker/picker';
import Icon from '@react-native-vector-icons/ionicons';
// Helper to produce consistent chat IDs
const getChatId = (a: string, b: string): string => {
const cleanA = a.replace(/\D/g, '');
const cleanB = b.replace(/\D/g, '');
return cleanA < cleanB ? `${cleanA}_${cleanB}` : `${cleanB}_${cleanA}`;
};
type Message = {text: string; sender: string; timestamp: number};
type DisplayMessage = Message & {translated: string};
export default function ChatScreen({route, navigation}) {
const {userData, language} = route.params;
const userPhone = route.params.callerPhone ?? '';
const otherPhone = route.params.calleePhone ?? '';
const [messages, setMessages] = useState<Message[]>([]);
const [displayed, setDisplayed] = useState<DisplayMessage[]>([]);
const [inputText, setInputText] = useState<string>('');
const [lang, setLang] = useState<string>(language);
// Listen for new messages
useEffect(() => {
if (!userPhone || !otherPhone) return;
const chatId = getChatId(userPhone, otherPhone);
const ref = database().ref(`/messages/${chatId}`);
setMessages([]);
const listener = ref.on('child_added', snap => {
const msg = snap.val() as Message;
setMessages(prev => [...prev, msg]);
});
return () => ref.off('child_added', listener);
}, [userPhone, otherPhone]);
// Translate messages when they arrive or lang changes
useEffect(() => {
(async () => {
if (!process.env.OPENAI_API_KEY) {
// no key, show original texts
setDisplayed(messages.map(m => ({...m, translated: m.text})));
return;
}
const translated: DisplayMessage[] = [];
for (const m of messages) {
if (lang === 'English') {
translated.push({...m, translated: m.text});
} else {
try {
const res = await fetch(
'https://api.openai.com/v1/chat/completions',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
},
body: JSON.stringify({
model: 'gpt-3.5-turbo',
messages: [
{
role: 'user',
content: `Translate to ${lang}:\n\n${m.text}`,
},
],
}),
},
);
const j = await res.json();
const out = j.choices?.[0]?.message?.content?.trim() ?? m.text;
translated.push({...m, translated: out});
} catch {
translated.push({...m, translated: m.text});
}
}
}
setDisplayed(translated);
})();
}, [messages, lang]);
// Send a new message
const sendMessage = () => {
if (!inputText.trim()) return;
const chatId = getChatId(userPhone, otherPhone);
const msg: Message = {
text: inputText.trim(),
sender: userPhone,
timestamp: Date.now(),
};
database()
.ref(`/messages/${chatId}`)
.push(msg)
.then(() => setInputText(''))
.catch(console.error);
};
const renderHeader = () => (
<View style={styles.header}>
<TouchableOpacity
style={styles.backButton}
onPress={() => navigation.goBack()}>
<Icon name="arrow-back" size={24} color="#000" />
</TouchableOpacity>
<View style={styles.profileContainer}>
<Image
source={{uri: 'https://randomuser.me/api/portraits/women/44.jpg'}}
style={styles.profileImage}
/>
<View style={styles.profileInfo}>
<Text style={styles.profileName}>
{userData?.userName ?? 'Unknown'}
</Text>
<Text style={styles.profileStatus}>Online</Text>
</View>
</View>
<View style={styles.headerIcons}>
<TouchableOpacity style={styles.iconButton}>
<Icon name="call" size={22} color="#000" />
</TouchableOpacity>
<TouchableOpacity style={styles.iconButton}>
<Icon name="ellipsis-vertical" size={22} color="#000" />
</TouchableOpacity>
</View>
</View>
);
const renderMessageDate = () => (
<View style={styles.dateContainer}>
<Text style={styles.dateText}>Today</Text>
</View>
);
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
{renderHeader()}
<Picker
selectedValue={lang}
onValueChange={setLang}
style={styles.picker}>
<Picker.Item label="English" value="English" />
<Picker.Item label="Hindi" value="Hindi" />
<Picker.Item label="Bengali" value="Bengali" />
<Picker.Item label="Gujarati" value="Gujarati" />
<Picker.Item label="Arabic" value="Arabic" />
</Picker>
<KeyboardAvoidingView
style={styles.keyboardAvoid}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
keyboardVerticalOffset={Platform.OS === 'ios' ? 90 : 0}>
<FlatList
style={styles.list}
contentContainerStyle={styles.listContent}
data={displayed}
keyExtractor={(item, index) => index.toString()}
ListHeaderComponent={renderMessageDate}
renderItem={({item}) => (
<View
style={[
styles.bubble,
item.sender === userPhone
? styles.myBubble
: styles.theirBubble,
]}>
<View style={styles.messageRow}>
<Text style={styles.messageMainText}>{item.translated}</Text>
<Text style={styles.messageMeta}>
{new Date(item.timestamp).toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
})}
</Text>
{item.sender === userPhone && (
<Icon
name="checkmark-done"
size={14}
color="#ccc"
style={{marginLeft: 3}}
/>
)}
</View>
</View>
)}
/>
<View style={styles.inputRow}>
<View style={styles.inputContainer}>
<TouchableOpacity style={styles.iconInputButton}>
<Icon name="happy-outline" size={24} color="#666" />
</TouchableOpacity>
<TextInput
style={styles.input}
placeholder="Message"
placeholderTextColor="#666"
value={inputText}
onChangeText={setInputText}
/>
<TouchableOpacity style={styles.iconInputButton}>
<Icon name="camera-outline" size={24} color="#666" />
</TouchableOpacity>
<TouchableOpacity style={styles.iconInputButton}>
<Icon name="mic-outline" size={24} color="#666" />
</TouchableOpacity>
</View>
<TouchableOpacity style={styles.sendButton} onPress={sendMessage}>
<Icon
name={inputText.trim() ? 'send' : 'add'}
size={24}
color="#fff"
/>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {flex: 1, backgroundColor: '#fff'},
keyboardAvoid: {flex: 1},
header: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 10,
paddingVertical: 10,
borderBottomWidth: 1,
borderBottomColor: '#eee',
marginTop: 10,
},
backButton: {padding: 5},
profileContainer: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
marginLeft: 10,
},
profileImage: {width: 40, height: 40, borderRadius: 20},
profileInfo: {marginLeft: 10},
profileName: {fontSize: 16, fontWeight: '600', color: '#000'},
profileStatus: {fontSize: 12, color: '#666'},
headerIcons: {flexDirection: 'row', alignItems: 'center'},
iconButton: {padding: 8},
dateContainer: {alignItems: 'center', marginVertical: 15},
dateText: {
fontSize: 13,
color: '#666',
backgroundColor: '#f0f0f0',
paddingHorizontal: 12,
paddingVertical: 4,
borderRadius: 10,
},
list: {flex: 1},
listContent: {paddingHorizontal: 15, paddingBottom: 15},
bubble: {marginVertical: 4, padding: 10, borderRadius: 18, maxWidth: '75%'},
myBubble: {
backgroundColor: '#000',
alignSelf: 'flex-end',
borderBottomRightRadius: 3,
},
theirBubble: {
backgroundColor: '#f0f0f0',
alignSelf: 'flex-start',
borderBottomLeftRadius: 3,
},
messageRow: {flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap'},
messageMainText: {
fontSize: 16,
fontWeight: '600',
color: '#fff',
marginRight: 6,
},
messageMeta: {fontSize: 12, color: '#ccc'},
inputRow: {
flexDirection: 'row',
alignItems: 'flex-end',
padding: 10,
borderTopColor: '#eee',
backgroundColor: '#fff',
marginBottom: 22,
},
inputContainer: {
flex: 1,
flexDirection: 'row',
backgroundColor: '#F7F4FA',
borderRadius: 25,
alignItems: 'center',
paddingHorizontal: 10,
borderWidth: 1,
},
input: {
flex: 1,
fontSize: 16,
color: '#000',
paddingVertical: 10,
paddingHorizontal: 5,
},
iconInputButton: {paddingHorizontal: 6},
sendButton: {
marginLeft: 10,
width: 46,
height: 46,
borderRadius: 23,
backgroundColor: '#000',
justifyContent: 'center',
alignItems: 'center',
},
picker: {
width: '100%',
marginTop: 10,
marginBottom: 5,
alignSelf: 'center',
},
});