feat(fork): optimistic sidebar entry + Fork | prefix for forked tasks (#4353)

* feat(fork): optimistic sidebar entry + rename to 'Fork | Chat Name'

Adds the forked task to the sidebar immediately by writing an optimistic
entry into the task list cache in onSuccess, before the background refetch
arrives. Without this, the sidebar showed no new task until the invalidation
refetch completed after navigation.

Also moves the Fork prefix to the front of the name ('Fork | Chat Name')
so it is immediately scannable in the sidebar.

* fix(fork): cancel in-flight queries before optimistic sidebar write
This commit is contained in:
Waleed
2026-04-29 22:39:59 -07:00
committed by GitHub
parent 2bb5c91554
commit cb43b596b9
2 changed files with 21 additions and 2 deletions
@@ -74,8 +74,8 @@ export const POST = withRouteHandler(
: []
const newId = generateId()
const baseTitle = (parent.title ?? 'New task').replace(/ \| Fork$/, '')
const title = `${baseTitle} | Fork`
const baseTitle = (parent.title ?? 'New task').replace(/^Fork \| /, '')
const title = `Fork | ${baseTitle}`
const now = new Date()
const [newChat] = await db
+19
View File
@@ -609,6 +609,25 @@ export function useForkTask(workspaceId?: string) {
const queryClient = useQueryClient()
return useMutation({
mutationFn: forkChat,
onSuccess: async (data, variables) => {
await queryClient.cancelQueries({ queryKey: taskKeys.list(workspaceId) })
const existing = queryClient.getQueryData<TaskMetadata[]>(taskKeys.list(workspaceId))
if (existing) {
const sourceTask = existing.find((t) => t.id === variables.chatId)
const baseName = (sourceTask?.name ?? 'New task').replace(/^Fork \| /, '')
const optimisticTask: TaskMetadata = {
id: data.id,
name: `Fork | ${baseName}`,
updatedAt: new Date(),
isActive: false,
isUnread: false,
}
queryClient.setQueryData<TaskMetadata[]>(taskKeys.list(workspaceId), [
optimisticTask,
...existing,
])
}
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: taskKeys.list(workspaceId) })
},