import React, { useEffect, useRef, useState } from 'react'; import { View, Text, TouchableOpacity, FlatList, StyleSheet, Animated, Dimensions, Pressable, } from 'react-native'; import type { Conversation } from '../../store/chatTypes'; import { colors, spacing, typography, borderRadius } from '../../theme/lightTheme'; const DRAWER_WIDTH = Math.min(Dimensions.get('window').width * 0.80, 310); interface Props { visible: boolean; conversations: Conversation[]; activeConversationId: string | null; onClose: () => void; onNewChat: () => void; onSelectConversation: (id: string) => void; onDeleteConversation: (id: string) => void; } function formatDate(ts: number): string { const diffDays = Math.floor((Date.now() - ts) / 86400000); if (diffDays === 0) return "Aujourd'hui"; if (diffDays === 1) return 'Hier'; if (diffDays < 7) return `Il y a ${diffDays} j`; return new Date(ts).toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' }); } export default function ChatDrawer({ visible, conversations, activeConversationId, onClose, onNewChat, onSelectConversation, onDeleteConversation, }: Props) { const translateX = useRef(new Animated.Value(-DRAWER_WIDTH)).current; const backdropOpacity = useRef(new Animated.Value(0)).current; // Keep mounted during the close animation const [mounted, setMounted] = useState(false); useEffect(() => { if (visible) { setMounted(true); Animated.parallel([ Animated.timing(translateX, { toValue: 0, duration: 250, useNativeDriver: true, }), Animated.timing(backdropOpacity, { toValue: 0.5, duration: 250, useNativeDriver: true, }), ]).start(); } else { Animated.parallel([ Animated.timing(translateX, { toValue: -DRAWER_WIDTH, duration: 210, useNativeDriver: true, }), Animated.timing(backdropOpacity, { toValue: 0, duration: 210, useNativeDriver: true, }), ]).start(() => setMounted(false)); } }, [visible, translateX, backdropOpacity]); if (!mounted) return null; const sorted = [...conversations].sort((a, b) => b.updatedAt - a.updatedAt); const renderItem = ({ item }: { item: Conversation }) => { const isActive = item.id === activeConversationId; const lastMsg = item.messages[item.messages.length - 1]; return ( { onSelectConversation(item.id); onClose(); }} activeOpacity={0.75} > {item.title} {item.messages.length} msg ยท {formatDate(item.updatedAt)} {lastMsg ? ( {lastMsg.role === 'user' ? '๐Ÿ‘ค ' : '๐Ÿค– '} {lastMsg.content} ) : ( Vide )} onDeleteConversation(item.id)} hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} > โœ• ); }; return ( {/* Backdrop */} {/* Drawer panel */} {/* Header */} Conversations โœ• {/* New chat button */} { onNewChat(); onClose(); }} activeOpacity={0.85} > ๏ผ‹ Nouveau chat {/* Conversations list */} item.id} renderItem={renderItem} contentContainerStyle={styles.listContent} showsVerticalScrollIndicator={false} ListEmptyComponent={ Aucune conversation } /> ); } const styles = StyleSheet.create({ backdrop: { ...StyleSheet.absoluteFillObject, backgroundColor: colors.textPrimary, }, drawer: { position: 'absolute', left: 0, top: 0, bottom: 0, width: DRAWER_WIDTH, backgroundColor: colors.surface, shadowColor: colors.textPrimary, shadowOffset: { width: 4, height: 0 }, shadowOpacity: 0.25, shadowRadius: 12, elevation: 16, }, drawerHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: spacing.lg, paddingTop: spacing.xxl, paddingBottom: spacing.md, borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: colors.border, }, drawerTitle: { fontSize: typography.sizes.lg, fontWeight: typography.weights.semibold, color: colors.textPrimary, }, closeBtnText: { fontSize: 18, color: colors.textSecondary, }, newChatBtn: { margin: spacing.md, paddingVertical: spacing.md, backgroundColor: colors.primary, borderRadius: borderRadius.lg, alignItems: 'center', }, newChatBtnText: { color: colors.surface, fontSize: typography.sizes.md, fontWeight: typography.weights.semibold, }, listContent: { paddingHorizontal: spacing.sm, paddingBottom: spacing.xxl, }, convItem: { flexDirection: 'row', alignItems: 'center', paddingVertical: spacing.sm, paddingHorizontal: spacing.md, borderRadius: borderRadius.lg, marginBottom: 2, }, convItemActive: { backgroundColor: colors.surfaceSecondary, borderLeftWidth: 3, borderLeftColor: colors.primary, }, convItemContent: { flex: 1, marginRight: spacing.sm, }, convTitle: { fontSize: typography.sizes.sm, fontWeight: typography.weights.medium, color: colors.textPrimary, marginBottom: 2, }, convTitleActive: { color: colors.primary, fontWeight: typography.weights.semibold, }, convMeta: { fontSize: 11, color: colors.textTertiary, marginBottom: 2, }, convPreview: { fontSize: 12, color: colors.textSecondary, }, convPreviewEmpty: { fontSize: 12, color: colors.textTertiary, fontStyle: 'italic', }, deleteBtn: { padding: 4, }, deleteBtnText: { fontSize: 14, color: colors.textTertiary, }, emptyText: { textAlign: 'center', color: colors.textTertiary, marginTop: spacing.xl, fontSize: typography.sizes.sm, }, });