mirror of
https://github.com/coder/coder.git
synced 2026-06-03 04:58:23 +00:00
bcdc35ee3e
## Summary Adds read/unread tracking for chats so users can see which agent conversations have new assistant messages they haven't viewed. ## Backend Changes - Adds `last_read_message_id` column to the `chats` table (migration 000439). - Computes `has_unread` as a virtual column in `GetChatsByOwnerID` using an `EXISTS` subquery checking for assistant messages beyond the read cursor. - Exposes `has_unread` on the `codersdk.Chat` struct and auto-generated TypeScript types. - Updates `last_read_message_id` on stream connect/disconnect in `streamChat`, avoiding per-message API calls during active streaming. - Uses `context.WithoutCancel` for the deferred disconnect write so the DB update succeeds even after the client disconnects. ## Frontend Changes - Bold title (`font-semibold`) for unread chats in the sidebar. - Small blue dot indicator next to the relative timestamp. - Suppresses unread indicator for the currently active chat via `isActive` from NavLink. ## Design Decisions - Only `assistant` messages count as unread — the user's own messages don't trigger the indicator. - No foreign key on `last_read_message_id` since messages can be deleted (via rollback/truncation) and the column is just a high-water mark. - Zero API calls during streaming: exactly 2 DB writes per stream session (connect + disconnect). - Unread state refreshes on chat list load and window focus. The `watchChats` WebSocket optimistically marks non-active chats as unread on `status_change` events, but does not carry a server-computed `has_unread` field. Navigating to a chat optimistically clears its unread indicator in the cache.
10 lines
428 B
SQL
10 lines
428 B
SQL
ALTER TABLE chats ADD COLUMN last_read_message_id BIGINT;
|
|
|
|
-- Backfill existing chats so they don't appear unread after deploy.
|
|
-- The has_unread query uses COALESCE(last_read_message_id, 0), so
|
|
-- leaving this NULL would mark every existing chat as unread.
|
|
UPDATE chats SET last_read_message_id = (
|
|
SELECT MAX(cm.id) FROM chat_messages cm
|
|
WHERE cm.chat_id = chats.id AND cm.role = 'assistant' AND cm.deleted = false
|
|
);
|