Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-notification-handling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'default': patch
---

Fix notification handling with null safety and improved logic
15 changes: 1 addition & 14 deletions src/app/pages/client/BackgroundNotifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -326,20 +326,7 @@ export function BackgroundNotifications() {
// For "Mention & Keywords": respect the push rule (only notify if it matches).
const shouldForceDMNotification =
isDM && notificationType !== NotificationType.MentionsAndKeywords;
// For reactions: Only notify if someone reacted to your own message
let shouldForceReactionNotification = false;
if (eventType === 'm.reaction') {
const relatesTo = mEvent.getContent()['m.relates_to'];
const reactedToEventId = relatesTo?.event_id;
if (reactedToEventId) {
const reactedToEvent = room.findEventById(reactedToEventId);
if (reactedToEvent && reactedToEvent.getSender() === mx.getUserId()) {
shouldForceReactionNotification = true;
}
}
}
const shouldNotify =
pushActions?.notify || shouldForceDMNotification || shouldForceReactionNotification;
const shouldNotify = pushActions?.notify || shouldForceDMNotification;

if (!shouldNotify) {
return;
Expand Down
35 changes: 29 additions & 6 deletions src/app/utils/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,15 +214,36 @@ const NOTIFICATION_EVENT_TYPES = [
'm.sticker',
'm.reaction',
];
export const isNotificationEvent = (mEvent: MatrixEvent) => {
export const isNotificationEvent = (mEvent: MatrixEvent, room?: Room, userId?: string) => {
const eType = mEvent.getType();
if (!NOTIFICATION_EVENT_TYPES.includes(eType)) {
return false;
}
if (eType === 'm.room.member') return false;

if (mEvent.isRedacted()) return false;
return mEvent.getRelation()?.rel_type !== 'm.replace';
const relation = mEvent.getRelation();
const relationType = relation?.rel_type;

// Filter out edits - they shouldn't count as new notifications
if (relationType === 'm.replace') return false;

// For reactions: only count them if they're reactions to the current user's messages
if (relationType === 'm.annotation') {
if (!room || !userId || !relation) {
// If we don't have room/userId/relation context, filter out all reactions (safe default)
return false;
}
// Get the event being reacted to
const reactedToEventId = relation.event_id;
if (!reactedToEventId) return false;

const reactedToEvent = room.findEventById(reactedToEventId);
// Only count as notification if the reacted-to message was sent by current user
return reactedToEvent?.getSender() === userId;
}

return true;
};

export const roomHaveNotification = (room: Room): boolean => {
Expand Down Expand Up @@ -254,7 +275,7 @@ export const roomHaveUnread = (mx: MatrixClient, room: Room) => {
if (event.getId() === readUpToId) {
return false;
}
if (isNotificationEvent(event)) {
if (isNotificationEvent(event, room, userId)) {
return true;
}
}
Expand Down Expand Up @@ -298,7 +319,9 @@ export const getUnreadInfo = (room: Room, options?: UnreadInfoOptions): UnreadIn
.reverse()
.find(
(event) =>
!event.isSending() && event.getSender() !== userId && isNotificationEvent(event)
!event.isSending() &&
event.getSender() !== userId &&
isNotificationEvent(event, room, userId)
);
const latestNotificationId = latestNotification?.getId();
if (latestNotificationId && room.hasUserReadEvent(userId, latestNotificationId)) {
Expand All @@ -321,7 +344,7 @@ export const getUnreadInfo = (room: Room, options?: UnreadInfoOptions): UnreadIn
const event = liveEvents[i];
if (!event) break;
if (event.getId() === readUpToId) break;
if (isNotificationEvent(event) && event.getSender() !== userId) {
if (isNotificationEvent(event, room, userId) && event.getSender() !== userId) {
fallbackTotal += 1;
const pushActions = pushProcessor.actionsForEvent(event);
if (pushActions?.tweaks?.highlight) fallbackHighlight += 1;
Expand All @@ -343,7 +366,7 @@ export const getUnreadInfo = (room: Room, options?: UnreadInfoOptions): UnreadIn
const liveEvents = room.getLiveTimeline().getEvents();

const hasActivity = liveEvents.some(
(event) => event.getSender() !== userId && isNotificationEvent(event)
(event) => event.getSender() !== userId && isNotificationEvent(event, room, userId)
);

if (hasActivity) {
Expand Down
Loading