mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-04 14:57:42 +00:00
🧽 fix: Clear Deleted Chats From Message Cache (#14270)
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
GitNexus Index / index (push) Waiting to run
GitNexus Index / post-index (push) Blocked by required conditions
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
GitNexus Index / index (push) Waiting to run
GitNexus Index / post-index (push) Blocked by required conditions
* fix: clear deleted conversation message caches * test: cover deleted chat cache cleanup * test: clarify deleted cache scenarios
This commit is contained in:
parent
481e2a9257
commit
305e0f5003
4 changed files with 86 additions and 1 deletions
|
|
@ -14,6 +14,7 @@ import {
|
|||
findConversationInInfinite,
|
||||
updateConvoInAllQueries,
|
||||
removeConvoFromAllQueries,
|
||||
clearDeletedConversationMessagesCache,
|
||||
} from '~/utils';
|
||||
import useUpdateTagsInConvo from '~/hooks/Conversations/useUpdateTagsInConvo';
|
||||
import { updateConversationTag } from '~/utils/conversationTags';
|
||||
|
|
@ -544,6 +545,7 @@ export const useDeleteConversationMutation = (
|
|||
|
||||
if (vars.conversationId) {
|
||||
removeConvoFromAllQueries(queryClient, vars.conversationId);
|
||||
clearDeletedConversationMessagesCache(queryClient, vars.conversationId);
|
||||
}
|
||||
|
||||
// Also remove from all archivedConversations caches
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import type { TMessage } from 'librechat-data-provider';
|
|||
import type { LocalizeFunction } from '~/common';
|
||||
import {
|
||||
clearMessagesCache,
|
||||
clearDeletedConversationMessagesCache,
|
||||
isValidTimestamp,
|
||||
getMessageAriaLabel,
|
||||
getMessageTimestamp,
|
||||
|
|
@ -50,6 +51,57 @@ describe('clearMessagesCache', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('clearDeletedConversationMessagesCache', () => {
|
||||
it('clears both caches when the new-conversation cache contains deleted chat messages', () => {
|
||||
const queryClient = new QueryClient();
|
||||
const conversationId = 'conversation-1';
|
||||
const messages = [makeMessage({ conversationId })];
|
||||
queryClient.setQueryData([QueryKeys.messages, conversationId], messages);
|
||||
queryClient.setQueryData(
|
||||
[QueryKeys.messages, Constants.NEW_CONVO],
|
||||
messages.map((message) => ({ ...message })),
|
||||
);
|
||||
|
||||
clearDeletedConversationMessagesCache(queryClient, conversationId);
|
||||
|
||||
expect(queryClient.getQueryData([QueryKeys.messages, conversationId])).toBeUndefined();
|
||||
expect(queryClient.getQueryData([QueryKeys.messages, Constants.NEW_CONVO])).toEqual([]);
|
||||
});
|
||||
|
||||
it('clears a shared new-conversation cache before its message IDs are hydrated', () => {
|
||||
const queryClient = new QueryClient();
|
||||
const conversationId = 'conversation-1';
|
||||
const messages = [makeMessage({ conversationId: Constants.NEW_CONVO as string })];
|
||||
queryClient.setQueryData([QueryKeys.messages, conversationId], messages);
|
||||
queryClient.setQueryData([QueryKeys.messages, Constants.NEW_CONVO], messages);
|
||||
|
||||
clearDeletedConversationMessagesCache(queryClient, conversationId);
|
||||
|
||||
expect(queryClient.getQueryData([QueryKeys.messages, conversationId])).toBeUndefined();
|
||||
expect(queryClient.getQueryData([QueryKeys.messages, Constants.NEW_CONVO])).toEqual([]);
|
||||
});
|
||||
|
||||
it('preserves an unrelated new-conversation message cache', () => {
|
||||
const queryClient = new QueryClient();
|
||||
const conversationId = 'conversation-1';
|
||||
const newConversationMessages = [
|
||||
makeMessage({ messageId: 'new-message', conversationId: Constants.NEW_CONVO as string }),
|
||||
];
|
||||
queryClient.setQueryData(
|
||||
[QueryKeys.messages, conversationId],
|
||||
[makeMessage({ conversationId })],
|
||||
);
|
||||
queryClient.setQueryData([QueryKeys.messages, Constants.NEW_CONVO], newConversationMessages);
|
||||
|
||||
clearDeletedConversationMessagesCache(queryClient, conversationId);
|
||||
|
||||
expect(queryClient.getQueryData([QueryKeys.messages, conversationId])).toBeUndefined();
|
||||
expect(queryClient.getQueryData([QueryKeys.messages, Constants.NEW_CONVO])).toEqual(
|
||||
newConversationMessages,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMessageAriaLabel', () => {
|
||||
it('returns "Message N" when depth is present and valid', () => {
|
||||
const msg = makeMessage({ depth: 2 });
|
||||
|
|
|
|||
|
|
@ -312,6 +312,33 @@ export const clearMessagesCache = (
|
|||
queryClient.setQueryData<TMessage[]>([QueryKeys.messages, Constants.NEW_CONVO], []);
|
||||
};
|
||||
|
||||
/** Removes a deleted conversation's message cache and any matching new-chat cache alias. */
|
||||
export const clearDeletedConversationMessagesCache = (
|
||||
queryClient: QueryClient,
|
||||
conversationId: string,
|
||||
): void => {
|
||||
const deletedMessages = queryClient.getQueryData<TMessage[]>([
|
||||
QueryKeys.messages,
|
||||
conversationId,
|
||||
]);
|
||||
const newConversationMessages = queryClient.getQueryData<TMessage[]>([
|
||||
QueryKeys.messages,
|
||||
Constants.NEW_CONVO,
|
||||
]);
|
||||
const newConversationAliasesDeleted =
|
||||
newConversationMessages != null &&
|
||||
(newConversationMessages === deletedMessages ||
|
||||
newConversationMessages.some((message) => message.conversationId === conversationId));
|
||||
|
||||
queryClient.removeQueries([QueryKeys.messages, conversationId], { exact: true });
|
||||
|
||||
if (!newConversationAliasesDeleted) {
|
||||
return;
|
||||
}
|
||||
|
||||
queryClient.setQueryData<TMessage[]>([QueryKeys.messages, Constants.NEW_CONVO], []);
|
||||
};
|
||||
|
||||
/** Returns a 1-based message number, or null if depth is absent or invalid. */
|
||||
const getMessageNumber = (message: TMessage): number | null => {
|
||||
if (message.depth == null || message.depth < 0) {
|
||||
|
|
|
|||
|
|
@ -84,7 +84,9 @@ test.describe('conversation management', () => {
|
|||
await expect(page.getByTestId('convo-item').filter({ hasText: renamedTitle })).toBeVisible();
|
||||
});
|
||||
|
||||
test('deletes a conversation from the sidebar and blocks direct URL access', async ({ page }) => {
|
||||
test('deletes a conversation, clears its messages, and blocks direct URL access', async ({
|
||||
page,
|
||||
}) => {
|
||||
const label = uniqueLabel('sidebar-delete');
|
||||
const renamedTitle = `Delete ${label}`;
|
||||
|
||||
|
|
@ -111,6 +113,8 @@ test.describe('conversation management', () => {
|
|||
|
||||
await expect(page).toHaveURL(/\/c\/new$/);
|
||||
await expect(page.getByTestId('convo-item').filter({ hasText: renamedTitle })).toHaveCount(0);
|
||||
await expect(messagesView(page).getByText(turn.prompt)).toHaveCount(0);
|
||||
await expect(messagesView(page).getByText(turn.reply)).toHaveCount(0);
|
||||
|
||||
await page.goto(conversationUrl, { timeout: 10000 });
|
||||
await expect(page.getByRole('textbox', { name: 'Message input' })).toBeVisible();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue