# 21-Jun-2026 — recipient-only mail routing, per-role notifications via the main save, live unread badg... [#293](https://github.com/nbarrett/ngx-ramblers/issues/293)

## [build 710](https://github.com/nbarrett/ngx-ramblers/actions/runs/27905275631) — [commit d77a308](https://github.com/nbarrett/ngx-ramblers/commit/d77a3088b04cd34d65534fc3da4898107bc95ab1)

_____

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:

- The component emits its pending changes (per role: no notification, notify the
assigned member, or notify a different address) via a pendingChanges output;
it no longer saves directly and has no own button.
- system-settings collects them (inboxRoleNotificationsPendingSave) and persists
the whole batch in save() via a single atomic write: PUT /aliases/notifications
applies every change to the committee config in one read-modify-write. The
earlier approach wrote each role separately and concurrently, so the writes
clobbered each other (lost update) and not every change stuck.
- Undo Changes / Exit Without Saving discard the pending edits and reload the
component via an incrementing refresh token (bubbled through
system-gmail-inbox-settings). New InboxRoleNotificationSetting model type
carries each pending change.

## 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

- The "verify project" step is no longer fatal. When Cloud Resource Manager
cannot resolve the project name (commonly because that API is not enabled on
the project), it records a skipped result and continues with the project
number, which Pub/Sub and Service Usage accept directly, rather than aborting
the whole setup.
- The setup result is transient run feedback instead of persisted state shown on
every page load. It appears only while a run is in progress or immediately
after one (setupStarted is then stripped from the URL so a refresh does not
re-show it), collapses to the "complete" banner on success (the per-step list,
including the skipped verify, is hidden), and shows only the failed step on
failure. The summary banner no longer duplicates a step that is already listed
below.

## 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.