Commit Graph
1057 Commits
Author SHA1 Message Date
Linus Rath 9ee463084d Merge pull request #1150 from lucamzanon/feat/cross-account-copy
feat: copy messages to a folder of another connected account
2026-10-02 10:33:15 +02:00
Linus Rath 07625bc513 fix: show the translated check-your-Sent message for an unconfirmed send #1019
A send answered without an EmailSubmission threw a fixed English
message, which the composer showed as is in every language. It now
throws `SendUnconfirmedError`, and the composer shows the existing
`send_timeout` text, the same advice as for a send whose answer was
lost: check Sent before sending again.
2026-10-02 10:33:10 +02:00
Linus Rath d8f10f4fd6 Merge pull request #1148 from lucamzanon/fix/push-settings-refresh
fix(settings): check push again when the user comes back to the tab
2026-10-02 10:30:43 +02:00
Linus Rath 15839afec2 Merge pull request #1154 from dealerweb/feat/filter-all-messages
feat: a filter condition that matches every message
2026-10-02 10:30:42 +02:00
dealerweb 6da2bf9a43 fix: count the forwards one message can collect against the redirect limit
The rule editor added up the forwards of all enabled rules against the
server's redirect limit, which applies per message. Rules run in order,
and a rule that stops ends the script for its messages, so a rule that
forwards and stops locked forwarding in every other rule, although no
message can reach both.

Count the most forwards one message can collect instead, and warn above
the rule list when the order, a switch or a drag lets one message
collect more than the limit.

Builds on the stop fix: without it, "Stop processing" after "Delete
silently" or "Reject" writes no stop.
2026-10-01 16:33:51 +02:00
dealerweb c55b9f4929 feat: a filter condition that matches every message
The rule editor had no way to say "every message that arrives"; the
workaround was a condition like "From contains @", which only works if
you know it. "All messages" is now a condition of its own, at the end of
the field list. It takes no comparator and no value, writes Sieve's
`true` test, and combines with other conditions as any condition does.
The rule list shows it on its own. A rule with it is never applied to
the messages already in a folder: it would act on all of them.
2026-10-01 14:58:00 +02:00
lucamzanon f02dbf3a40 feat: copy messages to a folder of another connected account
With several accounts connected, mail could only be moved between
them (drag to another account's folder). There was no way to file a
copy in another account and keep the original where it is.

crossAccountMoveEmails and copyEmailAcrossAccounts take an optional
`{ keepOriginal: true }`. With it the source message is not destroyed,
and the store leaves the list, the open message and the selection as
they are; only the destination's folders are refreshed. Without it
nothing changes.

The message menu gets "Copy to account..." when more than one account
is connected. It lists the writable folders of the other accounts
(their folder lists are fetched when the menu opens) and copies the
selection, or the message the menu was opened on, through the new
copyEmailsToAccount action. That groups the messages by the login and
JMAP account they live in and refuses a copy into the account a
message is already in.
2026-10-01 12:53:14 +02:00
lucamzanon ce3b12aa21 fix(settings): check push again when the user comes back to the tab
The notification settings read the push state once, when they opened.
Push turned off meanwhile - in the browser's site settings, or from
another tab - still showed as on until the page was reloaded. The state
is now read again when the window regains focus or the tab becomes
visible, unless an enable or disable is in progress or has just failed.
A failed check reads as off instead of leaving the panel unchanged.
2026-10-01 12:43:40 +02:00
Linus Rath 410d6b3532 Merge branch 'main' of https://github.com/bulwarkmail/webmail 2026-10-01 11:52:29 +02:00
Linus Rath 26cbca73cb feat: let @ recipient mentions be turned off and announce them to screen readers #1134
Mentions change what Enter and Tab do right after `@word` and open a
list for everyone, so they get a setting next to the empty subject
warning (`recipientMentionsEnabled`, on by default, translated in
every locale). Turned off, the composer offers no candidates and the
editor keeps its keys.

While the list is open, the editor now carries `aria-autocomplete`,
`aria-controls` and `aria-activedescendant`, as the To/Cc
autocomplete does, so screen readers hear the list and the highlighted
name. Each row now leads with the `@name` that will be inserted,
followed by the full name and the address: for a surname-first name
such as "Nagy János" the inserted word is not what the name alone
suggests.
2026-10-01 11:10:04 +02:00
Linus Rath 90c8195a00 Merge pull request #1134 from dealerweb/feat/recipient-mentions
feat: mention a recipient by first name with @ in the message body
2026-10-01 11:06:00 +02:00
Linus Rath c0db46de87 fix: keep a restored forward's parts copyable and stop a send whose copy fails #1142
Three gaps in copying forwarded parts to the sending account:
- A composer stashed before its first autosave (pro tab move, unmount)
  lost the parts' owning account, because the snapshot and the restore
  dropped `sourceAccountId`. The restored forward then failed every
  save and send with blobNotFound.
- A failed copy was swallowed and the send went ahead, failing with the
  server's raw blobNotFound. It now stops with the existing "an
  attachment failed to upload" message and leaves the composer as is.
- Copies were recorded only after the whole loop, so a failure on a
  later part re-copied the earlier ones on every retry. Each part is
  now recorded as soon as it is copied.

Quoted inline images are only read at send time, so they are no longer
copied on every draft save, and at send only those the body still
shows are copied.
2026-10-01 11:05:54 +02:00
Linus Rath 182a9533bf Merge pull request #1142 from lucamzanon/fix/forward-parts-from-other-account
fix: copy forwarded parts to the sending account before saving or sending
2026-10-01 11:02:44 +02:00
Linus Rath 6bce3323a9 fix: keep previews that only start like a style sheet #1136
The leading-CSS strip emptied or cut ordinary previews:
"@media team, the brief is attached", "@Page can you check?" and
"@import the CSV ..." became empty, and "Reminder: your appointment
{date: 2026-10-01} is confirmed" became "is confirmed".

At-rule names now match in lower case only, only `@import` and
`@charset` end at a semicolon, an at-rule word with no block after it
is prose, and a selector colon must not be followed by a space.

The conversation view's collapsed cards, contact activity, the pro
folder tab and the list's row-height estimate now use `cleanPreview`
too, so they agree with the list rows. The test's invisible characters
are written as escapes.
2026-10-01 11:02:26 +02:00
Linus Rath 1a186f8962 Merge pull request #1136 from lucamzanon/fix/preview-leading-css
fix: skip a style sheet at the start of a message preview
2026-10-01 11:01:28 +02:00
Linus Rath e5755c821a fix: give the unified loading count a screen-reader label that is read #1140
An aria-label on a plain span is not reliably read out, so the count
now comes with a visually hidden label and the digits themselves are
hidden from screen readers. Also reuses the sidebar's existing accounts
selector instead of subscribing to the same list twice.
2026-10-01 11:00:20 +02:00
Linus Rath 54c1b0a2b1 Merge pull request #1140 from lucamzanon/feat/unified-loading-indicator
feat(unified): show how many accounts the unified mailbox holds while logins reconnect
2026-10-01 11:00:06 +02:00
Linus Rath 9765258fd7 Merge pull request #1138 from dealerweb/fix/list-scroll-top
fix: open another folder at the top of the message list
2026-10-01 10:59:55 +02:00
Linus Rath 5c74d716fc Merge pull request #1139 from dealerweb/fix/filter-rule-wrap
fix: wrap a filter condition's values in the expanded rule view
2026-10-01 10:59:50 +02:00
Linus Rath 4f48af6307 fix: spam siege restyle 2026-10-01 10:29:20 +02:00
lucamzanon cd9536dcf1 fix: copy forwarded parts to the sending account before saving or sending
A forward carries the original message's attachments, and the quoted
body's inline images, as part blobIds of the account that holds it
(replyTo.accountId, #1104). When the forward goes out through another
account - a From picked from another account's identities, or the
holding account has none of its own - the draft was saved and sent
through that other account, which cannot resolve those blobs: Email/set
failed with blobNotFound and the forward could not be sent.

Parts of the original are now tagged with the holding account. Before a
draft save or a send, any part owned by another account is downloaded
through that account's client and uploaded through the composing one,
once; later saves reuse the copy. Blob/copy cannot do it, as every login
is its own JMAP session. Quoted inline images are loaded through the
holding account's client as well. Forwards that go out from the holding
account keep referencing the original blobs exactly as before.
2026-10-01 04:43:49 +02:00
lucamzanon 717f18fc46 feat(unified): say how many accounts the unified mailbox holds while logins reconnect
After a load the logins other than the one on screen reconnect in the
background, and until they are all back the unified counts cover only
some of them. With many logins that lasts seconds, and a partial total
looks exactly like the whole one.

While that restore runs, the "All accounts" header shows how many logins
are already in the scope, e.g. "4/10", with a translated label for screen
readers. It goes away when the scope is complete or the restore is over -
a login that failed to reconnect does not keep it up.

`restoringAccounts` in the auth store marks the background restore; the
count itself is a pure function with its own tests.
2026-10-01 04:38:04 +02:00
Linus Rath 29c74c3b77 feat: sign in with an access token 2026-10-01 00:24:47 +02:00
dealerweb 9130ec7e14 fix: wrap a filter condition's values in the expanded rule view
The values of a multi-value condition were glued together as one
unbreakable run - the space around each "or" was a margin, not a
character the line could break at - so a rule with several addresses ran
out of its card. Each value is now an item of its own: they wrap between
each other under the first value, with "or" leading the next line and
still inside the condition's chip, apart from the "and"/"or" between
conditions. A single value too long for any line, in a condition or an
action, breaks inside itself instead of overflowing.
2026-09-30 23:40:13 +02:00
dealerweb 17a42c42fc fix: open another folder at the top of the message list
The list's scroll container outlives a switch of folder, so the next
folder opened wherever the last one was scrolled to - often halfway down,
or past its end. Opening another folder, tag, account, unified view or
plugin tab, or running another search, now starts the list at the top.
Refreshes, new mail and loading more keep the view and the position.
2026-09-30 23:13:27 +02:00
dealerweb 2b5110e0e4 feat: mention a recipient by first name with @ in the message body
Typing @ at the start of a word in the rich-text body offers the To and
Cc recipients (contact groups expanded) and inserts the chosen one's
first name as plain text. The name comes from the display name; with a
firstname.lastname@ address, the address tells which word of the name is
the first name, and without a name the address supplies it. A bare
initial keeps the mailbox name (@m.mustermann), and recipients who share
a first name get their full name. Bcc recipients are never offered -
naming one in the text would disclose them to everyone else.

The list only opens while a recipient matches, so Enter and Escape
otherwise keep their usual meaning. While it is open, Escape closes the
list rather than the composer, which handles Escape in the capture phase,
and the list closes when the editor loses focus.

Built on @tiptap/suggestion, at the version of the other TipTap packages.
2026-09-30 22:43:59 +02:00
lucamzanon 2d521d19b2 fix: skip a style sheet at the start of a message preview
Some servers build a message's preview from its HTML body without skipping
the <style> element, so a marketing mail's preview - in the list and in its
push notification - can read "@media screen and (min-width:600px){.hide{
display:none!important;…" and never reach the text.

Drop whole leading rules from the preview before showing it; a preview that
ends inside the style sheet was nothing but CSS and becomes empty, so the
usual "no preview" text shows instead. A rule only counts as CSS when its
block holds declarations or further rules, so prose with a brace in it
("the meeting is at {time} tomorrow") is left alone.
2026-09-30 21:35:20 +02:00
Linus Rath 2d2b0753be Merge pull request #1132 from lucamzanon/perf/multi-account-startup
perf: stop the startup request storm with many logins
2026-09-30 20:33:32 +02:00
Linus Rath ca7d5e22df fix: keep a late notification link from pulling you out of what you opened #1131
A notification link now waits up to 20 s for its login to reconnect.
If the user opened another folder or message, switched account or left
Mail in the meantime, the late link used to switch account anyway and
take them away from it. It now stays put.

When the account switch was overtaken by another one or failed, a
folder link was resolved against whichever folders were on screen, so
an Inbox link could open the wrong account's Inbox. It now stops there.
2026-09-30 20:33:08 +02:00
Linus Rath efe44e09cc Merge pull request #1131 from lucamzanon/fix/deep-link-late-login
fix: let a notification link wait for a login that is still reconnecting
2026-09-30 20:32:44 +02:00
Linus Rath 692be6aa9d Merge pull request #1095 from lucletoffe/fix/calendar-location-links
fix(calendar): surface the meeting link and open the location in maps
2026-09-30 20:32:44 +02:00
Linus Rath ac09146054 feat: turn pasted dash and numbered lines into lists in the composer 2026-09-30 20:00:01 +02:00
Linus Rath bfc8e0e52e fix: show only that folder's unread mail when its unread count is clicked
Clicking a folder's unread count turns on the unread filter and runs
the search. Since the search scope stopped following the open folder
(#788) its default is "all folders", and since #1082 that fans out over
every shared account too, so the Inbox badge listed unread mail from
every folder of every account the login reaches.

The badge now scopes the search to its folder. That scope moves with
the open folder, the way the search itself carries across folders
(#553); picking a scope in the dropdown or clearing the filters stops
it. Clicking the badge from a tag view or the Scheduled list now also
leaves that view, as clicking the folder does.
2026-09-30 19:49:13 +02:00
Linus Rath c081a51842 fix: keep blank lines when pasting plain text into the composer 2026-09-30 19:46:05 +02:00
lucamzanon a578150c2d perf(unified): fetch each login's folders once, all logins at once
The unified scope is rebuilt on every connection while a browser restores
its logins, on every background push, and on every unified browse,
load-more and search - and each build asked every login for its folders
again, one login after the other. With ten logins that was ten full
Mailbox/get per build and well over a hundred in the first seconds, and the
slowest build waited on the sum of every login's round trip.

Fetch the logins in parallel, and fetch each login's list once: a build
shares a fetch that is already in flight and reuses a finished one until
that login reports a change - the push handlers invalidate the login they
hear from, so a background push now costs one Mailbox/get instead of one
per login. A 60 s limit backstops a change whose push was lost.

A reused list is never written back into `accountMailboxes`: optimistic
updates land there directly, and publishing an older list over them would
snap a just-changed counter back.
2026-09-30 19:03:34 +02:00
lucamzanon 2d5756bff7 perf(push): reconcile push bindings instead of rebuilding them
The push effect re-runs every time a login connects, which while a browser
with many logins restores means once per account. Each run closed every
client's push and set it up again: the live streams were torn down and
reopened and every slow poll re-primed its baseline with a full state read
(Mailbox, Email, Calendar, CalendarEvent, SieveScript) - quadratic in the
number of accounts. With ten logins that was 21 stream openings and about
180 state-poll methods in the first seconds.

Reconcile instead: bind the clients that are new, unbind the ones that are
gone, and on an account switch re-bind only the two logins whose role
changed, handing the live stream to the newly active one when it has
none. Handlers are read through a ref at event time, so a binding never has
to be re-created because a callback changed identity.

The reconciliation is a pure function (lib/push-bindings.ts) with its own
tests; the effect only supplies the callbacks.
2026-09-30 19:03:34 +02:00
lucamzanon 1d82c59469 fix: resolve a folder link against the mailbox it switched to
Once a link had waited for its login and switched to it, the folder was
looked up at once, in a store that still held the previous mailbox's
folders or none at all: an inbox link opened on a mailbox that was just
switched to reported "this folder is no longer available". Wait for the
new mailbox's folder list to arrive before resolving the link against it.
2026-09-30 19:01:24 +02:00
lucamzanon 1c5dbbcd0b fix: let a link wait for a mailbox that is still reconnecting
A notification tapped with the app closed reopens it on a link that names
its mailbox (`?slot=`). The browser reconnects its logins one at a time,
so that mailbox is often not connected yet when the link is read: with ten
logins, one on the Gmail bridge came back at 3.3 s and the link was judged
at 1.8 s. It was declared unavailable - "sign in to that account to open
this link" - and the app stayed on whichever mailbox was active.

Wait for the login instead, and give up only when it is not one of this
browser's logins, its restore fails, or 20 s go by.
2026-09-30 19:01:14 +02:00
Linus Rath 7f5728fea2 Merge pull request #1101 from dealerweb/fix/wopi-base-path
fix: send the WOPI status and launch requests under the base path
2026-09-30 16:46:11 +02:00
Linus Rath 13b2ea3d19 fix: keep a previewed SVG attachment from running script in the webmail origin 2026-09-30 15:12:19 +02:00
Linus Rath d0a1d0183b Merge pull request #1127 from dealerweb/fix/preview-declared-type
fix: preview attachments whose declared type the browser cannot show
2026-09-30 14:57:33 +02:00
Linus Rath e127f5d24b fix: reuse the translated saving label on the event Save button #921 2026-09-30 14:23:33 +02:00
Linus Rath 7282adbb06 Merge pull request #921 from texchi2/fix/calendar-save-in-flight
fix(calendar): show in-flight state on Save, and surface the silent length-guard rejection
2026-09-30 14:23:09 +02:00
Linus Rath fa789a6d9f Merge pull request #748 from lucletoffe/fix/calendar-blank-cn-and-description
fix(calendar): stop writing blank participant names and empty descriptions
2026-09-30 14:21:20 +02:00
dealerweb 1512898cdb fix: preview attachments whose declared type the browser cannot show
The preview hands the attachment's blob to the browser's own viewer,
which picks the viewer from the blob's type and downloads anything it
cannot show - under the blob's UUID. It only corrected the exact
application/octet-stream declaration from the file name. Other
declarations kept the preview from working: IBM i (AS/400) systems send
every attachment as application/x-as400attachment, some senders use
application/x-pdf or force-download, and the octet-stream check was
case-sensitive.

Move the decision into previewBlobType(): a PDF, image, audio or video
preview takes the first type its viewer can show - declared, then
served, then the one the file name implies. Other previews keep the
declared type unless it is generic, and the name never yields a
script-capable type.
2026-09-30 14:20:20 +02:00
Linus Rath 1531dae7eb feat: create filter rules from the message right-click menu 2026-09-30 13:52:41 +02:00
Linus Rath 62e07608fd fix: stop the mail list hiding dividers between some rows 2026-09-30 00:21:21 +02:00
Linus Rath e6a565ee27 fix: bring the mail list back on tablet when the reading pane empties 2026-09-30 00:08:02 +02:00
Linus Rath 9d140fa762 Merge pull request #1052 from lucamzanon/feat/account-row-colours
feat: colour unified rows by account and give every tag a hue of its own
2026-09-29 23:30:24 +02:00
Linus Rath 308fb083a4 Merge pull request #1122 from sanitz/fix/week-allday-visible-week
fix(calendar): size the week view all-day area from the visible week
2026-09-29 23:24:42 +02:00