Second half of the interface refresh: stacked on #59, merge that one first.
Shared and group mailboxes: when someone shares folders with you, or you're in a group with its own mailbox, the account menu gets a "Mail to show" list. Picking one shows that mail in place of yours, with an accent bar across the top and a "Back to my mail" button. Only accounts that actually answer Mailbox/get are offered, because Stalwart lists every capability on every shared account.
More than one account signed in: "Add account" in the account menu asks for an address and password. You can switch between accounts, "Sign out" ends only the one in front, and "Sign out of all accounts" ends the lot. Up to five accounts, all on the same Stalwart server.
New mail in the other accounts: each account in the menu shows its Inbox unread count, and the avatar gets a dot. While the app is open, a desktop notification names the account.
Background push for every account: with "Notify me even when ihasmail is closed" on, each signed-in account gets its own push subscription. A notification shows whose mail it is, and opening it switches to that account. This also fixes background notifications landing on "That folder no longer exists" instead of opening the message.
Strings: 17 new, translated in all ten catalogs (6 shared mail, 8 account switcher, 3 unread). Checks run: typecheck; tests (web 1549, server 257); build; i18n parity at the main baseline. Also checked in Chrome against the mock with two users: add, switch, unread count and dot, and signing out of one.
Second half of the interface refresh: stacked on #59, merge that one first.
- **Shared and group mailboxes**: when someone shares folders with you, or you're in a group with its own mailbox, the account menu gets a "Mail to show" list. Picking one shows that mail in place of yours, with an accent bar across the top and a "Back to my mail" button. Only accounts that actually answer `Mailbox/get` are offered, because Stalwart lists every capability on every shared account.
- **More than one account signed in**: "Add account" in the account menu asks for an address and password. You can switch between accounts, "Sign out" ends only the one in front, and "Sign out of all accounts" ends the lot. Up to five accounts, all on the same Stalwart server.
- **New mail in the other accounts**: each account in the menu shows its Inbox unread count, and the avatar gets a dot. While the app is open, a desktop notification names the account.
- **Background push for every account**: with "Notify me even when ihasmail is closed" on, each signed-in account gets its own push subscription. A notification shows whose mail it is, and opening it switches to that account. This also fixes background notifications landing on "That folder no longer exists" instead of opening the message.
Strings: 17 new, translated in all ten catalogs (6 shared mail, 8 account switcher, 3 unread). Checks run: typecheck; tests (web 1549, server 257); build; i18n parity at the main baseline. Also checked in Chrome against the mock with two users: add, switch, unread count and dot, and signing out of one.
jcoffey-dev
changed target branch from feature/interface-refresh to main2026-10-20 20:51:58 +00:00
A group's members, and anyone a folder was shared with, could reach that
mail over IMAP but not here: Mail read only the reader's own account.
Calendars, contacts and files already list other people's shares beside
the reader's own; Mail now offers them too.
Such an account is listed under "Mail to show" in the account menu and
opens in place of the reader's own mail. Only accounts whose Mailbox/get
answers with a mailbox are offered: Stalwart advertises every capability
on any shared account, so a colleague who shared one calendar would
otherwise appear with mail. That is what sank the old account switcher,
which moved the whole app; this switches Mail alone.
While one is in view:
- a bar in the palette's accent names it, with "Back to my mail", and
the tab title names it;
- every folder in it shows (subscriptions are the reader's own);
- calendars, contacts, files, settings and push stay the reader's own;
- writing a message uses that account's identities;
- it is polled every minute, since push covers only the reader's own
account, and new mail in the reader's own inbox is still announced.
Losing the account (removed from the group, share withdrawn) takes the
reader back to their own mail with a notice. A message being written
blocks the switch.
New strings: 6, in all ten catalogs.
Someone who looks after several mailboxes of their own can now keep
them all signed in in one browser and move between them from the
account menu, without signing out.
The session cookie is unchanged: it is the account in front, and every
request is answered with it. The others ride in a second cookie,
<name>_more, as a list of their own session cookies. Each session stays
its own -- sealed credential, expiry and "this is my device" -- and
nothing about one is read through another. At most 5 in all, all on
the same Stalwart server.
- Add account (account menu) asks for the address and password in a
small dialog. Refused, and the front stays, when the account is on
another server or 5 are open. The same account again just comes to
the front.
- Switching (POST /api/auth/accounts/<id>/front) swaps it into front;
the web app clears what it cached for the previous account and
reloads. A message being written blocks the switch.
- Sign out ends only the account in front, and the next one comes
forward; Sign out of all accounts ends every one.
- GET /api/auth/accounts lists them, the front first, and says whether
one more may be added.
The mock can sign in a second user (MOCK_SECOND_USER/PASS) for the
server tests; the client tests cover listing, switching and both
sign-outs.
New strings (8, translated in all ten catalogs): "Add account", "Add an
account", "Sign out of all accounts", "Both accounts stay signed in
here; switch between them from this menu.", "Working…", "That account
couldn't be added.", "You can't add more accounts here.", and the
other-server refusal.
With more than one account signed in, mail arriving in one that isn't
in front went unseen until someone switched to it.
- GET /api/auth/accounts/unread answers the Inbox unread count of each
account not in front, asked through that account's own session, kept
a minute per account.
- The web app asks every two minutes while another account is signed
in. The account menu shows each one's count beside its name, and the
avatar carries a dot when any of them has unread mail.
- When a count rises while the app is open and desktop notifications
are on, a notification names the account ("New mail for
[email protected]"); clicking it switches to that account. An
account seen for the first time doesn't notify: its mail was already
there.
New strings (3, translated in all ten catalogs): "New mail for {name}",
"Unread in the Inbox: {count}", "Account: new mail in another account".
First of two for notifications with the app closed, for every
signed-in account. No behavior changes yet.
A JMAP push subscription belongs to whoever signs the request, so an
account that isn't in front can only have one registered, verified and
renewed through its own session. POST /api/auth/accounts/<sessionId>/jmap
forwards to Stalwart as that session, when it is one of this browser's
other accounts, and only for PushSubscription/get and /set, Mailbox/get,
and Email/set limited to keywords and mailboxIds updates (what a
notification's Archive and Mark read need). Anything else is 403; the
account in front, or a session this browser doesn't hold, is 404. The
browser already holds the session, so nothing new becomes reachable.
On the web app side the push helpers (list, create, extend, destroy,
verify) take a JMAP caller, defaulting to the account in front exactly
as before, and lib/notify/otherAccount gives the caller for another
account through the route.
Tests: the allowlist, the route end to end with two accounts, and the
caller. No new strings.
Turning on "Notify me even when ihasmail is closed" now registers this
browser in every signed-in account, each through its own session (the
route from the previous change), and renewal keeps them all current.
GET /api/auth/accounts says which mail account each session is, so the
subscription can name it. The remembered endpoint is kept per account,
and survives the clean-up that follows switching accounts.
The service worker is told who the other accounts are. A push for one
of them is titled with that account's address, shown even while a tab
is focused (the tab only shows the front account's mail), and its Mark
read and Archive act through that account's session. Clicking it brings
that account to the front and opens the message. While push is on, the
tab's own polling of other accounts stops notifying, so nothing arrives
twice.
Signing out of one account removes this device's subscription there
only; signing out of all, or turning push off, removes every one.
Also fixes where every background notification opened: the worker
linked to /mail/inbox/<thread>, and the route takes a mailbox id there,
so a click landed on the inbox list with "That folder no longer exists".
The worker now gets each account's inbox id and links to the message.
No new strings.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Second half of the interface refresh: stacked on #59, merge that one first.
Mailbox/getare offered, because Stalwart lists every capability on every shared account.Strings: 17 new, translated in all ten catalogs (6 shared mail, 8 account switcher, 3 unread). Checks run: typecheck; tests (web 1549, server 257); build; i18n parity at the main baseline. Also checked in Chrome against the mock with two users: add, switch, unread count and dot, and signing out of one.
With more than one account signed in, mail arriving in one that isn't in front went unseen until someone switched to it. - GET /api/auth/accounts/unread answers the Inbox unread count of each account not in front, asked through that account's own session, kept a minute per account. - The web app asks every two minutes while another account is signed in. The account menu shows each one's count beside its name, and the avatar carries a dot when any of them has unread mail. - When a count rises while the app is open and desktop notifications are on, a notification names the account ("New mail for [email protected]"); clicking it switches to that account. An account seen for the first time doesn't notify: its mail was already there. New strings (3, translated in all ten catalogs): "New mail for {name}", "Unread in the Inbox: {count}", "Account: new mail in another account".