Commit Graph
487 Commits
Author SHA1 Message Date
Linus Rath f87be32388 fix: keep an empty draft subject empty so send still warns #1189 2026-10-07 20:22:51 +02:00
Linus Rath 1b9aeff4e5 Merge pull request #1188 from VasilisNtovolos/fix/shared-account-blob-downloads
fix: route shared-account blob downloads by message owner
2026-10-07 19:33:24 +02:00
Linus Rath 8889346371 feat: warn when a message's sender can't be verified 2026-10-07 19:04:15 +02:00
VasilisNtovolos bb3ef6d9fc fix: route shared-account blob downloads by message owner 2026-10-07 15:57:45 +03:00
Linus Rath 92f1973bcc feat: attach files from the Files app in the composer #1179 2026-10-06 15:18:58 +02:00
Linus Rath b51de3baaa feat: add or remove a label on all selected mails at once #1077 2026-10-04 20:54:32 +02:00
Linus Rath c317cd9679 fix: offer the shared account's folders first when moving its mail #1149 2026-10-02 23:04:28 +02:00
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
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
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
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
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 ac09146054 feat: turn pasted dash and numbered lines into lists in the composer 2026-09-30 20:00:01 +02:00
Linus Rath c081a51842 fix: keep blank lines when pasting plain text into the composer 2026-09-30 19:46:05 +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 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 affa999cd0 fix: report recipients the server rejects at send #1123 2026-09-29 20:49:40 +02:00
Linus Rath 324fb48312 fix: keep an opened mail in place in unread-first lists until another is opened 2026-09-29 20:28:39 +02:00
Linus Rath 0b3e47e3d2 fix: keep the composer body reachable in short reading panes #1114 2026-09-27 21:54:02 +02:00
Linus Rath 9ab2035113 fix: centre the unread dot on the sender avatar 2026-09-27 11:28:06 +02:00
Linus Rath 3ed484df35 feat: offer the code from sign-in mail as a copy chip in the list and reader 2026-09-27 11:19:48 +02:00
Linus Rath 7b25d3a95b fix: keep message list from jumping while attachment chips load 2026-09-26 20:24:23 +02:00
Linus Rath efdc6a1dc3 fix: keep a reply draft in its thread when it is re-opened or undone
Drafts were saved without In-Reply-To and References, and a draft
re-opens in compose mode, where the composer has no original message to
rebuild them from. A reply that was saved and re-opened, or sent and
brought back with Undo, therefore went out as a new conversation.

`createDraft` now stores the threading headers, the composer saves them
with every draft of a reply, and re-opening a draft carries them into
the composer for later saves and the send.
2026-09-26 16:06:28 +02:00
Linus Rath d583443292 fix: keep an open draft on its own account across an account switch
The composer took its client, identities and active account live from
the stores. Switching accounts with a draft open moved it: autosave
created the draft in the other account, and replacing the previous
save sent that account a delete for the old draft's id, which there
names an unrelated message. The composer now stays on the account it
was opened in; the other account's identities and client are ignored
until it is closed.
2026-09-26 15:37:18 +02:00
Linus Rath 928633d087 fix: close remote-content gaps in the print view and the external-URL checks
Printing a message opened an about:blank popup that carries the app's
CSP (`img-src https:`), not the message iframe's strict one, so with
remote content blocked anything the DOM walk missed still loaded when
printing. The print document now carries the iframe CSP and
`no-referrer`.

The DOM walk itself missed several spellings the browser still loads:

- backslashes, which the URL parser reads as slashes (`/\host`,
  `https:\host`), and `https:host` without slashes
- an escaped `url` keyword in a style attribute (`\75 rl(`)
- `image-set()` with plain strings
- an unclosed `url(` at the end of a style attribute
2026-09-26 15:11:46 +02:00
Linus Rath 6e94b1a330 fix: treat image-map links like anchors so they cannot keep the opener
`applyNewTabToAnchor` and the viewer's click interceptor only looked at
`<a>`. An `<area>` in an image map kept the target and `rel="opener"`
its sender wrote and took the browser's default click, so the opened
page held `window.opener` and could navigate the webmail tab (reverse
tabnabbing, top navigation in Firefox). `<area>` now gets the same
rel/target treatment and goes through the same click gate, in the
desktop viewer and in the conversation view.
2026-09-26 14:53:40 +02:00
Linus Rath d13db4c756 test: give the conversation view remote-block test room to import 2026-09-26 14:51:40 +02:00
Linus Rath 3ac2ab1d97 fix: keep remote images in a quoted reply from loading in the composer
The quoted original in a reply or forward renders in the app document
(a shadow root), where the viewer's iframe CSP does not apply. Opening
a reply therefore loaded every remote image and background of the
original, firing the trackers the viewer had just held back.

The quote's node view now parks remote references in
`data-bulwark-remote-*` attributes for display and restores them when
edits are synced back, so the sent reply still quotes the original as
written. Remote content shows when the policy is `allow` or the sender
is trusted.
2026-09-26 14:51:14 +02:00
Linus Rath 809db3b38d fix: block remote content in the mobile conversation view
The conversation view used on phones built its own sanitizer hook that
only caught `<img src>` starting with a lowercase scheme and inline
style `url()`, and its iframe CSP always allowed http(s) images. With
remote content blocked (the default `ask` policy) uppercase schemes,
srcset, `<source>`, video posters and background attributes still
loaded, so trackers fired when a phone user opened a thread.

The view now uses the desktop viewer's `blockExternalResourcesOnNode`,
and the strict iframe CSP is shared as `emailIframeCsp()` and applied
whenever remote content is blocked, together with `no-referrer`.
2026-09-26 14:45:20 +02:00
Linus Rath f0aa0eedab fix: show what a mailto unsubscribe sends and limit it to one recipient
A newsletter's List-Unsubscribe `mailto:` was sent from the user's own
account as written: every `to=` field became a recipient, and subject
and body were the sender's choice. The confirmation said only
"Unsubscribe from this sender?", so one click could mail
"I resign" to someone's boss and HR.

The unsubscribe now goes to the single address in the URL (a list of
addresses is refused and `to=` is ignored), the subject is kept on one
line and both subject and body are capped. Both confirmations show the
recipient, subject and body before anything is sent.
2026-09-26 14:41:14 +02:00
Linus Rath ad84588f6a fix: keep an SVG attachment thumbnail from running script in the webmail origin
Attachment thumbnails were object URLs typed with the sender's MIME
type. Object URLs share the webmail origin, so an `image/svg+xml`
attachment opened from its thumbnail ("Open image in new tab", or
dragged to the tab strip) ran the sender's script as the webmail in
Chromium and Firefox. The drag-out URL and Files thumbnails had the
same shape.

`fetchBlobAsObjectUrl` now re-types every blob to an inert type, and
image thumbnails in mail and Files go through `imageBlobUrl`, which
hands an SVG over as a `data:` URL: it still renders in the `<img>`,
but opened on its own it gets an opaque origin.
2026-09-26 14:30:19 +02:00
Linus Rath daf67850d2 fix: reply from the receiving account's identity in the unified inbox #1104 2026-09-26 12:31:29 +02:00
Luis Felipe Marzagao aef8ed2b9e fix: update dark/light mode titles and add translations for multiple languages 2026-09-24 23:07:15 -03:00
Linus Rath 3823d2da17 fix: sign in and show addresses on internationalized domains #1100 2026-09-24 21:15:22 +02:00
Linus Rath 1c00cf75cb fix: use the from override as envelope sender where the server allows it #1009 2026-09-23 19:58:26 +02:00
Linus Rath 53dff446a2 Merge pull request #1032 from douwezijlstra-frl/fix/hover-tag-quick-action
fix: open the tag picker from the labels hover quick-action
2026-09-23 19:16:55 +02:00
Linus Rath 7e5b996ee6 feat: open office attachments in mail read-only in the document editor #1047 2026-09-23 19:01:47 +02:00
Linus Rath a69d8a71e9 fix: load attachment chips lazily instead of in the message list #1089 2026-09-23 18:09:35 +02:00
Linus Rath bea0d00053 fix: refuse attachments over the server's size limits before uploading
The composer uploaded any file and learned about `maxSizeUpload` (per
blob) or `maxSizeAttachmentsPerEmail` (per message) only from a failed
upload or a failed send. Files over the per-file limit, and files that
would push the message past the per-message limit, are now skipped
with a message naming the limit. The limits come from the account the
message is composed in.
2026-09-22 21:32:04 +02:00
Linus Rath bb60505530 fix: add the identity's Bcc addresses when sending with it 2026-09-22 21:27:31 +02:00
Linus Rath bab19898d9 fix: cap scheduled send at the 7 days Stalwart actually accepts
Stalwart advertises `maxDelayedSend` as a fixed 30 days, but its MTA
rejects any hold beyond `futureRelease`, 7 days by default. Scheduling
a message 8 to 30 days ahead passed the picker and then failed with
"501 5.5.4 Requested hold time exceeds maximum of 604800 seconds".

On Stalwart the advertised 30 days now counts as 7. When a submission
is still rejected for its hold time, the limit from the error message
replaces it for the session, and the composer shows the translated
"later than the server allows" message instead of the raw SMTP reply.
2026-09-22 20:31:53 +02:00