fix: thread list state order after dragable (#5141)
* fix: thread list state order after dragable * fix: new chat order * chore: revert data provider
This commit is contained in:
parent
27c2a360f0
commit
426dc2ab87
@ -83,10 +83,10 @@ const SortableItem = memo(({ thread }: { thread: Thread }) => {
|
||||
const plainTitleForRename = useMemo(() => {
|
||||
// Basic HTML stripping for simple span tags.
|
||||
// If thread.title is undefined or null, treat as empty string before replace.
|
||||
return (thread.title || '').replace(/<span[^>]*>|<\/span>/g, '');
|
||||
}, [thread.title]);
|
||||
return (thread.title || '').replace(/<span[^>]*>|<\/span>/g, '')
|
||||
}, [thread.title])
|
||||
|
||||
const [title, setTitle] = useState(plainTitleForRename || 'New Thread');
|
||||
const [title, setTitle] = useState(plainTitleForRename || 'New Thread')
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -148,7 +148,7 @@ const SortableItem = memo(({ thread }: { thread: Thread }) => {
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
setOpenDropdown(false)
|
||||
setTitle(plainTitleForRename || 'New Thread');
|
||||
setTitle(plainTitleForRename || 'New Thread')
|
||||
}
|
||||
}}
|
||||
>
|
||||
@ -268,9 +268,14 @@ function ThreadList({ threads }: ThreadListProps) {
|
||||
|
||||
const sortedThreads = useMemo(() => {
|
||||
return threads.sort((a, b) => {
|
||||
if (a.order && b.order) return a.order - b.order
|
||||
|
||||
// Later on top
|
||||
// If both have order, sort by order (ascending, so lower order comes first)
|
||||
if (a.order != null && b.order != null) {
|
||||
return a.order - b.order
|
||||
}
|
||||
// If only one has order, prioritize the one with order (order comes first)
|
||||
if (a.order != null) return -1
|
||||
if (b.order != null) return 1
|
||||
// If neither has order, sort by updated time (newer threads first)
|
||||
return (b.updated || 0) - (a.updated || 0)
|
||||
})
|
||||
}, [threads])
|
||||
@ -293,17 +298,25 @@ function ThreadList({ threads }: ThreadListProps) {
|
||||
const { active, over } = event
|
||||
if (active.id !== over?.id && over) {
|
||||
// Access Global State
|
||||
const allThreadsMap = useThreads.getState().threads;
|
||||
const allThreadsArray = Object.values(allThreadsMap);
|
||||
const allThreadsMap = useThreads.getState().threads
|
||||
const allThreadsArray = Object.values(allThreadsMap)
|
||||
|
||||
// Calculate Global Indices
|
||||
const oldIndexInGlobal = allThreadsArray.findIndex((t) => t.id === active.id);
|
||||
const newIndexInGlobal = allThreadsArray.findIndex((t) => t.id === over.id);
|
||||
const oldIndexInGlobal = allThreadsArray.findIndex(
|
||||
(t) => t.id === active.id
|
||||
)
|
||||
const newIndexInGlobal = allThreadsArray.findIndex(
|
||||
(t) => t.id === over.id
|
||||
)
|
||||
|
||||
// Reorder Globally and Update State
|
||||
if (oldIndexInGlobal !== -1 && newIndexInGlobal !== -1) {
|
||||
const reorderedGlobalThreads = arrayMove(allThreadsArray, oldIndexInGlobal, newIndexInGlobal);
|
||||
setThreads(reorderedGlobalThreads);
|
||||
const reorderedGlobalThreads = arrayMove(
|
||||
allThreadsArray,
|
||||
oldIndexInGlobal,
|
||||
newIndexInGlobal
|
||||
)
|
||||
setThreads(reorderedGlobalThreads)
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
@ -46,10 +46,14 @@ export const useThreads = create<ThreadState>()(
|
||||
(acc: Record<string, Thread>, thread) => {
|
||||
acc[thread.id] = thread
|
||||
return acc
|
||||
}, {} as Record<string, Thread>)
|
||||
},
|
||||
{} as Record<string, Thread>
|
||||
)
|
||||
set({
|
||||
threads: threadMap,
|
||||
searchIndex: new Fzf<Thread[]>(Object.values(threadMap), { selector: (item: Thread) => item.title })
|
||||
searchIndex: new Fzf<Thread[]>(Object.values(threadMap), {
|
||||
selector: (item: Thread) => item.title,
|
||||
}),
|
||||
})
|
||||
},
|
||||
getFilteredThreads: (searchTerm: string) => {
|
||||
@ -63,25 +67,26 @@ export const useThreads = create<ThreadState>()(
|
||||
|
||||
let currentIndex = searchIndex
|
||||
if (!currentIndex) {
|
||||
currentIndex = new Fzf<Thread[]>(
|
||||
Object.values(threads),
|
||||
{ selector: (item: Thread) => item.title }
|
||||
)
|
||||
currentIndex = new Fzf<Thread[]>(Object.values(threads), {
|
||||
selector: (item: Thread) => item.title,
|
||||
})
|
||||
set({ searchIndex: currentIndex })
|
||||
}
|
||||
|
||||
// Use the index to search and return matching threads
|
||||
const fzfResults = currentIndex.find(searchTerm)
|
||||
return fzfResults.map((result: { item: Thread; positions: Set<number> }) => {
|
||||
const thread = result.item; // Fzf stores the original item here
|
||||
// Ensure result.positions is an array, default to empty if undefined
|
||||
const positions = Array.from(result.positions) || [];
|
||||
const highlightedTitle = highlightFzfMatch(thread.title, positions);
|
||||
return {
|
||||
...thread,
|
||||
title: highlightedTitle, // Override title with highlighted version
|
||||
};
|
||||
});
|
||||
return fzfResults.map(
|
||||
(result: { item: Thread; positions: Set<number> }) => {
|
||||
const thread = result.item // Fzf stores the original item here
|
||||
// Ensure result.positions is an array, default to empty if undefined
|
||||
const positions = Array.from(result.positions) || []
|
||||
const highlightedTitle = highlightFzfMatch(thread.title, positions)
|
||||
return {
|
||||
...thread,
|
||||
title: highlightedTitle, // Override title with highlighted version
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
toggleFavorite: (threadId) => {
|
||||
set((state) => {
|
||||
@ -107,7 +112,9 @@ export const useThreads = create<ThreadState>()(
|
||||
deleteThread(threadId)
|
||||
return {
|
||||
threads: remainingThreads,
|
||||
searchIndex: new Fzf<Thread[]>(Object.values(remainingThreads), { selector: (item: Thread) => item.title })
|
||||
searchIndex: new Fzf<Thread[]>(Object.values(remainingThreads), {
|
||||
selector: (item: Thread) => item.title,
|
||||
}),
|
||||
}
|
||||
})
|
||||
},
|
||||
@ -119,7 +126,7 @@ export const useThreads = create<ThreadState>()(
|
||||
})
|
||||
return {
|
||||
threads: {},
|
||||
searchIndex: null // Or new Fzf([], {selector...})
|
||||
searchIndex: null, // Or new Fzf([], {selector...})
|
||||
}
|
||||
})
|
||||
},
|
||||
@ -157,21 +164,24 @@ export const useThreads = create<ThreadState>()(
|
||||
id: ulid(),
|
||||
title: title ?? 'New Thread',
|
||||
model,
|
||||
// order: 1,
|
||||
order: 1, // Will be set properly by setThreads
|
||||
updated: Date.now() / 1000,
|
||||
assistants: assistant ? [assistant] : [],
|
||||
}
|
||||
return await createThread(newThread).then((createdThread) => {
|
||||
set((state) => {
|
||||
const newThreads = {
|
||||
...state.threads,
|
||||
[createdThread.id]: createdThread,
|
||||
};
|
||||
// Get all existing threads as an array
|
||||
const existingThreads = Object.values(state.threads)
|
||||
|
||||
// Create new array with the new thread at the beginning
|
||||
const reorderedThreads = [createdThread, ...existingThreads]
|
||||
|
||||
// Use setThreads to handle proper ordering (this will assign order 1, 2, 3...)
|
||||
get().setThreads(reorderedThreads)
|
||||
|
||||
return {
|
||||
threads: newThreads,
|
||||
currentThreadId: createdThread.id,
|
||||
searchIndex: new Fzf<Thread[]>(Object.values(newThreads), { selector: (item: Thread) => item.title }),
|
||||
};
|
||||
}
|
||||
})
|
||||
return createdThread
|
||||
})
|
||||
@ -221,10 +231,12 @@ export const useThreads = create<ThreadState>()(
|
||||
title: newTitle,
|
||||
}
|
||||
updateThread(updatedThread) // External call, order is fine
|
||||
const newThreads = { ...state.threads, [threadId]: updatedThread };
|
||||
const newThreads = { ...state.threads, [threadId]: updatedThread }
|
||||
return {
|
||||
threads: newThreads,
|
||||
searchIndex: new Fzf<Thread[]>(Object.values(newThreads), { selector: (item: Thread) => item.title }),
|
||||
searchIndex: new Fzf<Thread[]>(Object.values(newThreads), {
|
||||
selector: (item: Thread) => item.title,
|
||||
}),
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useMessages } from '@/hooks/useMessages'
|
||||
import { useModelProvider } from '@/hooks/useModelProvider'
|
||||
import { useThreads } from '@/hooks/useThreads'
|
||||
|
||||
import { useAppUpdater } from '@/hooks/useAppUpdater'
|
||||
import { fetchMessages } from '@/services/messages'
|
||||
import { fetchModels } from '@/services/models'
|
||||
@ -15,7 +15,7 @@ import { getAssistants } from '@/services/assistants'
|
||||
|
||||
export function DataProvider() {
|
||||
const { setProviders } = useModelProvider()
|
||||
const { setThreads } = useThreads()
|
||||
|
||||
const { setMessages } = useMessages()
|
||||
const { checkForUpdate } = useAppUpdater()
|
||||
const { setServers } = useMCPServers()
|
||||
@ -35,7 +35,6 @@ export function DataProvider() {
|
||||
|
||||
useEffect(() => {
|
||||
fetchThreads().then((threads) => {
|
||||
setThreads(threads)
|
||||
threads.forEach((thread) =>
|
||||
fetchMessages(thread.id).then((messages) =>
|
||||
setMessages(thread.id, messages)
|
||||
|
||||
@ -56,7 +56,7 @@ export const createThread = async (thread: Thread): Promise<Thread> => {
|
||||
},
|
||||
],
|
||||
metadata: {
|
||||
// order: 1,
|
||||
order: thread.order,
|
||||
},
|
||||
})
|
||||
.then((e) => {
|
||||
@ -67,7 +67,7 @@ export const createThread = async (thread: Thread): Promise<Thread> => {
|
||||
id: e.assistants?.[0]?.model?.id,
|
||||
provider: e.assistants?.[0]?.model?.engine,
|
||||
},
|
||||
// order: 1,
|
||||
order: e.metadata?.order ?? thread.order,
|
||||
assistants: e.assistants ?? [defaultAssistant],
|
||||
} as Thread
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user