Room list: add notifications to section headers (#33826)

* feat(sc): add notification decoration to header

* feat(vm): add notification decoration to vm

* test(e2e): add test
This commit is contained in:
Florian Duros
2026-06-15 18:05:51 +00:00
committed by GitHub
parent 86a89c2aa5
commit 1c8654e394
8 changed files with 460 additions and 27 deletions
@@ -34,6 +34,12 @@
.menu {
display: initial;
}
/* When a menu is present and revealed on hover, hide the decoration to avoid clutter.
Default sections have no menu, so their decoration stays visible on hover. */
.notificationDecoration {
display: none;
}
}
.chevron {
@@ -59,6 +59,17 @@ const meta = {
isExpanded: true,
isFocused: false,
isUnread: false,
notification: {
hasAnyNotificationOrActivity: false,
isUnsentMessage: false,
invited: false,
isMention: false,
isActivityNotification: false,
isNotification: false,
hasUnreadCount: false,
count: 0,
muted: false,
},
displaySectionMenu: true,
onClick: fn(),
onFocus: fn(),
@@ -119,3 +130,22 @@ export const Unread: Story = {
isUnread: true,
},
};
export const WithNotificationDecoration: Story = {
args: {
isUnread: true,
notification: {
hasAnyNotificationOrActivity: true,
isUnsentMessage: true,
invited: true,
isMention: true,
isActivityNotification: false,
isNotification: true,
hasUnreadCount: true,
count: 12,
muted: false,
callType: "video",
},
isExpanded: false,
},
};
@@ -19,6 +19,10 @@ import { Flex } from "../../../core/utils/Flex";
import { useI18n } from "../../../core/i18n/i18nContext";
import { getGroupHeaderAccessibleProps } from "../../../core/VirtualizedList";
import { _t } from "../../../core/i18n/i18n";
import {
NotificationDecoration,
type NotificationDecorationData,
} from "../RoomListItemWrapper/RoomListItemView/NotificationDecoration";
/**
* The observable state snapshot for a room list section header.
@@ -32,6 +36,8 @@ export interface RoomListSectionHeaderViewSnapshot {
isExpanded: boolean;
/** Whether the section is unread (has any unread rooms) */
isUnread: boolean;
/** The merged notification decoration aggregating the notifications of the rooms in the section */
notification?: NotificationDecorationData;
/** Wether to display the section menu */
displaySectionMenu: boolean;
}
@@ -102,7 +108,7 @@ export const RoomListSectionHeaderView = memo(function RoomListSectionHeaderView
roomCountInSection,
}: Readonly<RoomListSectionHeaderViewProps>): JSX.Element {
const { translate: _t } = useI18n();
const { id, title, isExpanded, isUnread, displaySectionMenu } = useViewModel(vm);
const { id, title, isExpanded, isUnread, notification, displaySectionMenu } = useViewModel(vm);
const isLastSection = sectionIndex === sectionCount - 1;
const { ref: droppableRef, isDropTarget } = useDroppable({
@@ -177,6 +183,11 @@ export const RoomListSectionHeaderView = memo(function RoomListSectionHeaderView
/>
<span className={styles.title}>{title}</span>
</Flex>
{!isExpanded && notification && (
<div className={styles.notificationDecoration} aria-hidden={true}>
<NotificationDecoration {...notification} />
</div>
)}
{displaySectionMenu && <MenuComponent vm={vm} />}
</Flex>
</button>