mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-04 09:38:50 +01:00
feat: Enhance updateConvoInAllQueries to support moving conversations to the top
This commit is contained in:
parent
c7bc5548bc
commit
4c115b684c
3 changed files with 134 additions and 10 deletions
|
|
@ -310,7 +310,7 @@ export default function useEventHandlers({
|
|||
if (requestMessage.parentMessageId === Constants.NO_PARENT) {
|
||||
addConvoToAllQueries(queryClient, update);
|
||||
} else {
|
||||
updateConvoInAllQueries(queryClient, update.conversationId!, (_c) => update);
|
||||
updateConvoInAllQueries(queryClient, update.conversationId!, (_c) => update, true);
|
||||
}
|
||||
} else if (setConversation) {
|
||||
setConversation((prevState) => {
|
||||
|
|
@ -385,7 +385,7 @@ export default function useEventHandlers({
|
|||
if (parentMessageId === Constants.NO_PARENT) {
|
||||
addConvoToAllQueries(queryClient, update);
|
||||
} else {
|
||||
updateConvoInAllQueries(queryClient, update.conversationId!, (_c) => update);
|
||||
updateConvoInAllQueries(queryClient, update.conversationId!, (_c) => update, true);
|
||||
}
|
||||
}
|
||||
} else if (setConversation) {
|
||||
|
|
|
|||
|
|
@ -596,6 +596,77 @@ describe('Conversation Utilities', () => {
|
|||
expect(data!.pages[0].conversations[0].model).toBe('gpt-4');
|
||||
});
|
||||
|
||||
it('updateConvoInAllQueries with moveToTop moves convo to front and updates updatedAt', () => {
|
||||
// Add more conversations so 'a' is not at position 0
|
||||
const convoC = { conversationId: 'c', updatedAt: '2024-01-03T12:00:00Z' } as TConversation;
|
||||
queryClient.setQueryData(['allConversations'], {
|
||||
pages: [{ conversations: [convoC, convoA], nextCursor: null }],
|
||||
pageParams: [],
|
||||
});
|
||||
|
||||
const before = new Date().toISOString();
|
||||
updateConvoInAllQueries(queryClient, 'a', (c) => ({ ...c, model: 'gpt-4' }), true);
|
||||
const data = queryClient.getQueryData<InfiniteData<any>>(['allConversations']);
|
||||
|
||||
// 'a' should now be at position 0
|
||||
expect(data!.pages[0].conversations[0].conversationId).toBe('a');
|
||||
expect(data!.pages[0].conversations[0].model).toBe('gpt-4');
|
||||
// updatedAt should be updated
|
||||
expect(
|
||||
new Date(data!.pages[0].conversations[0].updatedAt).getTime(),
|
||||
).toBeGreaterThanOrEqual(new Date(before).getTime());
|
||||
// 'c' should now be at position 1
|
||||
expect(data!.pages[0].conversations[1].conversationId).toBe('c');
|
||||
});
|
||||
|
||||
it('updateConvoInAllQueries with moveToTop from second page', () => {
|
||||
const convoC = { conversationId: 'c', updatedAt: '2024-01-03T12:00:00Z' } as TConversation;
|
||||
const convoD = { conversationId: 'd', updatedAt: '2024-01-04T12:00:00Z' } as TConversation;
|
||||
queryClient.setQueryData(['allConversations'], {
|
||||
pages: [
|
||||
{ conversations: [convoC, convoD], nextCursor: 'cursor1' },
|
||||
{ conversations: [convoA, convoB], nextCursor: null },
|
||||
],
|
||||
pageParams: [],
|
||||
});
|
||||
|
||||
updateConvoInAllQueries(queryClient, 'a', (c) => ({ ...c, title: 'Updated' }), true);
|
||||
const data = queryClient.getQueryData<InfiniteData<any>>(['allConversations']);
|
||||
|
||||
// 'a' should now be at front of page 0
|
||||
expect(data!.pages[0].conversations[0].conversationId).toBe('a');
|
||||
expect(data!.pages[0].conversations[0].title).toBe('Updated');
|
||||
// Page 0 should have 3 conversations now
|
||||
expect(data!.pages[0].conversations.length).toBe(3);
|
||||
// Page 1 should have 1 conversation (only 'b' remains)
|
||||
expect(data!.pages[1].conversations.length).toBe(1);
|
||||
expect(data!.pages[1].conversations[0].conversationId).toBe('b');
|
||||
});
|
||||
|
||||
it('updateConvoInAllQueries with moveToTop when already at position 0 updates in place', () => {
|
||||
const originalUpdatedAt = convoA.updatedAt;
|
||||
updateConvoInAllQueries(queryClient, 'a', (c) => ({ ...c, model: 'gpt-4' }), true);
|
||||
const data = queryClient.getQueryData<InfiniteData<any>>(['allConversations']);
|
||||
|
||||
expect(data!.pages[0].conversations[0].conversationId).toBe('a');
|
||||
expect(data!.pages[0].conversations[0].model).toBe('gpt-4');
|
||||
// updatedAt should still be updated even when already at top
|
||||
expect(data!.pages[0].conversations[0].updatedAt).not.toBe(originalUpdatedAt);
|
||||
});
|
||||
|
||||
it('updateConvoInAllQueries with moveToTop returns original data if convo not found', () => {
|
||||
const dataBefore = queryClient.getQueryData<InfiniteData<any>>(['allConversations']);
|
||||
updateConvoInAllQueries(
|
||||
queryClient,
|
||||
'nonexistent',
|
||||
(c) => ({ ...c, model: 'gpt-4' }),
|
||||
true,
|
||||
);
|
||||
const dataAfter = queryClient.getQueryData<InfiniteData<any>>(['allConversations']);
|
||||
|
||||
expect(dataAfter).toEqual(dataBefore);
|
||||
});
|
||||
|
||||
it('removeConvoFromAllQueries deletes conversation', () => {
|
||||
removeConvoFromAllQueries(queryClient, 'a');
|
||||
const data = queryClient.getQueryData<InfiniteData<any>>(['allConversations']);
|
||||
|
|
|
|||
|
|
@ -352,6 +352,7 @@ export function updateConvoInAllQueries(
|
|||
queryClient: QueryClient,
|
||||
conversationId: string,
|
||||
updater: (c: TConversation) => TConversation,
|
||||
moveToTop = false,
|
||||
) {
|
||||
const queries = queryClient
|
||||
.getQueryCache()
|
||||
|
|
@ -362,15 +363,67 @@ export function updateConvoInAllQueries(
|
|||
if (!oldData) {
|
||||
return oldData;
|
||||
}
|
||||
return {
|
||||
...oldData,
|
||||
pages: oldData.pages.map((page) => ({
|
||||
...page,
|
||||
conversations: page.conversations.map((c) =>
|
||||
c.conversationId === conversationId ? updater(c) : c,
|
||||
|
||||
// Find conversation location (single pass with early exit)
|
||||
let pageIdx = -1;
|
||||
let convoIdx = -1;
|
||||
for (let pi = 0; pi < oldData.pages.length; pi++) {
|
||||
const ci = oldData.pages[pi].conversations.findIndex(
|
||||
(c) => c.conversationId === conversationId,
|
||||
);
|
||||
if (ci !== -1) {
|
||||
pageIdx = pi;
|
||||
convoIdx = ci;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pageIdx === -1) {
|
||||
return oldData;
|
||||
}
|
||||
|
||||
const found = oldData.pages[pageIdx].conversations[convoIdx];
|
||||
const updated = moveToTop
|
||||
? { ...updater(found), updatedAt: new Date().toISOString() }
|
||||
: updater(found);
|
||||
|
||||
// If not moving to top, or already at top of page 0, update in place
|
||||
if (!moveToTop || (pageIdx === 0 && convoIdx === 0)) {
|
||||
return {
|
||||
...oldData,
|
||||
pages: oldData.pages.map((page, pi) =>
|
||||
pi === pageIdx
|
||||
? {
|
||||
...page,
|
||||
conversations: page.conversations.map((c, ci) => (ci === convoIdx ? updated : c)),
|
||||
}
|
||||
: page,
|
||||
),
|
||||
})),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// Move to top: only modify affected pages
|
||||
const newPages = oldData.pages.map((page, pi) => {
|
||||
if (pi === 0 && pageIdx === 0) {
|
||||
// Source is page 0: remove from current position, add to front
|
||||
const convos = page.conversations.filter((_, ci) => ci !== convoIdx);
|
||||
return { ...page, conversations: [updated, ...convos] };
|
||||
}
|
||||
if (pi === 0) {
|
||||
// Add to front of page 0
|
||||
return { ...page, conversations: [updated, ...page.conversations] };
|
||||
}
|
||||
if (pi === pageIdx) {
|
||||
// Remove from source page
|
||||
return {
|
||||
...page,
|
||||
conversations: page.conversations.filter((_, ci) => ci !== convoIdx),
|
||||
};
|
||||
}
|
||||
return page;
|
||||
});
|
||||
|
||||
return { ...oldData, pages: newPages };
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue