🏷️ 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

@ -10,6 +10,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
import { dataService, MutationKeys, QueryKeys, defaultOrderQuery } from 'librechat-data-provider';
import type t from 'librechat-data-provider';
import type { InfiniteData, UseMutationResult } from '@tanstack/react-query';
import useUpdateTagsInConvo from '~/hooks/Conversations/useUpdateTagsInConvo';
import { updateConversationTag } from '~/utils/conversationTags';
import { normalizeData } from '~/utils/collection';
import store from '~/store';
@ -89,89 +90,6 @@ export const useUpdateConversationMutation = (
);
};
const useUpdateTagsInConversation = () => {
const queryClient = useQueryClient();
// Update the queryClient cache with the new tag when a new tag is added/removed to a conversation
const updateTagsInConversation = (conversationId: string, tags: string[]) => {
// Update the tags for the current conversation
const currentConvo = queryClient.getQueryData<t.TConversation>([
QueryKeys.conversation,
conversationId,
]);
if (!currentConvo) {
return;
}
const updatedConvo = {
...currentConvo,
tags,
} as t.TConversation;
queryClient.setQueryData([QueryKeys.conversation, conversationId], updatedConvo);
queryClient.setQueryData<t.ConversationData>([QueryKeys.allConversations], (convoData) => {
if (!convoData) {
return convoData;
}
return updateConvoFields(
convoData,
{
conversationId: currentConvo.conversationId,
tags: updatedConvo.tags,
} as t.TConversation,
true,
);
});
};
// update the tag to newTag in all conversations when a tag is updated to a newTag
// The difference with updateTagsInConversation is that it adds or removes tags for a specific conversation,
// whereas this function is for changing the title of a specific tag.
const replaceTagsInAllConversations = (tag: string, newTag: string) => {
const data = queryClient.getQueryData<InfiniteData<ConversationListResponse>>([
QueryKeys.allConversations,
]);
const conversationIdsWithTag = [] as string[];
// update tag to newTag in all conversations
const newData = JSON.parse(JSON.stringify(data)) as InfiniteData<ConversationListResponse>;
for (let pageIndex = 0; pageIndex < newData.pages.length; pageIndex++) {
const page = newData.pages[pageIndex];
page.conversations = page.conversations.map((conversation) => {
if (conversation.conversationId && conversation.tags?.includes(tag)) {
conversationIdsWithTag.push(conversation.conversationId);
conversation.tags = conversation.tags.map((t) => (t === tag ? newTag : t));
}
return conversation;
});
}
queryClient.setQueryData<InfiniteData<ConversationListResponse>>(
[QueryKeys.allConversations],
newData,
);
// update the tag to newTag from the cache of each conversation
for (let i = 0; i < conversationIdsWithTag.length; i++) {
const conversationId = conversationIdsWithTag[i];
const conversation = queryClient.getQueryData<t.TConversation>([
QueryKeys.conversation,
conversationId,
]);
if (conversation && conversation.tags) {
const updatedConvo = {
...conversation,
tags: conversation.tags.map((t) => (t === tag ? newTag : t)),
} as t.TConversation;
queryClient.setQueryData<t.TConversation>(
[QueryKeys.conversation, conversationId],
updatedConvo,
);
}
}
};
return { updateTagsInConversation, replaceTagsInAllConversations };
};
/**
* Add or remove tags for a conversation
*/
@ -179,7 +97,7 @@ export const useTagConversationMutation = (
conversationId: string,
): UseMutationResult<t.TTagConversationResponse, unknown, t.TTagConversationRequest, unknown> => {
const query = useConversationTagsQuery();
const { updateTagsInConversation } = useUpdateTagsInConversation();
const { updateTagsInConversation } = useUpdateTagsInConvo();
return useMutation(
(payload: t.TTagConversationRequest) =>
dataService.addTagToConversation(conversationId, payload),
@ -385,21 +303,6 @@ export const useDeleteSharedLinkMutation = (
});
};
// If the number of conversations tagged is incorrect, recalculate the tag information.
export const useRebuildConversationTagsMutation = (): UseMutationResult<
t.TConversationTagsResponse,
unknown,
unknown,
unknown
> => {
const queryClient = useQueryClient();
return useMutation(() => dataService.rebuildConversationTags(), {
onSuccess: (_data) => {
queryClient.setQueryData<t.TConversationTag[]>([QueryKeys.conversationTags], _data);
},
});
};
// Add a tag or update tag information (tag, description, position, etc.)
export const useConversationTagMutation = (
tag?: string,
@ -407,7 +310,7 @@ export const useConversationTagMutation = (
): UseMutationResult<t.TConversationTagResponse, unknown, t.TConversationTagRequest, unknown> => {
const queryClient = useQueryClient();
const { ..._options } = options || {};
const { updateTagsInConversation, replaceTagsInAllConversations } = useUpdateTagsInConversation();
const { updateTagsInConversation, replaceTagsInAllConversations } = useUpdateTagsInConvo();
return useMutation(
(payload: t.TConversationTagRequest) =>
tag
@ -427,6 +330,9 @@ export const useConversationTagMutation = (
},
] as t.TConversationTag[];
}
if (!tag) {
return [...data, _data].sort((a, b) => a.position - b.position);
}
return updateConversationTag(data, vars, _data, tag);
});
if (vars.addToConversation && vars.conversationId && _data.tag) {