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:
@@ -108,7 +108,7 @@ postscript in [`STORAGE-DECISION.md`](STORAGE-DECISION.md). Package root
|
||||
| `data/prefs/` | `SettingsPrefs` (DataStore). |
|
||||
| `data/di/` | `DataModule` (binds + provides), `Qualifiers` (`@IoDispatcher`, `@ApplicationScope`). |
|
||||
| `data/demo/` | `DemoSeeder` (debug-only sample data). |
|
||||
| `ui/` | `theme/`, `common/` (ListChip, PriorityChip, reminder pickers), `navigation/` (`AgendulaNavHost` + `Dest`), `lists/`, `tasklist/`, `detail/`, `edit/`, `settings/`, `permission/` (each a screen + ViewModel + UiState), `crash/`, `RootScreen`. |
|
||||
| `ui/` | `theme/`, `common/` (ListChip, PriorityChip, reminder pickers), `navigation/` (`AgendulaNavHost` + `Dest`), `lists/`, `tasklist/`, `detail/`, `edit/`, `settings/` (hub + sub-screens, `StorageScreen` among them), `export/`, `permission/` (each a screen + ViewModel + UiState), `crash/`, `RootScreen`. |
|
||||
| root | `AgendulaApp` (Hilt app), `MainActivity`. |
|
||||
|
||||
---
|
||||
@@ -447,10 +447,21 @@ fallback in `ui/theme/`). Each screen area (`lists`, `tasklist`, `detail`,
|
||||
`RootScreen` is the entry composable: it gates on `ProviderStatus`
|
||||
(`NO_PROVIDER` / `NEEDS_PERMISSION` → onboarding `Gate`; `READY` →
|
||||
`AgendulaNavHost`). In `OWN` mode the status is always `READY`, so that gate is
|
||||
only ever seen in External mode. Routes are the `Dest` table in
|
||||
`ui/navigation/` (lists → task list → detail / edit, plus settings). Follow the
|
||||
`material-3` skill for component choices (M3 `ListItem` rows, expressive
|
||||
checkbox/FAB/swipe motion).
|
||||
only ever seen in External mode — and it offers a way back to our own store,
|
||||
because it is the only screen an External user can reach once their provider app
|
||||
stops answering. Routes are the `Dest` table in `ui/navigation/` (lists → task
|
||||
list → detail / edit, plus settings). Follow the `material-3` skill for component
|
||||
choices (M3 `ListItem` rows, expressive checkbox/FAB/swipe motion).
|
||||
|
||||
Settings is a hub of sliding sub-screens rather than routes; **Storage** is the
|
||||
one with teeth. It holds the §4.1 store picker — which asks for an external
|
||||
provider's runtime permission *before* writing the mode, so a denial leaves the
|
||||
readable store in place instead of stranding the user on the gate — and the
|
||||
export screen (`ui/export/`, one `.ics` per ticked list, written through SAF to a
|
||||
folder or a single zip). Because the mode is now switchable while the process
|
||||
lives, `AgendulaApp` re-arms reminders on `ProviderResolver.onModeChanged`: an
|
||||
alarm is scheduled off whichever store was active at the time, so the whole set
|
||||
has to be rebuilt against the new one.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user