2024-08-16 10:30:14 +02:00
|
|
|
import { useState, type FC, useCallback } from 'react';
|
2024-07-29 07:45:59 -07:00
|
|
|
import { useRecoilValue } from 'recoil';
|
2024-08-08 21:25:10 -04:00
|
|
|
import { Constants } from 'librechat-data-provider';
|
2024-08-16 10:30:14 +02:00
|
|
|
import { Menu, MenuButton, MenuItems } from '@headlessui/react';
|
2024-07-29 07:45:59 -07:00
|
|
|
import { BookmarkFilledIcon, BookmarkIcon } from '@radix-ui/react-icons';
|
|
|
|
|
import { useConversationTagsQuery, useTagConversationMutation } from '~/data-provider';
|
|
|
|
|
import { BookmarkMenuItems } from './Bookmarks/BookmarkMenuItems';
|
|
|
|
|
import { BookmarkContext } from '~/Providers/BookmarkContext';
|
2024-08-16 10:30:14 +02:00
|
|
|
import { BookmarkEditDialog } from '~/components/Bookmarks';
|
|
|
|
|
import { NotificationSeverity } from '~/common';
|
|
|
|
|
import { useToastContext } from '~/Providers';
|
|
|
|
|
import { useBookmarkSuccess } from '~/hooks';
|
2024-07-29 07:45:59 -07:00
|
|
|
import { Spinner } from '~/components';
|
|
|
|
|
import { cn } from '~/utils';
|
|
|
|
|
import store from '~/store';
|
|
|
|
|
|
|
|
|
|
const BookmarkMenu: FC = () => {
|
2024-08-16 10:30:14 +02:00
|
|
|
const { showToast } = useToastContext();
|
2024-07-29 07:45:59 -07:00
|
|
|
|
2024-08-16 10:30:14 +02:00
|
|
|
const conversation = useRecoilValue(store.conversationByIndex(0)) || undefined;
|
2024-08-08 21:25:10 -04:00
|
|
|
const conversationId = conversation?.conversationId ?? '';
|
|
|
|
|
const onSuccess = useBookmarkSuccess(conversationId);
|
|
|
|
|
const [tags, setTags] = useState<string[]>(conversation?.tags || []);
|
2024-08-16 10:30:14 +02:00
|
|
|
const [open, setOpen] = useState(false);
|
2024-07-29 07:45:59 -07:00
|
|
|
|
2024-08-08 21:25:10 -04:00
|
|
|
const { mutateAsync, isLoading } = useTagConversationMutation(conversationId);
|
2024-07-29 07:45:59 -07:00
|
|
|
|
|
|
|
|
const { data } = useConversationTagsQuery();
|
|
|
|
|
|
2024-08-16 10:30:14 +02:00
|
|
|
const isActiveConvo = Boolean(
|
2024-08-08 21:25:10 -04:00
|
|
|
conversation &&
|
2024-08-16 10:30:14 +02:00
|
|
|
conversationId &&
|
|
|
|
|
conversationId !== Constants.NEW_CONVO &&
|
|
|
|
|
conversationId !== 'search',
|
|
|
|
|
);
|
2024-07-29 07:45:59 -07:00
|
|
|
|
2024-08-16 10:30:14 +02:00
|
|
|
const handleSubmit = useCallback(
|
|
|
|
|
async (tag?: string): Promise<void> => {
|
|
|
|
|
if (tag === undefined || tag === '' || !conversationId) {
|
|
|
|
|
showToast({
|
|
|
|
|
message: 'Invalid tag or conversationId',
|
|
|
|
|
severity: NotificationSeverity.ERROR,
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-07-29 07:45:59 -07:00
|
|
|
|
2024-08-16 10:30:14 +02:00
|
|
|
const newTags = tags.includes(tag) ? tags.filter((t) => t !== tag) : [...tags, tag];
|
|
|
|
|
await mutateAsync(
|
|
|
|
|
{
|
|
|
|
|
tags: newTags,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
onSuccess: (newTags: string[]) => {
|
|
|
|
|
setTags(newTags);
|
|
|
|
|
onSuccess(newTags);
|
2024-08-08 21:25:10 -04:00
|
|
|
},
|
2024-08-16 10:30:14 +02:00
|
|
|
onError: () => {
|
|
|
|
|
showToast({
|
|
|
|
|
message: 'Error adding bookmark',
|
|
|
|
|
severity: NotificationSeverity.ERROR,
|
|
|
|
|
});
|
2024-08-08 21:25:10 -04:00
|
|
|
},
|
2024-08-16 10:30:14 +02:00
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
[tags, conversationId, mutateAsync, setTags, onSuccess, showToast],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!isActiveConvo) {
|
|
|
|
|
return <></>;
|
|
|
|
|
}
|
2024-07-29 07:45:59 -07:00
|
|
|
|
2024-08-08 21:25:10 -04:00
|
|
|
const renderButtonContent = () => {
|
|
|
|
|
if (isLoading) {
|
2024-08-16 10:30:14 +02:00
|
|
|
return <Spinner aria-label="Spinner" />;
|
2024-08-08 21:25:10 -04:00
|
|
|
}
|
2024-08-16 10:30:14 +02:00
|
|
|
if (tags.length > 0) {
|
|
|
|
|
return <BookmarkFilledIcon className="icon-sm" aria-label="Filled Bookmark" />;
|
2024-08-08 21:25:10 -04:00
|
|
|
}
|
2024-08-16 10:30:14 +02:00
|
|
|
return <BookmarkIcon className="icon-sm" aria-label="Bookmark" />;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleToggleOpen = () => {
|
|
|
|
|
setOpen(!open);
|
|
|
|
|
return Promise.resolve();
|
2024-08-08 21:25:10 -04:00
|
|
|
};
|
|
|
|
|
|
2024-07-29 07:45:59 -07:00
|
|
|
return (
|
2024-08-16 10:30:14 +02:00
|
|
|
<>
|
|
|
|
|
<Menu as="div" className="group relative">
|
|
|
|
|
{({ open }) => (
|
|
|
|
|
<>
|
|
|
|
|
<MenuButton
|
|
|
|
|
aria-label="Add bookmarks"
|
|
|
|
|
className={cn(
|
|
|
|
|
'mt-text-sm flex size-10 items-center justify-center gap-2 rounded-lg border border-border-light text-sm transition-colors duration-200 hover:bg-surface-hover',
|
|
|
|
|
open ? 'bg-surface-hover' : '',
|
|
|
|
|
)}
|
|
|
|
|
data-testid="bookmark-menu"
|
|
|
|
|
>
|
|
|
|
|
{renderButtonContent()}
|
|
|
|
|
</MenuButton>
|
|
|
|
|
<MenuItems
|
|
|
|
|
anchor="bottom start"
|
|
|
|
|
className="overflow-hidden rounded-lg bg-header-primary p-1.5 shadow-lg outline-none"
|
|
|
|
|
>
|
|
|
|
|
<BookmarkContext.Provider value={{ bookmarks: data || [] }}>
|
|
|
|
|
<BookmarkMenuItems
|
|
|
|
|
handleToggleOpen={handleToggleOpen}
|
|
|
|
|
tags={tags}
|
|
|
|
|
handleSubmit={handleSubmit}
|
|
|
|
|
/>
|
|
|
|
|
</BookmarkContext.Provider>
|
|
|
|
|
</MenuItems>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</Menu>
|
|
|
|
|
<BookmarkEditDialog
|
|
|
|
|
conversation={conversation}
|
|
|
|
|
tags={tags}
|
|
|
|
|
setTags={setTags}
|
|
|
|
|
open={open}
|
|
|
|
|
setOpen={setOpen}
|
|
|
|
|
/>
|
|
|
|
|
</>
|
2024-07-29 07:45:59 -07:00
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default BookmarkMenu;
|