feat(ui): message-list date separators (Today / Yesterday / date) (LC-244) #299

Merged
longjacksonle merged 1 commit from feat/lc-244-date-separators into main 2026-06-09 04:34:06 +02:00

Summary

Implements LC-244 (subtask LC-245). Inserts a separator row between messages whose UTC calendar day differs, labeled "Today" / "Yesterday" / an absolute date. Mirrors the existing unread-divider pattern and gives the message list the day structure it lacked.

Implementation

Server-side initial render. The room and DM page builders (routes/room.rs, routes/dm.rs - the loops that already compute is_follow_up / show_unread_divider) track the previous rendered message's UTC day and set a new MessageView.day_label on the first message and on each day change. views::room::day_label_for maps a UTC date to a localized Today/Yesterday (room-day-today / room-day-yesterday, en + es) or an English long-form date. room/message.html renders the label as a role="separator" row above the message and carries data-lc-day on every row.

Live arrivals. Per-message WS fragments keep day_label = None (no server divider appended) but every row carries data-lc-day. A hook in partials/auto_scroll.html, on a new message appended to #messages, compares the appended row's UTC day to the previous message row's and inserts the same divider markup when they differ - labeled with the localized "Today" string carried on #messages (live sends are always today UTC). Inserted before the follow-to-bottom scroll so a pinned viewer stays at the true bottom; skipped when a divider already precedes the row.

Decisions (per the ticket)

  • UTC day boundaries, to match the app's existing UTC inline timestamps. Viewer-local boundaries and month-name localization are deferred to a future timezone pass that would localize both (doing only one is inconsistent).
  • Older-day label is English long-form ("June 9, 2026") for v1; Today/Yesterday are localized.
  • Deleting the only message of a day may leave a stranded divider until the next full render (accepted for v1).

Scope

Rooms and DMs (shared room/messages.html + message.html). Thread panel out of scope.

Tests

  • Server render: a divider with the right labels and data-lc-day on a day change; exactly one divider for same-day messages.
  • LC-77 render fixtures regenerated to capture the new data-lc-day attribute (diff verified to be only that attribute).
  • Live insertion is JS-only; covered by the LC-245 QA pass.
  • just test and just test-saas green; clippy + fmt clean.

No operator-visible surface, so no [operator-action] marker.

🤖 Generated with Claude Code

## Summary Implements LC-244 (subtask LC-245). Inserts a separator row between messages whose UTC calendar day differs, labeled "Today" / "Yesterday" / an absolute date. Mirrors the existing unread-divider pattern and gives the message list the day structure it lacked. ## Implementation **Server-side initial render.** The room and DM page builders (`routes/room.rs`, `routes/dm.rs` - the loops that already compute `is_follow_up` / `show_unread_divider`) track the previous rendered message's UTC day and set a new `MessageView.day_label` on the first message and on each day change. `views::room::day_label_for` maps a UTC date to a localized Today/Yesterday (`room-day-today` / `room-day-yesterday`, en + es) or an English long-form date. `room/message.html` renders the label as a `role="separator"` row above the message and carries `data-lc-day` on every row. **Live arrivals.** Per-message WS fragments keep `day_label = None` (no server divider appended) but every row carries `data-lc-day`. A hook in `partials/auto_scroll.html`, on a new message appended to `#messages`, compares the appended row's UTC day to the previous message row's and inserts the same divider markup when they differ - labeled with the localized "Today" string carried on `#messages` (live sends are always today UTC). Inserted before the follow-to-bottom scroll so a pinned viewer stays at the true bottom; skipped when a divider already precedes the row. ## Decisions (per the ticket) - **UTC day boundaries**, to match the app's existing UTC inline timestamps. Viewer-local boundaries and month-name localization are deferred to a future timezone pass that would localize both (doing only one is inconsistent). - Older-day label is English long-form ("June 9, 2026") for v1; Today/Yesterday are localized. - Deleting the only message of a day may leave a stranded divider until the next full render (accepted for v1). ## Scope Rooms and DMs (shared `room/messages.html` + `message.html`). Thread panel out of scope. ## Tests - Server render: a divider with the right labels and `data-lc-day` on a day change; exactly one divider for same-day messages. - LC-77 render fixtures regenerated to capture the new `data-lc-day` attribute (diff verified to be only that attribute). - Live insertion is JS-only; covered by the LC-245 QA pass. - `just test` and `just test-saas` green; clippy + fmt clean. No operator-visible surface, so no `[operator-action]` marker. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(ui): message-list date separators (Today / Yesterday / date) (LC-244)
All checks were successful
check-secrets / Nosey parker (push) Successful in 6s
check-secrets / Kingfisher (push) Successful in 7s
check-secrets / TruffleHog (push) Successful in 8s
check-secrets / Nosey parker (pull_request) Successful in 4s
Check / clippy + fmt + tests (pull_request) Successful in 1m56s
check-secrets / TruffleHog (pull_request) Successful in 4s
check-secrets / Kingfisher (pull_request) Successful in 7s
Create release / Create release from merged PR (pull_request) Has been skipped
ed7ec4bbc8
Insert a separator row between messages whose UTC calendar day differs, labeled "Today" / "Yesterday" / an absolute date ("June 9, 2026"). Mirrors the existing unread-divider pattern and gives the message list day structure it lacked.

Server-side initial render: the room and DM page builders (routes/room.rs and routes/dm.rs, the loops that already compute is_follow_up / show_unread_divider) track the previous rendered message's UTC day and set a new MessageView.day_label on the first message and on each day change. day_label_for (views/room.rs) maps a UTC date to a localized Today/Yesterday (Fluent room-day-today / room-day-yesterday, en + es) or an English long-form date. message.html renders the label as a role="separator" row above the message and carries data-lc-day on every row.

Live arrivals: the per-message WS fragments keep day_label = None (so no server divider is appended), but every row carries data-lc-day. A hook in partials/auto_scroll.html, on a new message appended to #messages, compares the appended row's UTC day to the previous message row's and inserts the same divider markup when they differ, labeled with the localized "Today" string carried on #messages (live sends are always today UTC). Inserted before the follow-to-bottom scroll so a pinned viewer stays at the true bottom; no duplicate when a divider already precedes the row.

Day boundaries are UTC to match the app's existing UTC inline timestamps; viewer-local boundaries and month-name localization are deferred to a future timezone pass that would localize both. Deleting the only message of a day may leave a stranded divider until the next full render (accepted for v1).

Tests: server render asserts a divider with the right labels and data-lc-day on a day change, and exactly one divider for same-day messages; LC-77 render fixtures regenerated to capture the new data-lc-day attribute (verified the diff is only that attribute). just test and just test-saas green; clippy + fmt clean.

#LC-244
#LC-245

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle deleted branch feat/lc-244-date-separators 2026-06-09 04:34:06 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!299
No description provided.