fix(notifications): don't badge/notify stale device-verification requests
In-room verification requests arrive as m.room.message with msgtype 'm.key.verification.request', so isNotificationEvent() counted them (type is m.room.message). A stale/old request at the tail of a DM therefore re-lit the room's unread dot — and could fire a toast/OS notification — on every fresh sync (app update / CTRL+F5 cache clear); opening the room only cleared it via the local read-receipt echo, so it returned on the next reload. Exclude that msgtype from isNotificationEvent so verification control messages never drive unread/notifications (mirrors the existing member/redaction/edit exclusions). The rest of the verification flow already uses distinct event types that aren't notification events. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -357,6 +357,28 @@ test('isNotificationEvent accepts message/sticker but rejects member, redacted,
|
||||
),
|
||||
false,
|
||||
);
|
||||
|
||||
// Device-verification requests (m.room.message + this msgtype) are crypto
|
||||
// control messages — must NOT count as unread/notify (stale-request nag fix).
|
||||
assert.equal(
|
||||
isNotificationEvent(
|
||||
mockEvent({
|
||||
getType: () => 'm.room.message',
|
||||
getContent: () => ({ msgtype: 'm.key.verification.request' }),
|
||||
}),
|
||||
),
|
||||
false,
|
||||
);
|
||||
// Regression guard: a normal text message still notifies.
|
||||
assert.equal(
|
||||
isNotificationEvent(
|
||||
mockEvent({
|
||||
getType: () => 'm.room.message',
|
||||
getContent: () => ({ msgtype: 'm.text', body: 'hi' }),
|
||||
}),
|
||||
),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
// --- roomHaveNotification / getUnreadInfo --------------------------------
|
||||
|
||||
@@ -195,12 +195,21 @@ const NOTIFICATION_EVENT_TYPES = [
|
||||
'm.room.member',
|
||||
'm.sticker',
|
||||
];
|
||||
// In-room device-verification requests are sent as m.room.message with this
|
||||
// msgtype (the rest of the flow — start/accept/key/mac/done/cancel — uses its own
|
||||
// event types, already excluded above). They're crypto control messages, not chat.
|
||||
const VERIFICATION_REQUEST_MSGTYPE = 'm.key.verification.request';
|
||||
export const isNotificationEvent = (mEvent: MatrixEvent) => {
|
||||
const eType = mEvent.getType();
|
||||
if (!NOTIFICATION_EVENT_TYPES.includes(eType)) {
|
||||
return false;
|
||||
}
|
||||
if (eType === 'm.room.member') return false;
|
||||
// Don't badge/notify a verification request — otherwise a stale one at the tail
|
||||
// of a DM re-lights the room's unread dot on every fresh sync (cache clear).
|
||||
if (eType === 'm.room.message' && mEvent.getContent().msgtype === VERIFICATION_REQUEST_MSGTYPE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mEvent.isRedacted()) return false;
|
||||
if (mEvent.getRelation()?.rel_type === 'm.replace') return false;
|
||||
|
||||
Reference in New Issue
Block a user