feat(lists): manage lists in the app, and fix four store defects

Owning the store left a fresh install with no lists and no way to make
one, so no way to save a task. The seam gains updateList/deleteList
beside createLocalList on both paths — the External one addresses the row
as its own account's sync adapter, the only caller the provider lets
write tasklists. ListEditorSheet is the family's full-screen sheet: name
field, a 12-colour palette, and a destructive row behind a confirm when
editing. Entry points are a "New list" row under the home Lists section,
an empty state with a create button, and the home FAB switching to "New
list" while there are none. Deleting takes the list's tasks with it and
is offered only for device-only lists.

Four defects a review of the branch turned up:

- completing one occurrence closed the whole series — setCompleted wrote
  the master, the row TaskDao.tasks filters on. setCompletedInstance
  forks a RECURRENCE-ID override the way updateInstance does; phase 2
  always specified this, only the edit half had it
- the expansion ceiling was spent on the past, so a sub-daily series
  stopped expanding months before today and never reached Today or
  Upcoming
- an imported START-referenced reminder fired off DUE, because the seam
  collapsed alarms to a bare minute count. TaskReminder carries the
  anchor now
- registerObserver bound a live flow to whichever store was active at
  subscription, so a Settings store switch left every screen listening to
  the store it had stopped reading
This commit is contained in:
2026-09-04 13:46:05 +02:00
parent faee90f8b1
commit fcee1d1736
25 changed files with 1067 additions and 79 deletions

View File

@@ -207,6 +207,18 @@ most of it broken, absent or unusable. Reasoning in
- ✅ Fallout: `ReminderScheduler.sync()` gated on `resolve() != null`, which is
what `OWN` returns, so no due reminder armed in the default mode. It now gates
on `ProviderResolver.canReadStore()`, with tests.
- ✅ Fallout, second pass — four defects a review of the branch turned up:
**completing one occurrence closed the whole series** (`setCompleted` wrote the
master, which is the row `TaskDao.tasks` filters on, so every occurrence left
every list); the **expansion ceiling was spent on the past**, so a sub-daily
series stopped expanding months before today and never reached Today or
Upcoming; an imported **`START`-referenced reminder fired off `DUE`**, because
the seam collapsed alarms to a bare minute count; and `registerObserver` bound
a live flow to whichever store was active at subscription, so a Settings
store switch would have left every screen listening to the store it had
stopped reading. `setCompletedInstance` now forks a `RECURRENCE-ID` override
the way `updateInstance` does — phase 2 always specified this, only the edit
half had it.
-**Run the instrumented suite on a device.** Six classes — the Room seam, the
DAOs, the import, the migration harness, the restore path and the performance
check — all compile and none has ever executed. Everything load-bearing about
@@ -215,6 +227,21 @@ most of it broken, absent or unusable. Reasoning in
v0.3.2 APK with seeded data landing every task, list and reminder.
- ⬜ Per-locale release notes for the dropped authority and permissions.
### ✅ Managing lists in the app
Owning the store made this mandatory: there is no longer a provider app to
create a list in, so a fresh install had no lists, no way to make one, and
therefore no way to save a task. The seam gained `updateList` / `deleteList`
beside the existing `createLocalList`, implemented on both the Room and the
External path (which addresses the row as its own account's sync adapter, the
only caller the provider lets write `tasklists`).
-`ListEditorSheet` — the family's full-screen sheet with a name field, the
12-colour palette and, when editing, a destructive row behind a confirm.
- ✅ Entry points: a "New list" row under the home Lists section, a real empty
state with a create button, and the home FAB switching to "New list" while
there are none. Editing is the pencil in a list's own top bar.
- ✅ Deleting a list deletes its tasks — `tasks.list_id` cascades — and is
offered only for device-only lists; an account's collection is its server's.
---
## Open decisions / to verify