11import { Button } from '@open-design/components' ;
2- import { useCallback , useEffect , useId , useMemo , useRef , useState , type RefObject } from 'react' ;
2+ import { useCallback , useEffect , useId , useRef , useState , type RefObject } from 'react' ;
33import { createPortal } from 'react-dom' ;
44
55import { useI18n , type Locale } from '../i18n' ;
@@ -11,19 +11,12 @@ import {
1111 pullMessageCenter ,
1212 readAnonymousMessages ,
1313 readAnonymousReadIds ,
14- type MessageCenterFilter ,
1514 type MessageCenterMessage ,
1615 writeAnonymousState ,
1716} from '../message-center-client' ;
1817import { Icon } from './Icon' ;
1918import styles from './MessageCenter.module.css' ;
2019
21- const FILTERS : Array < { id : MessageCenterFilter ; label : 'messageCenter.filterAll' | 'messageCenter.filterUnread' | 'messageCenter.filterRead' } > = [
22- { id : 'all' , label : 'messageCenter.filterAll' } ,
23- { id : 'unread' , label : 'messageCenter.filterUnread' } ,
24- { id : 'read' , label : 'messageCenter.filterRead' } ,
25- ] ;
26-
2720function unreadBadgeLabel ( count : number ) : string {
2821 return count > 9 ? '9+' : String ( count ) ;
2922}
@@ -74,7 +67,6 @@ export function MessageCenter({
7467 } ,
7568 [ onOpenChange ] ,
7669 ) ;
77- const [ filter , setFilter ] = useState < MessageCenterFilter > ( 'all' ) ;
7870 const [ messages , setMessages ] = useState < MessageCenterMessage [ ] > ( [ ] ) ;
7971 const [ readIds , setReadIds ] = useState < Set < string > > ( new Set ( ) ) ;
8072 const [ loggedIn , setLoggedIn ] = useState ( false ) ;
@@ -174,10 +166,6 @@ export function MessageCenter({
174166 useEffect ( ( ) => {
175167 onUnreadCountChange ?.( unreadCount ) ;
176168 } , [ unreadCount , onUnreadCountChange ] ) ;
177- const visibleMessages = useMemo (
178- ( ) => messages . filter ( ( message ) => filter === 'all' || ( filter === 'read' ? Boolean ( message . readAt ) : ! message . readAt ) ) ,
179- [ filter , messages ] ,
180- ) ;
181169
182170 /** The control keyboard focus must land on after the panel closes. Opening
183171 * focuses the portaled dialog, so closing always unmounts the focused node —
@@ -241,15 +229,14 @@ export function MessageCenter({
241229 } ;
242230
243231 const openLabel = unreadCount > 0 ? `${ t ( 'messageCenter.openAria' ) } (${ t ( 'messageCenter.unreadCount' , { count : unreadCount } ) } )` : t ( 'messageCenter.openAria' ) ;
244- const emptyTitle = filter === 'unread' ? t ( 'messageCenter.emptyUnreadTitle' ) : filter === 'read' ? t ( 'messageCenter.emptyReadTitle' ) : t ( 'messageCenter.emptyAllTitle' ) ;
245232
246233 return < div className = { styles . root } >
247234 { hideTrigger ? null : < button ref = { triggerRef } type = "button" className = { `settings-icon-btn od-tooltip ${ styles . trigger } ` } onClick = { ( ) => setOpen ( ! open ) } title = { t ( 'messageCenter.openAria' ) } data-tooltip = { t ( 'messageCenter.openAria' ) } data-tooltip-placement = "bottom" aria-label = { openLabel } aria-haspopup = "dialog" aria-expanded = { open } data-testid = "message-center-trigger" >
248235 < Icon name = "bell" size = { 17 } /> { unreadCount > 0 ? < span className = { styles . badge } aria-hidden > { unreadBadgeLabel ( unreadCount ) } </ span > : null }
249236 </ button > }
250237 { open ? createPortal ( < div className = { styles . backdrop } data-testid = "message-center-backdrop" > < aside ref = { panelRef } className = { styles . panel } role = "dialog" aria-modal = "true" aria-labelledby = { titleId } tabIndex = { - 1 } data-testid = "message-center-dialog" >
251238 < header className = { styles . header } > < div className = { styles . headerCopy } > < h2 id = { titleId } > { t ( 'messageCenter.title' ) } </ h2 > < p > { t ( 'messageCenter.subtitle' ) } </ p > </ div > < Button size = "icon" className = { styles . close } onClick = { closePanel } aria-label = { t ( 'messageCenter.close' ) } > < Icon name = "close" size = { 18 } strokeWidth = { 2 } /> </ Button > </ header >
252- < div className = { styles . controls } > < div className = { styles . filters } role = "group" aria-label = { t ( 'messageCenter.title' ) } > { FILTERS . map ( ( item ) => < button key = { item . id } type = "button" className = { ` ${ styles . filter } ${ filter === item . id ? ` ${ styles . filterActive } ` : '' } ` } aria-pressed = { filter === item . id } onClick = { ( ) => setFilter ( item . id ) } > { t ( item . label ) } { item . id === 'unread' && unreadCount > 0 ? < span className = { styles . filterBadge } aria-hidden > { unreadBadgeLabel ( unreadCount ) } </ span > : null } </ button > ) } </ div > < button type = "button" className = { styles . markAll } onClick = { ( ) => void markAllRead ( ) . catch ( ( ) => setSyncState ( 'error' ) ) } disabled = { unreadCount === 0 } > { t ( 'messageCenter.markAllRead' ) } </ button > </ div >
239+ < div className = { styles . controls } > < button type = "button" className = { styles . markAll } onClick = { ( ) => void markAllRead ( ) . catch ( ( ) => setSyncState ( 'error' ) ) } disabled = { unreadCount === 0 } > { t ( 'messageCenter.markAllRead' ) } </ button > </ div >
253240 < div className = { styles . list } aria-live = "polite" >
254241 { syncState === 'error' && messages . length > 0 ? (
255242 < div className = { styles . syncStatus } role = "status" >
@@ -274,7 +261,7 @@ export function MessageCenter({
274261 </ button >
275262 </ div >
276263 </ div >
277- ) : visibleMessages . length === 0 ? < div className = { styles . empty } > < Icon name = "bell" size = { 20 } /> < strong > { emptyTitle } </ strong > < p > { t ( 'messageCenter.emptyBody' ) } </ p > </ div > : visibleMessages . map ( ( message ) => < MessageItem key = { message . id } locale = { locale } message = { message } onRead = { markRead } onError = { ( ) => setSyncState ( 'error' ) } /> ) }
264+ ) : messages . length === 0 ? < div className = { styles . empty } > < Icon name = "bell" size = { 20 } /> < strong > { t ( 'messageCenter.emptyAllTitle' ) } </ strong > < p > { t ( 'messageCenter.emptyBody' ) } </ p > </ div > : messages . map ( ( message ) => < MessageItem key = { message . id } locale = { locale } message = { message } onRead = { markRead } onError = { ( ) => setSyncState ( 'error' ) } /> ) }
278265 </ div >
279266 < footer className = { styles . footer } > < p > { t ( 'messageCenter.desktopSettingsHint' ) } </ p > { onOpenNotificationSettings ? < Button variant = "ghost" onClick = { ( ) => { closePanel ( ) ; onOpenNotificationSettings ( ) ; } } > { t ( 'messageCenter.desktopSettings' ) } </ Button > : null } </ footer >
280267 </ aside > </ div > , document . body ) : null }
0 commit comments