Commit Graph
863 Commits
Author SHA1 Message Date
jcoffey-dev 3e044057f6 Merge pull request 'Composer: smarter suggestions, links over selections, big images attached' (#44) from feat/composer-polish into main
ci / node (push) Skipped
ci / version (push) Skipped
ci / docker-build (push) Skipped
ci / publish (push) Skipped
ci / announce (push) Canceled after 0s
ci / github (push) Canceled after 6s
2026-10-05 19:12:00 +00:00
jcoffey-dev 69ccb313e3 Merge pull request 'Save the people you write to as contacts' (#45) from feat/collect-recipients into main
ci / node (push) Skipped
ci / version (push) Skipped
ci / announce (push) Canceled after 0s
ci / github (push) Canceled after 0s
ci / publish (push) Skipped
ci / docker-build (push) Skipped
2026-10-05 19:11:57 +00:00
jcoffey-dev 29795f3988 Merge remote-tracking branch 'origin/main' into feat/collect-recipients
ci / node (pull_request) Skipped
ci / version (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 6m46s
ci / docker-build (pull_request) Skipped
ci / publish (pull_request) Skipped
ci / announce (pull_request) Skipped
# Conflicts:
#	web/src/store/compose.ts
2026-10-05 12:04:39 -07:00
jcoffey-dev 38dcbd9e0c Merge pull request 'Never send a message twice after a lost reply' (#42) from fix/send-no-duplicates into main
ci / node (push) Skipped
ci / version (push) Skipped
github/ci (branch) GitHub Actions
ci / github (push) Successful in 3m4s
ci / docker-build (push) Skipped
ci / publish (push) Skipped
ci / announce (push) Skipped
2026-10-05 19:03:59 +00:00
jcoffey-dev c2502d36bd Save the people you write to as contacts
ci / node (pull_request) Skipped
ci / version (pull_request) Skipped
ci / docker-build (pull_request) Skipped
ci / publish (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 3m5s
ci / announce (pull_request) Skipped
The addresses written to were remembered only in the browser that sent
the message, as a list of recent recipients, so a new device or a cleared
browser suggested nobody. After each confirmed send, the recipients who
are not contacts yet are now saved on the server, in an address book of
their own called Collected, so they are suggested everywhere.

- Only addresses on no card in any address book, own or shared, are
  added, de-duplicated, and never the sender's own identities.
- The book is created on first use and remembered by id in the synced
  settings, so its name can be anything; a deleted one is replaced.
- Names are split as the contact editor does ("Smith, Jane").
- Settings > Calendar & contacts > Contacts has a switch, on by default,
  as mail clients do; the book can be emptied or deleted like any other.

New strings: 3, in all ten catalogs (1699 -> 1702).
2026-10-05 11:55:51 -07:00
jcoffey-dev b11f3997d4 Composer: smarter suggestions, links over selections, big images attached
ci / node (pull_request) Skipped
ci / version (pull_request) Skipped
ci / docker-build (pull_request) Skipped
ci / publish (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 3m5s
ci / announce (pull_request) Skipped
Recipient suggestions:
- the words typed match in any order, each one the start of a word in the
  name, a nickname, the organization or the address: "jane smi" finds
  "Smith, Jane", and "globex" finds the people at Globex;
- someone written to lately ranks a little above an equal match;
- an address already in To, Cc or Bcc is no longer offered in the other
  two fields.

Pasting:
- a single web or mailto address pasted over selected words makes those
  words the link, instead of replacing them with the address;
- a pasted or dropped image over 10 MB goes in as an attachment rather
  than inline, where it would swell every reply.

No new strings.
2026-10-05 11:52:54 -07:00
jcoffey-dev 152311b035 Never send a message twice after a lost reply
ci / node (pull_request) Skipped
ci / version (pull_request) Skipped
ci / docker-build (pull_request) Skipped
ci / publish (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 1m44s
ci / announce (pull_request) Skipped
When a send's request went out and no answer came back, the composer said
"Send failed" and offered the draft back. If the server had in fact
accepted it and only the reply was lost, sending the draft again
delivered the message twice. Nothing identified the first attempt, so
nothing could check.

Each send now carries its own Message-ID (on the sending identity's
domain, RFC 5322 §3.6.4), kept on the draft if it comes back. A failure
that may have happened after the server acted (no answer, a timeout, a
5xx from the proxy) is followed by asking the server what it did:

- a message with that Message-ID was submitted (EmailSubmission/query by
  emailIds, RFC 8621 §7.3): it was sent, and the composer says so;
- one exists with no submission: it was created and never sent; it is
  removed from Sent and the failure stands, so resending is safe;
- none exists: the failure stands;
- the server can't be asked either: the composer says it couldn't confirm
  and to check Sent, instead of a plain "Send failed".

A refusal (4xx) means the server did not run the request, so it is not
checked. Sending a draft again that came back from a failure asks first,
and sends nothing if a message with its Message-ID already exists;
submissions are expunged after the server's hold period, so later on any
surviving copy counts as sent (every failure path removes the copy it
made).

New strings: 3, in all ten catalogs (1699 -> 1702, no new fallbacks).
2026-10-05 11:43:55 -07:00
jcoffey-dev 3434a5ed39 Merge pull request 'Fixes and Turkish from public ihasmail (#29–#36)' (#41) from sync/ihasmail-2026-10-05 into main
github/ci (branch) GitHub Actions
ci / node (push) Skipped
ci / version (push) Skipped
github/ci (tag inbuxa-v2026.10.5-g3434a5e) GitHub Actions
ci / github (push) Successful in 3m44s
ci / docker-build (push) Skipped
ci / publish (push) Skipped
announce / announce (release) Successful in 25s
ci / announce (push) Successful in 28s
inbuxa-v2026.10.5-g3434a5e
2026-10-05 17:43:08 +00:00
jcoffey-dev 64b5db01f3 Keep a narrow list's labels inside the row, on the sender's line (ihasmail #36)
ci / node (pull_request) Skipped
ci / version (pull_request) Skipped
ci / docker-build (pull_request) Skipped
ci / publish (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 2m25s
ci / announce (pull_request) Skipped
On a phone or a narrow list, rows are two lines, and labels went on a third
line of their own. A row is a fixed height at each density, and that third
line fit it only at Comfortable: at Cozy, the default, the labels were cut
off at the bottom, and at Compact they sat outside the row.

They now sit on the sender's line, after the name, which has room to spare,
so the row's height is unchanged at every density. When the line runs short
the name gives way first, then each label truncates; the date stays.

Reported in coffey-labs/ihasmail#35.

(cherry picked from commit 22490d8fe0190d85d8039a977435245b0d7da3cc)
2026-10-05 10:35:26 -07:00
jcoffey-dev af9b3a7f70 Indent the mark-read effect's first two lines again
Lost while resolving the conflict in the #30 pick.
2026-10-05 10:33:48 -07:00
jcoffey-dev 8a670c5e70 Turkish: the three image size strings (ihasmail #34)
(cherry picked from commit 5987f9c815d13df133841c4873b2aa9c394bc433)
2026-10-05 10:33:41 -07:00
jcoffey-dev cfb28ada45 Let mail wider than the pane scroll sideways (ihasmail #31)
(cherry picked from commit 14680d0e1b2cc966aa840a45e73b238322ed5e17)
2026-10-05 10:33:41 -07:00
jcoffey-dev 7b9da44116 Keep an open message unread after Mark as unread (ihasmail #30)
(cherry picked from commit 4a6bf677cbed9ecd23d02c0788c927e0dc9e941a)
2026-10-05 10:33:41 -07:00
jcoffey-dev 300ec1df99 Resize images in the composer (ihasmail #33)
(cherry picked from commit f17bdc9fff1455a734e19fdd8004fcd6b7970825)
2026-10-05 10:33:31 -07:00
jcoffey-dev 3f19f82960 Add Turkish translation (contributed by Hakan Arslan) (ihasmail #32)
(cherry picked from commit 017b1b5eb812ba18c3577ff79c544a25f7f91aaa)
2026-10-05 10:33:27 -07:00
jcoffey-dev 2e7f30c64e Finish the settings load when the tree remounts mid-load (ihasmail #29)
(cherry picked from commit 5ec06f40125cc320fba64edaa4ee1a1fb23d22d2)
2026-10-05 10:33:08 -07:00
jcoffey-dev c558693db5 Merge pull request 'ci: let the announce workflow run by hand for a given tag' (#40) from ci/announce-dispatch into main
ci / node (push) Skipped
ci / version (push) Skipped
ci / docker-build (push) Skipped
ci / publish (push) Skipped
github/ci (branch) GitHub Actions
ci / github (push) Successful in 2m25s
ci / announce (push) Skipped
2026-10-05 03:56:29 +00:00
jcoffey-dev c87a6d1cd6 ci: let the announce workflow run by hand for a given tag
ci / node (pull_request) Skipped
ci / version (pull_request) Skipped
ci / docker-build (pull_request) Skipped
ci / publish (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 2m7s
ci / announce (pull_request) Skipped
A release event runs the workflow as it was at the tag, so re-running a
failed announcement repeats the failure even after main is fixed. A manual
run takes the tag and uses main's workflow; the action already accepts it.
2026-10-04 20:53:46 -07:00
jcoffey-dev 34d3f21b4b Merge pull request 'ci: pin discourse-release at 9282c6f, which maps inbuxa-webmail' (#39) from ci/announce-pin-inbuxa-webmail into main
ci / node (push) Skipped
ci / version (push) Skipped
ci / docker-build (push) Skipped
ci / publish (push) Skipped
github/ci (branch) GitHub Actions
ci / github (push) Successful in 1m55s
ci / announce (push) Skipped
2026-10-05 03:49:11 +00:00
jcoffey-dev 0c5ad7a3b8 ci: pin discourse-release at 9282c6f, which maps inbuxa-webmail
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 2m26s
ci / announce (pull_request) Skipped
ci / version (pull_request) Skipped
ci / docker-build (pull_request) Skipped
ci / publish (pull_request) Skipped
ci / node (pull_request) Skipped
The release map knew this repo only by its old name, so announcing
inbuxa-v2026.10.5-g17a8093 failed. The action itself is unchanged.
2026-10-04 20:46:27 -07:00
jcoffey-dev 17a8093c09 Merge pull request 'Rename the repository to inbuxa-webmail' (#38) from chore/rename-to-inbuxa-webmail into main
github/ci (branch) GitHub Actions
github/ci (tag inbuxa-v2026.10.5-g17a8093) GitHub Actions
ci / github (push) Successful in 3m47s
announce / announce (release) Failing after 24s
ci / node (push) Skipped
ci / docker-build (push) Skipped
ci / version (push) Skipped
ci / publish (push) Skipped
ci / announce (push) Failing after 22s
inbuxa-v2026.10.5-g17a8093
2026-10-05 03:31:51 +00:00
jcoffey-dev 5664896f53 Rename the repository to inbuxa-webmail
ci / node (pull_request) Skipped
ci / version (pull_request) Skipped
ci / docker-build (pull_request) Skipped
ci / publish (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 1m30s
ci / announce (pull_request) Skipped
The repository moved from inbuxa/ihasmail-inbuxa to inbuxa/inbuxa-webmail,
matching inbuxa-server and inbuxa-admin. Point the source links, the image
name and the package link at the new name. The OAuth client id stays
ihasmail-inbuxa, since that is what the server registers.
2026-10-04 20:29:09 -07:00
jcoffey-dev 4d7668601e Merge pull request 'ci: copy each release image to GHCR as a replica' (#37) from ci/ghcr-replica into main
ci / node (push) Skipped
ci / version (push) Skipped
ci / docker-build (push) Skipped
ci / publish (push) Skipped
github/ci (branch) GitHub Actions
ci / github (push) Successful in 2m34s
ci / announce (push) Skipped
2026-09-30 16:30:40 +00:00
jcoffey-dev bdee5de814 ci: copy each release image to GHCR as a replica
ci / node (pull_request) Skipped
ci / version (pull_request) Skipped
ci / docker-build (pull_request) Skipped
ci / publish (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 2m23s
ci / announce (pull_request) Skipped
The Gitea registry stays authoritative; GHCR becomes a copy of it, the way
the GitHub repository is a copy of the Gitea one. After the tag build has
pushed the release image to the registry, a new ghcr job copies it to
ghcr.io under the same version tag and :latest with `imagetools create` --
a copy, not a rebuild, so the digest on GHCR is the digest on the registry.

Anything still pulling the old ghcr.io name, including the TrueNAS app
submission, keeps receiving releases. The job uses the run's own token and is
left out of the status reported to Gitea, so a GHCR problem cannot fail a
release.
2026-09-30 09:27:48 -07:00
jcoffey-dev b1436d4d2a Merge pull request 'ci: copy each release to GitHub after the tag build' (#36) from ci/github-release-copy into main
ci / node (push) Skipped
ci / version (push) Skipped
ci / docker-build (push) Skipped
ci / publish (push) Skipped
github/ci (branch) GitHub Actions
ci / github (push) Successful in 2m16s
ci / announce (push) Skipped
2026-09-30 13:54:41 +00:00
jcoffey-dev a7c7d88408 ci: copy each release to GitHub after the tag build
ci / node (pull_request) Skipped
ci / version (pull_request) Skipped
ci / docker-build (pull_request) Skipped
ci / publish (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 2m29s
ci / announce (pull_request) Skipped
The mirror carries tags to GitHub but not releases, so the replica's
Releases page -- and anyone watching the repository there -- stopped at the
last release made on GitHub. After the tag build has published, a new
github-release job copies the tag's Gitea release to a GitHub release: the
same notes, with PR and issue numbers rewritten to Gitea links, the same
files, and a line pointing back to the Gitea release.

It uses the run's own token and is left out of the status reported to
Gitea, so it cannot fail a release. With no Gitea release for the tag it
does nothing.
2026-09-30 06:52:03 -07:00
jcoffey-dev 63ddc23ee2 Merge pull request 'ci: name the tag in GitHub's tag status, and make digests re-runnable' (#35) from ci/tag-path-hardening into main
ci / node (push) Skipped
ci / version (push) Skipped
ci / docker-build (push) Skipped
ci / publish (push) Skipped
github/ci (branch) GitHub Actions
ci / github (push) Successful in 2m24s
ci / announce (push) Skipped
2026-09-30 13:47:42 +00:00
jcoffey-dev ae83d27e4b ci: name the tag in GitHub's tag status, and make digests re-runnable
ci / node (pull_request) Skipped
ci / version (pull_request) Skipped
ci / docker-build (pull_request) Skipped
ci / publish (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 2m29s
ci / announce (pull_request) Skipped
Commit statuses belong to the commit, not the tag. Upstream v* tags and
inbuxa-v* tags can sit on the same commit, so Gitea's github job, waiting
for "github/ci (tag)", could read the other tag's older result and move on
before this tag's build finished. A tag's status is now "github/ci (tag
<name>)" on both sides.

The per-architecture digest artifacts are also overwritable and kept for a
week, so re-running a build, or publish on its own, still works.
2026-09-30 06:44:52 -07:00
jcoffey-dev 043fe4ca54 Merge pull request 'ci: run the github wait job on its own runner label' (#34) from ci/wait-runner into main
ci / node (push) Skipped
ci / version (push) Skipped
ci / docker-build (push) Skipped
ci / publish (push) Skipped
github/ci (branch) GitHub Actions
ci / github (push) Successful in 2m13s
ci / announce (push) Skipped
2026-09-30 07:39:03 +00:00
jcoffey-dev 24dccdb47d ci: run the github wait job on its own runner label
ci / node (pull_request) Skipped
ci / version (pull_request) Skipped
ci / docker-build (pull_request) Skipped
ci / publish (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 2m38s
ci / announce (pull_request) Skipped
The github job only polls Gitea for GitHub's commit status, but it holds a
runner slot for as long as the GitHub build takes -- the better part of an
hour for a cold build. On the shared build runners a handful of those
could take every slot and stall real work, so it now runs on the `wait`
label: a runner of its own, with many slots, no docker socket and a small
CPU and memory cap.
2026-09-30 00:36:13 -07:00
jcoffey-dev 6e1166319a Merge pull request 'ci: a cancelled GitHub run no longer reports failure to Gitea' (#33) from fix/ci-report-cancelled into main
ci / node (push) Skipped
ci / version (push) Skipped
ci / docker-build (push) Skipped
ci / publish (push) Skipped
github/ci (branch) GitHub Actions
ci / github (push) Successful in 2m22s
ci / announce (push) Skipped
2026-09-30 07:29:52 +00:00
jcoffey-dev ddb6ce4611 ci: a cancelled GitHub run no longer reports failure to Gitea
ci / node (pull_request) Skipped
ci / version (pull_request) Skipped
ci / docker-build (pull_request) Skipped
ci / publish (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 2m30s
ci / announce (pull_request) Skipped
The mirror can push one commit twice in quick succession. GitHub then
starts two runs and cancels the older, and that run's report job posted
"failure" for the commit. Gitea's github job, seeing the newest status,
failed the check while the surviving run was still building and later
passed.

A cancelled run now posts nothing and leaves the result to the run that
superseded it. A real failure still reports failure.
2026-09-30 00:26:40 -07:00
jcoffey-dev bf35e75f88 Merge pull request 'docs: point issues and discussions at Gitea and the forum' (#32) from docs/mirror-note into main
ci / node (push) Skipped
ci / version (push) Skipped
ci / docker-build (push) Skipped
ci / publish (push) Skipped
github/ci (branch) GitHub Actions
ci / github (push) Successful in 2m28s
ci / announce (push) Skipped
2026-09-30 07:18:31 +00:00
jcoffey-dev 3251478d61 docs: point issues and discussions at Gitea and the forum
ci / node (pull_request) Skipped
ci / docker-build (pull_request) Skipped
ci / version (pull_request) Skipped
ci / publish (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 1m49s
ci / announce (pull_request) Skipped
This repository is now push-mirrored to GitHub, where issues and pull
requests would never reach the maintainers. A note under the title says
where development happens, and sends issues to git.coffeylabs.org and
discussions to community.coffeylabs.org.
2026-09-30 00:16:10 -07:00
jcoffey-dev 401c32a3e6 Merge pull request 'ci: build on GitHub via the mirror, switchable with BUILD_ON' (#31) from ci/build-on-github into main
ci / version (push) Skipped
ci / github (push) Skipped
ci / node (push) Successful in 2m58s
ci / publish (push) Skipped
ci / announce (push) Skipped
ci / docker-build (push) Successful in 39s
ci / node (pull_request) Skipped
ci / version (pull_request) Skipped
ci / docker-build (pull_request) Skipped
ci / publish (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 2m4s
ci / announce (pull_request) Skipped
Reviewed-on: inbuxa/ihasmail-inbuxa#31
2026-09-30 06:52:38 +00:00
jcoffey-dev 216ebd4dfe Build on GitHub Actions when BUILD_ON=github
ci / docker-build (pull_request) Successful in 1m20s
ci / github (pull_request) Skipped
ci / version (pull_request) Skipped
ci / node (pull_request) Successful in 2m23s
ci / publish (pull_request) Skipped
ci / announce (pull_request) Skipped
Gitea stays the source of truth and push-mirrors this repository to
GitHub. The org variable BUILD_ON, set on both forges, picks where the
heavy work runs:

- unset: nothing changes. Gitea's jobs run as before and every job in
  the GitHub workflow is skipped.
- github: Gitea skips its test, build and publish jobs. GitHub Actions
  runs them on hosted runners, arm64 natively rather than under QEMU,
  publishes to the same Gitea registry, and posts a commit status back
  to Gitea. A new `github` job in Gitea's ci.yml waits for that status
  and passes or fails with it, so the Gitea run still decides a PR.

Announcing and releasing stay on Gitea whatever BUILD_ON says.

The GitHub-era workflows go: cleanup.yml pruned GHCR, release.yml was a
second weekly scheduler, and publish.yml pushed to GHCR. Their work is
in the new .github/workflows/ci.yml or stays on Gitea. dependabot.yml
goes too: its pull request branches would exist only on GitHub, and
every mirror sync would delete them.
2026-09-29 23:06:27 -07:00
jcoffey-dev 829e46beae Merge pull request 'Confirm a typed password without replaying it over JMAP' (#30) from fix/confirm-password-without-basic into main
ci / version (push) Skipped
ci / node (push) Successful in 2m32s
ci / publish (push) Skipped
ci / announce (push) Skipped
ci / docker-build (push) Successful in 2m20s
2026-09-29 17:03:54 +00:00
jcoffey-dev 1acf2f29e7 Confirm a typed password without replaying it over JMAP
ci / version (pull_request) Skipped
ci / node (pull_request) Successful in 1m5s
ci / publish (pull_request) Skipped
ci / announce (pull_request) Skipped
ci / docker-build (pull_request) Successful in 35s
Creating an app password asks for the account password. A session
holding a token has no password to compare with, so it sent the typed
one to the mail server as HTTP Basic on the JMAP session. INBUXA's
server now takes no password outside DAV (contract C-23), so that check
would always fail.

It now asks the server's sign-in endpoint, the one its own sign-in page
posts to, as this client, to its registered redirect URI, with a PKCE
challenge whose verifier is thrown away so the code can never be
exchanged. "Two-factor code needed" counts as confirmed: the server
says so only after the password matched, so accounts with two-factor
sign-in now pass where the Basic check failed them.

The mock answers /api/auth like the server and can refuse Basic on
JMAP; the app-password test turns that on, and fails on the old check.
2026-09-29 06:50:32 -07:00
jcoffey-dev d73f5e8f9e Merge pull request 'DLP on send: warnings, blocks and held mail in the composer' (#29) from feature/dlp-dialogs into main
ci / version (push) Skipped
ci / node (push) Successful in 3m17s
ci / publish (push) Skipped
ci / announce (push) Skipped
ci / docker-build (push) Successful in 19s
2026-09-29 01:55:54 +00:00
jcoffey-dev 07cfe1310e DLP on send: warnings, blocks and held mail in the composer
ci / version (pull_request) Skipped
ci / node (pull_request) Successful in 2m13s
ci / publish (pull_request) Skipped
ci / announce (pull_request) Skipped
ci / docker-build (pull_request) Successful in 1m13s
The webmail half of inbuxa's DLP (dlp-and-mail-flow-rules spec, §2.5,
§4):

- A send the server's DLP rules refuse (inbuxa:dlpWarning or
  inbuxa:dlpBlocked) comes back to the composer with the rules' notices
  instead of a generic "Send failed". A warning offers "Send anyway…",
  which asks for a reason and sends again with inbuxa:dlpOverride; the
  server records the reason. A block can only be answered by changing
  the message.
- A message DLP held for review says so on sending ("Held for review:
  it's sent once a reviewer releases it"), from the submission's
  inbuxa:held.
- Tests: the override travels with the submission only when there's a
  reason; refusals are told apart from other errors.

Nine new English strings (the notice labels, the prompt, the toasts);
the other catalogs fall back to English until translated.
2026-09-28 18:51:45 -07:00
jcoffey-dev 290bc63dbb Merge pull request 'Say which legacy protocols are off when only some are' (#28) from feature/per-protocol-legacy-note into main
ci / version (push) Skipped
ci / node (push) Successful in 1m19s
ci / publish (push) Skipped
ci / announce (push) Skipped
ci / docker-build (push) Successful in 16s
2026-09-28 06:27:12 +00:00
jcoffey-dev 44f8e30c45 Say which legacy protocols are off when only some are
ci / version (pull_request) Skipped
ci / node (pull_request) Successful in 1m10s
ci / publish (pull_request) Skipped
ci / announce (pull_request) Skipped
ci / docker-build (pull_request) Successful in 46s
inbuxa can now switch IMAP, POP3 and ManageSieve off one at a time,
server-wide and per organization, and the session lists what is still
allowed for the account (legacyAllowed). Where the webmail said
"legacy protocols are off", it now also covers the case where only
some are:

- Security & sessions, above app passwords: "Your organization has
  turned off POP3 for mail apps. Mail apps that use it can't connect
  to this account; others still can."
- The Administration dashboard: "Some legacy mail protocols are off for
  your organization: POP3."
- An organization's sheet in Administration: its switch stays the
  all-or-nothing one; when only some are off it names them and points
  to the console, where they're switched one at a time, and "Turn
  legacy protocols back on" turns them all back on.

With every protocol off, the existing wording shows, as before. From a
server that doesn't send legacyAllowed nothing new appears.

3 new strings in all nine catalogs, unreviewed.

Tested: unit tests for reading the session and a tenant's switches;
the existing tests updated for the new field; typecheck; the whole
suite (1475 tests); and in headless Chrome against a local server with
POP3 off, where Security & sessions showed the new note.
2026-09-27 23:21:21 -07:00
jcoffey-dev ac0f8789f6 Merge pull request 'Delete people in the console, not the webmail's Administration' (#27) from feature/delete-in-console into main
ci / node (push) Successful in 1m42s
ci / publish (push) Skipped
ci / announce (push) Skipped
ci / version (push) Skipped
ci / docker-build (push) Successful in 27s
2026-09-28 02:34:53 +00:00
jcoffey-dev 5c08fb9fe9 Delete people in the console, not the webmail's Administration
ci / version (pull_request) Skipped
ci / publish (pull_request) Skipped
ci / docker-build (pull_request) Successful in 34s
ci / node (pull_request) Successful in 1m8s
ci / announce (pull_request) Skipped
Deleting a person's account is the console's now, beside locking it and
legal holds: the console asks why, for the audit log, and says when a
hold keeps the data. Where Delete was, the account's page says so and
links to the account in the console when the server names one. Groups,
lists, domains and tenants keep their delete here.

2 new strings in all nine catalogs, unreviewed.
2026-09-27 19:32:33 -07:00
jcoffey-dev 9e47437ef8 Merge pull request 'Open a locked account whole: calendar, contacts and files too' (#26) from feature/delegate-whole-account into main
ci / version (push) Skipped
ci / node (push) Successful in 1m7s
ci / publish (push) Skipped
ci / announce (push) Skipped
ci / docker-build (push) Successful in 15s
2026-09-28 01:06:55 +00:00
jcoffey-dev 173680cc41 Open a locked account whole: calendar, contacts and files too
ci / version (pull_request) Skipped
ci / node (pull_request) Successful in 1m7s
ci / publish (pull_request) Skipped
ci / announce (pull_request) Skipped
ci / docker-build (pull_request) Successful in 35s
Switching to a locked account handed to the reader moved only the mail.
Now calendar, contacts and files follow it as well, through a new
viewAccountFor that the three stores use for what they show. Settings,
signatures and push keep ownAccountFor, so nothing of the reader's is
ever written into the locked account (inbuxa AL-7).
2026-09-27 18:04:38 -07:00
jcoffey-dev 22d891b1ed Merge pull request 'Seven app fixes from public ihasmail' (#25) from merge/public-app-fixes into main
ci / version (push) Skipped
ci / node (push) Successful in 2m30s
ci / publish (push) Skipped
ci / announce (push) Skipped
ci / docker-build (push) Successful in 1m56s
2026-09-27 23:32:21 +00:00
jcoffey-dev c64a23f9d9 List folders in sidebar order in the move-to picker
ci / version (pull_request) Skipped
ci / node (pull_request) Successful in 2m29s
ci / publish (pull_request) Skipped
ci / announce (pull_request) Skipped
ci / docker-build (pull_request) Successful in 1m23s
The picker sorted folders A-Z by path, with Inbox first, so a folder
dragged into place in the sidebar turned up somewhere else when moving
mail. It now walks the tree in compareFolders order, the sidebar's
order with every folder expanded: Inbox, then the saved order, then
the special folders, then A-Z, with subfolders under their parent.

treeOrder lives beside compareFolders. A folder the walk from the top
cannot reach is appended rather than dropped, so it stays pickable as
it was before.

Closes #1

(cherry picked from commit ea03406646062359f74e16ad8a8aed074b4dc409)
2026-09-27 16:27:24 -07:00
jcoffey fedc34698e The toolbar above an open message acts on that message (#414) (#417)
With conversation view off, marking a message unread from the list --
the hover button, the right-click menu -- marked that message. Opening
it and pressing Mark as unread in the toolbar above it marked every
message in its thread, and so did Move to, Report spam and Delete.

The setting already reaches all the way into the reading pane: the list
draws one row per message, and `visibleMessages` narrows the pane to the
one opened. The toolbar was half converted. Its labels were right --
Mark as unread against Mark as read, the star, the labels shown -- all
of those read `messages`, which is the narrowed set. Only `rowIds`, the
one thing actually handed to the action, still read `thread.emailIds`.
So the button said one message and did the whole conversation.

`rowIds` is now the same question `visibleMessages` answers for the
pane, asked of the same ids, with the same fallback: an id that names
nothing in the thread -- a link from somebody with conversation view on,
a stale `m` in the URL -- shows the conversation, so the toolbar takes
the conversation. Conversation view on is unchanged: nothing is singled
out, so the whole thread comes back as before.

No new strings.

(cherry picked from commit f627bfc1237d6e8bbc728147022f624c3834d648)
2026-09-27 16:27:24 -07:00
jcoffey 8bfc7a85a9 A reply to a self-addressed message follows its Reply-To (#415) (#416)
A website contact form mails the site's own address: From and To are
both info@thesite, and the person who filled the form in is in Reply-To.
Replying addressed the draft to info@thesite -- the site's own desk --
instead of to them.

The reply already knows two shapes. A message somebody sent me is
answered to its Reply-To, which is what that header is for. A message
*I* sent is answered to the people I wrote to, and deliberately not to
my own Reply-To, which is where answers to me belong and would send my
reply to myself. A contact form passes the test for the second: every
address in From is mine.

So it fell down the chain the second shape keeps for a message with
nobody obvious to answer -- To without me, then Cc, then, having run
out, every address on the message, which here was mine alone.

The Reply-To now goes in that chain, one step before the last: when no
recipient but me is left and the message names a Reply-To that is not
mine either, that address is who it is really from. Keeping it after the
Cc is what leaves a message I did send alone -- somebody I actually
wrote to still beats my own Reply-To, which is the case the existing
guard was built for and its test still holds.

No new strings.

(cherry picked from commit 01dc322aebcff0e8075c54f81eb7d171a32fb9e7)
2026-09-27 16:27:24 -07:00