Skip to content

Commit 0f0ae4a

Browse files
author
elifive555555
committed
fix(web): polish message center states
1 parent 67f8c85 commit 0f0ae4a

4 files changed

Lines changed: 30 additions & 67 deletions

File tree

apps/web/src/components/MessageCenter.module.css

Lines changed: 27 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,23 @@
5050
}
5151

5252
.header {
53+
position: relative;
5354
flex: 0 0 auto;
5455
display: flex;
5556
align-items: flex-start;
5657
justify-content: space-between;
5758
gap: 14px;
5859
padding: 18px 18px 14px;
59-
border-bottom: 1px solid var(--border-soft);
60+
}
61+
62+
.header::after {
63+
content: '';
64+
position: absolute;
65+
right: 12px;
66+
bottom: 0;
67+
left: 12px;
68+
height: 1px;
69+
background: var(--border-soft);
6070
}
6171

6272
.headerCopy {
@@ -101,7 +111,6 @@
101111
}
102112

103113
.close.close:focus-visible,
104-
.markAll:focus-visible,
105114
.syncStatus button:focus-visible,
106115
.itemSummary:focus-visible,
107116
.itemActions button:focus-visible,
@@ -111,44 +120,12 @@
111120
outline-offset: 2px;
112121
}
113122

114-
.controls {
115-
flex: 0 0 auto;
116-
display: flex;
117-
align-items: center;
118-
justify-content: flex-end;
119-
padding: 9px 14px;
120-
border-bottom: 1px solid var(--border-soft);
121-
}
122-
123-
.markAll {
124-
border: 0;
125-
background: transparent;
126-
color: var(--text-muted);
127-
cursor: pointer;
128-
font-size: 12px;
129-
line-height: 1;
130-
}
131-
132-
.markAll:hover:not(:disabled) {
133-
color: var(--text-strong);
134-
}
135-
136-
.markAll {
137-
flex: 0 0 auto;
138-
padding: 6px 4px;
139-
border-radius: var(--radius-pill, 999px);
140-
}
141-
142-
.markAll:disabled {
143-
cursor: default;
144-
opacity: 0.45;
145-
}
146-
147123
.list {
148124
flex: 1 1 auto;
149125
min-height: 0;
150126
overflow-x: hidden;
151127
overflow-y: auto;
128+
scrollbar-gutter: stable;
152129
overscroll-behavior: contain;
153130
display: flex;
154131
flex-direction: column;
@@ -221,7 +198,20 @@
221198
}
222199

223200
.itemUnread {
224-
box-shadow: inset 2px 0 0 var(--accent);
201+
position: relative;
202+
}
203+
204+
.itemUnread::before {
205+
content: '';
206+
position: absolute;
207+
z-index: 1;
208+
top: 38px;
209+
left: 3px;
210+
width: 6px;
211+
height: 6px;
212+
border-radius: 50%;
213+
background: var(--red, #ef4444);
214+
pointer-events: none;
225215
}
226216

227217
.itemExpanded {
@@ -242,6 +232,7 @@
242232
gap: 6px;
243233
padding: 12px 13px 10px;
244234
border: 0;
235+
border-radius: var(--radius-sm);
245236
background: transparent;
246237
color: inherit;
247238
text-align: left;
@@ -302,10 +293,7 @@
302293

303294
.itemExpanded .bodyPreview {
304295
overflow: visible;
305-
padding-top: 2px;
306296
color: var(--text);
307-
font-size: 13px;
308-
line-height: 1.58;
309297
overflow-wrap: anywhere;
310298
text-overflow: clip;
311299
white-space: normal;

apps/web/src/components/MessageCenter.tsx

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
clearAnonymousState,
88
isAmrLoggedIn,
99
markAccountMessageRead,
10-
markAllAccountMessagesRead,
1110
pullMessageCenter,
1211
readAnonymousMessages,
1312
readAnonymousReadIds,
@@ -214,20 +213,6 @@ export function MessageCenter({
214213
commitState(nextMessages, nextIds, { persistAnonymous: !account });
215214
};
216215

217-
const markAllRead = async () => {
218-
const account = await resolveLoggedInForWrite();
219-
if (account) await markAllAccountMessagesRead();
220-
const readAt = new Date().toISOString();
221-
const nextIds = new Set(messagesRef.current.map((message) => message.id));
222-
const nextMessages = messagesRef.current.map((message) => ({ ...message, readAt: message.readAt ?? readAt }));
223-
if (account) {
224-
pendingReadIdsRef.current = new Set(nextIds);
225-
clearAnonymousState(window.localStorage);
226-
}
227-
invalidateSyncResponses();
228-
commitState(nextMessages, nextIds, { persistAnonymous: !account });
229-
};
230-
231216
const openLabel = unreadCount > 0 ? `${t('messageCenter.openAria')} (${t('messageCenter.unreadCount', { count: unreadCount })})` : t('messageCenter.openAria');
232217

233218
return <div className={styles.root}>
@@ -236,7 +221,6 @@ export function MessageCenter({
236221
</button>}
237222
{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">
238223
<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>
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>
240224
<div className={styles.list} aria-live="polite">
241225
{syncState === 'error' && messages.length > 0 ? (
242226
<div className={styles.syncStatus} role="status">

apps/web/src/message-center-client.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,6 @@ export async function markAccountMessageRead(messageId: string): Promise<void> {
9999
if (!response.ok) throw new Error(`Mark message read failed: ${response.status}`);
100100
}
101101

102-
export async function markAllAccountMessagesRead(): Promise<void> {
103-
const response = await fetch(`${ACCOUNT_PROXY}/read-all`, { method: 'POST' });
104-
if (!response.ok) throw new Error(`Mark all messages read failed: ${response.status}`);
105-
}
106-
107102
function apiLocale(locale: string): string {
108103
const mapping: Record<string, string> = { en: 'en-US', 'es-ES': 'es', 'pt-BR': 'pt' };
109104
return mapping[locale] ?? locale;

apps/web/tests/components/MessageCenter.test.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,18 +120,14 @@ describe('MessageCenter', () => {
120120
await waitFor(() => expect(vi.mocked(fetch).mock.calls.some(([url, init]) => String(url).includes('/release/read') && init?.method === 'POST')).toBe(true));
121121
});
122122

123-
it('shows read and unread messages in one flat list and marks all read', async () => {
123+
it('shows read and unread messages in one flat list without filter or bulk actions', async () => {
124124
renderMessageCenter();
125125
const dialog = await openCenter();
126126

127127
expect(within(dialog).queryByRole('button', { name: 'All' })).toBeNull();
128128
expect(within(dialog).queryByRole('button', { name: 'Unread' })).toBeNull();
129129
expect(within(dialog).queryByRole('button', { name: 'Read' })).toBeNull();
130-
expect(within(dialog).getByText('OpenDesign 0.14 is available')).toBeTruthy();
131-
expect(within(dialog).getByText('Credits added')).toBeTruthy();
132-
133-
fireEvent.click(within(dialog).getByRole('button', { name: 'Mark all read' }));
134-
await waitFor(() => expect(screen.queryByLabelText(/unread/)).toBeNull());
130+
expect(within(dialog).queryByRole('button', { name: 'Mark all read' })).toBeNull();
135131
expect(within(dialog).getByText('OpenDesign 0.14 is available')).toBeTruthy();
136132
expect(within(dialog).getByText('Credits added')).toBeTruthy();
137133
});
@@ -396,7 +392,7 @@ describe('MessageCenter', () => {
396392
expect(localStorage.getItem('open-design.message-center.anonymous-read-ids.v1')).toContain('release'),
397393
);
398394

399-
fireEvent.click(screen.getByRole('button', { name: 'Mark all read' }));
395+
fireEvent.click(screen.getByRole('button', { name: /Security notice/ }));
400396
await waitFor(() =>
401397
expect(localStorage.getItem('open-design.message-center.anonymous-read-ids.v1')).toContain('security'),
402398
);

0 commit comments

Comments
 (0)