Skip to content

Commit b375d99

Browse files
committed
fix(mothership): defer state writes and log restore failures
Build the restored state in locals first and only apply on success so a partial throw can't leave stale contexts in the UI with the draft already cleared. Switch the empty catch to logger.error so corrupt-draft incidents surface in production logs.
1 parent 9339583 commit b375d99

1 file changed

Lines changed: 29 additions & 18 deletions

File tree

  • apps/sim/app/workspace/[workspaceId]/home/components/user-input

apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
useRef,
1212
useState,
1313
} from 'react'
14+
import { createLogger } from '@sim/logger'
1415
import { Paperclip } from 'lucide-react'
1516
import { useParams } from 'next/navigation'
1617
import { Button, Tooltip } from '@/components/emcn'
@@ -58,6 +59,8 @@ import type { ChatContext } from '@/stores/panel'
5859

5960
export type { FileAttachmentForApi } from '@/app/workspace/[workspaceId]/home/types'
6061

62+
const logger = createLogger('UserInput')
63+
6164
function getCaretAnchor(
6265
textarea: HTMLTextAreaElement,
6366
caretPos: number
@@ -190,34 +193,42 @@ export const UserInput = forwardRef<UserInputHandle, UserInputProps>(function Us
190193
useEffect(() => {
191194
if (hasRestoredDraftRef.current || !draftScopeKey) return
192195
hasRestoredDraftRef.current = true
196+
let restoredContexts: ChatContext[] | null = null
197+
let restoredFiles: AttachedFile[] | null = null
198+
let caretText: string | null = null
193199
try {
194200
const draft = useMothershipDraftsStore.getState().drafts[draftScopeKey]
195201
if (!draft) return
196202
if (draft.contexts?.length) {
197-
contextManagement.setSelectedContexts(draft.contexts)
203+
restoredContexts = draft.contexts
198204
}
199205
if (draft.fileAttachments?.length) {
200-
files.restoreAttachedFiles(
201-
draft.fileAttachments.map((a) => ({
202-
id: a.id,
203-
name: a.filename,
204-
size: a.size,
205-
type: a.media_type,
206-
path: a.path ?? '',
207-
key: a.key,
208-
uploading: false,
209-
}))
210-
)
206+
restoredFiles = draft.fileAttachments.map((a) => ({
207+
id: a.id,
208+
name: a.filename,
209+
size: a.size,
210+
type: a.media_type,
211+
path: a.path ?? '',
212+
key: a.key,
213+
uploading: false,
214+
}))
211215
}
212216
if (typeof draft.text === 'string' && draft.text.length > 0) {
213-
const textarea = textareaRef.current
214-
if (textarea) {
215-
textarea.focus()
216-
textarea.setSelectionRange(draft.text.length, draft.text.length)
217-
}
217+
caretText = draft.text
218218
}
219-
} catch {
219+
} catch (err) {
220+
logger.error('Failed to read draft, clearing', { err })
220221
useMothershipDraftsStore.getState().clearDraft(draftScopeKey)
222+
return
223+
}
224+
if (restoredContexts) contextManagement.setSelectedContexts(restoredContexts)
225+
if (restoredFiles) files.restoreAttachedFiles(restoredFiles)
226+
if (caretText !== null) {
227+
const textarea = textareaRef.current
228+
if (textarea) {
229+
textarea.focus()
230+
textarea.setSelectionRange(caretText.length, caretText.length)
231+
}
221232
}
222233
}, []) // eslint-disable-line react-hooks/exhaustive-deps -- intentional mount-only restore
223234

0 commit comments

Comments
 (0)