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