Skip to content

Commit be839ca

Browse files
committed
Fix double-click handling of Win notifs foregrounding the app
1 parent 9935369 commit be839ca

1 file changed

Lines changed: 26 additions & 10 deletions

File tree

‎app/src/browser/notification-ipc.ts‎

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ interface NotificationOptions {
2020
toastXml?: string;
2121
}
2222

23+
const handledWindowsToastXMLProtocolActionsForIds: string[] = [];
24+
2325
// Track active notifications by ID, with metadata for thread-based dismissal
2426
const activeNotifications = new Map<string, { notification: Notification; threadId?: string }>();
2527

@@ -35,7 +37,7 @@ const activeNotifications = new Map<string, { notification: Notification; thread
3537
*
3638
* Uses path.resolve() to prevent directory traversal attacks.
3739
*/
38-
const validateIconPath = (iconPath: string): string | null => {
40+
const validateIconPath = (iconPath: string | undefined): string | null => {
3941
if (!iconPath) {
4042
return null;
4143
}
@@ -149,16 +151,27 @@ const displayNotification = (
149151
const notification = new Notification(notifOptions);
150152

151153
// Handle click event
152-
// On Windows with toastXml + activationType="protocol", clicks are routed through
153-
// the OS protocol handler (mailspring:// URLs) rather than Electron's Activated callback,
154-
// so this event won't fire. We register it anyway as a fallback for non-toastXml
155-
// notifications or if protocol activation fails.
156154
notification.on('click', () => {
157-
sendToAllWindows('notification:clicked', {
155+
const payload = {
158156
id: options.id,
159157
threadId: options.threadId,
160158
messageId: options.messageId,
161-
});
159+
};
160+
if (process.platform === 'win32') {
161+
// On Windows with toastXml + activationType="protocol", clicks are routed through
162+
// the OS protocol handler (mailspring:// URLs) rather than Electron's Activated callback,
163+
// but this event still fires first. It seems worth handling it in case the protocol handler
164+
// fails, since it's a big of a fragile approach, but we need to wait and let the event arrive
165+
// from the second-instance that is launched to handle the URL.
166+
setTimeout(() => {
167+
if (handledWindowsToastXMLProtocolActionsForIds.includes(payload.id)) {
168+
return;
169+
}
170+
sendToAllWindows('notification:clicked', payload);
171+
}, 1500);
172+
} else {
173+
sendToAllWindows('notification:clicked', payload);
174+
}
162175
});
163176

164177
// Handle close event
@@ -227,16 +240,19 @@ export function registerNotificationIPCHandlers(ipcMain: IpcMain) {
227240
}
228241

229242
export function handleWindowsToastXMLProtocolAction(parts: UrlWithParsedQuery) {
230-
const windowsNotifEventArgs = {
243+
const payload = {
231244
id: parts.query.id as string,
232245
threadId: parts.query.threadId as string,
233246
messageId: parts.query.messageId as string,
234247
};
235248

249+
handledWindowsToastXMLProtocolActionsForIds.unshift(payload.id);
250+
handledWindowsToastXMLProtocolActionsForIds.splice(10);
251+
236252
if (parts.host === 'notification-click') {
237-
sendToAllWindows('notification:clicked', windowsNotifEventArgs);
253+
sendToAllWindows('notification:clicked', payload);
238254
} else if (parts.host === 'notification-action') {
239255
const actionIndex = parseInt(parts.query.actionIndex as string, 10);
240-
sendToAllWindows('notification:action', { ...windowsNotifEventArgs, actionIndex });
256+
sendToAllWindows('notification:action', { ...payload, actionIndex });
241257
}
242258
}

0 commit comments

Comments
 (0)