Skip to content

v0.3.6

Choose a tag to compare

@karatektus karatektus released this 20 Oct 06:49
· 35 commits to main since this release
aba0130

Image: ghcr.io/karatektus/pl_mail:0.3.6

Several Gmail accounts import without failing on a duplicate key. With more than one Gmail account connected, mail one of them forwarded to another (or that both received) was merged into the first account's copy, and that copy's Gmail id was replaced with the second mailbox's. The first account then stored the message again, and the next import batch to reach it failed with duplicate key value violates unique constraint "uniq_message_gmail_id_account", was fetched again on every retry and used up the Gmail quota, so 429 rateLimitExceeded followed. A message another Gmail account holds is now left with that account and written down as such (app:gmail:skipped lists it as held by another Gmail account). Messages this already happened to stay listed twice. #196, by @karatektus.

A label a rule puts on new mail shows on its conversation. A rule with Apply label labelled the arriving message, but the conversation lost the label again in the same step, so it was missing from that label's list and from a tab filled by it. A message joining a conversation plMail already had was affected unless it was the first one that label was ever applied to on the account. Mail labelled before this update gets its conversation's label back the next time any label in that conversation changes. #185, by @karatektus.

A conversation no longer splits when its replies are synced first. When replies arrived before the message they answer, which is the usual case right after connecting an IMAP account (the Inbox is synced before Sent Items), each reply that found nothing to hang on started a conversation of its own, and the message that arrived later started another: a four-message exchange could be listed as three conversations, until app:backfill rethread was run. A message now also brings in the mail that already answers it, so a conversation ends up as one whatever order its folders are synced in. The conversation that was already in your list is the one kept, with its star, its tab and its labels. Conversations split before this update stay split until the next app:backfill rethread. Migration: one index on message, built on boot; on a large mailbox the first start after the update takes longer while it is built, and new mail waits for it. #176, by @phdelodder.

IMAP folders that never synced now do. On some servers, Dovecot among them, a folder's UIDVALIDITY (the number that says whether its message numbers still mean what they did) is above 2147483647, and plMail could not store a number that large: every sync of such a folder stopped with "value … is out of range for type integer" in the worker log, and its mail never arrived. Other folders of the same account were not affected. Migration: mailbox.uid_validity is widened from INT to BIGINT, which rewrites the small mailbox table once on boot; no stored value changes. Reversible: rolling back forgets a folder's UIDVALIDITY that only the wider column can hold, and the next sync records it again. #174, by @phdelodder.

Release notes after an update, and a feedback form. The first time you open plMail after it has been updated, a dialog lists what changed since the version you last saw: the highlights first, then every other change as a headline you can open. The ? in the top bar opens it again at any time, with a dot on it while there is something you have not seen, and the whole changelog is under Full changelog. It never opens on a fresh install, only after an update; if you would rather not be shown it, tick Don't show this after updates, and Settings → General → What's new turns it back on. Send feedback, in the dialog and under the ?, sends a mail from your own account, with your plMail version and, if you tick it, a screenshot of the page you were on; a copy stays in your Sent folder. It goes to feedback@pluetzner.de, or to APP_FEEDBACK_TO where that is set. Migration: everybody who already has an account is recorded as having seen 0.3.1, so their first look is every release since; nothing else changes in the schema. #166, by @karatektus.

Drafts from Outlook and Gmail open in the editor. A draft that arrived by sync (from Outlook or Gmail) was listed in its conversation, and clicking it gave a "Not found" page: it had been marked as already sent, which the editor refuses to open. It is now kept as the unsent draft it is, like one written in plMail. The visible side effect is that a conversation holding such a draft is no longer moved up the list by the draft's date, the same as for a draft written here. A draft you then send from Outlook or Gmail is recognised as sent, and no longer offered for editing. Migration: drafts already stored that the provider still files as drafts are made openable too; one that was sent elsewhere before the upgrade is left as it was. #164, by @Flouss.

Pull to sync on phones. Reaching for the button at the top of the screen is no longer the only way: pulling the mail list down from its top now syncs it, through the same sync as that button. Drag past the Release to sync indicator and let go, and the sync icon spins while the run lasts. It is for a finger on a phone-width screen only. A mouse, a trackpad and wider windows behave as before, a list scrolled down scrolls as it always did, and a pull while the sidebar is open does nothing. Reduced motion removes the indicator's slide. No schema change, no deployment change. #163, by @Flouss.

Notifications only for newly arrived mail. The notification on a phone or browser was sent for every change to a message — opening a conversation, marking it read, starring, archiving, a spam move, a copy of something you sent — and again a few minutes later when Outlook reported your own reading back, and it always read "New mail". A device switched on under Settings → Notifications is now told when mail arrives in the Inbox of a mailbox that has finished importing, and about nothing else. Spam, drafts, sent copies and an import in progress no longer notify, and neither does a message whose arrival time plMail cannot work out from its headers. Reminders from Calendar alerts and the update notice for administrators are separate pushes and unchanged. Apps that register through JMAP, and an Android phone through Firebase, are still told about every change: they ask what moved. Migration: one boolean column on jmap_push_subscription, false by default; devices registered through the in-app toggle (an id starting web-) are switched to the new behaviour once, so nobody has to turn notifications off and on. Reversible. #162, by @Flouss.

Outlook no longer reports moves that did not happen. Microsoft sends a message again whenever anything about it changes — plMail marking it read is enough — and each of those was written to the change log as a move into the folder the message was already in. Only a message whose folder actually changed is recorded now. The effect runs one way: a JMAP client, and an Android phone through Firebase, are told about fewer Email and Thread changes from an Outlook mailbox, and none that describe nothing. No migration. #162, by @Flouss.

Category tabs no longer add browser history entries. A tab switch, in the inbox or on an account's page, replaces the current entry, as Gmail does, so the browser's Back leaves the inbox instead of stepping back through every tab you looked at. The address still follows the tab. No migration. #161, by @Flouss.

Read and star changes made in Outlook sync to plMail. An Outlook account follows each folder with a change feed, and the feed was begun asking for a message's id, folder and Message-ID only. Exchange reports a change in the properties a feed asks for, so a message whose only change was being read, unread, flagged or unflagged was not reported at all: mail handled in Outlook, on a phone or by a rule stayed as plMail had last seen it. (The other direction, plMail to Outlook, was not affected.) The feed now asks for both. A feed already running keeps what it was begun with, so after the upgrade every Outlook folder is listed once more and followed from a new feed; the account shows as importing until that pass is done, which costs its listing pages and fetches nothing it already has. Each message listed is also compared with the server's read state and star, so the few that drifted while nothing was listening move to match the server: a message plMail has as read that Outlook still has unread goes back to unread, and the other way round, so a badge can go up as well as down. Migration: clears the stored change feeds of Outlook accounts; nothing else changes in the schema. #114, by @Flouss.

Fixed saving attachments to Paperless-ngx. The "Save to…" chooser listed your Paperless tags as folders to open, but a tag has nothing inside it, so clicking one only reloaded the same list and the dialog seemed to do nothing. A tag is now the choice itself: click it and the attachment is filed under it, or pick No tag to file it under none. The same goes for the folder a filter saves to. The upload itself was also refused ("No file was submitted") because it was sent in a form Paperless could not read; it now goes through. Paperless still turns down file types it cannot consume, a ZIP for one, and says so. Nextcloud, Google Drive, OneDrive, Dropbox, Immich and Google Photos choose exactly as before. No migration. #78, by @Flouss.

Mail read in plMail is marked read in Outlook. On Microsoft accounts, marking mail read (or starring it) is sent to Outlook by a background job, and that job used to work out the new state by reading the message back from plMail's database. When the job started a moment before the change had been saved, it read the old state and told Outlook the message was still unread: Outlook was touched, nothing changed, and an hour later the next sync brought the "unread" back into plMail. In a bulk mark-read several messages were affected. The job now carries what you chose. Archive, trash and label changes are unchanged, and a retried push still sends the current state rather than the original choice. Messages already stuck this way are not repaired; mark them read again. Gmail and IMAP jobs already carried the change themselves. No migration. #111, by @Flouss.

Fixed the message header in narrow panes. On a phone, or with the reading pane on the right and dragged narrow, the sender's address was cut mid-word across three lines while the date and the buttons took half the width. Below 36rem of pane width the date, the menu and the arrow now sit on a row of their own under the header, so the sender gets the full width. It follows the width of the pane, not of the window. Wider panes are unchanged. No migration.

Outlook mail moved in another app leaves its old folder here too. A message marked as spam (or archived, or deleted) in the phone's mail app was taken out of its old folder and put in the new one, but its conversation kept the old folder as well. The message sat in Spam while the inbox list went on showing it. A conversation now sits in exactly the folders its messages do, so the move shows in the list at the next sync. Conversations already left that way are put right the next time one of their messages changes folder. #121, by @Flouss.

Optional All Mail view (#57). Archived mail had no list of its own to be browsed in, only search. All Mail holds it, together with what arrived, what you sent and your drafts, as Gmail's All Mail does, leaving out only Spam and the bin. It is off until you switch it on with its eye in Settings → Mail → Labels; then it is a row beside Drafts for every account together, and a row at the top of each account's folders for that account alone. Archiving or snoozing from it leaves the conversation in the list. A conversation with several labels is listed once. No migration.

Outlook changes appear without a reload. Marking a message unread in Outlook on the web moved the unread counts in the sidebar and left the list showing the old row until the page was reloaded; moving or deleting one there did the same. A sync that only changed such a message ingested no mail, and "an account changed" was announced only after a sync had ingested something, so the open list was never told. It is told now, once per sync, and only when a row really changed: a server that agrees with what plMail already has says nothing. #122, by @Flouss.

Profile pictures use the full file picker. Settings → Profile showed a small grid of its own: the first couple of dozen images at the top of the service, with no albums, no people, no search, no way to scroll on. Each connection's button now opens the full picker, with "Use as profile picture" where a mail has "Attach", and one picture can be chosen at a time. A PDF, or a file the service says is over 4 MB, is shown greyed out rather than hidden; Immich and Google Photos do not report sizes, so a large photo from them is still refused after it is chosen, with the reason under the picture field. A scanned photograph from Paperless-ngx is now taken as the original — it used to arrive as the archived PDF and be refused. The setup wizard's profile step still has the small grid, because the picker would replace the wizard it is inside. No migration.

Option to always show remote images (#124). Settings → Mail → Images has one switch, Always show remote images, off until you turn it on. With it on, no message asks before loading its pictures; they still come through plMail's image proxy, so a sender learns that the message was opened but not your address. Mail in Spam stays blocked as before, and the senders you trusted one by one are kept for when it is switched off again. The choice lives in your user settings and a config backup carries it. No migration.

Category tabs in an account's own inbox (#55). Clicking an account in the sidebar opened a plain list, so on an Outlook account — or any account — the tabs were nowhere to be found unless you went back to the top-level Inbox. The categories were being worked out all along; that page just never drew them. Settings → Mail → Categories now has Tabs on each account, off until you turn it on: with it on, an account's page shows Primary, Social, Promotions, Updates, Forums and your own tabs for that account's mail; off, it stays the account's whole inbox in one list, as before. It is offered only while Inbox tabs is on, and a config backup carries it. The tabs on this page are plainer than the top-level Inbox's: no "new" pills, sender hints or unread tint (those add up every account), and no dropping a conversation onto a tab. No migration.

Single sign-on keeps you signed in. A single sign-on sign-in left nothing but the PHP session holding you in: a cookie that ends with the browser, kept in a file that is cleaned up after 24 minutes without a request and that goes with the container whenever the server is updated. Which is why it signed people out every few hours. It now sets the same 60-day remember-me cookie, stored in the database, so a lapsed session is picked up again without a visit to the provider. Accounts with two-factor authentication are still asked for their code when the cookie signs them back in. Everyone signed in through the provider before the upgrade signs in once more to get the cookie.

Gmail import includes all mail in the mailbox. plMail kept a Gmail message only when its headers named the account, and on a real mailbox (app:gmail:skipped) what it dropped was the account owner's own mail: 202 messages sent from "send mail as" addresses, 89 forwarded in from an address not connected here, 43 drafts and 4 addressed to the googlemail.com spelling of the address. A message in a Gmail mailbox is now that account's unless another connected account has a better claim to it; drafts arrive as drafts, and googlemail.com is read as gmail.com. Only chats are still left out. The import also fetches Spam and Trash now, which it said it did and never asked Google for — the reason an import could not reach Gmail's own total. Every Gmail account lists its mailbox once more after the upgrade to take these in, and shows as importing until that pass is done. A message that names nobody and that Gmail collected from another connected account is merged into that account's copy rather than listed twice. Migration: clears the record of skipped Gmail messages except chats, and the backfill markers of Gmail accounts. #90, reported by @karamanliev. The progress line also counts mail the import stored on another connected account — Gmail collecting a mailbox that is connected here too — so it no longer stops short of Gmail's total by that mailbox's size. Once everything is in, the import confirms it within about ten minutes instead of showing "Importing · 100%" for up to an hour, and the count no longer runs past a total that new mail has outgrown.

Spam and Trash are no longer processed. Mail arriving in either — from any provider, and now from a Gmail import — is no longer categorised by the language model, held back from the inbox tabs, read for parcels, invoices or meetings, or offered as a calendar event, and senders of spam no longer become contacts. A forged invitation in Spam stays out of the calendar. Read receipts filtered into Spam are still matched to the mail they are about. No migration.

French translation. Choose Français under Settings → General → Language, or when creating the first account. The whole interface is translated, including the admin panel, the validation and sign-in messages and the emoji picker's search; French also defaults to the 24-hour clock, as German does. Mail written in French is read as German and English mail already was, whatever language the interface is in: an appointment like "mardi prochain à 14h30" is offered for the calendar, a "facture" with its "net à payer" becomes an invoice card, and a "code de vérification" a login-code card. A slashed date such as 04/08/2026 is read day first for a French reader. No migration.

Customisable inbox tabs. The new Settings → Categories, in the Mail group, holds everything about the tabs: what sorts mail into them (the card that used to sit under General), which of the five built-in tabs are on, the tabs you make yourself, and the order of all of them — dragged, or moved with buttons from a keyboard or a phone. Primary is always first and always on. A tab you switch off takes no mail: the header rules, Google's labels and the assistant all file what would have gone there under Primary, and switching it back on re-sorts and brings it back. A tab of your own is filled by a filter whose action is File in category; mail filed there leaves the built-in tab it would otherwise be in, and the tab shows while it holds mail, takes drag-and-drop, and counts and badges like the others. A filter files Gmail mail too, whatever you chose about Google's own sorting. Only mail whose sender passes authentication is filed, so a forged "your parcel is waiting" never lands in a tab named after the real carrier; on a mailbox where plMail cannot tell which server vouches for the mail such a filter files nothing, and the filters page says so. Mail stays in a tab when the filter that put it there is changed or deleted. The Android app and other JMAP clients can draw the same tabs and move mail between them (urn:plmail:params:jmap:inboxtabs), and a re-sort that moves conversations between tabs now tells them — a phone used to keep showing a conversation where it had been. Re-sorting also reads only the mailbox being re-sorted, where it used to sort every user's mail once per account. Migration: one new table, a nullable column each on messages and conversations and two settings columns on users, added without rewriting any mail. With tabs on, every inbox page and sidebar-counts poll makes one more indexed read. #87, by @Flouss.