fix: preserve task lane answers across page reload and browser close
Two issues fixed: 1. TaskLane useEffect on [questions, actions] was resetting all tasks to pending with empty values, wiping restored user answers. Now checks sessionStorage for saved state before resetting. 2. selectChat was setting activeQuestions/activeActions before writing responses to sessionStorage, causing a race where TaskLane mounted with new props but empty sessionStorage. Now writes responses to sessionStorage first so TaskLane can restore them on prop change. The backend saveTaskLane debounce (2s) persists responses to the DB, and selectChat restores them via pending_task_lane.responses. This chain now survives browser close. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -147,8 +147,16 @@ export function TaskLane({ questions, actions, sessionId, onSubmit, onClose, loa
|
||||
return () => { if (saveTimerRef.current) clearTimeout(saveTimerRef.current) }
|
||||
}, [sessionId, tasks]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
// Reset when new tasks come in from AI response
|
||||
// Reset when new tasks come in from AI response — but preserve saved state
|
||||
useEffect(() => {
|
||||
if (sessionId) {
|
||||
const saved = loadTaskState(sessionId)
|
||||
if (saved && saved.length > 0) {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- intentional: syncs derived state from prop changes
|
||||
setTasks(saved)
|
||||
return
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- intentional: syncs derived state from prop changes
|
||||
setTasks([
|
||||
...questions.map((q): QuestionResponse => ({
|
||||
@@ -158,7 +166,7 @@ export function TaskLane({ questions, actions, sessionId, onSubmit, onClose, loa
|
||||
type: 'action', label: a.label, command: a.command, description: a.description, state: 'pending', value: '',
|
||||
})),
|
||||
])
|
||||
}, [questions, actions])
|
||||
}, [questions, actions]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const updateTask = (idx: number, updates: Partial<TaskResponse>) => {
|
||||
setTasks(prev => prev.map((t, i) => i === idx ? { ...t, ...updates } as TaskResponse : t))
|
||||
|
||||
@@ -232,16 +232,17 @@ export default function AssistantChatPage() {
|
||||
const q = detail.pending_task_lane.questions || []
|
||||
const a = detail.pending_task_lane.actions || []
|
||||
if (q.length > 0 || a.length > 0) {
|
||||
setActiveQuestions(q)
|
||||
setActiveActions(a)
|
||||
setShowTaskLane(true)
|
||||
// Pre-load user's saved responses into sessionStorage so TaskLane restores them
|
||||
// Pre-load user's saved responses into sessionStorage BEFORE setting props
|
||||
// so TaskLane can restore them on mount/prop-change
|
||||
const responses = (detail.pending_task_lane as Record<string, unknown>).responses as unknown[] | undefined
|
||||
if (responses && responses.length > 0) {
|
||||
try {
|
||||
sessionStorage.setItem(`rf-tasklane-state:${chatId}`, JSON.stringify(responses))
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
setActiveQuestions(q)
|
||||
setActiveActions(a)
|
||||
setShowTaskLane(true)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
||||
Reference in New Issue
Block a user