Extend onboarding into a wizard (#163)

The first launch becomes a counted flow. Two new steps: a backup step,
shown only when nothing you write to is synced anywhere, which picks a
folder and turns automatic backup on in one action; and a view step that
picks the default view and month style from live previews.

onboardingPlan() derives the whole flow from the stored answers, keeping
completed steps so the counter never renumbers. The optional steps only
exist for an install that went through the grant in-app, so existing
users see nothing new.

Week, Day and Agenda gain the scaled-down live preview Month already had,
sharing one viewport scaler and one set of stand-in events.
This commit is contained in:
2026-08-08 21:02:44 +02:00
parent 342af07e09
commit d277578e77
27 changed files with 1458 additions and 342 deletions

View File

@@ -27,7 +27,7 @@ the package list (recurring writes, save conflicts, reminder delivery).
```mermaid
flowchart TD
subgraph UI ["ui/ — Compose screens + ViewModels"]
Screens["Month / Week / Day\nDetail / Edit / Settings\nPermission + Reminder onboarding"]
Screens["Month / Week / Day\nDetail / Edit / Settings\nOnboarding wizard"]
end
subgraph Data ["data/"]
Repo["CalendarRepository\n(interface + impl, Flow-based, io-dispatched)"]
@@ -78,8 +78,8 @@ flowchart TD
## Navigation
There is no navigation library. `MainActivity` hosts `RootScreen`, which
gates on the calendar permission and the one-time reminder onboarding, then
shows `CalendarHost`. `CalendarHost` holds the active view (month/week/day)
gates on the first-launch wizard (`ui/onboarding/`), then shows
`CalendarHost`. `CalendarHost` holds the active view (month/week/day)
plus overlay state for detail, edit, and settings — full-screen overlays
driven by `AnimatedVisibility` with a *held-key* pattern: the last shown
key stays alive through the slide-out so content never flashes empty.
@@ -87,6 +87,24 @@ A tapped reminder notification routes through `MainActivity` (`singleTop` +
`onNewIntent`) as an external detail key that `CalendarHost` consumes
exactly like an event tap.
### First-launch wizard
`ui/onboarding/` is a plan, not a navigation graph: `onboardingPlan()` is a
pure function of the stored answers that returns the whole flow *plus* the
step it is on, and `RootScreen` renders that step. Two rules make it work:
- The plan keeps **completed** steps, so finishing one never renumbers the
counter under the user.
- The optional steps (backup, view) only exist for an install that went
through the grant in-app — recorded by `armOnboardingWizard()`, which
refuses to arm if the reminder step was already answered, so an existing
user who revokes and re-grants the permission is not re-onboarded.
Whether the backup step applies (nothing writable is synced anywhere) cannot
be known before the grant, so it is assumed until the calendar list can be
read: the flow may shrink after the grant, never sprout a step the counter
had not accounted for.
## Recurring writes
The provider's invariants drive the design (learned the hard way, verified