Skip to content

Show intentional mention push rules in notification settings - #35031

Merged
florianduros merged 11 commits into
element-hq:developfrom
barodeur:mention-settings-intentional-rules
Sep 15, 2026
Merged

florianduros merged 11 commits into
element-hq:developfrom
barodeur:mention-settings-intentional-rules

Conversation

@barodeur

@barodeur barodeur commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Fix #35032

Synapse is going to stop advertising the legacy mention rules by default (MSC4210, via its msc4210_enabled feature flag). It will only serve the intentional mention rules.
When that feature is enabled, the mention notification settings disappear completely:

Screenshot notification-settings-before-fix

This PR makes sure users can still configure their mention notifications when only the intentional rules are served.

Nothing changes for a server that still serves the legacy rules: same rows, same displayed state, same writes to both rule sets. The intentional rows only appear once the legacy rules are absent from the user's push rules.

Server side: element-hq/synapse#20215 migrates existing legacy overrides onto the intentional rules before Synapse stops serving the legacy rules, so preferences survive the switch for every user.

Checklist

  • I have read through review guidelines and CONTRIBUTING.md.
  • I have linked the PR to an issue that describes what needs changing.
  • I have written tests for new code (and old code if feasible).
  • I have ensured new or updated public/exported symbols have accurate TSDoc documentation.
  • I have confirmed linter and other CI checks pass.
  • I have have included screenshots if what the user sees will change
  • I have licensed the changes to Element by completing the Contributor License Agreement (CLA)
  • I will no longer force push to this branch

Matrix v1.17 removed the legacy text-matching mention push rules
(.m.rule.contains_display_name, .m.rule.contains_user_name,
.m.rule.roomnotif) from the base rule set (MSC4210), and Synapse is
about to stop serving them by default. Settings > Notifications built
its "Mentions and keywords" rows from those rule IDs, so against such a
server it lost both mention rows.

The section now renders .m.rule.is_user_mention and
.m.rule.is_room_mention, with the legacy rules as their synced rules:
servers that still serve them stay in sync, others get no writes. The
displayed state comes from the intentional rule alone, and the
intentional rule wins when the legacy rules disagree. The two legacy
user-mention rows collapse into one.

Adds unit tests for the new rows and the sync monitor, and Playwright
specs that run against Synapse with msc4210_enabled both off and on.
The first version made the intentional mention rules the rows everywhere,
which dropped the "Messages containing my display name" row and changed
the displayed state and sync direction for servers that still serve the
legacy rules. Users of such servers must see no change.

The legacy definitions, the sync monitor, the strings and the screenshot
baseline are back to what develop has. .m.rule.is_user_mention and
.m.rule.is_room_mention get definitions of their own and are only
rendered when the legacy rule they replace is absent from the user's
rules; otherwise they stay hidden and remain the legacy rows' synced
rules, as before.

Unit and Playwright tests now assert both server modes.
@CLAassistant

CLAassistant commented Sep 10, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Comment on lines +24 to +36
/**
* Record every failed request to the push rules API, so a test can assert that
* the client never writes to a rule the server does not have.
*/
function trackPushRuleErrors(page: Page): string[] {
const errors: string[] = [];
page.on("response", (response) => {
if (response.url().includes("/pushrules/") && response.status() >= 400) {
errors.push(`${response.status()} ${response.request().method()} ${response.url()}`);
}
});
return errors;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should prevent introducing a bug similar to #34999. Where Element Web tries to write rules that aren't served anymore and HS returns 404.

A failed push rule write now re-reads the rules from the server, so
that a settings tab left open across a homeserver upgrade recovers on
the next toggle instead of retrying against rows the server no longer
serves.

Tests: the two write tests for the legacy rows are merged into one;
unit tests cover the mid-session switch with the legacy write accepted
and with it rejected; the sync monitor test that will go away with the
legacy definitions is named as transitional, and a guard checks that
matrix-js-sdk does not add legacy rules as client defaults; a Playwright
test follows a preference across the switch.

Claude-Session: https://claude.ai/code/session_01BGRjcEDsBoUibL17XP98ww
@barodeur

Copy link
Copy Markdown
Contributor Author

CI seems to be failing because of a flaky test

Comment on lines +92 to +98
@@ -93,6 +93,9 @@ const RULE_DISPLAY_ORDER: string[] = [
RuleId.ContainsUserName,
RuleId.AtRoomNotification,
RuleId.ContainsDisplayName,
// Shown in place of the legacy rules above once the server no longer serves them
RuleId.IsUserMention,
RuleId.IsRoomMention,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
RuleId.IsRoomMention,
// Mentions
RuleId.IsUserMention,
RuleId.IsRoomMention,
// Legacy mentions rules, takes priority until the server no longer serves them
RuleId.ContainsUserName,
RuleId.AtRoomNotification,
RuleId.ContainsDisplayName,

We can put the new rules as default and put the legacy comment on the old ones

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread apps/web/src/components/views/settings/Notifications.tsx
await expect(userMentionRow.getByRole("radio", { name: "Off", exact: true })).toBeChecked();
await expect(userMentionRow.getByText(UPDATE_ERROR)).toHaveCount(0);

let rules = await fetchRules();

@florianduros florianduros Sep 14, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of fetching the push rules. I wonder if we should send a mention in a room and check that the notification is correct. It'll completely test that it's working from the settings to the room

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just modified the test in 1b2a04d so

  • for each notification configuration
    • a bot would
      • create a room
      • invite the user
      • then mention the room or the user
    • The notification is then verified as aria-label on the room row
┌──────────────┬──────────────────────────────────┬──────────────┬─────────────────┐
│  Room name   │   Settings state when the bot    │ Bot message  │ Expected label  │
│              │              sends               │              │                 │
├──────────────┼──────────────────────────────────┼──────────────┼─────────────────┤
│ defaults     │ server defaults, both rules      │ user mention │ 1 unread        │
│ user         │ Noisy                            │              │ mention         │
├──────────────┼──────────────────────────────────┼──────────────┼─────────────────┤
│ defaults     │ server defaults, both rules      │ @room        │ 1 unread        │
│ room         │ Noisy                            │ mention      │ mention         │
├──────────────┼──────────────────────────────────┼──────────────┼─────────────────┤
│ user off     │ user mentions Off, @room On      │ user mention │ 1 unread        │
│              │                                  │              │ message         │
├──────────────┼──────────────────────────────────┼──────────────┼─────────────────┤
│ room on      │ user mentions Off, @room On      │ @room        │ 1 unread        │
│              │                                  │ mention      │ message         │
├──────────────┼──────────────────────────────────┼──────────────┼─────────────────┤
│ user back on │ user mentions Noisy, @room On    │ user mention │ 1 unread        │
│              │                                  │              │ mention         │
└──────────────┴──────────────────────────────────┴──────────────┴─────────────────┘

Review feedback: rather than only reading the push rules back from the
homeserver, have a bot mention the user in a room after each change and
assert what the room list shows. The homeserver computes the unread and
highlight counts from the user's push rules, so this covers the whole
path from the settings page to the room, in both server modes.

The push rules are still read back once, to check which rules each
toggle wrote: the legacy rules along with the intentional ones while
served, and only the intentional ones otherwise.

Claude-Session: https://claude.ai/code/session_01BGRjcEDsBoUibL17XP98ww
@florianduros
florianduros added this pull request to the merge queue Sep 15, 2026
Merged via the queue into element-hq:develop with commit 976ce3a Sep 15, 2026
51 checks passed

This branch was successfully deployed

3 active deployments
EndToEndTests — 1b2a04dc Deployed Sep 14, 2026 by github-actions[bot]
Netlify — 1b2a04dc Deployed Sep 14, 2026 by github-actions[bot]
SharedComponentStorybook — 1b2a04dc Deployed Sep 14, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-Defect Z-Community-PR Issue is solved by a community member's PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Notification settings lose the mention rows once the server stops serving the legacy mention push rules (MSC4210)

3 participants