import React, { useState, useEffect, useCallback, useMemo, } from 'react'; import { Button } from '../ui/Button'; import { Input } from '../ui/input'; import Typography from '../ui/Typography'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../ui/dialog'; import { Tabs, TabsList, TabsTrigger, TabsContent } from '../ui/tabs'; import { FiMessageSquare, FiSearch, FiCheck, FiPlus, FiArrowRight, FiAlertCircle, FiX, } from 'react-icons/fi'; import { cn } from '../../lib/utils'; export interface NewChatModalProps { isOpen: boolean; onClose: () => void; onSuccess: (data?: { sessionId?: string; isRegistryBroker?: boolean; agentName?: string }) => void; initialTargetAccountId?: string | null; } type ViewMode = 'browse-agents' | 'direct-connect'; interface Agent { id: string; accountId: string; name: string; profile?: { display_name?: string; bio?: string; profileImage?: string; alias?: string; isAI?: boolean; isRegistryBroker?: boolean; }; inboundTopicId?: string; } interface ConnectionWarning { message: string; variant: 'outbound' | 'inbound' | 'connected' | null; } interface SuccessViewProps { onClose: () => void; onConnectAnother: () => void; } interface ConnectionWarningDisplayProps { message: string; } interface SubmitButtonLabelProps { isLoading: boolean; variant: 'outbound' | 'inbound' | 'connected' | null; } /** * Displays the success message after sending a connection request. */ const SuccessView: React.FC = ({ onClose, onConnectAnother, }) => { return (
Connection Request Sent! Your request has been sent successfully and is awaiting response.
); }; /** * Displays the connection warning message. */ const ConnectionWarningDisplay: React.FC = ({ message, }) => { return (
{message}
); }; /** * Determines the label for the submit button based on state. */ const SubmitButtonLabel: React.FC = ({ isLoading, variant, }) => { if (isLoading) { return (
Sending...
); } if (variant === 'inbound') { return ( Accept Request Instead ); } if (variant === 'connected' || variant === 'outbound') { return ( Send Another Request ); } return ( Send Connection Request ); }; export const NewChatModal: React.FC = ({ isOpen, onClose, onSuccess, initialTargetAccountId = null, }) => { const [targetAccountId, setTargetAccountId] = useState(''); const [memo, setMemo] = useState(''); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); const [isSuccess, setIsSuccess] = useState(false); const [viewMode, setViewMode] = useState('browse-agents'); const [agentSearchTerm, setAgentSearchTerm] = useState(''); const [availableAgents, setAvailableAgents] = useState([]); const [loadingAgents, setLoadingAgents] = useState(true); useEffect(() => { if (isOpen) { setMemo("Hello! I'd like to connect with you."); setError(''); setIsSuccess(false); setTargetAccountId(''); setAgentSearchTerm(''); if (!initialTargetAccountId) { setViewMode('browse-agents'); } else { setTargetAccountId(initialTargetAccountId); setViewMode('direct-connect'); } loadAvailableAgents(); } }, [isOpen, initialTargetAccountId]); const loadAvailableAgents = useCallback(async () => { setLoadingAgents(true); try { const result = await window.electron.invoke('hcs10:discover-agents', { filters: {}, pagination: { page: 1, limit: 50 }, }); if (result.success && result.data?.agents) { const transformedAgents: Agent[] = result.data.agents.map((agent: any, index: number) => ({ id: agent.accountId || `agent-${index}`, accountId: agent.accountId, name: agent.profile?.display_name || agent.metadata?.display_name || agent.profile?.alias || agent.metadata?.alias || agent.accountId, profile: { display_name: agent.profile?.display_name || agent.metadata?.display_name, bio: agent.profile?.bio || agent.metadata?.bio, profileImage: agent.profile?.profileImage || agent.metadata?.profileImage, alias: agent.profile?.alias || agent.metadata?.alias, isAI: agent.profile?.type === 'AI_AGENT' || agent.metadata?.type === 'AI_AGENT', }, inboundTopicId: agent.profile?.inboundTopicId || agent.metadata?.inboundTopicId, })); setAvailableAgents(transformedAgents); } else { setAvailableAgents([]); } } catch (error) { setError('Failed to load available agents. Please try again later.'); setAvailableAgents([]); } finally { setLoadingAgents(false); } }, []); const filteredAgents = useMemo(() => { if (!agentSearchTerm) return availableAgents; const searchLower = agentSearchTerm.toLowerCase(); return availableAgents.filter((agent) => { const displayName = agent.profile?.display_name || agent.name || ''; const description = agent.profile?.bio || ''; return ( displayName.toLowerCase().includes(searchLower) || agent.accountId.includes(searchLower) || description.toLowerCase().includes(searchLower) ); }); }, [availableAgents, agentSearchTerm]); const resetForm = useCallback(() => { setTargetAccountId(''); setMemo(''); setError(''); setIsSuccess(false); setAgentSearchTerm(''); setViewMode('browse-agents'); }, []); const handleClose = useCallback(() => { onClose(); resetForm(); }, [onClose, resetForm]); const onSuccessClose = useCallback(() => { handleClose(); }, [handleClose]); const onSuccessConnectAnother = useCallback(() => { resetForm(); }, [resetForm]); const handleSubmit = async (e: React.FormEvent): Promise => { e.preventDefault(); setIsLoading(true); setError(''); if (!targetAccountId) { setError('Please enter an account ID.'); setIsLoading(false); return; } try { const result = await window.electron.invoke('hcs10:send-connection-request', { targetAccountId, message: memo || "Hello! I'd like to connect with you.", }); if (!result.success) { throw new Error(result.error || 'Failed to send connection request'); } setIsSuccess(true); onSuccess(); } catch (err: unknown) { setError((err instanceof Error ? err.message : String(err)) || 'An unexpected error occurred.'); } finally { setIsLoading(false); } }; const handleMemoChange = useCallback( (event: React.ChangeEvent) => { setMemo(event.target.value); }, [] ); const handleSearchTermChange = useCallback( (event: React.ChangeEvent) => { setAgentSearchTerm(event.target.value); }, [] ); const clearSearch = useCallback(() => { setAgentSearchTerm(''); }, []); const handleAgentSelect = useCallback((agent: Agent) => { setTargetAccountId(agent.accountId); setError(''); }, []); const isSubmitDisabled = useMemo(() => { return isLoading || !targetAccountId; }, [targetAccountId, isLoading]); const connectionWarning: ConnectionWarning = useMemo(() => { return { message: '', variant: null as 'outbound' | 'inbound' | 'connected' | null }; }, [targetAccountId]); let modalTitleString = 'New Chat'; if (isSuccess) { modalTitleString = 'Successfully Connected!'; } else if (viewMode === 'direct-connect') { modalTitleString = 'Connect to an Agent'; } else if (viewMode === 'browse-agents') { modalTitleString = 'Browse Available Agents'; } return ( {modalTitleString}
{isSuccess ? ( ) : (
setViewMode(value as ViewMode)} className="w-full"> Browse Agents Direct Connect
Account ID setTargetAccountId(e.target.value)} className="w-full" disabled={isLoading} />
Search Agents
{agentSearchTerm && ( )}
{loadingAgents ? (
Loading agents...
) : filteredAgents.length > 0 ? ( filteredAgents.map((agent) => (
handleAgentSelect(agent)} className={cn( 'p-3 rounded-lg border cursor-pointer transition-all bg-white dark:bg-gray-900', targetAccountId === agent.accountId ? 'border-[#5599fe] bg-blue-50 dark:bg-blue-900/20' : 'border-gray-200 dark:border-gray-700 hover:border-[#5599fe]/50 dark:hover:border-[#5599fe]/70' )} >
{agent.profile?.display_name?.[0] || agent.name[0] || '?'}
{agent.profile?.display_name || agent.name}
{agent.accountId}
{agent.profile?.bio && (
{agent.profile.bio}
)}
)) ) : (
No agents found
)}
{connectionWarning.message && ( )}
Connection Message
{error && (
{error}
)}
)}
); }; export default NewChatModal;