import React, { useState, useEffect, useCallback } from 'react'; import { View, Text, StyleSheet, FlatList, SafeAreaView, Alert, ActivityIndicator, Pressable, Platform, Dimensions } from 'react-native'; import { useTheme, useFocusEffect } from '@react-navigation/native'; import { useRouter } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; import { useAuth } from '../context/AuthProvider'; import { useAppTheme } from '../theme/ThemeProvider'; import CustomDrawer from '../components/CustomDrawer'; import { getConversations, getMessages, deleteConversation, archiveConversation } from '../services/conversation'; import { supabase } from '../utils/supabase'; // Typendefinitionen für Konversationen type ConversationItem = { id: string; modelName: string; title: string; lastMessage: string; timestamp: Date; mode: 'frei' | 'geführt' | 'vorlage'; }; // Hilfsfunktion zur Formatierung des Datums const formatDate = (date: Date) => { const day = date.getDate().toString().padStart(2, '0'); const month = new Intl.DateTimeFormat('de-DE', { month: 'short' }).format(date); const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); return `${day}. ${month}, ${hours}:${minutes}`; }; export default function ConversationsScreen() { const { colors } = useTheme(); const router = useRouter(); const { user } = useAuth(); const [conversations, setConversations] = useState([]); const [isLoading, setIsLoading] = useState(true); const { isDarkMode } = useAppTheme(); const [isDrawerOpen, setIsDrawerOpen] = useState(false); // Eine Funktion, die Konversationen lädt und wiederverwendet werden kann const loadConversations = async () => { if (!user) return; setIsLoading(true); try { console.log("Lade Konversationen für User:", user.id); // Lade alle nicht-archivierten Konversationen des Benutzers const userConversations = await getConversations(user.id); console.log(`${userConversations.length} Konversationen geladen`, new Date().toLocaleTimeString()); // Lade für jede Konversation die letzte Nachricht und das Modell const conversationItems: ConversationItem[] = []; for (const conv of userConversations) { try { // Lade die Nachrichten der Konversation const messages = await getMessages(conv.id); // Lade das Modell aus der Datenbank const { data: modelData } = await supabase .from('models') .select('name') .eq('id', conv.model_id) .single(); // Finde die letzte Nachricht (die nicht vom System ist) const lastMessage = messages .filter(msg => msg.sender !== 'system') .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0]; if (lastMessage) { conversationItems.push({ id: conv.id, modelName: modelData?.name || 'Unbekanntes Modell', title: conv.title || 'Unbenannte Konversation', lastMessage: lastMessage.message_text, timestamp: new Date(conv.updated_at), mode: conv.conversation_mode === 'free' ? 'frei' : conv.conversation_mode === 'guided' ? 'geführt' : 'vorlage' }); } } catch (error) { console.error(`Fehler beim Laden der Details für Konversation ${conv.id}:`, error); } } setConversations(conversationItems); } catch (error) { console.error('Fehler beim Laden der Konversationen:', error); Alert.alert('Fehler', 'Die Konversationen konnten nicht geladen werden.'); } finally { setIsLoading(false); } }; // Lade die Konversationen beim ersten Rendern und wenn sich der User ändert useEffect(() => { loadConversations(); }, [user]); // Lade Konversationen erneut, wenn der Screen fokussiert wird useFocusEffect( useCallback(() => { if (user) loadConversations(); return () => {}; }, [user]) ); const handleConversationPress = (id: string) => { // Navigiere zum Konversations-Screen mit der ID router.push(`/conversation/${id}`); }; // Löschen einer Konversation const handleDeleteConversation = (id: string) => { Alert.alert( "Konversation löschen", "Möchtest du diese Konversation wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.", [ { text: "Abbrechen", style: "cancel" }, { text: "Löschen", style: "destructive", onPress: async () => { try { const success = await deleteConversation(id); if (success) { // Aus der lokalen Liste entfernen setConversations(prev => prev.filter(conv => conv.id !== id)); Alert.alert("Erfolg", "Die Konversation wurde gelöscht."); } else { Alert.alert("Fehler", "Die Konversation konnte nicht gelöscht werden."); } } catch (error) { console.error('Fehler beim Löschen der Konversation:', error); Alert.alert("Fehler", "Die Konversation konnte nicht gelöscht werden."); } } } ] ); }; // Archivieren einer Konversation const handleArchiveConversation = async (id: string) => { try { const success = await archiveConversation(id); if (success) { // Aus der lokalen Liste entfernen setConversations(prev => prev.filter(conv => conv.id !== id)); Alert.alert("Erfolg", "Die Konversation wurde archiviert."); } else { Alert.alert("Fehler", "Die Konversation konnte nicht archiviert werden."); } } catch (error) { console.error('Fehler beim Archivieren der Konversation:', error); Alert.alert("Fehler", "Die Konversation konnte nicht archiviert werden."); } }; // Zustandsverwaltung für die Optionsmenüs der Konversationselemente const [expandedConversationId, setExpandedConversationId] = useState(null); // Toggle-Funktion für das Optionsmenü const toggleOptionsMenu = (id: string) => { setExpandedConversationId(expandedConversationId === id ? null : id); }; const renderConversationItem = ({ item }: { item: ConversationItem }) => { const showOptions = expandedConversationId === item.id; return ( [ styles.conversationItem, hovered && { backgroundColor: colors.cardHover }, pressed && { opacity: 0.9 } ]} onPress={() => handleConversationPress(item.id)} onLongPress={() => toggleOptionsMenu(item.id)} > {({ pressed, hovered }) => ( <> {item.title} {item.modelName} {item.mode === 'frei' ? 'Frei' : item.mode === 'geführt' ? 'Geführt' : 'Vorlage'} {formatDate(item.timestamp)} {item.lastMessage} [ styles.optionsButton, hovered && { backgroundColor: colors.menuItemHover }, pressed && { opacity: 0.7 } ]} onPress={() => toggleOptionsMenu(item.id)} > {({ pressed, hovered }) => ( )} )} {showOptions && ( [ styles.optionButton, hovered && { backgroundColor: colors.menuItemHover }, pressed && { opacity: 0.8 } ]} onPress={() => handleArchiveConversation(item.id)} > {({ pressed, hovered }) => ( <> Archivieren )} [ styles.optionButton, hovered && { backgroundColor: colors.dangerHover }, pressed && { opacity: 0.8 } ]} onPress={() => handleDeleteConversation(item.id)} > {({ pressed, hovered }) => ( <> Löschen )} )} ); }; return ( {/* Permanenter Drawer links */} {isDrawerOpen && ( setIsDrawerOpen(false)} /> )} {/* Hauptinhalt */} [ styles.menuButton, hovered && { backgroundColor: colors.menuItemHover }, pressed && { opacity: 0.7 } ]} onPress={() => setIsDrawerOpen(!isDrawerOpen)} > {({ pressed, hovered }) => ( )} Konversationen {/* Konversationsliste */} {isLoading ? ( Konversationen werden geladen... ) : conversations.length > 0 ? ( item.id} renderItem={renderConversationItem} contentContainerStyle={styles.listContent} numColumns={Platform.OS === 'web' ? Math.min(Math.floor((Dimensions.get('window').width - 32) / 400), 3) : 1} key={Platform.OS === 'web' ? Math.min(Math.floor((Dimensions.get('window').width - 32) / 400), 3) : 1} /> ) : ( Keine Konversationen vorhanden Starte eine neue Konversation über den Hauptbildschirm )} ); } const styles = StyleSheet.create({ container: { flex: 1, }, mainLayout: { flex: 1, flexDirection: 'row', }, mainContainer: { flex: 1, alignItems: 'center', }, drawerContainer: { width: 260, height: '100%', position: 'absolute', left: 0, top: 0, bottom: 0, zIndex: 10, }, contentContainer: { flex: 1, maxWidth: 1200, width: '100%', }, headerContainer: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 20, paddingTop: 16, paddingBottom: 8, zIndex: 10, // Stelle sicher, dass der Header über allem anderen liegt elevation: 10, // Für Android }, menuButton: { padding: 10, marginRight: 12, zIndex: 5, borderRadius: 20, width: 40, height: 40, alignItems: 'center', justifyContent: 'center', }, headerTitle: { fontSize: 28, fontWeight: 'bold', }, listContainer: { flex: 1, width: '100%', paddingHorizontal: 16, }, listContent: { paddingBottom: 20, paddingTop: 12, gap: 16, alignSelf: 'center', justifyContent: Platform.OS === 'web' ? 'flex-start' : undefined, }, conversationItemWrapper: { borderRadius: 12, overflow: 'hidden', margin: 8, width: Platform.OS === 'web' ? 380 : undefined, ...Platform.select({ ios: { shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, }, android: { elevation: 3, }, web: { boxShadow: '0 2px 6px rgba(0, 0, 0, 0.1)', }, }), }, conversationItem: { flexDirection: 'row', alignItems: 'flex-start', padding: 16, }, conversationContent: { flex: 1, display: 'flex', flexDirection: 'column', height: '100%', }, optionsContainer: { flexDirection: 'row', justifyContent: 'flex-end', paddingHorizontal: 16, paddingBottom: 12, paddingTop: 8, }, optionButton: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 12, paddingVertical: 6, marginLeft: 8, borderRadius: 6, }, optionText: { fontSize: 14, marginLeft: 6, fontWeight: '500', }, conversationHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6, }, titleRow: { flexDirection: 'row', alignItems: 'center', flex: 1, }, titleIcon: { marginRight: 8, }, title: { fontSize: 16, fontWeight: '600', flex: 1, marginBottom: 2, }, badgeContainer: { flexDirection: 'row', alignItems: 'center', marginBottom: 10, gap: 8, flexWrap: 'wrap', }, modelBadge: { paddingHorizontal: 8, paddingVertical: 3, borderRadius: 12, }, modelName: { fontSize: 12, fontWeight: '500', }, modeBadge: { paddingHorizontal: 8, paddingVertical: 3, borderRadius: 12, }, timestamp: { fontSize: 11, marginLeft: 'auto', // Um es an den rechten Rand zu schieben }, lastMessage: { fontSize: 14, marginBottom: 6, lineHeight: 20, marginTop: 4, flex: 1, // Damit die Nachricht den verbleibenden Platz einnimmt }, modeText: { fontSize: 11, fontWeight: '500', }, optionsButton: { width: 36, height: 36, borderRadius: 18, alignItems: 'center', justifyContent: 'center', }, // Container für den Ladezustand loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 32, marginTop: 40, }, loadingText: { fontSize: 16, marginTop: 16, textAlign: 'center', }, emptyContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 32, marginTop: 40, }, emptyText: { fontSize: 18, fontWeight: '600', marginTop: 16, textAlign: 'center', }, emptySubtext: { fontSize: 14, textAlign: 'center', marginTop: 8, }, });