🏷️ fix: Address Statefulness Issues for Bookmarks (#3590)

* refactor: optimize tag methods, remove rebuild

* refactor(tags): add lean db operations, fix updateTagsForConversation, remove rebuild button, only send convoId once

* refactor: Update BookmarkMenu to use Constants.NEW_CONVO constant for comparison

* style: Update BookmarkMenu styles and constants, use theming

* refactor: move tags query from package to client workspace

* refactor: optimize ConversationTag document creation and update logic

* style: Update BookmarkMenuItems to use theming

* refactor: JSDocs + try/catch for conversation tags API routes

* refactor: Update BookmarkNav theming classes and new data provider location

* fix: statefulness of conversation bookmarks
- move non-mutation hook to hooks/Conversation
- remove use of deprecated global convo
- update convo infinite data as well as current convo state upon successful tag add

* refactor: Update BookmarkMenu styles and constants, use theming

* refactor: Add lean option to ConversationTag deletion query

* fix(BookmarkTable): position order rendering esp. when new tag is created

* refactor: Update useBookmarkSucess to useBookmarkSuccess for consistency

* refactor: Update ConversationTag creation logic to increment count only if addToConversation is true

* style: theming
This commit is contained in:
Danny Avila 2024-08-08 21:25:10 -04:00 committed by GitHub
parent 6ea2628b56
commit 016ed866a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 622 additions and 536 deletions

View file

@ -1,38 +1,23 @@
import { BookmarkPlusIcon } from 'lucide-react';
import { useConversationTagsQuery, useRebuildConversationTagsMutation } from '~/data-provider';
import { useConversationTagsQuery } from '~/data-provider';
import { Button } from '~/components/ui';
import { BookmarkContext } from '~/Providers/BookmarkContext';
import { BookmarkEditDialog } from '~/components/Bookmarks';
import BookmarkTable from './BookmarkTable';
import { Spinner } from '~/components/svg';
import { useLocalize } from '~/hooks';
import HoverCardSettings from '~/components/Nav/SettingsTabs/HoverCardSettings';
const BookmarkPanel = () => {
const localize = useLocalize();
const { mutate, isLoading } = useRebuildConversationTagsMutation();
const { data } = useConversationTagsQuery();
const rebuildTags = () => {
mutate({});
};
return (
<div className="h-auto max-w-full overflow-x-hidden">
<BookmarkContext.Provider value={{ bookmarks: data || [] }}>
<BookmarkTable />
<div className="flex justify-between gap-2">
<Button variant="outline" onClick={rebuildTags} className="w-50 text-sm">
{isLoading ? (
<Spinner />
) : (
<div className="flex gap-2">
{localize('com_ui_bookmarks_rebuild')}
<HoverCardSettings side="top" text="com_nav_info_bookmarks_rebuild" />
</div>
)}
</Button>
<BookmarkEditDialog
trigger={
<Button variant="outline" onClick={rebuildTags} className="w-full text-sm">
<Button variant="outline" className="w-full text-sm">
<BookmarkPlusIcon className="mr-1 size-4" />
<div className="break-all">{localize('com_ui_bookmarks_new')}</div>
</Button>

View file

@ -14,7 +14,11 @@ const BookmarkTable = () => {
const { bookmarks } = useBookmarkContext();
useEffect(() => {
setRows(bookmarks?.map((item) => ({ id: item.tag, ...item })) || []);
setRows(
bookmarks
?.map((item) => ({ id: item.tag, ...item }))
.sort((a, b) => a.position - b.position) || [],
);
}, [bookmarks]);
const moveRow = useCallback((dragIndex: number, hoverIndex: number) => {
@ -22,13 +26,16 @@ const BookmarkTable = () => {
const updatedRows = [...prevTags];
const [movedRow] = updatedRows.splice(dragIndex, 1);
updatedRows.splice(hoverIndex, 0, movedRow);
return updatedRows;
return updatedRows.map((row, index) => ({ ...row, position: index }));
});
}, []);
const renderRow = useCallback((row: TConversationTag, position: number) => {
return <BookmarkTableRow key={row.tag} moveRow={moveRow} row={row} position={position} />;
}, []);
const renderRow = useCallback(
(row: TConversationTag) => {
return <BookmarkTableRow key={row.tag} moveRow={moveRow} row={row} position={row.position} />;
},
[moveRow],
);
const filteredRows = rows.filter((row) =>
row.tag.toLowerCase().includes(searchQuery.toLowerCase()),
@ -58,7 +65,7 @@ const BookmarkTable = () => {
</TableCell>
</TableRow>
</TableHeader>
<TableBody>{currentRows.map((row, i) => renderRow(row, i))}</TableBody>
<TableBody>{currentRows.map((row) => renderRow(row))}</TableBody>
</Table>
</div>
<div className="flex items-center justify-between py-4">

View file

@ -1,8 +1,12 @@
import React, { useState } from 'react';
import React, { useState, useRef } from 'react';
import { useDrag, useDrop } from 'react-dnd';
import type { TConversationTag } from 'librechat-data-provider';
import { DeleteBookmarkButton, EditBookmarkButton } from '~/components/Bookmarks';
import { useConversationTagMutation } from '~/data-provider';
import { TableRow, TableCell } from '~/components/ui';
import { NotificationSeverity } from '~/common';
import { useToastContext } from '~/Providers';
import { useLocalize } from '~/hooks';
interface BookmarkTableRowProps {
row: TConversationTag;
@ -10,13 +14,39 @@ interface BookmarkTableRowProps {
position: number;
}
interface DragItem {
index: number;
id: string;
type: string;
}
const BookmarkTableRow: React.FC<BookmarkTableRowProps> = ({ row, moveRow, position }) => {
const [isHovered, setIsHovered] = useState(false);
const ref = React.useRef<HTMLTableRowElement>(null);
const ref = useRef<HTMLTableRowElement>(null);
const mutation = useConversationTagMutation(row.tag);
const localize = useLocalize();
const { showToast } = useToastContext();
const handleDrop = (item: DragItem) => {
const data = {
...row,
position: item.index,
};
mutation.mutate(data, {
onError: () => {
showToast({
message: localize('com_ui_bookmarks_update_error'),
severity: NotificationSeverity.ERROR,
});
},
});
};
const [, drop] = useDrop({
accept: 'bookmark',
hover(item: { index: number }) {
drop: (item: DragItem) => handleDrop(item),
hover(item: DragItem) {
if (!ref.current) {
return;
}
@ -43,7 +73,7 @@ const BookmarkTableRow: React.FC<BookmarkTableRowProps> = ({ row, moveRow, posit
return (
<TableRow
ref={ref}
className="cursor-move hover:bg-gray-100 dark:hover:bg-gray-800"
className="cursor-move hover:bg-surface-secondary"
style={{ opacity: isDragging ? 0.5 : 1 }}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}