-
Notifications
You must be signed in to change notification settings - Fork 1.8k
fix: ensure WebSocket notifications are sent before application quits #4381
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,3 @@ | ||
| export { register as registerControl } from './control'; | ||
| export { register as registerAuth } from './auth'; | ||
| export { register as registerWebsocket } from './websocket'; | ||
| export { register as registerWebsocket, wsNotifyClose } from './websocket'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -56,6 +56,18 @@ export const register = ( | |
| ); | ||
| }; | ||
|
|
||
| // Notify all WebSocket clients that playback has stopped (e.g. app closing/hiding) | ||
| // We intentionally do NOT close or clear sockets here so that clients like | ||
| // Boring Notch keep their connection alive and can receive updates when | ||
| // Pear Desktop comes back from hiding. | ||
| wsNotifyClose = () => { | ||
| console.log(`[API Server WebSocket] NotifyClose called. Sending isPlaying:false to ${sockets.size} clients.`); | ||
| send(DataTypes.PlayerStateChanged, { | ||
| isPlaying: false, | ||
| position: lastSongInfo?.elapsedSeconds ?? 0, | ||
| }); | ||
| }; | ||
|
Comment on lines
+63
to
+69
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: No, the before-quit event does not wait for asynchronous work to finish. The event handler is treated as synchronous; any returned Promise is ignored, and Electron proceeds with quitting regardless. The recommended pattern for delaying quit until a WebSocket notification is flushed (or timed out) is to use a state flag in the main process: 1. Listen for the before-quit event. 2. If cleanup (WebSocket flush) is not complete, call event.preventDefault to cancel the quit. 3. Perform the async WebSocket operation (e.g., send notification and await acknowledgment or use a timeout). 4. Set the flag to indicate completion. 5. Call app.quit manually, which will trigger before-quit again but skip prevention due to the flag. Example code: let hasFlushed = false; async function flushWebSocket { // Send notification over WebSocket // Await ack or use Promise.race with timeout, e.g.: return Promise.race([ websocket.sendAndAwaitAck('notification'), new Promise(resolve => setTimeout(resolve, 5000)) // 5s timeout ]); } app.on('before-quit', (event) => { if (!hasFlushed) { event.preventDefault; flushWebSocket.then( => { hasFlushed = true; app.quit; }).catch( => { hasFlushed = true; app.quit; // Timeout or error: still quit }); } }); This pattern is consistently recommended across sources and works for network operations like WebSocket flushes. Use IPC if the WebSocket is in a renderer process. Citations:
🏁 Script executed: # Find the websocket.ts file
fd -type f -name "websocket.ts"Repository: pear-devs/pear-desktop Length of output: 236 🏁 Script executed: # Also check the main.ts file that was mentioned
fd -type f -name "main.ts" | grep -i "api-server\|backend"Repository: pear-devs/pear-desktop Length of output: 236 🏁 Script executed: # Find the websocket.ts file with correct syntax
fd -t f "websocket.ts"Repository: pear-devs/pear-desktop Length of output: 116 🏁 Script executed: # Find main.ts files in the codebase
fd -t f "main.ts" | head -20Repository: pear-devs/pear-desktop Length of output: 316 🏁 Script executed: # Read the websocket.ts file to see the implementation
cat -n "src/plugins/api-server/backend/routes/websocket.ts" | head -180Repository: pear-devs/pear-desktop Length of output: 5877 🏁 Script executed: # Read the main.ts file for the api-server to see how wsNotifyClose is used
cat -n "src/plugins/api-server/backend/main.ts"Repository: pear-devs/pear-desktop Length of output: 7381 Return a Promise from
Make Affects lines 63–69 and line 169. 🧰 Tools🪛 ESLint[error] 64-64: Replace (prettier/prettier) 🤖 Prompt for AI Agents |
||
|
|
||
| const createPlayerState = ({ | ||
| songInfo, | ||
| volumeState, | ||
|
|
@@ -152,3 +164,7 @@ export const register = ( | |
| })) as (ctx: Context, next: Next) => Promise<Response>, | ||
| ); | ||
| }; | ||
|
|
||
| // Exposed so the backend can call it on app close/hide | ||
| export let wsNotifyClose: (() => void) | undefined; | ||
|
|
||
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.
Is there a reason you used
await import?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.
Maybe they were not aware that vite does tree-shaking