Notifications: keep the bell badge live while the panel is closed - #112080
Notifications: keep the bell badge live while the panel is closed#112080arthur791004 wants to merge 7 commits into
Conversation
Jetpack Cloud Live (direct link)
Automattic for Agencies Live (direct link)
Dashboard Live (dotcom) (direct link)
|
|
Looks like one of the E2E tests has failed. You can fix them following these steps:
|
| locale={ localeSlug } | ||
| actionHandlers={ this.actionHandlers } | ||
| closePanel={ this.closePanel } | ||
| setUnseenCount={ this.props.setUnseenCount } |
There was a problem hiding this comment.
[fix here] Issue: Prop name mismatch — this passes setUnseenCount but RedesignedNotifications destructures updateUnseenCount (line 115). The callback will be undefined at runtime, so the websocket push will throw a TypeError when it tries to call updateUnseenCount( count ).
Suggestion: Rename the prop to match the destructured name:
| setUnseenCount={ this.props.setUnseenCount } | |
| updateUnseenCount={ this.props.setUnseenCount } |
Source: general correctness — the prop name at the call site must match the destructured name in the receiving component.
There was a problem hiding this comment.
Good catch — fixed in 13391b0. Renamed the call-site prop to updateUnseenCount to match the destructured name, so the note-stream callback resolves.
The Dropdown consumers (redesigned masterbar, Dashboard, Reader) mount the notifications app only while the panel is open, so the unread bell badge froze once closed — the app's RestClient stopped polling. Add a panel-independent poller in the notifications app (subscribeUnseenNotifications) that polls the user's has_unseen_notes flag and reports changes. It's a plain subscribe/unsubscribe function, not a hook, so it works from the masterbar class component and can be dynamically imported by the Dashboard (which disallows static imports from the app). Each consumer wires it to its own badge while closed; the open panel keeps driving the exact count via APP_RENDER_NOTES. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Use api-core's fetchUser() in the background poller instead of a hand-rolled /me request, reusing the canonical /me fetcher and the typed User. Drops the wpcom argument since the fetcher uses the shared client. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the /me has_unseen_notes poll with a slim subscriber that reuses the panel's existing note-stream mechanism: a pinghub websocket push (real-time) with a /notifications poll fallback, deriving the unseen count from notes newer than the server's last-seen time. This gives the closed badge a real-time, exact count instead of a 60s boolean, matching how the open panel already decides the count. Drops the api-core dep. Masterbar shows the exact live count; Dashboard/Reader map it to their boolean indicator. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
We only need to know whether there are unseen notes (and a small badge count), so fetch 10 instead of 100. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The call site passed setUnseenCount but RedesignedNotifications reads updateUnseenCount, so the live-count callback was undefined and the note-stream push threw. Match the prop name. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
13391b0 to
a56de88
Compare
|
Here is how your PR affects size of JS and CSS bundles shipped to the user's browser: App Entrypoints (~109 bytes added 📈 [gzipped]) DetailsCommon code that is always downloaded and parsed every time the app is loaded, no matter which route is used. Sections (~86 bytes added 📈 [gzipped]) DetailsSections contain code specific for a given set of routes. Is downloaded and parsed only when a particular route is navigated to. Async-loaded Components (~559 bytes added 📈 [gzipped]) DetailsReact components that are loaded lazily, when a certain part of UI is displayed for the first time. Legend What is parsed and gzip size?Parsed Size: Uncompressed size of the JS and CSS files. This much code needs to be parsed and stored in memory. Generated by performance advisor bot at iscalypsofastyet.com. |
| let pollTimer: ReturnType< typeof setTimeout > | undefined; | ||
| let cooldownTimer: ReturnType< typeof setTimeout > | undefined; | ||
|
|
||
| const fetchCount = () => { |
There was a problem hiding this comment.
Thanks for the PR! What worries me is that this PR is adding duplicated fetch mechanism on top of what the existing RestClient already provides. I'm trying a different alternative and will let you know...
There was a problem hiding this comment.
I haven't forgotten about this, will post something later today or tomorrow.
There was a problem hiding this comment.
Can you check this alternative approach without duplicating the fetch mechanism? #113086
|
Closing it in favor of #113086 |
Fixes DOTMSD-1208
Proposed Changes
Why are these changes being made?
The notifications app subscribes to the note stream and computes the unread count, but it only runs while the panel is open. The Dropdown surfaces unmount it on close, so the bell froze at its last value until the user reopened the panel — notifications arriving while it was closed weren't reflected. This keeps the badge live by reusing the same note-stream and count logic, decoupled from the panel UI.
Considerations
has_unseen_notesflag, but nothing else polls it and it's only a boolean, so this matches the established path and keeps the exact count.Testing Instructions
notifications/redesign), leave the panel closed and trigger a notification from another account (like/comment).Pre-merge Checklist