|
import { useState, useRef, useEffect } from 'react'; |
|
import { useRecoilState, useSetRecoilState } from 'recoil'; |
|
import { useUpdateConversationMutation } from '@librechat/data-provider'; |
|
import RenameButton from './RenameButton'; |
|
import DeleteButton from './DeleteButton'; |
|
import ConvoIcon from '../svg/ConvoIcon'; |
|
|
|
import store from '~/store'; |
|
|
|
export default function Conversation({ conversation, retainView }) { |
|
const [currentConversation, setCurrentConversation] = useRecoilState(store.conversation); |
|
const setSubmission = useSetRecoilState(store.submission); |
|
|
|
const { refreshConversations } = store.useConversations(); |
|
const { switchToConversation } = store.useConversation(); |
|
|
|
const updateConvoMutation = useUpdateConversationMutation(currentConversation?.conversationId); |
|
|
|
const [renaming, setRenaming] = useState(false); |
|
const inputRef = useRef(null); |
|
|
|
const { conversationId, title } = conversation; |
|
|
|
const [titleInput, setTitleInput] = useState(title); |
|
|
|
const clickHandler = async () => { |
|
if (currentConversation?.conversationId === conversationId) { |
|
return; |
|
} |
|
|
|
|
|
setSubmission(null); |
|
|
|
|
|
document.title = title; |
|
|
|
|
|
if (conversation?.endpoint === 'gptPlugins') { |
|
const lastSelectedTools = JSON.parse(localStorage.getItem('lastSelectedTools')) || []; |
|
switchToConversation({ ...conversation, tools: lastSelectedTools }); |
|
} else { |
|
switchToConversation(conversation); |
|
} |
|
}; |
|
|
|
const renameHandler = (e) => { |
|
e.preventDefault(); |
|
setTitleInput(title); |
|
setRenaming(true); |
|
setTimeout(() => { |
|
inputRef.current.focus(); |
|
}, 25); |
|
}; |
|
|
|
const cancelHandler = (e) => { |
|
e.preventDefault(); |
|
setRenaming(false); |
|
}; |
|
|
|
const onRename = (e) => { |
|
e.preventDefault(); |
|
setRenaming(false); |
|
if (titleInput === title) { |
|
return; |
|
} |
|
updateConvoMutation.mutate({ conversationId, title: titleInput }); |
|
}; |
|
|
|
useEffect(() => { |
|
if (updateConvoMutation.isSuccess) { |
|
refreshConversations(); |
|
if (conversationId == currentConversation?.conversationId) { |
|
setCurrentConversation((prevState) => ({ |
|
...prevState, |
|
title: titleInput, |
|
})); |
|
} |
|
} |
|
|
|
}, [updateConvoMutation.isSuccess]); |
|
|
|
const handleKeyDown = (e) => { |
|
if (e.key === 'Enter') { |
|
onRename(e); |
|
} |
|
}; |
|
|
|
const aProps = { |
|
className: |
|
'animate-flash group relative flex cursor-pointer items-center gap-3 break-all rounded-md bg-gray-800 py-3 px-3 pr-14 hover:bg-gray-800', |
|
}; |
|
|
|
if (currentConversation?.conversationId !== conversationId) { |
|
aProps.className = |
|
'group relative flex cursor-pointer items-center gap-3 break-all rounded-md py-3 px-3 hover:bg-gray-800 hover:pr-4'; |
|
} |
|
|
|
return ( |
|
<a data-testid="convo-item" onClick={() => clickHandler()} {...aProps}> |
|
<ConvoIcon /> |
|
<div className="relative max-h-5 flex-1 overflow-hidden text-ellipsis break-all"> |
|
{renaming === true ? ( |
|
<input |
|
ref={inputRef} |
|
type="text" |
|
className="m-0 mr-0 w-full border border-blue-500 bg-transparent p-0 text-sm leading-tight outline-none" |
|
value={titleInput} |
|
onChange={(e) => setTitleInput(e.target.value)} |
|
onBlur={onRename} |
|
onKeyDown={handleKeyDown} |
|
/> |
|
) : ( |
|
title |
|
)} |
|
</div> |
|
{currentConversation?.conversationId === conversationId ? ( |
|
<div className="visible absolute right-1 z-10 flex text-gray-300"> |
|
<RenameButton |
|
conversationId={conversationId} |
|
renaming={renaming} |
|
renameHandler={renameHandler} |
|
onRename={onRename} |
|
/> |
|
<DeleteButton |
|
conversationId={conversationId} |
|
renaming={renaming} |
|
cancelHandler={cancelHandler} |
|
retainView={retainView} |
|
/> |
|
</div> |
|
) : ( |
|
<div className="absolute inset-y-0 right-0 z-10 w-8 rounded-r-md bg-gradient-to-l from-gray-900 group-hover:from-gray-700/70" /> |
|
)} |
|
</a> |
|
); |
|
} |
|
|