feat: persist task lane state across reloads and session switches
TaskLane now saves user's in-progress answers (typed text, checked items) to sessionStorage keyed by session ID. On reload or session switch, the full task lane state restores — including partial work. - TaskLane: saves tasks array to sessionStorage on every change, restores from sessionStorage on mount - AssistantChatPage: saves task lane metadata (visibility, questions, actions, chatId) to sessionStorage, restores on mount - Closing the task lane clears its sessionStorage entry Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,7 @@ import { analytics } from '@/lib/analytics'
|
||||
import { toast } from '@/lib/toast'
|
||||
import { ChatSidebar, ChatSidebarCollapsedBar } from '@/components/assistant/ChatSidebar'
|
||||
import { ChatMessage } from '@/components/assistant/ChatMessage'
|
||||
import { TaskLane } from '@/components/assistant/TaskLane'
|
||||
import { TaskLane, clearTaskState } from '@/components/assistant/TaskLane'
|
||||
import { ConcludeSessionModal } from '@/components/assistant/ConcludeSessionModal'
|
||||
import type { ChatListItem, ConclusionOutcome } from '@/types/assistant-chat'
|
||||
import type { SuggestedFlow } from '@/types/copilot'
|
||||
@@ -42,9 +42,27 @@ export default function AssistantChatPage() {
|
||||
const [logContent, setLogContent] = useState('')
|
||||
const [pendingUploads, setPendingUploads] = useState<PendingUpload[]>([])
|
||||
const [isDragOver, setIsDragOver] = useState(false)
|
||||
const [activeQuestions, setActiveQuestions] = useState<QuestionItem[]>([])
|
||||
const [activeActions, setActiveActions] = useState<ActionItem[]>([])
|
||||
const [showTaskLane, setShowTaskLane] = useState(false)
|
||||
const [activeQuestions, setActiveQuestions] = useState<QuestionItem[]>(() => {
|
||||
try {
|
||||
const saved = sessionStorage.getItem('rf-tasklane-meta')
|
||||
if (saved) { const d = JSON.parse(saved); return d.questions || [] }
|
||||
} catch { /* ignore */ }
|
||||
return []
|
||||
})
|
||||
const [activeActions, setActiveActions] = useState<ActionItem[]>(() => {
|
||||
try {
|
||||
const saved = sessionStorage.getItem('rf-tasklane-meta')
|
||||
if (saved) { const d = JSON.parse(saved); return d.actions || [] }
|
||||
} catch { /* ignore */ }
|
||||
return []
|
||||
})
|
||||
const [showTaskLane, setShowTaskLane] = useState(() => {
|
||||
try {
|
||||
const saved = sessionStorage.getItem('rf-tasklane-meta')
|
||||
if (saved) { const d = JSON.parse(saved); return d.show === true && d.chatId === (urlSessionId || null) }
|
||||
} catch { /* ignore */ }
|
||||
return false
|
||||
})
|
||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(() =>
|
||||
localStorage.getItem('rf-chat-sidebar-collapsed') === 'true'
|
||||
)
|
||||
@@ -137,6 +155,18 @@ export default function AssistantChatPage() {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
// Persist task lane metadata to sessionStorage
|
||||
useEffect(() => {
|
||||
try {
|
||||
sessionStorage.setItem('rf-tasklane-meta', JSON.stringify({
|
||||
show: showTaskLane,
|
||||
chatId: activeChatId,
|
||||
questions: activeQuestions,
|
||||
actions: activeActions,
|
||||
}))
|
||||
} catch { /* ignore */ }
|
||||
}, [showTaskLane, activeChatId, activeQuestions, activeActions])
|
||||
|
||||
// Auto-scroll
|
||||
useEffect(() => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
|
||||
@@ -737,8 +767,12 @@ export default function AssistantChatPage() {
|
||||
<TaskLane
|
||||
questions={activeQuestions}
|
||||
actions={activeActions}
|
||||
sessionId={activeChatId}
|
||||
onSubmit={handleTaskSubmit}
|
||||
onClose={() => setShowTaskLane(false)}
|
||||
onClose={() => {
|
||||
setShowTaskLane(false)
|
||||
if (activeChatId) clearTaskState(activeChatId)
|
||||
}}
|
||||
loading={loading}
|
||||
/>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user