import { useState, useEffect } from 'react'; import { Modal, View, Text, Pressable, ScrollView, Alert, KeyboardAvoidingView, Platform } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { MessageSquare, X, Plus, Trash2 } from 'lucide-react-native'; import AsyncStorage from '@react-native-async-storage/async-storage'; import type { UseWebSocketReturn } from '../hooks/use-websocket'; const STORAGE_KEY = '@voice-dev:conversation-id'; interface Conversation { id: string; lastUpdated: string; messageCount: number; } interface ConversationSelectorProps { currentConversationId: string | null; onSelectConversation: (conversationId: string | null) => void; websocket: UseWebSocketReturn; } export function ConversationSelector({ currentConversationId, onSelectConversation, websocket, }: ConversationSelectorProps) { const [conversations, setConversations] = useState([]); const [isOpen, setIsOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); // Listen for conversation list responses useEffect(() => { const unsubscribe = websocket.on('list_conversations_response', (message) => { if (message.type !== 'list_conversations_response') return; setConversations(message.payload.conversations); setIsLoading(false); }); return unsubscribe; }, [websocket]); // Listen for delete conversation responses useEffect(() => { const unsubscribe = websocket.on('delete_conversation_response', (message) => { if (message.type !== 'delete_conversation_response') return; console.log('[ConversationSelector] Delete response:', message.payload); if (message.payload.success) { // Refresh conversations list fetchConversations(); // If we deleted the current conversation, start a new one if (message.payload.conversationId === currentConversationId) { handleNewConversation(); } } else { Alert.alert('Error', `Failed to delete conversation: ${message.payload.error}`); } }); return unsubscribe; }, [websocket, currentConversationId]); // Fetch conversations when modal opens useEffect(() => { if (isOpen) { fetchConversations(); } }, [isOpen]); function fetchConversations() { setIsLoading(true); console.log('[ConversationSelector] Requesting conversations via WebSocket'); websocket.send({ type: 'session', message: { type: 'list_conversations_request', }, }); } function handleDeleteConversation(id: string) { Alert.alert( 'Delete Conversation', 'Are you sure you want to delete this conversation?', [ { text: 'Cancel', style: 'cancel' }, { text: 'Delete', style: 'destructive', onPress: () => { console.log('[ConversationSelector] Deleting conversation via WebSocket:', id); websocket.send({ type: 'session', message: { type: 'delete_conversation_request', conversationId: id, }, }); }, }, ] ); } function handleClearAll() { Alert.alert( 'Clear All Conversations', 'Are you sure you want to delete all conversations?', [ { text: 'Cancel', style: 'cancel' }, { text: 'Clear All', style: 'destructive', onPress: () => { console.log('[ConversationSelector] Clearing all conversations via WebSocket'); // Delete all conversations conversations.forEach((conv) => { websocket.send({ type: 'session', message: { type: 'delete_conversation_request', conversationId: conv.id, }, }); }); // Clear local state setConversations([]); // Start new conversation handleNewConversation(); setIsOpen(false); }, }, ] ); } async function handleSelectConversation(id: string) { try { // Save to AsyncStorage for persistence await AsyncStorage.setItem(STORAGE_KEY, id); onSelectConversation(id); setIsOpen(false); } catch (error) { console.error('[ConversationSelector] Failed to save conversation ID:', error); } } async function handleNewConversation() { try { // Clear saved conversation ID await AsyncStorage.removeItem(STORAGE_KEY); onSelectConversation(null); setIsOpen(false); } catch (error) { console.error('[ConversationSelector] Failed to clear conversation ID:', error); } } function formatDate(dateString: string): string { const date = new Date(dateString); const now = new Date(); const diffMs = now.getTime() - date.getTime(); const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMs / 3600000); const diffDays = Math.floor(diffMs / 86400000); if (diffMins < 1) return 'Just now'; if (diffMins < 60) return `${diffMins}m ago`; if (diffHours < 24) return `${diffHours}h ago`; if (diffDays < 7) return `${diffDays}d ago`; return date.toLocaleDateString(); } return ( setIsOpen(true)} className="bg-zinc-800 px-4 py-2 rounded-lg" > setIsOpen(false)} > setIsOpen(false)} /> {/* Header */} Conversations {conversations.length > 0 && `(${conversations.length})`} setIsOpen(false)}> {/* New Conversation Button */} New Conversation {/* Conversations List */} {isLoading && ( Loading... )} {!isLoading && conversations.length === 0 && ( No saved conversations )} {!isLoading && conversations.map((conversation) => ( handleSelectConversation(conversation.id)} > {conversation.messageCount} messages {formatDate(conversation.lastUpdated)} handleDeleteConversation(conversation.id)} style={{ padding: 8 }} hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} > ))} {!isLoading && conversations.length > 0 && ( Clear All )} ); }