-
Notifications
You must be signed in to change notification settings - Fork 43
feat(room-nav): show topic and last-message preview for rooms in the sidebar #669
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
4eeaa38
260a4e8
9036ec9
db9c1a4
216aa6a
b848ac2
ec10020
20376bf
4b29d29
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@sable/client': minor | ||
| --- | ||
|
|
||
| feat(dm-list): show last-message preview below DM room name | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@sable/client': minor | ||
| --- | ||
|
|
||
| feat(room-nav): show topic and last-message preview for rooms in the sidebar, fetching enough timeline events to handle reactions and edits correctly |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,95 @@ | ||||||||||||||||||||||||||||||||||||||||
| import { useEffect, useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||
| MatrixClient, | ||||||||||||||||||||||||||||||||||||||||
| MatrixEvent, | ||||||||||||||||||||||||||||||||||||||||
| MatrixEventEvent, | ||||||||||||||||||||||||||||||||||||||||
| MsgType, | ||||||||||||||||||||||||||||||||||||||||
| Room, | ||||||||||||||||||||||||||||||||||||||||
| RoomEvent as RoomEventEnum, | ||||||||||||||||||||||||||||||||||||||||
| } from '$types/matrix-sdk'; | ||||||||||||||||||||||||||||||||||||||||
| import { MessageEvent } from '$types/matrix/room'; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| function eventToPreviewText(ev: MatrixEvent): string | undefined { | ||||||||||||||||||||||||||||||||||||||||
| if (ev.isRedacted()) return undefined; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| const type = ev.getType(); | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| if (type === MessageEvent.RoomMessageEncrypted) return '🔒 Encrypted message'; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| if (type === MessageEvent.RoomMessage) { | ||||||||||||||||||||||||||||||||||||||||
| const content = ev.getContent(); | ||||||||||||||||||||||||||||||||||||||||
| const { msgtype } = content; | ||||||||||||||||||||||||||||||||||||||||
| if (msgtype === MsgType.Text || msgtype === MsgType.Emote || msgtype === MsgType.Notice) { | ||||||||||||||||||||||||||||||||||||||||
| return content.body; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+19
to
+24
|
||||||||||||||||||||||||||||||||||||||||
| if (msgtype === MsgType.Image) return '📷 Image'; | ||||||||||||||||||||||||||||||||||||||||
| if (msgtype === MsgType.Video) return '📹 Video'; | ||||||||||||||||||||||||||||||||||||||||
| if (msgtype === MsgType.Audio) return '🎵 Audio'; | ||||||||||||||||||||||||||||||||||||||||
| if (msgtype === MsgType.File) return '📎 File'; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| if (type === MessageEvent.Sticker) { | ||||||||||||||||||||||||||||||||||||||||
| return `🎉 ${ev.getContent().body ?? 'Sticker'}`; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| return undefined; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| function getLastMessageText(room: Room, mx: MatrixClient): string | undefined { | ||||||||||||||||||||||||||||||||||||||||
| const events = room.getLiveTimeline().getEvents(); | ||||||||||||||||||||||||||||||||||||||||
| const match = [...events].reverse().find((ev) => eventToPreviewText(ev) !== undefined); | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+38
to
+40
|
||||||||||||||||||||||||||||||||||||||||
| function getLastMessageText(room: Room, mx: MatrixClient): string | undefined { | |
| const events = room.getLiveTimeline().getEvents(); | |
| const match = [...events].reverse().find((ev) => eventToPreviewText(ev) !== undefined); | |
| function reactionOrEditEvent(ev: MatrixEvent): boolean { | |
| const type = ev.getType(); | |
| const content = ev.getContent(); | |
| if (type === 'm.reaction') return true; | |
| if (type !== MessageEvent.RoomMessage) return false; | |
| const relatesTo = content?.['m.relates_to']; | |
| return relatesTo?.rel_type === 'm.replace' || content?.['m.new_content'] !== undefined; | |
| } | |
| function getLastMessageText(room: Room, mx: MatrixClient): string | undefined { | |
| const events = room.getLiveTimeline().getEvents(); | |
| const match = [...events] | |
| .reverse() | |
| .find((ev) => !reactionOrEditEvent(ev) && eventToPreviewText(ev) !== undefined); |
Copilot
AI
Apr 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This hook is new behavior that affects sidebar rendering and has several edge cases (replies, edits, redactions, encrypted→decrypted transitions). The repo already uses Vitest for hook-level tests; adding focused unit tests for useRoomLastMessage (e.g., skipping edits/reactions, trimming reply fallback, updating on decryption) would help prevent regressions.
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -533,6 +533,8 @@ export function Space() { | |||||||
| * Determines the depth limit for the joined space hierarchy and the SpaceNavItems to start appearing | ||||||||
| */ | ||||||||
| const [subspaceHierarchyLimit] = useSetting(settingsAtom, 'subspaceHierarchyLimit'); | ||||||||
| const [roomTopicPreview] = useSetting(settingsAtom, 'roomTopicPreview'); | ||||||||
| const [roomMessagePreview] = useSetting(settingsAtom, 'roomMessagePreview'); | ||||||||
| /** | ||||||||
| * Creates an SVG used for connecting spaces to their subrooms. | ||||||||
| * @param virtualizedItems - The virtualized item list that will be used to render elements in the nav | ||||||||
|
|
@@ -826,6 +828,8 @@ export function Space() { | |||||||
| selected={selectedRoomId === roomId} | ||||||||
| showAvatar={mDirects.has(roomId)} | ||||||||
| direct={mDirects.has(roomId)} | ||||||||
| roomTopicPreview={roomTopicPreview} | ||||||||
| roomMessagePreview={roomMessagePreview} | ||||||||
|
||||||||
| roomMessagePreview={roomMessagePreview} | |
| roomMessagePreview={roomMessagePreview} | |
| dmMessagePreview={dmMessagePreview} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changeset introduces a separate minor release entry for the DM message preview feature. If DM previews are intentionally part of this PR, the PR title/description should mention it; otherwise this changeset should probably be removed/split so release notes match the actual PR scope.