Skip to content

fix: file attachment UX, thread status, and homepage hydration - #11

Merged
aiedwardyi merged 4 commits into
mainfrom
fix/file-feedback-and-thread-status
Apr 8, 2026
Merged

aiedwardyi merged 4 commits into
mainfrom
fix/file-feedback-and-thread-status

Conversation

@aiedwardyi

@aiedwardyi aiedwardyi commented Apr 8, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add unsupported file type validation with dismissible toast feedback on chat page (EN/KO)
  • Restore homepage file attachment with parse-before-navigate - files are parsed client-side and appended to the prompt before navigating to chat
  • Fix thread status not updating from "active" to "complete" after debate finishes
  • Fix SSR hydration mismatch caused by localStorage reads during server render

Test plan

  • Attach supported files (PDF, DOCX, XLSX, TXT, MD, CSV) on homepage and verify parsed content appears in chat
  • Attach unsupported file on both homepage and chat page, confirm amber toast appears and auto-dismisses
  • Toggle locale to KO and repeat - verify toast text is Korean
  • Start a debate and confirm thread status updates to "complete"
  • Hard refresh homepage - no hydration error in console

Re-add file upload UI (paperclip button, drag-and-drop, file chips)
to the homepage and parse files client-side before navigating to chat.
Parsed text is appended to the prompt in sessionStorage config.

Also fix SSR hydration mismatch by deferring localStorage reads for
locale, responseLength, and rounds to useEffect.
@aiedwardyi aiedwardyi changed the title fix: remove broken homepage file attachment, fix thread status reverting to active fix: file attachment UX, thread status, and homepage hydration Apr 8, 2026
@aiedwardyi
aiedwardyi requested a review from Copilot April 8, 2026 13:31

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves the end-to-end file attachment flow (homepage → chat), addresses a thread-status edge case when loading threads, and eliminates a homepage SSR hydration mismatch by deferring persisted-setting reads to post-mount.

Changes:

  • Add client-side unsupported file type validation with auto-dismissing inline feedback (EN/KO).
  • Restore homepage file attachment by parsing files client-side and appending extracted text to the prompt before navigating to /chat.
  • Prevent a “continue completed thread” effect from triggering incorrectly during thread load/reset; defer homepage localStorage hydration to useEffect.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
src/components/MessageInput.tsx Adds unsupported file validation + inline error banner and related state for chat input attachments.
src/app/page.tsx Adds homepage attachment UI + parse-before-navigate behavior; moves persisted settings hydration to useEffect to avoid SSR mismatch.
src/app/chat/page.tsx Resets prevShowSummary during thread load to avoid misinterpreting a reset as “continuing” a completed thread.
Comments suppressed due to low confidence (1)

src/components/MessageInput.tsx:142

  • IDs for attached files are generated with Math.random() + substr(), which is both collision-prone and uses a deprecated string API. Prefer crypto.randomUUID() (or at least .slice()) to produce stable unique IDs so remove/revoke logic can’t target the wrong file.
    if (supported.length === 0) return
    const newFiles = supported.map((file) => ({
      id: Math.random().toString(36).substr(2, 9),
      file,
      preview: file.type.startsWith("image/") ? URL.createObjectURL(file) : undefined,
    }))

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/app/page.tsx
Comment on lines +299 to +306
setFiles((prev) => [
...prev,
...supported.map((file) => ({
id: Math.random().toString(36).substr(2, 9),
file,
preview: file.type.startsWith("image/") ? URL.createObjectURL(file) : undefined,
})),
])

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDs for attached files are generated with Math.random() + substr(), which is collision-prone and uses a deprecated API. Prefer crypto.randomUUID() (or a shared helper) to avoid potential key collisions that could remove the wrong file chip.

Copilot uses AI. Check for mistakes.
Comment thread src/app/page.tsx Outdated
Comment on lines 324 to 335
@@ -287,15 +334,55 @@
setShowGate(true)
return

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the login gate triggers, pending debate config is saved using only prompt: prompt.trim() and ignores any attached files. This means users who attach files and then hit the gate will lose the file content after signing in. Consider parsing/appending file contents before calling savePendingDebate (store the resulting prompt string), or disable file attachment when gating is possible.

Copilot uses AI. Check for mistakes.
Comment thread src/app/page.tsx Outdated
Comment on lines +566 to +570
onDragOver={(e) => { e.preventDefault(); setIsDragging(true) }}
onDragLeave={(e) => { e.preventDefault(); setIsDragging(false) }}
onDrop={(e) => {
e.preventDefault()
setIsDragging(false)

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drag-and-drop still calls addFiles even while isParsing is true. This can lead to confusing UI/state (files appear to attach during parsing but won’t be included in the in-flight submit). Consider short-circuiting drag/drop handlers (and remove actions) while parsing, consistent with the disabled attach button.

Suggested change
onDragOver={(e) => { e.preventDefault(); setIsDragging(true) }}
onDragLeave={(e) => { e.preventDefault(); setIsDragging(false) }}
onDrop={(e) => {
e.preventDefault()
setIsDragging(false)
onDragOver={(e) => {
e.preventDefault()
if (isParsing) return
setIsDragging(true)
}}
onDragLeave={(e) => {
e.preventDefault()
setIsDragging(false)
if (isParsing) return
}}
onDrop={(e) => {
e.preventDefault()
setIsDragging(false)
if (isParsing) return

Copilot uses AI. Check for mistakes.
Comment thread src/app/page.tsx
Comment on lines 62 to 66
]

const SUPPORTED_EXTENSIONS = new Set(["pdf", "docx", "xlsx", "xls", "txt", "md", "csv"])

/* ─── Translations ─── */

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUPPORTED_EXTENSIONS is duplicated here and in MessageInput, while parseFile also has its own extension allowlist. To avoid the UI allowlist drifting from the parser behavior over time, consider exporting a single source of truth (e.g., from file-parser) and reusing it in both places.

Copilot uses AI. Check for mistakes.
Comment on lines 123 to +137
const addFiles = (files: File[]) => {
const newFiles = files.map((file) => ({
const supported: File[] = []
let hasUnsupported = false
for (const file of files) {
const ext = file.name.split(".").pop()?.toLowerCase() ?? ""
if (SUPPORTED_EXTENSIONS.has(ext)) {
supported.push(file)
} else {
hasUnsupported = true
}
}
if (hasUnsupported) {
setFileError(t.unsupported)
}
if (supported.length === 0) return

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addFiles() doesn’t guard against isParsing. Because handleSend clears attachedFiles after parsing/sending, any files added during an in-flight parse can be lost unexpectedly. Consider early-returning from addFiles when isParsing is true (or queuing additions until parsing completes).

Copilot uses AI. Check for mistakes.
…ough login gate

- Export SUPPORTED_EXTENSIONS from file-parser.ts as single source of truth
- Parse files before login gate check so file content is preserved in
  pending debate config
- Guard drag-and-drop on homepage during file parsing
@aiedwardyi
aiedwardyi merged commit 1e3071b into main Apr 8, 2026
1 check passed
@aiedwardyi
aiedwardyi deleted the fix/file-feedback-and-thread-status branch April 8, 2026 13:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants