Commit Graph
2684 Commits
Author SHA1 Message Date
Linus Rath 76c0bd1d41 test: save a subject-less draft by closing, as closing now saves without asking 2026-11-04 09:08:09 +01:00
Linus Rath 2b2b7b8298 fix: show every attachment in the composer, with type icons and a total, failed and uploading files first
The attachment list test now expects every chip and a bounded, scrolling row instead of three chips and a +N counter.
2026-11-04 09:01:04 +01:00
Linus Rath 69ec26fe33 fix: remove a recipient whole on the second Backspace instead of turning it into text
The mention test's Backspace check now expects the chip to be selected, not put back into the field as text.
2026-11-04 08:43:29 +01:00
Linus Rath 1fdfffad45 fix: read the send hook count safely in the composer's Options menu 2026-11-04 08:42:39 +01:00
Linus Rath 472ee53d88 fix: insert pasted and dropped images without a dialog, with an optional Add description chip
The image description tests now open the dialog from the chip on the image, since a drop no longer opens it by itself.
2026-11-04 08:36:25 +01:00
Linus Rath ad0b238e3d feat: open a reply under the message it answers, which stays readable above it 2026-11-04 08:32:05 +01:00
Linus Rath 7517b024ec fix: make Discard on the Continue draft bar delete the draft, with Undo, and reach it by keyboard 2026-11-04 08:23:01 +01:00
Linus Rath d98132b8e1 fix: close every composer the same way, keeping the draft with Draft saved and Discard
X, Escape, clicking another message and Escape in a pop-out window
now all save the draft and close, with a "Draft saved" toast that has
Discard (with Undo). The Save or discard question shows only when that
save fails. A Reply or Forward closed before the user changed anything
leaves no draft. Undo send being on by default no longer asks "Send
without a delay?" on servers without delayed sending; only a delay the
user picked does.

Changed test assertions: the close-guard, priority, account-switch,
threading, sign-out, mention, draft-attachments, discard-uploads and
save-pending-attachments tests reached saving or discarding through the
Save or discard dialog on close, which now only appears after a failed
save. They now close directly, or use the Discard button, or check the
failed-save question. The send-delay test marks its delay as chosen.
2026-11-04 08:22:56 +01:00
Linus Rath 8570f678e2 Merge branch 'feat/do-ux-compose-windows' into feat/do-ux-compose-fixes
# Conflicts:
#	components/email/email-composer.tsx
2026-11-04 07:42:11 +01:00
Linus Rath 17bf3c4e6f Merge branch 'feat/do-ux-compose-send' into feat/do-ux-compose-fixes 2026-11-04 07:41:26 +01:00
Linus Rath ad7ff4fc6b fix: label the Attach from Files dialog's X as Close 2026-11-04 07:37:26 +01:00
Linus Rath 48df0db1af feat: list To and Cc in the header of a forwarded message 2026-11-04 07:34:29 +01:00
Linus Rath e6f8c7a241 fix: show who a quick reply goes to and offer the full editor by name
The quick reply tests find the box by its new placeholder key, which
names the recipients instead of "Write a quick reply...".
2026-11-04 07:25:05 +01:00
Linus Rath 242d0bacfc fix: keep toasts at the bottom instead of over the composer's close button 2026-11-04 07:16:24 +01:00
Linus Rath 165c949960 feat: save a message as a template with just a name, and show each template's name once 2026-11-04 07:13:05 +01:00
Linus Rath 1b13b4ce88 feat: show Offline in the composer and keep the message when Send can't go out 2026-11-04 07:12:24 +01:00
Linus Rath 43c9ef9ad1 fix: name the TLS-only delivery option plainly and point to encryption add-ons 2026-11-04 07:11:03 +01:00
Linus Rath 0ced5482d5 feat: list your contacts in the composer's recipient picker, with the directory on its own tab 2026-11-04 07:03:15 +01:00
Linus Rath 0c5ba654c6 fix: show the name only on recipient chips, with the address on hover
The autocomplete, chip-drag and paste tests found chips by their old
"Name (address)" text; they now find them by the "Name <address>"
tooltip, which is where the address went.
2026-11-04 06:56:08 +01:00
Linus Rath 7d2859e064 feat: keep undo send in the pop-out or quick compose window until the time is over 2026-11-04 06:44:56 +01:00
Linus Rath 781013cc44 feat: close the composer as soon as Send is pressed and show Sending, then Sent with View 2026-11-04 06:41:51 +01:00
Linus Rath 80321e21a2 fix: explain replying from the received address in plain words
The admin-locks test finds the row by its label key, which moved to the
compose_words namespace with the new description.
2026-11-04 06:26:28 +01:00
Linus Rath c0300bcb7f feat: keep the formatting toolbar in one row, with the rest behind More formatting 2026-11-04 06:24:28 +01:00
Linus Rath 08dcff789f feat: add a full screen button to the composer, Escape brings it back 2026-11-04 06:24:27 +01:00
Linus Rath 461a7daf4f feat: let a composer move to its own window from a browser tab, as in Gmail 2026-11-04 06:24:26 +01:00
Linus Rath 007b007302 feat: open the schedule dialog from Send's arrow and say when a scheduled message goes 2026-11-04 06:22:53 +01:00
Linus Rath b425f4b211 feat: turn undo send on by default with 5 seconds
The policy-defaults test now expects the new build default of 5 seconds
for an invalid admin value, and the settings-search tests look for the
row under its new label "Undo send".
2026-11-04 06:18:29 +01:00
Linus Rath 8720ab6e1a feat: ask about everything before sending in one dialog, and allow sending without text
Tests changed: the "asked once" tests reached the second check through
a second dialog, which no longer exists, so they now stop the send at
"Send this message?" instead. The empty-body test asserted the error
under the editor; an empty body now asks instead of blocking Send.
2026-11-04 06:12:23 +01:00
Linus Rath 94bb55765d fix: give pop-out and quick compose windows the main composer's layout, with Send bottom right and Discard as a bin 2026-11-04 06:04:28 +01:00
Linus Rath f9503570ca Merge branch 'feat/desktop-offline' into feat/do-key-fixes
# Conflicts:
#	components/settings/app-lock-settings.tsx
#	stores/auth-store.ts
2026-11-04 04:16:14 +01:00
Linus Rath 88d64b7f95 fix: protect the keys to the mail on this device for real
The review found the store key readable from a copy of the disk. The
desktop app only added its OS layer with the lock on, the record from
before a lock stayed in LevelDB and opened the mail with no PIN, any
frame could unwrap the PIN record and guess at it, a lock left the key
in the renderer's memory, and a store waiting after a refused login went
to whoever signed in next under the same username@host.

- The desktop app wraps the store key with safeStorage, lock or no lock.
  A record without the layer gets it at its next open. A refusal is said
  out loud, and only a computer with no key store goes without, which
  settings say. So do they for a browser, which has no such layer.
- A record that gained layers, and every record after a changed PIN, asks
  for a new key: `sqlite3_backup` into a new file under a new key while
  the store keeps working, one write to the record, then the old file
  goes. A changed PIN makes a new lock key, since the old PIN's record
  stays on disk too. The passkey and OS unlock go with the old key.
- `wrap` and `unwrap` come through `claim()`, and the key under the PIN
  takes a secret from main, one counted trip per guess.
- A lock on the desktop ends the renderer rather than reload in it.
- A sign-in compares the waiting store's JMAP account and server first,
  and wipes on a mismatch. The account id keeps its form: every store,
  setting and server record of an account is keyed by it.
- The 30 days also run in an app that stays open.

Changes an existing assertion: turning the lock off now keeps the
safeStorage layer, by decision, so that test expects it to stay.
2026-11-04 04:15:31 +01:00
Linus Rath be8aba2ae7 Merge remote-tracking branch 'origin/main' into feat/desktop-offline
# Conflicts:
#	stores/auth-store.ts
2026-11-04 03:42:18 +01:00
Linus Rath 484ce34f5d Merge branch 'feat/do-browser-signin' into feat/desktop-offline 2026-11-04 03:19:32 +01:00
Linus Rath cb9a5ac810 Merge branch 'feat/do-edit-attachments' into feat/desktop-offline 2026-11-04 03:16:35 +01:00
Linus Rath 97b3ddefae test: cover what the page tells the worker about a quiet login 2026-11-04 03:13:59 +01:00
Linus Rath 8cb218ce3c fix: apply email notifications off and do not disturb per login, not to every login's mail 2026-11-04 03:13:59 +01:00
Linus Rath 09d6bff12a fix: say push isn't available in the demo instead of offering a switch that fails 2026-11-04 03:13:58 +01:00
Linus Rath 8b4fb72357 fix: say in plain words why push could not be turned on or off 2026-11-04 03:13:57 +01:00
Linus Rath 37d0200e43 Merge branch 'feat/desktop-offline' into feat/do-browser-signin 2026-11-04 03:11:34 +01:00
Linus Rath 81d6948240 Merge branch 'main' of https://github.com/bulwarkmail/webmail 2026-11-04 03:08:16 +01:00
Linus Rath 97c0bd633a Merge branch 'feat/desktop-offline' into feat/do-edit-attachments
# Conflicts:
#	lib/desktop/languages.ts
#	locales/ar/common.json
#	locales/ca/common.json
#	locales/cs/common.json
#	locales/da/common.json
#	locales/de/common.json
#	locales/en/common.json
#	locales/es/common.json
#	locales/et/common.json
#	locales/fa/common.json
#	locales/fr/common.json
#	locales/he/common.json
#	locales/hu/common.json
#	locales/it/common.json
#	locales/ja/common.json
#	locales/ko/common.json
#	locales/lv/common.json
#	locales/mn/common.json
#	locales/nb/common.json
#	locales/nl/common.json
#	locales/pl/common.json
#	locales/pt/common.json
#	locales/ro/common.json
#	locales/ru/common.json
#	locales/sk/common.json
#	locales/th/common.json
#	locales/tr/common.json
#	locales/uk/common.json
#	locales/vi/common.json
#	locales/zh-TW/common.json
#	locales/zh/common.json
2026-11-04 02:40:47 +01:00
Linus Rath ab21bf2df3 fix: keep a desktop sign-in in the browser's page until the user says yes
The hand-off parked a password before its question, for up to five
minutes, and on a shared computer another local user could ask for
someone's sign-in with a link of their own. Now nothing leaves the page
before the yes, and the instance has a switch for such machines.

- `/check` tests a password and keeps nothing, `/park` runs on the yes and
  hands over tokens wherever the server allows, a password as a last
  resort
- `/discard` and a timer take out what SSO has to park first, and a no
  revokes the refresh token, and the shared store's TTL is the real expiry
- `DESKTOP_BROWSER_SIGN_IN`, default on, closes the routes, the page's
  hand-off and the app's button; the question says which device asked
- tokens from a typed password stay a password account under the name
  typed (#1432)
- redeem refuses a server of the user's own when the device's rule says
  no
- the park budget is reserved before going upstream, and only failures
  count
- "Not now" ends the flow in the app at once, and the question names the
  account

`desktop-handoff-routes.test.ts` now tests the two-step flow against the
real budgets. `sso-start-route.test.ts`'s config mock answers the new
setting, and the SSO test's expected answer has the account in it.
2026-11-04 02:40:37 +01:00
Linus Rath d31d0af560 fix: say what an open program still holds, keep forwarded parts under their rules, and let main decide what a sent draft leaves 2026-11-04 02:36:48 +01:00
Linus Rath 02a900ef07 Merge commit 'dc8482ace' into feat/do-browser-signin 2026-11-04 02:11:07 +01:00
Linus Rath 71aaadef50 fix: run plugins' compose check in quick compose, keep a composer with one owner, and bring it back after a crash
The first version of quick compose skipped `onBeforeCompose`, a composer
moved back could end up open in both windows, a reply moved back under
Pro flagged the selected message, and a crashed pop-out came back blank.

- A new message in a pop-out runs `onBeforeCompose` with the same result
  as the main window: a plugin that stops it closes the window.
- The way back has two steps, so a pop-out that gave up can't leave a
  second owner. A handoff behind a plugin's gate makes it say busy.
- A Pro tab made from a composer moved back answers its own message.
- Each save writes `draft`, `account` and `owner` into the pop-out's
  address, which a reload after a crash starts from. What was typed since
  the last save, up to the 2 s autosave delay, is still lost.
- The recorder refuses keys that type a character on this keyboard, and
  what the shell refuses from a page, and says why.
2026-11-04 01:31:43 +01:00
Linus Rath c8c98e79d4 feat: sign in to the desktop app in your browser, with your password manager
The desktop login page gets "Sign in with your browser…" as its main
button. On an instance it opens the instance's real login page in the
system browser, with a PKCE challenge. The page checks the password or
runs SSO, parks the sign-in under a one-time code, asks once, and sends
the code to the app's loopback listener. This browser gets no session
out of it, and the app's own page redeems the code, so SameSite=Strict
is fine. Standalone runs the mail server's OAuth against the listener,
with a page from main for a server with no OAuth at all.

- `/api/auth/desktop-handoff/park` and `redeem`, and `sso/complete` parks
  tokens the same way for a loopback target
- `auth.claim()`: the calls are taken once per document, in the first
  render, in `WindowControlsHost`
- `BrowserSignIn` policy, default on, with its ADMX and samples
- strings in all 30 languages, and the old button's are gone

`runDesktopBrowserSignIn`'s tests changed because the code no longer goes
through the callback page's address: it's redeemed where it arrives.
2026-11-04 01:09:12 +01:00
Linus Rath e696fb1610 fix: ask before sending an older attachment also when the save was a moment ago 2026-11-04 00:17:15 +01:00
Linus Rath 69313623e0 fix: keep the edited file offers clear of the account menu 2026-11-04 00:02:34 +01:00
Linus Rath c0cf12c431 fix: show the edited file offers over the sidebar 2026-11-03 23:58:25 +01:00
Linus Rath 05e2e79ccf fix: keep the edited file offers clear of the toasts 2026-11-03 23:49:34 +01:00