From b400c4d6c3b63a4a1fa91ae45e4d08fbf3ab2d34 Mon Sep 17 00:00:00 2001 From: Max Litruv Boonzaayer Date: Fri, 27 Feb 2026 22:45:14 +1100 Subject: [PATCH] feat: enhance message notification handling with improved navigation and notification logic --- src/app/pages/client/ClientNonUIFeatures.tsx | 92 ++++++++++---------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/src/app/pages/client/ClientNonUIFeatures.tsx b/src/app/pages/client/ClientNonUIFeatures.tsx index 3d3ba10..35964ad 100644 --- a/src/app/pages/client/ClientNonUIFeatures.tsx +++ b/src/app/pages/client/ClientNonUIFeatures.tsx @@ -14,15 +14,19 @@ import { EmojiStyle, settingsAtom } from '../../state/settings'; import { allInvitesAtom } from '../../state/room-list/inviteList'; import { usePreviousValue } from '../../hooks/usePreviousValue'; import { useMatrixClient } from '../../hooks/useMatrixClient'; -import { getDirectRoomPath, getHomeRoomPath, getInboxInvitesPath } from '../pathUtils'; +import { getDirectRoomPath, getHomeRoomPath, getSpaceRoomPath, getInboxInvitesPath } from '../pathUtils'; import { getMemberDisplayName, getNotificationType, getUnreadInfo, isNotificationEvent, + getOrphanParents, + guessPerfectParent, } from '../../utils/room'; import { NotificationType, UnreadInfo } from '../../../types/matrix/room'; -import { getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix'; +import { getMxIdLocalPart, mxcUrlToHttp, getCanonicalAliasOrRoomId } from '../../utils/matrix'; +import { mDirectAtom } from '../../state/mDirectList'; +import { roomToParentsAtom } from '../../state/room/roomToParents'; import { useSelectedRoom } from '../../hooks/router/useSelectedRoom'; import { useInboxNotificationsSelected } from '../../hooks/router/useInbox'; import { useMediaAuthentication } from '../../hooks/useMediaAuthentication'; @@ -166,6 +170,8 @@ function MessageNotifications() { const useAuthentication = useMediaAuthentication(); const [showNotifications] = useSetting(settingsAtom, 'showNotifications'); const [notificationSound] = useSetting(settingsAtom, 'isNotificationSounds'); + const mDirects = useAtomValue(mDirectAtom); + const roomToParents = useAtomValue(roomToParentsAtom); const navigate = useNavigate(); const notificationSelected = useInboxNotificationsSelected(); @@ -196,56 +202,47 @@ function MessageNotifications() { eventId: string; isDm: boolean; }) => { - // In DMs, room name is the username, so don't duplicate it - const body = messageBody - ? (isDm ? messageBody : `${username}: ${messageBody}`) - : `New message from ${username}`; - - // Build the path to navigate to on click - const roomPath = isDm - ? getDirectRoomPath(roomId, eventId) - : getHomeRoomPath(roomId, eventId); - - // Check if running in Electron - const isElectron = 'electron' in window; - const hasElectronNotification = (window as any).electron?.notification?.show; - - console.log('Notification check:', { - isElectron, - hasElectronNotification, - isTauri: isTauri(), - username, - messageBody - }); - + const notificationTitle = `${username} - Paarrot`; + const notificationBody = messageBody || 'New message'; + + /** Replicates TitleBar click navigation logic */ + const navigateToRoom = () => { + if (!mx) return; + try { + const roomIdOrAlias = getCanonicalAliasOrRoomId(mx, roomId); + if (mDirects.has(roomId)) { + navigate(getDirectRoomPath(roomIdOrAlias, eventId)); + return; + } + const orphanParents = getOrphanParents(roomToParents, roomId); + if (orphanParents.length > 0) { + const parentSpace = guessPerfectParent(mx, roomId, orphanParents) ?? orphanParents[0]; + const pSpaceIdOrAlias = getCanonicalAliasOrRoomId(mx, parentSpace); + navigate(getSpaceRoomPath(pSpaceIdOrAlias, roomIdOrAlias, eventId)); + return; + } + navigate(getHomeRoomPath(roomIdOrAlias, eventId)); + } catch (err) { + console.error('[Notifications] Navigate error:', err); + } + }; + if (isTauri()) { + const roomPath = isDm + ? getDirectRoomPath(roomId, eventId) + : getHomeRoomPath(roomId, eventId); sendNotification({ - title: roomName, - body, + title: notificationTitle, + body: notificationBody, path: roomPath, onClick: () => { if (!window.closed) navigate(roomPath); }, }); - } else if (isElectron && hasElectronNotification) { - // Use Electron desktop notifications - const notificationTitle = `${username} - Paarrot`; - const notificationBody = messageBody || 'New message'; - - console.log('Sending Electron notification:', { title: notificationTitle, body: notificationBody }); - - (window as any).electron.notification.show({ - title: notificationTitle, - body: notificationBody, - icon: roomAvatar, - }).catch((err: any) => console.error('Electron notification error:', err)); } else { - // Fallback to web Notification API - also use custom format - const notificationTitle = `${username} - Paarrot`; - const notificationBody = messageBody || 'New message'; - - console.log('Sending web notification:', { title: notificationTitle, body: notificationBody }); - + // Use renderer window.Notification — works in both Electron and browser. + // Main-process Notification click events are unreliable on Windows without + // full COM/AUMID registration, so we keep everything in the renderer. const noti = new window.Notification(notificationTitle, { icon: roomAvatar, badge: roomAvatar, @@ -254,8 +251,9 @@ function MessageNotifications() { }); noti.onclick = () => { - window.focus(); - if (!window.closed) navigate(roomPath); + // Tell main process to bring window to front + (window as any).electron?.window?.focus(); + if (!window.closed) navigateToRoom(); noti.close(); notifRef.current = undefined; }; @@ -264,7 +262,7 @@ function MessageNotifications() { notifRef.current = noti; } }, - [mx, navigate] + [mx, navigate, mDirects, roomToParents] ); const playSound = useCallback(() => {