import React, { useCallback, useEffect, useRef, useState } from 'react' import { View, FlatList, StyleSheet, Alert, SafeAreaView, KeyboardAvoidingView, Platform, TouchableOpacity, Text, } from 'react-native' import type { NativeStackScreenProps } from '@react-navigation/native-stack' import { ImSDK } from '@xuqm/rn-sdk' import type { ImMessage, ImEventListener } from '@xuqm/rn-sdk' import { useAuth } from '../../context/AuthContext' import type { RootStackParams } from '../../navigation/types' import { MessageBubble } from '../../components/MessageBubble' import ChatInput from '../../components/ChatInput' type Props = NativeStackScreenProps export default function SingleChatScreen({ route, navigation }: Props) { const { targetId, targetName, targetAvatar } = route.params const { userId, profile } = useAuth() const [messages, setMessages] = useState([]) const [page, setPage] = useState(0) const [loadingMore, setLoadingMore] = useState(false) const listRef = useRef>(null) const loadHistory = useCallback(async (p = 0) => { try { const history = await ImSDK.fetchHistory(targetId, p, 30) setMessages(prev => p === 0 ? history : [...history, ...prev]) setPage(p) } catch {/* ignore */} }, [targetId]) useEffect(() => { loadHistory(0) const listener: ImEventListener = { onMessage(msg) { if ((msg.fromUserId === targetId && msg.toId === userId) || (msg.fromUserId === userId && msg.toId === targetId)) { setMessages(prev => { if (prev.find(m => m.id === msg.id)) return prev return [...prev, msg] }) setTimeout(() => listRef.current?.scrollToEnd({ animated: true }), 100) } }, } ImSDK.addListener(listener) ImSDK.markRead(targetId).catch(() => {}) return () => ImSDK.removeListener(listener) }, [targetId, userId, loadHistory]) const onSent = (msg: ImMessage) => { setMessages(prev => { if (prev.find(m => m.id === msg.id)) return prev return [...prev, msg] }) setTimeout(() => listRef.current?.scrollToEnd({ animated: true }), 100) } const handleRevoke = async (messageId: string) => { try { const revoked = await ImSDK.revokeMessage(messageId) setMessages(prev => prev.map(m => m.id === revoked.id ? revoked : m)) } catch (e: any) { Alert.alert('撤回失败', e?.message ?? '操作失败') } } const handleLongPress = (msg: ImMessage) => { if (msg.fromUserId !== userId) return if (msg.status === 'REVOKED' || msg.msgType === 'REVOKED') return Alert.alert('操作', '撤回这条消息?', [ { text: '取消', style: 'cancel' }, { text: '撤回', style: 'destructive', onPress: () => handleRevoke(msg.id) }, ]) } const loadMore = async () => { if (loadingMore) return setLoadingMore(true) await loadHistory(page + 1) setLoadingMore(false) } return ( m.id} renderItem={({ item }) => ( )} contentContainerStyle={styles.list} onEndReachedThreshold={0.1} onEndReached={loadMore} maintainVisibleContentPosition={{ minIndexForVisible: 0 }} /> ) } const styles = StyleSheet.create({ root: { flex: 1, backgroundColor: '#f0ede8' }, flex: { flex: 1 }, list: { padding: 8, paddingBottom: 4 }, })