mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-31 23:58:50 +01:00
🏷️ 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:
parent
6ea2628b56
commit
016ed866a3
28 changed files with 622 additions and 536 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue