import React, { useState, useEffect, useRef } from "react"; import customRF from '../utils/customRF'; import { View, Text, StyleSheet, TextInput, TouchableOpacity, FlatList, Platform, ImageBackground, StatusBar, SafeAreaView, Modal, Animated } from "react-native"; import { useNavigation } from "@react-navigation/native"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { chatService } from "../services/api/chat"; import useUserStore from "../store/user"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { t } from "../i18n"; interface Message { id?: string; mimetype: string; userWs: string; app_id: string; country: number; body: string; text: string; type: string; isMe?: boolean; timestamp?: Date; } type TabType = "customer" | "product" | "notification"; type RootStackParamList = { Login: undefined; // other screens... }; export const ChatScreen = () => { const [messages, setMessages] = useState([]); const [inputText, setInputText] = useState(""); const [activeTab, setActiveTab] = useState("customer"); const [country, setCountry] = useState(""); // Store the country code const { user } = useUserStore(); const navigation = useNavigation>(); // Add FlatList ref for auto-scrolling const flatListRef = useRef(null); // Auto-scroll to bottom when messages change useEffect(() => { if (messages.length > 0 && user.user_id) { setTimeout(() => { flatListRef.current?.scrollToEnd({ animated: true }); }, 100); } }, [messages]); // Get country from AsyncStorage useEffect(() => { const getCountry = async () => { try { const selectedCountry = await AsyncStorage.getItem('@selected_country'); if (selectedCountry) { const countryData = JSON.parse(selectedCountry); setCountry(countryData.name_en || ""); } } catch (error) { console.error('Error getting country data:', error); } }; // Only get country if user is logged in if (user.user_id) { getCountry(); } }, [user.user_id]); // 添加导航到登录页面的函数 const goToLogin = () => { navigation.navigate("Login"); }; const sendMessage = () => { // 如果用户未登录,直接返回 if (!user.user_id) { return; } if (inputText.trim() === "") return; const newMessage: Message = { mimetype: "text/plain", userWs: "unknown", app_id: user.user_id ? user.user_id.toString() : "", country: user.country_code, body: "", text: inputText, type: "text", isMe: true, timestamp: new Date(), id: Date.now().toString(), // Add unique id for keyExtractor }; // Extract only the properties that chatService.sendMessage expects const chatServiceMessage = { type: newMessage.type, mimetype: newMessage.mimetype, userWs: newMessage.userWs, app_id: newMessage.app_id, country: newMessage.country.toString(), body: newMessage.body, text: newMessage.text }; // Add user message to the chat UI setMessages([...messages, newMessage]); setInputText(""); // Add simulated response with loading indicator const simulatedId = `simulated-${Date.now()}`; const simulatedResponse: Message = { mimetype: "text/plain", userWs: "system", app_id: "system", country: user.country_code, body: "", text: `${t('chat.typing_message')}...`, type: "chat", isMe: false, timestamp: new Date(), id: simulatedId, }; // Add simulated message after a short delay to make it feel more natural setTimeout(() => { setMessages(prevMessages => [...prevMessages, simulatedResponse]); }, 800); const data = { newMessage:chatServiceMessage, } // Send actual message to API chatService.sendMessage(data) .then(response => { // When real response arrives, replace simulated message setMessages(prevMessages => { // Filter out the simulated message and add real response const filtered = prevMessages.filter(msg => msg.id !== simulatedId); // Create the real response message object const realResponse: Message = { mimetype: "text/plain", userWs: "system", app_id: "system", country: user.country_code, body: "", text: response?.reply || t('chat.default_response'), type: "chat", isMe: false, timestamp: new Date(), id: `real-${Date.now()}`, }; return [...filtered, realResponse]; }); }) .catch(error => { // In case of error, replace simulated message with error message console.error('Chat API error:', error); setMessages(prevMessages => { const filtered = prevMessages.filter(msg => msg.id !== simulatedId); const errorResponse: Message = { mimetype: "text/plain", userWs: "system", app_id: "system", country: user.country_code, body: "", text: t('chat.error_response'), type: "chat", isMe: false, timestamp: new Date(), id: `error-${Date.now()}`, }; return [...filtered, errorResponse]; }); }); }; // Generate a unique key for each message const keyExtractor = (item: Message, index: number): string => { return item.id || index.toString(); }; const renderMessage = ({ item }: { item: Message }) => ( {item.text} {item.timestamp?.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", })} ); const renderTabContent = () => { switch (activeTab) { case "customer": return ( ); case "product": return ( ); case "notification": return ( ); } }; return ( user.user_id && setActiveTab("customer")} disabled={!user.user_id} > {t('chat.customer_service')} user.user_id && setActiveTab("product")} disabled={!user.user_id} > {t('chat.product_support')} user.user_id && setActiveTab("notification")} disabled={!user.user_id} > {t('chat.notifications')} {renderTabContent()} {t('chat.send')} {/* 未登录遮罩 */} {!user.user_id && ( 💬 {t("chat.login_required_title", "请先登录")} {t("chat.login_required_subtitle", "登录后即可使用聊天功能")} {t("chat.login_now", "立即登录")} )} ); }; const styles = StyleSheet.create({ safeArea: { flex: 1, backgroundColor: '#fff', }, safeAreaContent: { flex: 1, paddingTop: Platform.OS === 'android' ? 0 : 0, }, container: { flex: 1, backgroundColor: '#fff', }, backgroundImage: { flex: 1, width: '100%', height: '100%', }, tabBar: { flexDirection: "row", backgroundColor: "#007a6c", borderBottomWidth: 1, borderBottomColor: "#eef0f1", shadowColor: "#000", shadowOffset: { width: 0, height: 2, }, shadowOpacity: 0.25, shadowRadius: 3.84, elevation: 5, }, tab: { flex: 1, paddingVertical: 15, alignItems: "center", }, activeTab: { borderBottomWidth: 2, borderBottomColor: "#eef0f1", }, tabText: { fontSize: customRF(14), color: "#fff", }, activeTabText: { color: "#fff", fontWeight: "600", }, tabContent: { flex: 1, }, messageList: { padding: 10, }, messageContainer: { maxWidth: "80%", padding: 10, borderRadius: 10, marginVertical: 5, }, myMessage: { alignSelf: "flex-end", backgroundColor: "#dcf8c6", }, theirMessage: { alignSelf: "flex-start", backgroundColor: "white", }, messageText: { fontSize: 16, }, timestamp: { fontSize: 12, color: "#666", alignSelf: "flex-end", marginTop: 5, }, inputContainer: { flexDirection: "row", padding: 10, backgroundColor: "white", borderTopWidth: 1, borderTopColor: "#ddd", }, input: { flex: 1, backgroundColor: "#f0f0f0", borderRadius: 20, paddingHorizontal: 15, paddingVertical: 8, marginRight: 10, maxHeight: 100, }, sendButton: { backgroundColor: "#128C7E", borderRadius: 20, paddingHorizontal: 20, justifyContent: "center", }, sendButtonText: { color: "white", fontSize: 16, }, loginOverlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(255, 255, 255, 0.9)', backdropFilter: 'blur(10px)', // iOS 毛玻璃效果 justifyContent: 'center', alignItems: 'center', zIndex: 1000, }, blurContainer: { width: '100%', height: '100%', justifyContent: 'center', alignItems: 'center', backgroundColor: Platform.OS === 'android' ? 'rgba(255, 255, 255, 0.95)' : 'transparent', }, loginPromptContainer: { backgroundColor: 'white', borderRadius: 20, padding: 40, alignItems: 'center', shadowColor: '#000', shadowOffset: { width: 0, height: 2, }, shadowOpacity: 0.25, shadowRadius: 3.84, elevation: 5, maxWidth: '80%', }, loginIcon: { width: 80, height: 80, marginBottom: 20, justifyContent: 'center', alignItems: 'center', backgroundColor: 'rgba(0, 122, 108, 0.1)', borderRadius: 40, }, loginIconText: { fontSize: 40, fontWeight: 'bold', color: '#007a6c', }, loginPromptTitle: { fontSize: 24, fontWeight: '700', color: '#333', marginBottom: 10, textAlign: 'center', }, loginPromptSubtitle: { fontSize: 16, color: '#666', marginBottom: 30, textAlign: 'center', lineHeight: 22, }, loginButton: { backgroundColor: '#007a6c', paddingHorizontal: 40, paddingVertical: 15, borderRadius: 25, minWidth: 160, }, loginButtonText: { color: 'white', fontSize: 18, fontWeight: '700', textAlign: 'center', }, disabledInput: { opacity: 0.6, }, disabledButton: { opacity: 0.6, }, });