import { View, Text, SectionList, TouchableOpacity, Animated, ActivityIndicator, StyleSheet, RefreshControl, TextInput } from 'react-native' import { SafeAreaView } from 'react-native-safe-area-context' import { useRouter } from 'expo-router' import { Ionicons } from '@expo/vector-icons' import { format, isToday, isYesterday } from 'date-fns' import { de } from 'date-fns/locale' import { trpc } from '@/lib/trpc' import { EmptyState } from '@/components/ui/EmptyState' import { Avatar } from '@/components/ui/Avatar' import { LoadingSpinner } from '@/components/ui/LoadingSpinner' import { useState, useEffect, useRef, useMemo, useCallback } from 'react' import { useFocusEffect } from 'expo-router' function SkeletonRow() { const anim = useRef(new Animated.Value(0.4)).current useEffect(() => { Animated.loop( Animated.sequence([ Animated.timing(anim, { toValue: 1, duration: 800, useNativeDriver: true }), Animated.timing(anim, { toValue: 0.4, duration: 800, useNativeDriver: true }), ]) ).start() }, []) return ( ) } const skeletonStyles = StyleSheet.create({ row: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 20, paddingVertical: 16, backgroundColor: '#FFFFFF', borderBottomWidth: 1, borderBottomColor: '#F1F5F9' }, avatar: { width: 48, height: 48, borderRadius: 24, backgroundColor: '#E2E8F0' }, lines: { flex: 1, marginLeft: 12, gap: 8 }, lineLong: { height: 14, borderRadius: 7, backgroundColor: '#E2E8F0', width: '60%' }, lineShort: { height: 12, borderRadius: 6, backgroundColor: '#F1F5F9', width: '80%' }, }) function formatTime(date: Date) { if (isToday(date)) return format(date, 'HH:mm') if (isYesterday(date)) return 'Gestern' return format(date, 'dd.MM.yy', { locale: de }) } export default function ChatListScreen() { const router = useRouter() const [searchQuery, setSearchQuery] = useState('') const [showSkeleton, setShowSkeleton] = useState(true) const { data: chats, isLoading, refetch: refetchChats, isRefetching } = trpc.messages.getConversations.useQuery(undefined, { refetchInterval: 10_000, staleTime: 8_000, }) useFocusEffect( useCallback(() => { setShowSkeleton(true) const t = setTimeout(() => setShowSkeleton(false), 800) return () => clearTimeout(t) }, []) ) const { data: members, isFetching: isFetchingMembers } = trpc.members.list.useQuery( { search: searchQuery }, { enabled: searchQuery.length > 0, staleTime: 30_000 } ) const [refreshing, setRefreshing] = useState(false) const refetch = useCallback(async () => { setRefreshing(true) try { await refetchChats() } finally { setRefreshing(false) } }, [refetchChats]) const filteredChats = (chats || []).filter(c => { if (!searchQuery) return true const q = searchQuery.toLowerCase() return ( c.other?.name?.toLowerCase().includes(q) || c.other?.betrieb?.toLowerCase().includes(q) || c.lastMessage?.body?.toLowerCase().includes(q) ) }) const sections = [] if (filteredChats.length > 0 || !searchQuery) { sections.push({ title: searchQuery ? 'Bestehende Chats' : '', data: filteredChats, type: 'chat' }) } if (searchQuery.length > 0) { const chatMemberIds = new Set((chats || []).map(c => c.other?.id).filter(Boolean)) const freshMembers = (members || []).filter(m => !chatMemberIds.has(m.id)) if (freshMembers.length > 0) { sections.push({ title: 'Weitere Mitglieder', data: freshMembers, type: 'member' }) } } const renderSectionHeader = ({ section }: { section: any }) => { if (!section.title) return null return ( {section.title} ) } const renderItem = ({ item, section }: { item: any, section: any }) => { if (section.type === 'chat') { return ( router.push({ pathname: '/(app)/chat/[id]', params: { id: item.conversationId, name: item.other?.name ?? 'Unbekannt', }, }) } activeOpacity={0.7} style={styles.chatRow} > {item.hasUnread && ( )} {item.other?.name ?? 'Unbekannt'} {item.lastMessage && ( {formatTime(new Date(item.lastMessage.createdAt))} )} {item.lastMessage?.body ?? 'Noch keine Nachrichten'} {item.other?.betrieb && ( {item.other.betrieb} )} ) } else { return ( router.push({ pathname: '/(app)/members/[id]', params: { id: item.id }, }) } activeOpacity={0.7} style={styles.chatRow} > {item.name} {item.betrieb && ( {item.betrieb} )} {item.sparte} • {item.ort} ) } } return ( {/* Header */} Nachrichten router.push('/(app)/members')} style={styles.newChatBtn} > {/* Search Bar */} {showSkeleton ? ( {[1, 2, 3, 4, 5].map((i) => )} ) : (!chats || chats.length === 0) && !searchQuery ? ( ) : searchQuery.length > 0 && isFetchingMembers && sections.length === 0 ? ( ) : sections.length === 0 && searchQuery ? ( ) : ( item.conversationId || item.id || String(index)} initialNumToRender={10} maxToRenderPerBatch={10} windowSize={5} refreshControl={ } contentContainerStyle={styles.list} renderItem={renderItem} renderSectionHeader={renderSectionHeader} /> )} ) } const styles = StyleSheet.create({ safeArea: { flex: 1, backgroundColor: '#F8FAFC', }, header: { backgroundColor: '#FFFFFF', paddingHorizontal: 20, paddingTop: 14, paddingBottom: 14, }, titleRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16, }, screenTitle: { fontSize: 28, fontWeight: '800', color: '#0F172A', letterSpacing: -0.5, }, newChatBtn: { width: 36, height: 36, borderRadius: 18, backgroundColor: '#EFF6FF', alignItems: 'center', justifyContent: 'center', }, searchContainer: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#F1F5F9', borderRadius: 12, paddingHorizontal: 12, height: 44, }, searchInput: { flex: 1, marginLeft: 8, fontSize: 15, color: '#0F172A', }, divider: { height: 1, backgroundColor: '#E2E8F0', }, list: { paddingBottom: 30, }, sectionHeader: { backgroundColor: '#F8FAFC', paddingHorizontal: 20, paddingVertical: 8, borderBottomWidth: 1, borderBottomColor: '#F1F5F9', }, sectionTitle: { fontSize: 13, fontWeight: '700', color: '#64748B', textTransform: 'uppercase', }, chatRow: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 20, paddingVertical: 16, backgroundColor: '#FFFFFF', borderBottomWidth: 1, borderBottomColor: '#F1F5F9', }, avatarContainer: { position: 'relative', }, unreadDot: { position: 'absolute', top: 0, right: 0, width: 12, height: 12, borderRadius: 6, backgroundColor: '#2563EB', borderWidth: 2, borderColor: '#FFFFFF', }, chatInfo: { flex: 1, marginLeft: 12, }, chatHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', }, chatName: { fontSize: 16, fontWeight: '600', color: '#1E293B', flex: 1, }, chatNameUnread: { fontWeight: '700', color: '#0F172A', }, timeText: { fontSize: 12, color: '#94A3B8', marginLeft: 8, }, messageText: { fontSize: 14, color: '#94A3B8', marginTop: 2, }, messageTextUnread: { fontWeight: '500', color: '#334155', }, companyText: { fontSize: 12, color: '#94A3B8', marginTop: 2, }, })