The create form (v1.2) now edits: a pencil on the detail screen (writable calendars only, contextual WRITE upgrade like delete) opens it prefilled via EventDetail.toEditForm; populated sections always show, the calendar is fixed, and a dirty-check writes only changed columns (pristine saves are no-ops). Saving a dirty recurring event parks in SaveUiState.AwaitingScope and asks how far the change reaches (Google model): "only this event" = modified-occurrence exception via CONTENT_EXCEPTION_URI (empty optionals as explicit NULLs since the provider clones the parent row), "this and all following" = series split (insert new event first, then truncate), "all events" = series-row update with the time delta applied to the series DTSTART. A changed rule drops the exception option. Delete gained the same middle scope. Recurrence: EventForm.rrule + SimpleRecurrence (FREQ/INTERVAL/UNTIL/COUNT + weekly BYDAY with locale-ordered weekday toggles) behind a picker on create and edit; unrepresentable rules render humanized (shared ui/common RecurrenceText) and survive verbatim. UNTIL validation flags rules ending before the event starts. Provider lessons baked in (verified on-device via adb probes): instance caches regenerate only from an update's own values, so truncation sends the full time-column set (truncateSeries) — RRULE-only updates left a stale duplicate occurrence on the split day; UNTIL is written as the local end of day in UTC (toRRule(zone), previousLocalDayEndUtcMillis) so UTC+x zones can't leak an extra day. Reminder edits reconcile against actual provider rows, keeping untouched rows' methods. Tests: RecurrenceTest (parse/render/round-trip, truncation), update/exception mapper paths, repository pass-throughs, prefill + populatedFields, raw-title mapper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
50 lines
1.8 KiB
Markdown
50 lines
1.8 KiB
Markdown
# Calendula — Requirements
|
|
|
|
See full design spec: `docs/superpowers/specs/2026-06-08-calendar-app-design.md`
|
|
|
|
## V1 Scope (Variant "B")
|
|
|
|
### Validated (shipped)
|
|
- Foundation & CI infrastructure — v0.1.0 (2026-06-08)
|
|
|
|
### Active (V1)
|
|
|
|
- [x] Foundation & CI infrastructure
|
|
- [x] Data Layer over `CalendarContract`
|
|
- [x] Permission flow (`READ_CALENDAR`)
|
|
- [ ] Month view (S1)
|
|
- [ ] Week view (S2)
|
|
- [ ] Day view (S3)
|
|
- [ ] Event Detail Sheet (S4)
|
|
- [ ] Multi-Calendar Filter (M3)
|
|
- [x] Today button (M2) — shipped v0.5; Jump-to-Date **cut from scope**
|
|
- [ ] View-Switcher (M1)
|
|
- [ ] Settings screen (M4)
|
|
- [ ] Empty / no-permission / no-calendars states
|
|
- [ ] German + English localization
|
|
- [ ] Loading/Failure/Success states per screen (architectural pattern)
|
|
|
|
### Out of Scope (V2+)
|
|
|
|
- Event create / edit / delete (V2)
|
|
- Home-screen widget
|
|
- Full-text search
|
|
- Quick-add
|
|
- ~~Custom notifications/reminders (system already handles these)~~ —
|
|
**reversed:** Calendula targets sole-calendar-app users, so no other app
|
|
posts reminder notifications. We post them ourselves (Etar model). Planned
|
|
for v1.4 — see `ROADMAP.md`.
|
|
- Tablet/foldable-specific layouts
|
|
- iOS support (Android-only by design)
|
|
|
|
## Constraints
|
|
|
|
- **Tech stack:** Kotlin + Jetpack Compose + Material 3 Expressive, Hilt, DataStore
|
|
- **Tech stack pin:** Hilt 2.59.2 + KSP 2.3.9; Kotlin 2.3.21 (KSP for Kotlin 2.4.0 not released yet). Material 3 pinned to `1.5.0-alpha21` (Expressive APIs only exist in alpha). Re-evaluate when KSP/Material3 stable land.
|
|
- **Platform:** Android 10+ (API 29 minimum), Android 16 (API 36) target
|
|
- **Offline-first:** all data lives in `CalendarContract`; no app-side network
|
|
- **Privacy:** zero telemetry, no analytics
|
|
- **i18n:** German + English from day one
|
|
- **Tests + CI from day one**
|
|
- **License:** MIT
|