import React, { useState, useEffect, useRef, useMemo } from 'react'; import { useRecoilValue } from 'recoil'; import { useParams } from 'react-router-dom'; import { Constants } from 'librechat-data-provider'; import type { TConversation } from 'librechat-data-provider'; import { useNavigateToConvo, useMediaQuery, useLocalize } from '~/hooks'; import { useUpdateConversationMutation } from '~/data-provider'; import EndpointIcon from '~/components/Endpoints/EndpointIcon'; import { useGetEndpointsQuery } from '~/data-provider'; import { NotificationSeverity } from '~/common'; import { ConvoOptions } from './ConvoOptions'; import { useToastContext } from '~/Providers'; import RenameForm from './RenameForm'; import ConvoLink from './ConvoLink'; import { cn } from '~/utils'; import store from '~/store'; interface ConversationProps { conversation: TConversation; retainView: () => void; toggleNav: () => void; isLatestConvo: boolean; } export default function Conversation({ conversation, retainView, toggleNav, isLatestConvo, }: ConversationProps) { const params = useParams(); const localize = useLocalize(); const { showToast } = useToastContext(); const currentConvoId = useMemo(() => params.conversationId, [params.conversationId]); const updateConvoMutation = useUpdateConversationMutation(currentConvoId ?? ''); const activeConvos = useRecoilValue(store.allConversationsSelector); const { data: endpointsConfig } = useGetEndpointsQuery(); const { navigateWithLastTools } = useNavigateToConvo(); const isSmallScreen = useMediaQuery('(max-width: 768px)'); const { conversationId, title = '' } = conversation; const [titleInput, setTitleInput] = useState(title || ''); const [renaming, setRenaming] = useState(false); const [isPopoverActive, setIsPopoverActive] = useState(false); const previousTitle = useRef(title); useEffect(() => { if (title !== previousTitle.current) { setTitleInput(title as string); previousTitle.current = title; } }, [title]); const isActiveConvo = useMemo(() => { if (conversationId === Constants.NEW_CONVO) { return currentConvoId === Constants.NEW_CONVO; } if (currentConvoId !== Constants.NEW_CONVO) { return currentConvoId === conversationId; } else { const latestConvo = activeConvos?.[0]; return latestConvo === conversationId; } }, [currentConvoId, conversationId, activeConvos]); const handleRename = () => { setIsPopoverActive(false); setTitleInput(title as string); setRenaming(true); }; const handleRenameSubmit = async (newTitle: string) => { if (!conversationId || newTitle === title) { setRenaming(false); return; } try { await updateConvoMutation.mutateAsync({ conversationId, title: newTitle.trim() || localize('com_ui_untitled'), }); setRenaming(false); } catch (error) { setTitleInput(title as string); showToast({ message: localize('com_ui_rename_failed'), severity: NotificationSeverity.ERROR, showIcon: true, }); setRenaming(false); } }; const handleCancelRename = () => { setTitleInput(title as string); setRenaming(false); }; const handleNavigation = (ctrlOrMetaKey: boolean) => { if (ctrlOrMetaKey) { toggleNav(); const baseUrl = window.location.origin; const path = `/c/${conversationId}`; window.open(baseUrl + path, '_blank'); return; } if (currentConvoId === conversationId || isPopoverActive) { return; } toggleNav(); if (typeof title === 'string' && title.length > 0) { document.title = title; } navigateWithLastTools( conversation, !(conversationId ?? '') || conversationId === Constants.NEW_CONVO, ); }; const convoOptionsProps = { title, retainView, renameHandler: handleRename, isActiveConvo, conversationId, isPopoverActive, setIsPopoverActive, }; return (