Commit Graph
18 Commits
Author SHA1 Message Date
Linus Rath fcbc6edb89 fix: stop losing the first local store call of a tab that just became the owner, which left it syncing for good 2026-11-03 07:17:23 +01:00
Linus Rath 20b2e7054e chore: merge search, notification actions and calendar into the mail branch 2026-11-03 07:02:39 +01:00
Linus Rath 2eb835b0b6 feat: merge offline calendar
# Conflicts:
#	lib/jmap/offline-hooks.ts
2026-11-03 06:39:38 +01:00
Linus Rath a3819f6a1c fix: keep offline calendar writes in order, and stop them getting lost or doubled
Review of the calendar store found writes that went missing, went out
wrong, or appeared twice.

- Writes with different scheduling flags stay separate and go out in
  order. Merging them collapsed the two steps of an occurrence change, so
  attendees who had answered got no mail, and "don't notify" went out to
  everyone with a later RSVP.
- An edit made after a create's lost response is sent as an update. An
  import of a uid the server has updates what differs.
- `createCalendarEvent`, `batchCreateCalendarEvents` and
  `createCalendarTask` choose a uid before the first send, so a lost
  answer online doesn't duplicate.
- The client resolves stand-in ids before the network, which covers an
  open dialog once the server is back.
- A stale whole `recurrenceOverrides` map no longer erases overrides.
- A 4xx refuses that write and the queue goes on. `serverFail`,
  `rateLimit` or an answer with no verdict retry instead of deleting.
- "This and all following" is refused offline with a clear message.
- A full list only drops what the server says it doesn't have. Sync adopts
  a stand-in by uid. Reminder keys survive a stand-in becoming real.
  Accounts the session no longer lists are forgotten.

Changed tests: the one asserting a sticky scheduling flag is gone, it
locked in the bug. Reminder keys now use the account and uid. Two flush
assertions drop `sent: 1`, because sync reads a lost write back first. The
two-account id test uses a listed account, as unlisted ones are removed.
2026-11-03 06:32:53 +01:00
Linus Rath 3060c60778 fix: stop calling every browser unsupported for the local store 2026-11-03 06:24:31 +01:00
Linus Rath 2481f060b7 feat: merge offline search 2026-11-03 06:17:13 +01:00
Linus Rath 41987cb5cf fix: stop hostile HTML stalling mail sync and index each message only once 2026-11-03 06:16:44 +01:00
Linus Rath 9271f0a6ef fix: never lose a message when a full sync runs across deletions, by paging on an anchor and asking the server before dropping anything
An existing assertion changes: resuming a full pass now starts from the last id seen rather than a position, since positions shift with every deletion.
2026-11-03 06:16:08 +01:00
Linus Rath f6db7ea53a fix: keep pinned and unread-first lists off a full sort, and stop syncing when the disk is full 2026-11-03 06:02:10 +01:00
Linus Rath e38b023ee3 chore: merge the store's speed work into the mail branch 2026-11-03 05:59:37 +01:00
Linus Rath 687e7cc012 feat: move offline mode onto the encrypted local store
The IndexedDB cache, its crypto, the responder and the page-side sync
go. lib/offline keeps its structure, policy, wipes, sign-out gates and
notices; answers, the outbox and sync now live in lib/store, in a worker
behind lib/store/client/api.ts.

What users get besides that:
- every folder is kept, not just the Inbox and opened ones
- delete for good and empty trash work offline
- a change the server refuses, or that meets a message changed on
  another device, is shown instead of dropped
- a send whose response got lost is looked up by a Message-ID fixed when
  it was queued, then finished, submitted or sent again
- with the store on, a start paints from the local copy, then connects

An account the app drops by itself no longer wipes its data: only a
sign-out or an explicit removal does. The outbox of the old database
moves over once, in lib/offline/migrate-legacy.ts, and the old database
goes only after the store committed. `isNetworkFailure` now counts
timeouts and proxy 5xx. Sends and outbox requests are never answered
from the device.

Tests moved with the code: cache.test.ts and responder.test.ts are
replaced by lib/store/__tests__/, and review-fixes.test.ts, outbox.test.ts
and client-fallback.test.ts now run the real engine on node:sqlite.
Changed assertions: an uncertain send is looked up rather than left alone
forever, destroy and empty trash are queued rather than refused, and #9
(opened folders) became "a folder the server deleted goes", since all
folders are kept.
2026-11-03 05:54:39 +01:00
Linus Rath 4ee918a98f fix: list a large folder off its index and sync message bodies in bigger batches 2026-11-03 05:46:55 +01:00
Linus Rath 877a7a93b2 feat: keep calendars on the device and queue calendar changes while offline
Adds a `calendar` area to the local store. It syncs calendars, participant
identities, events and tasks as masters, through CalendarEvent/changes,
and answers Calendar/get, CalendarEvent/query, /get and /set from SQLite
when the server can't be reached.

- A range query gets series in both client modes, with their overrides,
  and the calendar store expands them as it does for an older server.
- The synthetic-id probe gets the verdict from the last sync, so a client
  started offline behaves the same once the server is back.
- Create, edit, delete and RSVP queue in `calendar_pending`, one merged
  row per event, and go out through `calendar.flush` or the next sync.
  A create finds itself by uid after a lost response.
- A write the server refuses, or one on a value the server changed, goes
  to `calendar.conflicts`.
- `lib/store/calendar/reminders.ts` lists upcoming alerts with keys that
  don't depend on synthetic ids.
2026-11-03 05:42:01 +01:00
Linus Rath fe5b5d5746 feat: search mail while offline, with compound words, prefixes and highlights 2026-11-03 05:31:34 +01:00
Linus Rath eb8db7f27d feat: keep offline changes, sends and conflicts in the local store's outbox 2026-11-03 05:30:39 +01:00
Linus Rath 51ccac02ca feat: answer every mail read from the local store, including header forms, identities and quotas, and keep attachments 2026-11-03 05:30:09 +01:00
Linus Rath bad156eaed refactor: let each part of the local store expose its own calls to the page 2026-11-03 04:59:39 +01:00
Linus Rath 6da9a6a6f5 feat: add an encrypted local mail store that syncs in a worker 2026-11-03 04:57:20 +01:00