21-Jun-2026 — recipient-only mail routing, per-role notifications via the main save, live unread badg... #293

build 710commit d77a308


Several fixes and simplifications to the Gmail inbox, found while testing the Pub/Sub setup and per-role notifications on staging.

Inbound mail routing (bug fix)

Incoming messages were filed under every committee role whose known addresses matched any address on the message, including the sender. A message sent from an address that appears in another role's Inbound Forwarding recipients was therefore filed under that role as well, producing duplicate conversations: a test sent From nick.barrett36@gmail.com To nick.barrett@ngx-ramblers.org.uk landed under both support-representative (correct, its role address) and info (wrong, because the sending address is in info's forwarding recipients list).

Routing now matches on the recipients only (To/Cc), never the sender, for both the inbox and spam paths, so a message is filed only under the role(s) it was actually addressed to. New helper messageRecipientEmails; processGmailMessageIds and pollSpamForConnection use it in place of messageAddressEmails.

Per-role notifications use the page's main Save / Undo, saved atomically

The role-mailboxes step had its own "Save notification settings" button that wrote immediately via dedicated endpoints. It now defers to the system settings page's existing Save / Save settings and exit / Undo Changes, following the same membersPendingSave pattern already used for member edits:

Header unread badge updates live

The header inbox badge subscribed to the new-message and thread-updated websocket events but trusted the count carried in the broadcast, which was computed inconsistently (global unread for new mail, per-member for reads) and so was never right for every connected member; it also ignored events for a role that currently showed zero unread. The badge now re-fetches this member's own scoped counts (debounced) whenever a relevant inbox event arrives, so it reflects new mail (including a role going from zero to one) and the member's own read state without a full page refresh.

Google Cloud setup is tolerant of a missing Resource Manager API, and quieter

Push delivery applies on selection

Choosing "Push - real-time via Pub/Sub" on a connected Gmail now applies the watch automatically using the topic from the Google Cloud setup. The manual Pub/Sub topic field and the separate "Apply push" button are removed, replaced by a one-line read-only confirmation of the topic in use. If no topic is set up yet, delivery stays on polling with a prompt to run the Google Cloud setup first. Dropped the now-dead pushUrl field and an unused logger.