2025-01-09 15:40:10 -05:00
|
|
|
import { useState, useId, useRef, memo } from 'react';
|
2024-12-18 11:10:34 -05:00
|
|
|
import * as Menu from '@ariakit/react/menu';
|
|
|
|
|
import { Ellipsis, Share2, Copy, Archive, Pen, Trash } from 'lucide-react';
|
2024-08-16 10:30:14 +02:00
|
|
|
import { useGetStartupConfig } from 'librechat-data-provider/react-query';
|
2024-12-17 13:12:57 -05:00
|
|
|
import type { MouseEvent } from 'react';
|
2024-12-29 23:31:41 +01:00
|
|
|
import type * as t from '~/common';
|
2024-12-18 11:10:34 -05:00
|
|
|
import { useLocalize, useArchiveHandler, useNavigateToConvo } from '~/hooks';
|
|
|
|
|
import { useToastContext, useChatContext } from '~/Providers';
|
|
|
|
|
import { useDuplicateConversationMutation } from '~/data-provider';
|
2024-08-16 10:30:14 +02:00
|
|
|
import { DropdownPopup } from '~/components/ui';
|
|
|
|
|
import DeleteButton from './DeleteButton';
|
|
|
|
|
import ShareButton from './ShareButton';
|
2024-08-30 13:39:30 -04:00
|
|
|
import { cn } from '~/utils';
|
2024-08-16 10:30:14 +02:00
|
|
|
|
2025-01-09 15:40:10 -05:00
|
|
|
function ConvoOptions({
|
2024-12-17 13:12:57 -05:00
|
|
|
conversationId,
|
|
|
|
|
title,
|
2024-08-16 10:30:14 +02:00
|
|
|
retainView,
|
|
|
|
|
renameHandler,
|
|
|
|
|
isPopoverActive,
|
|
|
|
|
setIsPopoverActive,
|
|
|
|
|
isActiveConvo,
|
2024-12-17 13:12:57 -05:00
|
|
|
}: {
|
|
|
|
|
conversationId: string | null;
|
|
|
|
|
title: string | null;
|
|
|
|
|
retainView: () => void;
|
|
|
|
|
renameHandler: (e: MouseEvent) => void;
|
|
|
|
|
isPopoverActive: boolean;
|
|
|
|
|
setIsPopoverActive: React.Dispatch<React.SetStateAction<boolean>>;
|
|
|
|
|
isActiveConvo: boolean;
|
2024-08-16 10:30:14 +02:00
|
|
|
}) {
|
|
|
|
|
const localize = useLocalize();
|
2024-12-18 11:10:34 -05:00
|
|
|
const { index } = useChatContext();
|
2024-08-16 10:30:14 +02:00
|
|
|
const { data: startupConfig } = useGetStartupConfig();
|
2024-12-18 11:10:34 -05:00
|
|
|
const archiveHandler = useArchiveHandler(conversationId, true, retainView);
|
|
|
|
|
const { navigateToConvo } = useNavigateToConvo(index);
|
|
|
|
|
const { showToast } = useToastContext();
|
2024-12-29 23:31:41 +01:00
|
|
|
const shareButtonRef = useRef<HTMLButtonElement>(null);
|
|
|
|
|
const deleteButtonRef = useRef<HTMLButtonElement>(null);
|
2024-08-16 10:30:14 +02:00
|
|
|
const [showShareDialog, setShowShareDialog] = useState(false);
|
|
|
|
|
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
2024-12-18 11:10:34 -05:00
|
|
|
|
|
|
|
|
const duplicateConversation = useDuplicateConversationMutation({
|
|
|
|
|
onSuccess: (data) => {
|
2025-01-23 18:19:04 -05:00
|
|
|
navigateToConvo(data.conversation);
|
|
|
|
|
showToast({
|
|
|
|
|
message: localize('com_ui_duplication_success'),
|
|
|
|
|
status: 'success',
|
|
|
|
|
});
|
2024-12-18 11:10:34 -05:00
|
|
|
},
|
|
|
|
|
onMutate: () => {
|
|
|
|
|
showToast({
|
|
|
|
|
message: localize('com_ui_duplication_processing'),
|
|
|
|
|
status: 'info',
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
onError: () => {
|
|
|
|
|
showToast({
|
|
|
|
|
message: localize('com_ui_duplication_error'),
|
|
|
|
|
status: 'error',
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
});
|
2024-08-16 10:30:14 +02:00
|
|
|
|
|
|
|
|
const shareHandler = () => {
|
|
|
|
|
setShowShareDialog(true);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const deleteHandler = () => {
|
|
|
|
|
setShowDeleteDialog(true);
|
|
|
|
|
};
|
|
|
|
|
|
2024-12-18 11:10:34 -05:00
|
|
|
const duplicateHandler = () => {
|
|
|
|
|
setIsPopoverActive(false);
|
|
|
|
|
duplicateConversation.mutate({
|
|
|
|
|
conversationId: conversationId ?? '',
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2024-12-29 23:31:41 +01:00
|
|
|
const dropdownItems: t.MenuItemProps[] = [
|
2024-12-18 11:10:34 -05:00
|
|
|
{
|
|
|
|
|
label: localize('com_ui_share'),
|
|
|
|
|
onClick: shareHandler,
|
|
|
|
|
icon: <Share2 className="icon-sm mr-2 text-text-primary" />,
|
|
|
|
|
show: startupConfig && startupConfig.sharedLinksEnabled,
|
2024-12-29 23:31:41 +01:00
|
|
|
/** NOTE: THE FOLLOWING PROPS ARE REQUIRED FOR MENU ITEMS THAT OPEN DIALOGS */
|
|
|
|
|
hideOnClick: false,
|
|
|
|
|
ref: shareButtonRef,
|
|
|
|
|
render: (props) => <button {...props} />,
|
2024-12-18 11:10:34 -05:00
|
|
|
},
|
2024-08-16 10:30:14 +02:00
|
|
|
{
|
|
|
|
|
label: localize('com_ui_rename'),
|
|
|
|
|
onClick: renameHandler,
|
2024-12-18 11:10:34 -05:00
|
|
|
icon: <Pen className="icon-sm mr-2 text-text-primary" />,
|
2024-08-16 10:30:14 +02:00
|
|
|
},
|
|
|
|
|
{
|
2024-12-18 11:10:34 -05:00
|
|
|
label: localize('com_ui_duplicate'),
|
|
|
|
|
onClick: duplicateHandler,
|
|
|
|
|
icon: <Copy className="icon-sm mr-2 text-text-primary" />,
|
2024-08-16 10:30:14 +02:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: localize('com_ui_archive'),
|
|
|
|
|
onClick: archiveHandler,
|
2024-12-18 11:10:34 -05:00
|
|
|
icon: <Archive className="icon-sm mr-2 text-text-primary" />,
|
2024-08-16 10:30:14 +02:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: localize('com_ui_delete'),
|
|
|
|
|
onClick: deleteHandler,
|
2024-12-18 11:10:34 -05:00
|
|
|
icon: <Trash className="icon-sm mr-2 text-text-primary" />,
|
2024-12-29 23:31:41 +01:00
|
|
|
hideOnClick: false,
|
|
|
|
|
ref: deleteButtonRef,
|
|
|
|
|
render: (props) => <button {...props} />,
|
2024-08-16 10:30:14 +02:00
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
2024-08-30 13:39:30 -04:00
|
|
|
const menuId = useId();
|
|
|
|
|
|
2024-08-16 10:30:14 +02:00
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<DropdownPopup
|
|
|
|
|
isOpen={isPopoverActive}
|
|
|
|
|
setIsOpen={setIsPopoverActive}
|
|
|
|
|
trigger={
|
2024-12-18 11:10:34 -05:00
|
|
|
<Menu.MenuButton
|
2024-08-16 10:30:14 +02:00
|
|
|
id="conversation-menu-button"
|
2024-08-30 13:39:30 -04:00
|
|
|
aria-label={localize('com_nav_convo_menu_options')}
|
|
|
|
|
className={cn(
|
2024-09-10 10:11:39 -09:00
|
|
|
'z-30 inline-flex h-7 w-7 items-center justify-center gap-2 rounded-md border-none p-0 text-sm font-medium ring-ring-primary transition-all duration-200 ease-in-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
2024-08-30 13:39:30 -04:00
|
|
|
isActiveConvo === true
|
|
|
|
|
? 'opacity-100'
|
|
|
|
|
: 'opacity-0 focus:opacity-100 group-focus-within:opacity-100 group-hover:opacity-100 data-[open]:opacity-100',
|
|
|
|
|
)}
|
2024-08-16 10:30:14 +02:00
|
|
|
>
|
2024-09-10 10:11:39 -09:00
|
|
|
<Ellipsis className="icon-md text-text-secondary" aria-hidden={true} />
|
2024-12-18 11:10:34 -05:00
|
|
|
</Menu.MenuButton>
|
2024-08-16 10:30:14 +02:00
|
|
|
}
|
|
|
|
|
items={dropdownItems}
|
2024-08-30 13:39:30 -04:00
|
|
|
menuId={menuId}
|
2024-08-16 10:30:14 +02:00
|
|
|
/>
|
|
|
|
|
{showShareDialog && (
|
|
|
|
|
<ShareButton
|
2024-12-17 13:12:57 -05:00
|
|
|
conversationId={conversationId ?? ''}
|
2024-12-29 23:31:41 +01:00
|
|
|
open={showShareDialog}
|
|
|
|
|
onOpenChange={setShowShareDialog}
|
|
|
|
|
triggerRef={shareButtonRef}
|
2024-08-16 10:30:14 +02:00
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
{showDeleteDialog && (
|
|
|
|
|
<DeleteButton
|
2024-12-17 13:12:57 -05:00
|
|
|
title={title ?? ''}
|
2024-08-16 10:30:14 +02:00
|
|
|
retainView={retainView}
|
2024-12-17 13:12:57 -05:00
|
|
|
conversationId={conversationId ?? ''}
|
2024-08-16 10:30:14 +02:00
|
|
|
showDeleteDialog={showDeleteDialog}
|
|
|
|
|
setShowDeleteDialog={setShowDeleteDialog}
|
2024-12-29 23:31:41 +01:00
|
|
|
triggerRef={deleteButtonRef}
|
2024-08-16 10:30:14 +02:00
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-01-09 15:40:10 -05:00
|
|
|
|
|
|
|
|
export default memo(ConvoOptions);
|