Skip to content

Web Direct messages: newest first, last lines, New message (#3774, 2 of 3) - #4445

Merged
pdcarlson merged 4 commits into
mainfrom
claude/dm-own-place-3774
Nov 12, 2026
Merged

pdcarlson merged 4 commits into
mainfrom
claude/dm-own-place-3774

Conversation

@pdcarlson

Copy link
Copy Markdown
Owner

Part of #3774

Asked by: Shawn (shawnatomklein-eng), 2026-11-12 (coordinator chat on Paul's second account; person at the keyboard unconfirmed). He picked "match the phone" for the list on #3774: #3774 (comment)

Summary

Step 2 of 3. #4370 gave direct messages their own nav row and list on web. This makes that list read like the phone's Messages screen:

  • Newest conversation first. Pinned DMs stay on top, also sorted newest first. A conversation with no message yet goes last, A to Z.
  • Each row has a last line in the shared wording (channelPreviewLine): "You: sounds good" for your own message, no name in a one-to-one DM, the sender's first name in a group, and "No messages yet" when there are none. Rows are two lines and 48px tall.
  • A New message pencil in the list's header opens the Directory, where a DM starts. This is the same place the phone puts it, and where iMessage and Instagram put theirs.

Comparable apps: Slack's DMs view, Discord's Direct Messages home, iMessage and Instagram all list conversations newest first, each with its last line.

Design page with the built screen: https://claude.ai/artifact/9v85GbNiLSpBErGoLmhjWg. It is private to the account that ran this chat; Shawn can share it. Its substance is above.

Behavior details

  • The latest-messages read (GET /v1/channels/latest) runs only while the Direct messages list shows. The channels rail still draws no previews ([suggestion] Channel rows can't show s04's message preview and timestamp — no last-message projection exists #1191).
  • No reorder under the pointer. The list shows its loading placeholder until that read first answers, as the phone does. If a later background read fails, the last good order and lines stay.
  • Hidden DMs. The server keeps no latest row for a hidden DM, so a row in Hidden conversations stays one line with no preview, rather than a false "No messages yet". An open hidden DM leads the list, since it is the conversation on screen.
  • One shared order rule. The newest-first rule is now compareLatestFirst in @repo/hooks, and mobile's Messages screen uses it too. Mobile behaves as before.
  • The New message pencil shows only once the list has loaded.

Recorded decisions this changes

  • spec/behavior/chat/README.md § Sidebar arrangement said Direct messages sort A to Z, and that mobile's Messages screen was the only list ordered by recent activity. Web's Direct messages list now sorts newest first too. Dated lines are added under both.

Changes

  • apps/web/components/chat/chat-shell.tsx: the latest read, the previews map, the placeholder wait, and the New message button.
  • apps/web/components/chat/channel-list.tsx: newest-first order, two-line rows, hidden rows, and the docblock.
  • packages/hooks/src/channel-sidebar.ts: compareLatestFirst, with a test.
  • apps/mobile/app/(app)/messages.tsx: uses compareLatestFirst (same order as before).
  • Specs: spec/behavior/chat/README.md (§ Channel list previews, § Sidebar arrangement), spec/ui/web-dashboard/README.md (§ Chat).
  • What's new line: packages/validation/release-notes/claude-dm-list-previews-3774.md.

Docs / Spec impact

  • Spec impact: Updated (above).
  • Docs impact: None.

Test plan

  • Web tests: npx vitest run in apps/web, 299 files and 5174 tests passing. New tests cover:
    • newest-first order, including the pinned section
    • the one-to-one and "You:" wording
    • hidden DMs: no preview line, one-line rows, an open one leading
    • the placeholder while the first read runs
    • keeping the order when a later read fails
    • the read running only in Direct messages
    • New message
  • Mobile chat tests: 39 files and 764 tests passing. @repo/hooks channel-sidebar.spec.ts passes (the new comparator test).
  • tsc --noEmit clean on apps/web and apps/mobile; eslint clean on the touched files.
  • Manual check on the running app (local stack, demo chapter with a few DMs added locally): order, lines, the pencil, and "Pick a conversation".
  • CI checks pass.

Review

/diff-review ran a full workflow round: 5 finders, then 2 checkers, reviewed at c983863. Every confirmed finding was fixed in that commit:

  1. Hidden DMs read "No messages yet", and an open hidden DM sorted last.
  2. A failed background read flipped the list back to A to Z.
  3. The first open re-sorted rows under the pointer.
  4. Two specs and the docblock overclaimed or went stale.
  5. The order rule was copied from mobile, and the rows were cast instead of typed.
  6. Two test gaps.
  7. New message showed on an errored list.

One confirmed finding predates this PR and is filed as #4442: an edited or deleted last message keeps showing until the next refresh. Mobile already has the same gap; this PR makes it visible on web too.

Release label

release:minor

Debt spotted


Generated by Claude Code

- Hidden DMs get no last line (the server keeps none), Hidden conversations
  rows stay one line, and an open hidden DM leads the list.
- The list waits behind its skeleton for the first latest-messages read, and
  keeps the last good order and lines when a later read fails.
- One newest-first rule for both clients: compareLatestFirst in @repo/hooks,
  now used by mobile's Messages screen too; typed rows, no cast.
- New message shows only once the list has loaded.
- Specs and the ChannelList docblock say what the code does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7R7NNPw2Kpjnca1FFPvfH
@pdcarlson pdcarlson added the release:minor Bump minor version on merge to main label Nov 12, 2026 — with Claude

Copy link
Copy Markdown
Owner Author

api-tests is red here, but not because of this PR: it touches no API code. The failing test is PollService › close › … lets a moderator close an open-ended card … (#4046), and it fails on main too (reproduced at 304501b: 1 failed, 78 passed). #4390's new test mocks messageRepo.update, while #4421, merged separately, made closeCard write through updateIfLive.

The fix is in its own PR rather than widening this one: #4446. Once that merges, I'll merge main here so api-tests re-runs.


Generated by Claude Code

@pdcarlson
pdcarlson merged commit 5520024 into main Nov 12, 2026
28 checks passed
@pdcarlson
pdcarlson deleted the claude/dm-own-place-3774 branch November 12, 2026 12:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release:minor Bump minor version on merge to main

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants