feat(web): fidelity closeout — detail skeleton, optimistic status, attachment rendering (HT-23) - #66
Merged
Merged
Conversation
… attachments Closes the last three fidelity-checklist §5 gaps for the Agent Inbox UI: - Add conversations/[id]/loading.tsx, mirroring the inbox folder's skeleton pattern (ds Skeleton + ToolbarBand) but shaped to this screen's real layout — toolbar, subject row, message-band placeholders, context panel — so the swap to real content doesn't jump the eye around. - Make conversation status changes optimistic with rollback, mirroring the existing tags/assignee pattern exactly: flip local state immediately, revert and toast on a server rejection. The closed-reopens-on-reply case was already covered by sendReply's on-success branch (status only flips once the send itself is confirmed, since the send can't be optimistic). - Add AttachmentView + ThreadView.attachments to api-types.ts (1:1 with specs/api/agent-inbox-v1.md §2, HT-46), and render a minimal filename + size + new-tab download link on inbound message bands, composed from existing tokens (no new ds component). Renders nothing when the field is absent (pre-HT-46 API) or empty — zero layout shift. Item 3 (inbound-attachment rendering) is a TJ-approved addition beyond the Claude Design prototype and needs his visual sign-off before it's considered fidelity-complete. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…contract
- changeStatus now wraps the server-action await in try/catch: a rejected
promise (network drop, unreachable server) is treated exactly like
{ok:false} — rollback the optimistic pill and toast, instead of silently
stranding the UI in a status the server never applied.
- Serialize changeStatus against concurrent status changes with an
in-flight ref: a second click while one request is pending is a no-op,
so the rollback's captured `previous` is always a server-confirmed
value, never another in-flight call's unconfirmed optimism.
- ThreadView.attachments is now required (matching agent-inbox-v1.md §2,
which declares the v1.1 server always emits `[]` rather than omitting
the field), not optional — a server regression that drops the field now
fails the type at the boundary instead of silently rendering as "no
attachments". Mirrors customerViewedAt's required-nullable precedent.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThe conversation screen now renders inbound attachments, formats file sizes, serializes status updates with rollback handling, and adds a dedicated loading skeleton. Thread API types now require an attachments array. ChangesConversation UI updates
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implemented all three HT-23 fidelity-closeout items in the worktree
/Users/tjbaker/Projects/helpthread-worktrees/feat-ht-23-fidelity-closeout(branchfeat/ht-23-fidelity-closeout, commit e470500):Conversation detail-view loading skeleton — new file
web/src/app/(shell)/conversations/[id]/loading.tsx. Mirrorsinbox/[folder]/loading.tsx's pattern (bareSkeleton+ToolbarBandprimitives, no data) but shaped to the real conversation layout: toolbar band, subject-row placeholder, 3 message-band placeholders (avatar circle + line skeletons, one with the same-speaker hairline), and the 240px context-panel aside (panel-tone toolbar, avatar-circle skeleton, name/email lines, divider, one summary line).Optimistic status changes with rollback — in
web/src/components/ConversationScreen.tsx,changeStatusnow mirrorsupdateTags/updateAssigneeexactly: capturesprevious, setslocalStatusimmediately, awaitssetStatusAction, and rolls back + toasts on failure (dropped the old post-then-refreshstartTransition/router.refresh()shape to match). The four statusMenuItemonClicks now callvoid changeStatus(...), matching the assignee menu's pattern. The reply-to-closed reopen was already correctly handled insendReply's success branch (it flips status only once the send is confirmed — the send itself can't be optimistic — so no change was needed there; noted this explicitly rather than silently leaving it).Minimal inbound-attachment rendering (flagged for the maintainer's visual sign-off — this is beyond the Claude Design prototype, per his explicit instruction to flag it prominently) — added
AttachmentViewandThreadView.attachments?: AttachmentView[]toweb/src/lib/api-types.ts, 1:1 withspecs/api/agent-inbox-v1.md§2 (HT-46). Madeattachmentsoptional in the TS type (not just empty-array-default) because the task explicitly calls out that older API responses may omit the field entirely — the component guards onthread.attachments !== undefined && thread.attachments.length > 0. Added ahumanFileSize()helper toweb/src/lib/format.ts. Rendering is a plain pill-style list (background--ht-surface-2, radius 999 — same token composition asTagChip, no new ds component) with a paperclip icon, filename (or "Attachment" if null), and size, wrapped in an<a target="_blank" rel="noopener noreferrer">using the signed URL exactly as given (no caching/rewriting). Only rendered onkind === 'inbound'message bands.No new npm dependencies.
web/src/components/ds/**was read but never edited.Flagged for the maintainer's visual sign-off — the attachment rendering is an approved addition beyond the design prototype:
Status optimistic-update shape: removed the pre-existing
startTransition/router.refresh()wrapper entirely rather than keeping it alongside optimism, because tags/assignee (the exact pattern to mirror) use neither — they rely on the server action's ownrevalidatePathand don't gate onisPending. Kept consistent rather than hybrid.ThreadView.attachmentsmade optional (attachments?: AttachmentView[]) rather than required-with-empty-array-default, even though the spec models it as always-present ([]default). Rationale: the task instructions explicitly say "If a thread has no attachments field (older API)... render nothing," which only makes sense if the type permits the field's absence — so I read "1:1 with spec §2" as shape-fidelity (same fields, same names) rather than requiring TS-level non-optionality that would fight the stated defensive-UI requirement.Attachment styling: chose a
TagChip-derived pill (surface-2 background, radius 999, ink/ink-dim/ink-muted text) as "the most conservative composition" the task asked to pick between (TagChip-like vs. plain list) — it reuses only existing color/radius tokens and no new component.Did not modify
sendReply's existing closed→active-on-success logic for item 2, since it already satisfies the "reply-to-closed reopen" requirement correctly (status flips only on confirmed send, which is the right semantics — the send itself isn't safe to represent optimistically before delivery is confirmed). Documented this explicitly rather than silently treating the requirement as already met without comment.Review — 6 findings (4 actionable), fixes applied
Verification — gate exits: typecheck 0, lint 0, tests 0, web build 0, clean tree
link https://resonantiq.atlassian.net/browse/HT-23
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes