Skip to content

Commit b587664

Browse files
committed
fix: preserve room and sender data in push notifications
1 parent a339b5a commit b587664

5 files changed

Lines changed: 110 additions & 18 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
default: patch
3+
---
4+
5+
Preserve room names and sender details in push notifications.

‎src-tauri/Cargo.lock‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src-tauri/Cargo.toml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,12 @@ windows = { version = "0.62", features = [
110110
tauri-plugin-single-instance = { version = "2.4.3", features = ["deep-link"] }
111111

112112
[target.'cfg(any(windows, target_os = "linux"))'.dependencies]
113-
tauri-plugin-notifications = { git = "https://github.com/SableClient/tauri-plugin-notifications.git", rev = "f2f320f12a5904ece6d648ce551f9d56bb4a9175" }
113+
tauri-plugin-notifications = { git = "https://github.com/SableClient/tauri-plugin-notifications.git", rev = "88c798eaa16fb4a1210fd1c6107e6e93420125eb" }
114114

115115
# default-features = false drops notify-rust so macOS uses the native
116116
# UNUserNotificationCenter backend (needs a signed .app to deliver).
117117
[target.'cfg(target_os = "macos")'.dependencies]
118-
tauri-plugin-notifications = { git = "https://github.com/SableClient/tauri-plugin-notifications.git", rev = "f2f320f12a5904ece6d648ce551f9d56bb4a9175", default-features = false }
118+
tauri-plugin-notifications = { git = "https://github.com/SableClient/tauri-plugin-notifications.git", rev = "88c798eaa16fb4a1210fd1c6107e6e93420125eb", default-features = false }
119119

120120
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
121121
tauri-plugin-updater = { version = "2", optional = true }
@@ -139,7 +139,7 @@ libloading = "0.9"
139139
zbus = "5"
140140

141141
[target.'cfg(any(target_os = "android", target_os = "ios"))'.dependencies]
142-
tauri-plugin-notifications = { git = "https://github.com/SableClient/tauri-plugin-notifications.git", rev = "f2f320f12a5904ece6d648ce551f9d56bb4a9175", features = [
142+
tauri-plugin-notifications = { git = "https://github.com/SableClient/tauri-plugin-notifications.git", rev = "88c798eaa16fb4a1210fd1c6107e6e93420125eb", features = [
143143
"push-notifications",
144144
] }
145145
tauri-plugin-edge-to-edge = { git = "https://github.com/SableClient/tauri-plugin-edge-to-edge.git", rev = "33c6116c27be28c06df5a9d02231ecc5fdeb93c5" }

‎src/app/features/settings/notifications/UnifiedPushNotifications.test.ts‎

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,28 @@ describe('UnifiedPushNotifications', () => {
250250
);
251251
});
252252

253+
it.each(['m.room.encrypted', 'm.room.message'])(
254+
'uses the cached room name when a %s push omits it',
255+
async (type) => {
256+
matrixClient.getRoom.mockReturnValue(makeRoom());
257+
await listenAndPush(
258+
{
259+
...encryptedPush('$room-title'),
260+
type,
261+
room_name: undefined,
262+
sender_display_name: 'Alice',
263+
},
264+
makeSettings({ showEncryptedMessageContent: false })
265+
);
266+
267+
await vi.waitFor(() =>
268+
expect(notificationsApi.sendNotification).toHaveBeenCalledWith(
269+
expect.objectContaining({ title: 'Room' })
270+
)
271+
);
272+
}
273+
);
274+
253275
it('posts an encrypted baseline before a hanging local decryption completes', async () => {
254276
matrixClient.getRoom.mockReturnValue(makeRoom());
255277
let resolveDecryption!: (content: Record<string, unknown>) => void;
@@ -282,7 +304,7 @@ describe('UnifiedPushNotifications', () => {
282304
await vi.waitFor(() => expect(notificationsApi.sendNotification).toHaveBeenCalledTimes(2));
283305
});
284306

285-
it('posts message notifications as a conversation on the high-importance channel', async () => {
307+
it('keeps the room header for a named two-member conversation', async () => {
286308
matrixClient.getRoom.mockReturnValue(makeRoom());
287309

288310
await listenAndPush({
@@ -298,11 +320,68 @@ describe('UnifiedPushNotifications', () => {
298320
await vi.waitFor(() => expect(notificationsApi.sendNotification).toHaveBeenCalledOnce());
299321
expect(notificationsApi.sendNotification.mock.calls[0]?.[0]).toMatchObject({
300322
channelId: 'messages.v2',
301-
groupConversation: false,
323+
groupConversation: true,
302324
messages: [{ body: 'hello', senderName: 'Alice', senderKey: '@alice:example.com' }],
303325
});
304326
});
305327

328+
it('keeps the sender identity when a rich push omits its display name', async () => {
329+
matrixClient.getRoom.mockReturnValue(makeRoom());
330+
await listenAndPush(
331+
{
332+
...encryptedPush('$sender'),
333+
sender: '@alice:example.com',
334+
},
335+
makeSettings({ showEncryptedMessageContent: false })
336+
);
337+
338+
await vi.waitFor(() =>
339+
expect(notificationsApi.sendNotification).toHaveBeenCalledWith(
340+
expect.objectContaining({
341+
title: 'Room',
342+
body: 'alice: Encrypted message',
343+
extra: {
344+
user_id: '@user:example.com',
345+
room_id: '!room:example.com',
346+
event_id: '$sender',
347+
},
348+
messages: [
349+
expect.objectContaining({ senderName: 'alice', senderKey: '@alice:example.com' }),
350+
],
351+
})
352+
)
353+
);
354+
});
355+
356+
it('keeps room and sender data from an event-ID payload', async () => {
357+
await listenAndPush(
358+
{
359+
room_id: '!minimal:example.com',
360+
event_id: '$minimal',
361+
room_name: 'Project',
362+
sender_display_name: 'Alice',
363+
sender: '@alice:example.com',
364+
},
365+
makeSettings({ showMessageContent: false })
366+
);
367+
368+
await vi.waitFor(() =>
369+
expect(notificationsApi.sendNotification).toHaveBeenCalledWith(
370+
expect.objectContaining({
371+
title: 'Project',
372+
messages: [
373+
expect.objectContaining({ senderName: 'Alice', senderKey: '@alice:example.com' }),
374+
],
375+
extra: {
376+
user_id: '@user:example.com',
377+
room_id: '!minimal:example.com',
378+
event_id: '$minimal',
379+
},
380+
})
381+
)
382+
);
383+
});
384+
306385
it('posts invitations on their own channel', async () => {
307386
await listenAndPush({
308387
type: 'm.room.member',

‎src/app/features/settings/notifications/UnifiedPushNotifications.ts‎

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -673,10 +673,15 @@ async function handleRichPushPayload(
673673
});
674674

675675
const roomId: string | undefined = pushData?.room_id;
676+
const currentRoom = roomId ? settings.mx.getRoom(roomId) : undefined;
676677
const roomName: string =
677-
pushData?.room_name ?? pushData?.sender_display_name ?? 'Unknown Room';
678-
const senderName: string | undefined = pushData?.sender_display_name;
678+
pushData?.room_name || currentRoom?.name || pushData?.sender_display_name || 'Unknown Room';
679679
const senderId: string | undefined = pushData?.sender;
680+
const senderName =
681+
pushData?.sender_display_name ||
682+
(senderId
683+
? currentRoom?.getMember(senderId)?.name || getMxIdLocalPart(senderId) || senderId
684+
: undefined);
680685
const isSilent = !settings.notificationSoundEnabled;
681686

682687
if (!roomId) {
@@ -748,10 +753,9 @@ async function handleRichPushPayload(
748753
cache.messages = cache.messages.slice(-MAX_MESSAGES);
749754
}
750755

751-
const currentRoom = settings.mx.getRoom(roomId);
752-
if (currentRoom) {
753-
cache.isGroupConversation = (currentRoom.getJoinedMemberCount() ?? 0) > 2;
754-
}
756+
cache.isGroupConversation =
757+
Boolean(pushData?.room_name || currentRoom?.name) ||
758+
(currentRoom?.getJoinedMemberCount() ?? 0) > 2;
755759

756760
try {
757761
await postRoomNotification(userId, roomId, cache, isSilent, {
@@ -966,11 +970,16 @@ async function handleMinimalPushPayload(
966970
}
967971

968972
const room = settings.mx.getRoom(roomId);
969-
const roomName = room?.name ?? pushData?.sender_display_name ?? 'Unknown Room';
973+
const roomName =
974+
room?.name || pushData?.room_name || pushData?.sender_display_name || 'Unknown Room';
970975
const isEncryptedRoom = room ? !!getStateEvent(room, EventType.RoomEncryption) : false;
971976

972-
let senderName: string | undefined;
973-
let senderId: string | undefined;
977+
let senderId = pushData?.sender;
978+
let senderName =
979+
pushData?.sender_display_name ||
980+
(senderId
981+
? room?.getMember(senderId)?.name || getMxIdLocalPart(senderId) || senderId
982+
: undefined);
974983
let previewText: string | undefined;
975984
let inMemoryStillEncrypted = false;
976985
if (room && eventId) {
@@ -1023,9 +1032,8 @@ async function handleMinimalPushPayload(
10231032
cache.messages = cache.messages.slice(-MAX_MESSAGES);
10241033
}
10251034

1026-
if (room) {
1027-
cache.isGroupConversation = (room.getJoinedMemberCount() ?? 0) > 2;
1028-
}
1035+
cache.isGroupConversation =
1036+
Boolean(pushData?.room_name || room?.name) || (room?.getJoinedMemberCount() ?? 0) > 2;
10291037

10301038
try {
10311039
await postRoomNotification(userId, roomId, cache, !settings.notificationSoundEnabled, {

0 commit comments

Comments
 (0)