File size: 4,485 Bytes
9705b6c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
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 { MinimalIcon } from '~/components/Endpoints';
import { useConversations, useConversation } from '~/hooks';
import store from '~/store';
export default function Conversation({ conversation, retainView }) {
const [currentConversation, setCurrentConversation] = useRecoilState(store.conversation);
const setSubmission = useSetRecoilState(store.submission);
const { refreshConversations } = useConversations();
const { switchToConversation } = 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;
}
// stop existing submission
setSubmission(null);
// set document title
document.title = title;
// set conversation to the new conversation
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 });
};
const icon = MinimalIcon({
size: 20,
endpoint: conversation.endpoint,
model: conversation.model,
error: false,
className: 'mr-0',
});
useEffect(() => {
if (updateConvoMutation.isSuccess) {
refreshConversations();
if (conversationId == currentConversation?.conversationId) {
setCurrentConversation((prevState) => ({
...prevState,
title: titleInput,
}));
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [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}>
{icon}
<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}
title={title}
/>
</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>
);
}
|