-
Notifications
You must be signed in to change notification settings - Fork 2k
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
feat(notifications): use Pinghub websocket #46070
Conversation
Here is how your PR affects size of JS and CSS bundles shipped to the user's browser: App Entrypoints (~72 bytes removed 📉 [gzipped])
Common code that is always downloaded and parsed every time the app is loaded, no matter which route is used. Sections (~88 bytes removed 📉 [gzipped])
Sections contain code specific for a given set of routes. Is downloaded and parsed only when a particular route is navigated to. Async-loaded Components (~278 bytes removed 📉 [gzipped])
React 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. |
59df8b8
to
0e12e20
Compare
@@ -279,10 +274,11 @@ export class Notifications extends Component { | |||
export default connect( | |||
( state ) => ( { | |||
currentLocaleSlug: getCurrentLocaleVariant( state ) || getCurrentLocaleSlug( state ), | |||
forceRefresh: shouldForceRefresh( state ), |
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.
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.
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.
Glad it worked, @nsakaimbo 🎉
0e12e20
to
2ffb095
Compare
ea56feb
to
23289f7
Compare
@nsakaimbo Is the idea to split implementation for the web ( |
@nsakaimbo Are there updated testing instructions anywhere for testing PRs like this? I didn't realize Looking through, it seems you're mostly removing special desktop handling, so that the desktop app uses pinghub the same way Calypso does (but with allowances for |
@kwight This is correct. The special desktop handling was added fairly recently (by me 😅 ) in #45793, and while this mostly worked we discovered that Calypso's existing Pinghub connection does not work in the Desktop app (due to a low-level limitation in Electron that we can't even work around) - see pbwngY-7U-p2. For more robust/timely notification support, we're using the OAuth token to connect Electron's Node process to Pinghub directly.
Since we're reverting specialized logic that was only recently added to Calypso (i.e. now the Desktop/Electron will do the heavy lifting w.r.t. Pinghub), Calypso's behavior should remain unchanged (reverting to Calypso's behavior before the initial changes were made to |
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.
Just some small things. Looks good. Great Work!!
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.
Great, thank you for the changes.
eead598
to
6a406a9
Compare
}, 1000 ); | ||
} | ||
onNotificationsPanelRefresh: function () { | ||
this.store.dispatch( forceNotificationsRefresh( true ) ); |
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.
I think this should simply import the refreshNotes
method from the notifications
module and call it. Adding the Redux action and reducer framework around it should be completely unnecessary.
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.
I agree this seems a little convoluted but I can't find a prior example of the client
code importing helpers from apps/
directory. I gave this approach a try in 497823d3b9b119fbaa29379d61a1974f24f5e476 but ultimately reverted due to a couple issues:
- for some reason, it didn't work to refresh the notifications panel (confirmed by logging activity from the notifications
rest-client
). Not sure why. - the linter also complained about the import in
client/lib/desktop/index.js
:
import { refreshNotes } from '../../../apps/notifications/src/panel/notifications';
// error: file is not under 'rootDir'. `rootDir` is expected to contain all source files.
I can't find a prior example of client
code importing something from the apps
directory, but happy to revisit this as a refactor, though.
|
||
module.exports = { | ||
fetchNote: ( noteId ) => promiseTimeout( 300, fetchNote( noteId ) ), | ||
markReadStatus: ( noteId, isRead ) => promiseTimeout( 1000, markReadStatus( noteId, isRead ) ), // response time of this endpoint is very slow! |
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.
Why is there only a 0.3s timeout for fetchNote
? That's very unfriendly for users on slow connections. And why do we need hard timeouts at all?
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.
I went ahead and bumped the timeouts to a more forgiving value.
IMO it seems having timeout is useful from a user responsiveness perspective - at the very least, this gives us the opportunity to display some sort of error message, rather than leave them potentially hanging indefinitely (or for too long).
However if there's some sort of "natural timeout" that happens anyway and adding a hard timeout is truly redundant (either from the server or perhaps even the network request itself) then I'd be happy to take this out.
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.
If the fetchNote
or markReadStatus
request takes a lot of time, it doesn't block the UI. It's only that the incoming WebSocket notifications take long to appear in the UI, or that marking a notification as read takes a long time or is never really performed on server. The user can continue to use the UI normally.
Nowhere else in Calypso do we put extra timeouts on REST APIs. And even the 2 seconds still are way too short for, e.g., users in Australia or South Africa who are connecting to US servers.
I'd remove them everywhere except the initial boot sequence, which has a potential to brick the UI completely.
}, | ||
( error, body ) => { | ||
if ( error ) { | ||
reject( error ); |
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.
The callback should return
after rejecting.
a945a4f
to
b172b17
Compare
This reverts commit 497823d3b9b119fbaa29379d61a1974f24f5e476.
b172b17
to
6f916f7
Compare
Description
Uses direct Pinghub websocket connection to source realtime notification events.
Context
PushManager
service workers are unsupported by ElectronOrigin
header to the backend. Unfortunately, a bug in Electron's Chromium implementation prevents us from modifying headers of websocket connection requests made in the renderer process. 💩Implementation Overview
ws
for websocket connectionkeytar
to persist sensitive user credentials to the keychain (OAuth token) (cross-platform keychain support for Windows, Linux and Mac)wpcom-xhr-request
to interface with other notification APIs (fetch note, mark-note-as-read)Limitations/To-Dos
Trying to avoid using window events and instead use redux state to force a notifications panel refresh. Not sure if possible?Solved.