feat(settings): storage picker and export screen
The store picker and the export screen were the two frontend surfaces the own-store work left unbuilt, so both backends shipped unreachable. Settings gains a Storage section holding them: a full-screen picker over Own / an installed external provider (dimmed when none is present, named after the provider's own app), and an export screen with a per-list tick and the two SAF destinations, a folder or a single zip. The picker asks for the provider's runtime permission before writing the mode, so a denial leaves the readable store in place instead of dropping the user on the gate; a refusal is reported with a route to app settings. Making the mode switchable at runtime had two consequences: - reminders are armed off whichever store was active when they were scheduled, so a switch rebuilds the set. ReminderScheduler.sync() is now serialised — it is a read-modify-write over ScheduledReminderStore, and overlapping runs each wrote their own set as the whole truth - the permission gate is the only screen an External user can reach once their provider app stops answering, so it offers the way back to our own store ExportWriter no longer deletes a previous export before recreating it (a failure in between lost both), lists the target directory once instead of per document, and carries a typed ExportFailure so the screen can report in the user's language rather than an exception message.
This commit is contained in:
@@ -19,10 +19,11 @@ iCalendar has landed, and the Material 3 Expressive UI is built through **M5**:
|
||||
lists → task list (swipe gestures, inline add, smart-list section headers) →
|
||||
detail / edit with full CRUD, date-time pickers, priority, percent-complete,
|
||||
conflict-safe saves, per-task reminders, and subtask create + reparent — plus a
|
||||
one-time reminder onboarding step and a Settings screen. Remaining work is
|
||||
hardening the new store (`OWN-STORE.md` phase 6), the **frontend surfaces for
|
||||
what has landed** (a storage-mode picker, an export screen), then M6 (Glance
|
||||
widget, translations, F-Droid release) and the sync adapter.
|
||||
one-time reminder onboarding step and a Settings screen. The frontend surfaces
|
||||
for the new store have landed too: a Settings **Storage** section with the
|
||||
store picker and an **export screen**. Remaining work is verifying all of it on
|
||||
a device, then M6 (Glance widget, translations, F-Droid release) and the sync
|
||||
adapter.
|
||||
|
||||
---
|
||||
|
||||
@@ -147,8 +148,17 @@ what Posture B means (our own store, coexisting with everything — *not* squatt
|
||||
- ✅ Export to iCalendar (step 3) — a v1 feature now that own-mode data lives
|
||||
only in our app's private storage. One `.ics` per list, to a folder or a zip,
|
||||
via SAF. Backend only.
|
||||
- ⬜ **Frontend surfaces for the above** — a storage-mode picker in Settings and
|
||||
an export screen. The backend is done and unused until these exist.
|
||||
- ✅ **Frontend surfaces for the above** — Settings gained a **Storage** section
|
||||
holding both: a full-screen store picker (Own / an installed external provider,
|
||||
which is dimmed when none is present) that asks for the provider's runtime
|
||||
permission *before* committing the switch, and an export screen with a per-list
|
||||
tick and the two SAF destinations, a folder or a single zip. Two consequences
|
||||
of the mode becoming switchable at runtime came with it: reminders are re-armed
|
||||
against the new store on every switch (`AgendulaApp` listens on
|
||||
`ProviderResolver.onModeChanged`; previously only a restart, a boot or an edit
|
||||
resynced them), and the permission gate offers a way back to our own store —
|
||||
otherwise a user whose provider app went away is held on a gate with Settings
|
||||
behind it.
|
||||
- ⬜ File the DAVx5 issue (step 4) — non-blocking, cheap, serves F-Droid users.
|
||||
Note it now means "sync into an app that has no provider", so the ask has
|
||||
changed shape.
|
||||
@@ -264,9 +274,9 @@ These carry over from [`PLAN.md`](PLAN.md) §9; resolved ones are struck through
|
||||
resolved: our own store expands a series at read time and writes an edit to
|
||||
one occurrence as a `RECURRENCE-ID` override sharing the master's UID; in
|
||||
External mode the edit still goes through the instances URI.
|
||||
6. **Resolver ordering / mode-selection UX** — `autoMode()` picks a sane default
|
||||
today (see [`ARCHITECTURE.md`](ARCHITECTURE.md) §4.1); the Settings override it
|
||||
assumes is not built yet.
|
||||
6. ~~**Resolver ordering / mode-selection UX**~~ resolved: `autoMode()` picks the
|
||||
default (see [`ARCHITECTURE.md`](ARCHITECTURE.md) §4.1) and Settings → Storage
|
||||
→ Task store is the override it always assumed.
|
||||
7. ~~**Sync protocol coverage**, account model, conflict resolution — the next
|
||||
design discussion.~~ Taken up in [`SYNC.md`](SYNC.md); the remaining opens
|
||||
live on that document's list.
|
||||
|
||||
Reference in New Issue
Block a user