diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index f194096..e2b0288 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -236,22 +236,34 @@ pass on the existing controls; new toggles ride in with their own features. 9. **Reminders — defaults + delivery reliability** *(shipped v2.6.0)* — global default reminder **+ per-calendar override**, bundled with battery-exemption hardening. Full sketch in "Reminders — defaults & delivery reliability" below. -10. **The `.ics` engine — export + import** *(in progress → v2.7)* — one +10. **The `.ics` engine — export + import** *(shipped v2.7.0, 2026-06-18)* — one hand-rolled serializer/parser (zero deps, stays on `kotlinx-datetime`), - four surfaces: single-event share + whole-calendar backup (export), - open-`.ics`→form + whole-calendar restore (import). Closes the - device-local-calendar data-loss gap (#10/#11 merged here). Built as **two - sequential branches in one release**: `feat/ics-export` (write side + - UID-on-create precursor) then `feat/ics-import` (parser, restore, dedup). - Import is liberal-in/strict-out: skip-and-report foreign `VTIMEZONE` / - `RECURRENCE-ID` it can't model. Timezone rule: all-day `VALUE=DATE`, - non-recurring timed UTC `Z`, recurring timed `TZID`-labelled from the stored - `EVENT_TIMEZONE` (no `VTIMEZONE` blocks; resolved against the OS tz DB on - import). Plan: `docs/superpowers/plans/2026-06-18-05-ics-export.md`. -11. **Snooze / dismiss notification actions** *(next, after v2.7)* — follows the - `.ics` work; inherits v2.6's deferred exact-alarm/WorkManager decision (snooze - must re-fire an alarm). -12. Drag & drop rescheduling in day/week — big-ticket, own slice (recurring drops reuse the scope dialog) + four surfaces all shipped: single-event share + whole-calendar backup + (export), open-`.ics`→form + whole-calendar bulk import (import). Closed the + device-local-calendar data-loss gap (#10/#11 merged here). Built as two + sequential branches: `feat/ics-export` (write side + UID-on-create precursor) + then `feat/ics-import` (parser, restore, dedup by UID). Import is + liberal-in/strict-out: skip-and-report foreign `VTIMEZONE` / `RECURRENCE-ID` + / guest lists it can't model. Plans: + `docs/superpowers/plans/2026-06-18-05-ics-export.md` + `…-06-ics-import.md`. +11. **Snooze / dismiss notification actions** *(merged into release/v2.8.0)* — + followed the `.ics` work; inherits v2.6's deferred exact-alarm/WorkManager + decision (snooze must re-fire an alarm). + +**Tier 5 — close the read/write gap on the event model** *(opened 2026-06-22)* +12. **Attendee editing** *(next — promoted from the gated "Locations & People" + bucket, owner says high-importance 2026-06-22)* — attendees are already + *read* (queried, mapped, shown on the detail screen since v0.6) but the + event form can't *write* them, the last big read-only gap in the event + model. Add an attendees section to `EventEditScreen` / `EventForm`: add by + typed email (contact-picker entry can follow later), edit/remove rows, set + role (required / optional) — writing `CalendarContract.Attendees` rows on + insert + dirty-checked update, mirroring the reminders-diff pattern. + **Caveat to resolve first:** writing `Attendees` rows triggers sync-adapter + *invitation* behavior that differs by backend (Google auto-emails invites; + DAVx5/CalDAV writes the `ATTENDEE` property and lets the server decide) — + decide and document what Calendula promises before building. Full sketch in + "Attendee editing" under Locations & People below. **Gated — explicit go/no-go before any work (mostly INTERNET-permission calls)** - Remote calendar create/edit (re-implements DAVx5; INTERNET + credential storage) @@ -263,11 +275,13 @@ pass on the existing controls; new toggles ride in with their own features. 2026-06-17; cheap but low value, pick up only if asked **Unranked / fill-in** — pinch-to-zoom time scale, tablet/foldable layouts, -full-text search, ICS file import. Pulled in opportunistically, not sequenced. +full-text search. Pulled in opportunistically, not sequenced. -Debatable calls worth a second look: whether **local-calendar backup (#10)** -should lead Tier 4 outright (it's a silent data-loss risk, not a feature); -whether drag-drop (#12) jumps ahead given its daily-driver impact. +Tier 4 is now fully shipped (#9 reminders defaults v2.6.0, #10 `.ics` +export/import v2.7.0, #11 snooze/dismiss in release/v2.8.0; drag-drop rejected). +**Next committed work is Tier 5 #12 — attendee editing** (the last read-only gap +in the event model). After it: the theme-group ideas and Tier 2/3 leftovers +(quick-settings tile, now-line, week numbers in month, full-text search). ## Navigation & views @@ -288,8 +302,6 @@ whether drag-drop (#12) jumps ahead given its daily-driver impact. ## Event editing & creation -- Drag & drop rescheduling in day/week (recurring drops reuse the scope - dialog) — big-ticket, own slice - Duplicate event (detail action → prefilled create form) - **Per-event color** (`Events.EVENT_COLOR`, OptionCard picker in the form) *(next)* — chosen to follow the in-progress tap-to-create + calendar @@ -319,13 +331,11 @@ whether drag-drop (#12) jumps ahead given its daily-driver impact. go/no-go gate as the OSM/INTERNET item below. - Move event to another calendar (copy+delete model with a consequences warning — deferred from v2.0; `CALENDAR_ID` is sync-adapter-owned) *(was v3.0)* -- **Local-calendar backup / export** *(Tier 4 #10)* — device-only - (`ACCOUNT_TYPE_LOCAL`) calendars are first-class in Calendula but have **no - sync and therefore no backup**: a lost/wiped phone destroys them permanently. - Whole-calendar `.ics` (VCALENDAR) export to a user-chosen file (SAF), plus - restore-on-import that recreates events into a chosen local calendar. Reuses - the .ics serializer from the single-event share work; the restore path reuses - the import parser. A data-integrity obligation, not a feature. +- ~~**Local-calendar backup / export** *(Tier 4 #10)*~~ **shipped v2.7.0** — + device-only (`ACCOUNT_TYPE_LOCAL`) calendars had no sync and therefore no + backup. Settings → Calendars → Export writes every event to a user-chosen + `.ics` file (SAF); restore is the bulk-import path (pick a calendar, dedup by + UID). Closed the silent data-loss gap. ## Reminders — defaults & delivery reliability *(implemented 2026-06-17, `feat/default-reminders` — pending on-device review)* @@ -419,9 +429,11 @@ it directly undermines the feature's premise, so it rides in here. ## Sharing & interop -- Share event as .ics + open/receive .ics into a prefilled create form - (front-runs the import below) -- ICS file import (drag-and-drop) *(was v3.0, optional)* +- ~~Share event as .ics + open/receive .ics into a prefilled create form~~ + **shipped v2.7.0** — single-event share from detail; opening an `.ics` with one + event prefills the create form, many events opens a bulk import (dedup by UID) +- ~~ICS file import~~ **shipped v2.7.0** — covered by the open/receive `.ics` + flow above (single → form, many → bulk import) ## Platform & launchers @@ -455,9 +467,37 @@ in detail yet: trade-off is an explicit go/no-go decision before any work starts. - **Inline contact suggestions** while typing (needs READ_CONTACTS) — only if the picker proves clunky. -- **Attendee editing / invites from contacts** — own milestone; writing - `Attendees` rows touches sync-adapter invitation behavior (Google vs - DAVx5 differ). +- **Attendee editing** *(promoted out of this gated bucket 2026-06-22 — now + Tier 5 #12, high-importance; the no-permission typed-email path is not an + INTERNET/contacts call)*. See the "Attendee editing" sketch below. + +### Attendee editing *(Tier 5 #12, opened 2026-06-22)* + +The last read-only gap in the event model: attendees are read & shown on the +detail screen (since v0.6) but the form can't write them. Make guests editable. + +- **Read side already done:** `Attendee` domain model + status/relationship/type + enums, `queryAttendees` + `EventDetailMapper.toAttendee` (with tests), and the + attendees `DetailCard` + `AttendeeRow` in `EventDetailScreen`. Nothing to add + there. +- **Write side (new):** add `attendees` to `EventForm` and an attendees section + to `EventEditScreen` — add a guest by typed email (+ optional display name), + remove rows, set role (required / optional). Persist by diffing against the + provider's `CalendarContract.Attendees` rows on insert + dirty-checked update, + mirroring the reminders-diff (kept rows keep their fields). Organizer/self rows + are not user-editable. +- **No new permission for the typed-email path** — writing `Attendees` needs only + the existing `WRITE_CALENDAR`. A contact-picker entry (`ACTION_PICK` on emails, + one-shot, no READ_CONTACTS) can follow as a convenience, reusing the address- + picker mechanism above. +- **Sync-adapter caveat — decide before building:** writing `Attendees` rows + triggers backend-specific *invitation* behavior. Google's provider may auto-mail + invites; CalDAV/DAVx5 writes the `ATTENDEE` property and leaves delivery to the + server. Calendula sends nothing itself (no INTERNET), so what actually reaches a + guest depends entirely on the sync adapter. Settle and document the honest + promise (and any "your account may email this guest" copy) up front. +- **Out of scope (for now):** RSVP/your-own-response editing, free/busy lookups, + resource booking — all carry server round-trips or richer sync semantics. ## Consciously rejected @@ -465,3 +505,5 @@ in detail yet: - Natural-language quick entry (high effort, locale-fragile; the prefilled form already covers fast entry) - Quick-add sheet (the prefilled full form already covers it — cut in v2.0) +- Drag & drop rescheduling in day/week — **rejected** (owner decision, + reaffirmed 2026-06-22): not wanted. Rescheduling stays via the edit form. diff --git a/.planning/STATE.md b/.planning/STATE.md index 28e7971..cbae6fa 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -1,17 +1,27 @@ # Calendula — Current State -*Last updated: 2026-06-17* +*Last updated: 2026-06-22* ## Status -**Milestone:** 2 (write support) **complete** — v2.0.0 shipped 2026-06-11; -v2.1.0 (month event grid, drawer view tabs, cursor fix) shipped 2026-06-15. -**Phase:** post-2.1 backlog work. v2.2.0 (tap-to-create in day/week + local -calendar management) and v2.3.0 (Material 3 grouped-list redesign of Settings, -the calendar manager and the navigation drawer) both shipped 2026-06-16; -v2.4.0 (per-event colors) and v2.5.0 (jump-to-date, Agenda view, home-screen -agenda + month widgets, and a "New event" launcher shortcut) shipped -2026-06-17. The backlog is now organised by theme in `ROADMAP.md`. +**Milestone:** 2 (write support) **complete** — v2.0.0 shipped 2026-06-11. +**Phase:** post-2.x theme-based backlog work (organised in `ROADMAP.md`). +**Latest released tag: v2.7.5.** The whole Tier 4 (reliability/data-safety/ +interop) arc is now done or in flight: +- v2.4.0 per-event colors (2026-06-17) +- v2.5.0 jump-to-date, Agenda view, agenda + month home-screen widgets, "New + event" launcher shortcut (2026-06-17) +- v2.6.0 default reminders (global + per-calendar override, all-day default, + battery-exemption row) + system per-app language (2026-06-18) +- v2.7.0 **`.ics` engine** — single-event share, local-calendar backup export, + open/receive `.ics` (single → form, many → bulk import, dedup by UID) + (2026-06-18) +- v2.7.1–v2.7.5 — crash-reporting + F-Droid reproducible-build hardening + fixes + +**Next release `release/v2.8.0` (integration branch, not yet cut to main):** +holds crash reports via the public Codeberg tracker (MR !27) + reminder +snooze/dismiss notification actions (MR !28). Version bump to 2.8.0 happens at +release-cut. ## Progress @@ -120,11 +130,47 @@ agenda + month widgets, and a "New event" launcher shortcut) shipped sync" warning on the picker and in Settings. Color writes flow through insert / dirty-checked update / occurrence-exception; mapper + form tests. +- [x] v2.5 (shipped 2026-06-17) — Agenda view (4th top-level view), + jump-to-date drawer date picker, two home-screen widgets (scrolling + "Upcoming" agenda + month grid), and a "New event" launcher long-press + shortcut + +- [x] v2.6 (shipped 2026-06-18) — default reminders: global timed default + + separate all-day default + per-calendar override (timed), applied on create + with dirty-flag handling; three pickers + override list in Settings → + Notifications; battery-optimisation exemption row (status + system deep-link, + no new permission). Plus system per-app language (Android 13+) and an + immediate-effect fix for the in-app language picker + +- [x] v2.7 (shipped 2026-06-18) — the `.ics` engine: share a single event as + `.ics` from the detail screen; back up local calendars (Settings → Calendars + → Export) to a SAF file; open/receive an `.ics` — one event prefills the + create form, many events open a bulk import into a chosen calendar (dedup by + UID, skip-and-report unrepresentable VTIMEZONE / RECURRENCE-ID / guests). + Hand-rolled serializer/parser, zero deps. Plus all-day single-day UTC fix and + a widget R8 keep-rule crash fix + +- [x] v2.7.1–v2.7.5 (2026-06-21) — launch crash fix (listener before grant), + user-controlled crash reporting, widget loading-spinner R8 keep rule, and + F-Droid reproducible-build cleanups for the official repo + +- [~] release/v2.8.0 (not yet cut) — crash reports via the public Codeberg + tracker (MR !27) + reminder snooze/dismiss notification actions (MR !28, + snooze self-schedules an exact alarm; primary delivery stays provider-broadcast) + ## Next -1. Monitor the F-Droid build/publish for the v2.4.0 tag -2. Decide the "Locations & People" and "remote calendar create/edit" - go/no-go calls (both hinge on the INTERNET permission) — see `ROADMAP.md` -3. **Duplicate event** and **jump-to-date** are the cheap follow-ups; then - agenda view (strategic, backs a future widget). Full ranked sequence in - `ROADMAP.md` → "Near-term sequence". +1. Cut **v2.8.0** from `release/v2.8.0` (bump versionName → tag via the + merge-driven pipeline) once on-device review signs off +2. **Attendee editing** — the committed next feature (Tier 5 #12, high- + importance, opened 2026-06-22). Attendees are already read & shown on the + detail screen since v0.6; the gap is the write side — make guests editable + in `EventEditScreen` / `EventForm` (add by typed email, role, remove), + persisted by diffing `CalendarContract.Attendees`. No new permission for the + typed-email path. **Resolve first:** the backend-specific invitation/sync + behavior (Google auto-mails invites, CalDAV writes `ATTENDEE`) — decide and + document the honest promise. Full sketch in `ROADMAP.md` → "Attendee editing". +3. Then: the two INTERNET go/no-go calls (OSM autocomplete, remote calendar + create/edit) and Tier 2/3 leftovers (quick-settings tile, now-line, week + numbers in month, full-text search, accessibility pass). Drag-and-drop + rescheduling is **rejected**.