Linus Rath e6baa751a9 fix: keep single edits off a series opened as itself and end series west of UTC
More edits of a recurring meeting went wrong at the edges of a series
(0.16 and 1.0 alike, attendees included):

- A series the app shows as itself - opened from a reminder or an
  event link, or one an edit just made recurring (0.16 before 0.16.20
  showed it until the next reload) - took "This event only" as a change
  of the whole series: `CalendarEvent/set` on the series' id with the
  editor's title, start and rule. "Delete this event only" deleted the
  series. The series now stands for its first occurrence: a change
  becomes an override of it, in two writes when the user organizes the
  meeting, sends invitations and an attendee has answered (Stalwart
  sends a new override only to attendees who have not), and a delete
  excludes it. "This and following" from the first occurrence changes,
  or deletes, the whole series instead of ending it before its first
  occurrence. A series opened from a reminder on a calendar shared with
  the user is now known to the store, so the change goes to the owner's
  account under the series' own id.
- An edit that makes an event recurring, or single again, now loads the
  range again, so the grid shows the occurrences instead of the series
  itself.
- 0.16 (0.16.23 checked) reads a rule's UNTIL as the series' wall clock
  taken for UTC, so in a zone behind UTC a split or "delete this and
  following" kept the occurrence it was to end before. The UNTIL is now
  earlier by the zone's offset (the larger of the split's day and the
  day before), which ends the series there on 0.16 and 1.0 alike.
- 0.16.6 to 0.16.23 send a change of one occurrence with its SEQUENCE
  raised but do not keep the raise, so a later change of every
  occurrence - a new title - went out with a lower number for that
  occurrence and the attendee's server refused it. A change of every
  occurrence that the user as organizer sends, of something the
  attendees see, on a series with changed occurrences, now raises the
  SEQUENCE of the series and of each changed occurrence by one. 1.0
  raises and keeps the number itself; the result there is the same. The
  app's update sends `sequence` as it is, so nothing has to keep it.
- An existing override written on the base event (0.16 before 0.16.20,
  and ranges the device expands) gets the series' organizer again when
  it has none: 0.16.19 refuses an answer to such an occurrence. 1.0
  refuses the key inside an override once, and the store writes the
  override again without it.
2026-09-26 04:02:22 +02:00
2026-09-24 22:18:42 +02:00

Bulwark Webmail

Bulwark Mobile

Beta - work in progress. Many features are unfinished or rough. Expect bugs, missing functionality, and breaking changes between releases. Do not rely on this for primary email yet.

React Native (Expo SDK 54) client for Bulwark Webmail - a JMAP-based mail, calendar, and contacts app.

What works today

  • Sign in to any JMAP server (e.g. Stalwart)
  • Multiple accounts
  • Email list, threads, compose
  • Calendar (basic)
  • Contacts (basic)
  • Push notifications via the Bulwark relay - FCM by default, or UnifiedPush (e.g. ntfy) for devices without Google Play services
  • In-app sideload updates from GitHub Releases

What's missing or rough

  • iOS builds, but push notifications and client certificates are Android-only so far
  • S/MIME, plugins, themes - UI stubs only (filters & rules, the vacation responder and file storage are real implementations)
  • Calendar editing is partial; contacts editing is basic
  • No Play Store distribution yet (sideload APK from Releases); iOS ships via TestFlight

Run locally

npm install
npx expo start

Then press a for Android, i for iOS, or scan the QR with Expo Go.

For release APK builds and signing see docs/android-release.md. For iOS builds and TestFlight distribution see docs/ios-release.md.

License

AGPL-3.0-only. See LICENSE.

S
Description
Bulwark Mobile: React Native client for iOS and Android. Mail, calendar and contacts over JMAP. Beta.
Readme AGPL-3.0
32 MiB
Languages
TypeScript 96%
Kotlin 2.1%
JavaScript 0.9%
Swift 0.4%
HTML 0.3%
Other 0.2%