Commit Graph

467 Commits

Author SHA1 Message Date
fefe1f3652 feat(edit): add a searchable time-zone picker (#31)
Surface the zone as an optional form field, right after the time fields
it qualifies: hidden on ordinary events, revealed automatically when the
event already carries a foreign zone, and withheld entirely while all-day
is on (a date-anchored event has no zone to show).

The picker is a full-screen one per the app's convention, but it can't
reuse OptionPicker: that composes every option eagerly, and ~600 zones
would all compose on open. It drives its own LazyColumn instead, which
needs the kit's new `scrollable = false` — the scaffold's own
verticalScroll would otherwise throw on a nested same-axis scrollable.
The device zone and recently-picked zones pin to the top so the common
case needs no typing; search is accent- and case-insensitive.

Recents persist in DataStore, capped at five, dropping ids the tz
database no longer knows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 15:32:04 +02:00
7634df3cff feat(domain): let an event pin its own time zone (#31)
Every write sampled ZoneId.systemDefault() and stamped it into
EVENT_TIMEZONE, so the column was real but only ever held the device's
zone: an event synced from elsewhere could be read in its zone, never
authored in one.

Give EventForm a nullable `timezone`, where null keeps meaning "the
device zone at save time" — so every existing call site behaves exactly
as before — and a non-null value pins the event to a zone it then tracks
across DST. toWriteTimes resolves the form's zone ahead of the device's;
toEditForm pins only when the stored zone differs from the device's, and
prefills such an event in its own zone so the form shows the wall-clock
the event actually means.

Two provider-contract bugs fall out of this:

- Editing the time of a foreign-zone event rewrote EVENT_TIMEZONE to the
  device's. The instants stayed right, so nothing looked wrong, but the
  event silently stopped tracking its zone and would drift an hour at the
  next DST boundary. Only the timesChanged gate spared title-only edits.
- A zone change with an untouched wall-clock is still a time change (the
  same 09:00 elsewhere is a different instant), so it now trips
  timesChanged and rewrites DTSTART instead of being dropped.

All-day events keep carrying no zone at all: they're date-anchored, and
the UTC midnights they normalise to are an anchor rather than a location.

TimeZoneCatalog is pure JVM so the search ranking and DST-aware offsets
stay plain JUnit tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 15:31:54 +02:00
60938af9f0 chore: pin floret-kit to v0.2.0
Tracks the tagged kit main rather than the feature branch the merge left it on.
A release branch pointing at an unmerged branch is the failure mode main carried
since 2.15.0 (its pointer lived only on feat/custom-snooze-duration): the release
pipeline builds from the tagged source tree, so a rebased or deleted branch would
break the published release, not just a local checkout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 14:56:55 +02:00
accf0ac142 release: cut 2.16.0
Locale-aware calendar titles (#60): Month, Week and Day move onto the shared
formatter Agenda already used, the year drops out while you're in the current
one, and the Week title names its month rather than restating the day numbers
printed directly below it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 14:53:13 +02:00
f90badfcd5 Merge branch 'feat/unify-date-formatting' into release/v2.16.0 2026-07-17 14:51:41 +02:00
249606a358 fix(i18n): unify the calendar titles on one locale-aware formatter (#60)
Month, Week and Day each hand-rolled their title from a German-style template —
"$weekday, ${date.day}. $monthName ${date.year}" — so every language got a
trailing ordinal dot and day-before-month order, including ones that write the
month first. English read "Fri, 17. Jul 2026" where it should read
"Fri, Jul 17, 2026".

396a561 already fixed exactly this, but only for Agenda: it added
localizedDateFormatter and migrated the four agenda files, leaving Month, Week,
Day and MonthWidget on their originals. Day and Agenda therefore disagreed about
the same date. All four now route through formatCalendarTitle, so there is one
place left that decides how a title reads.

Two behaviour changes fall out of the reporter's point that the bar wastes space:

The year is dropped while you are in the current year. The title sits above a
grid that already says which year it is; the year's absence is itself the signal
that you are in the current one, and it reappears when you page out — the moment
it starts carrying information. Since a skeleton is a field list, this is just
appending "y", and the locale still places it.

The Week title names a month instead of a day range. "24. Jun – 31. Jun" restated
the day numbers printed in the column headers directly below, in the widest
string in the bar. Naming weekStart's month keeps a straddling week on the
outgoing month until it is fully gone — a week is seven contiguous days, so the
earlier month has a day in it exactly while weekStart is inside it. No straddle
conditional, and the title depends on nothing but weekStart, so it cannot drift
with the direction you paged in from.

currentLocale/localizedDateFormatter move to floret-kit's core-locale (neither is
calendar-specific); LocaleSupport.kt goes away and its 11 callers repoint.
MonthWidget keeps Locale.getDefault() — Glance has no LocalConfiguration and the
widget re-renders on a configuration change, matching AgendaWidget.

Does not touch the FAB stack the issue opens with: the buttons overlaying content
is intentional and matches Google Calendar.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 09:47:58 +02:00
5be5ff77d0 Merge pull request 'Release 2.15.0' (!79) from release/v2.15.0 into main
All checks were successful
Release — F-Droid repo + Gitea release / detect (push) Successful in 6s
Release — F-Droid repo + Gitea release / release (push) Successful in 13m40s
v2.15.0
2026-07-15 18:54:02 +00:00
6519cbca79 release: cut 2.15.0
All checks were successful
Translations / check (pull_request) Successful in 6s
CI / ci (pull_request) Successful in 11m17s
Move the Unreleased changelog under [2.15.0] — 2026-07-15, bump
versionName/versionCode to 2.15.0/21500, and sync the F-Droid
per-version changelog.

Milestone 2.15.0 (all integrated): #21 #35 #36 #39 #40 #46 #52.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 20:34:52 +02:00
50510a23da feat(colors): optional raw calendar colours + readable title contrast
Add a "Soften calendar colours" setting (Settings → Design, default on).
Turning it off paints calendar and event colours raw, exactly as the sync
source (DAVx5/CalDAV) publishes them, instead of the theme-fitting pastels
(#36).

Event/calendar colours now flow through shared eventFill()/eventInk()
helpers gated by a LocalSoftenColors composition local (widgets read the
pref directly). Event titles pick black or white text by the fill's WCAG
relative luminance, so a dark colour stays legible whether softened or raw
(#21) — previously always near-black.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 20:26:34 +02:00
2279738371 docs(changelog): note the new French and Polish translations
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 19:42:19 +02:00
3d59962112 i18n: offer French and Polish in the language picker
Both landed as community translations through Weblate (French ~33%, Polish
~57%), above the bar already shipped for zh-CN. Add them to locales_config.xml —
the single source of truth for the in-app language picker and Android's per-app
language settings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 19:40:10 +02:00
a96d8b6de9 Merge remote-tracking branch 'origin/main' into release/v2.15.0 2026-07-15 19:39:08 +02:00
94b9eeaa78 Merge pull request 'Translations update from Weblate' (!78) from weblate-bot/calendula:weblate-calendula-strings into main
All checks were successful
Release — F-Droid repo + Gitea release / detect (push) Successful in 5s
Release — F-Droid repo + Gitea release / release (push) Has been skipped
Reviewed-on: #78
2026-07-15 17:38:51 +00:00
Bazyli Cyran
85fb091e22 Translated using Weblate (Polish)
All checks were successful
Translations / check (pull_request) Successful in 4s
CI / ci (pull_request) Successful in 4m31s
Currently translated at 58.7% (252 of 429 strings)

Translation: Calendula/Strings
Translate-URL: https://weblate.dev.jeanlucmakiola.de/projects/calendula/strings/pl/
2026-07-15 17:34:15 +00:00
2139f8729c Merge remote-tracking branch 'origin/main' into release/v2.15.0 2026-07-15 19:28:22 +02:00
98a48aa795 docs(changelog): note the agenda date localization and gutter alignment fixes
Also correct the #35 entry — the empty-today marker is a card in the app but a
plain line in the widget, so call it a "note" rather than a card.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 19:27:20 +02:00
d255d232b9 Merge pull request 'Translations update from Weblate' (!76) from weblate-bot/calendula:weblate-calendula-strings into main
All checks were successful
Release — F-Droid repo + Gitea release / detect (push) Successful in 5s
Release — F-Droid repo + Gitea release / release (push) Has been skipped
Reviewed-on: #76
2026-07-15 17:24:26 +00:00
9999fcdd2a Merge pull request 'feat(detail): duplicate an event as a new one (#52)' (!77) from feat/duplicate-events into release/v2.15.0
Reviewed-on: #77
2026-07-15 17:23:44 +00:00
953ffdff97 fix(day): align the hour-gutter labels with the top bar hamburger
The day view's 48dp edge-to-edge hour gutter centred its labels at 24dp, 4dp
left of the hamburger. Give the gutter content the same 8dp start inset as the
week view so the labels centre on the hamburger (4dp bar inset + 24dp half icon).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 19:19:42 +02:00
5377a2b466 fix(calendar): align the week-number gutter with the top bar hamburger
The month grid was inset 4dp while its weekday header (and the loading grid) used
8dp, so the week-number column — and the day cells under their labels — sat 4dp
left of where they should. Bring the grid to 8dp: the gutter centre now lands on
the hamburger (4dp bar inset + 24dp half icon), and day cells sit under their
weekday labels.

The week view's header badge and hour labels had the same drift (a 48dp edge-to-
edge gutter centres its content at 24dp, not 28dp). Its top section background
bleeds full-width when scrolled, so instead of insetting the whole content, give
just the gutter content an 8dp start inset to centre it on the hamburger too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 19:15:51 +02:00
e73148dc6c fix(widget): keep the empty-today line plain, not a card
The agenda widget's event rows are plain (a colour stripe + text, no card), so
the rounded "No more events today" surface looked out of place. Render it as a
muted line indented to the event titles instead, matching the widget's style.
The in-app agenda keeps its coffee-cup card, where event rows are cards too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 19:15:51 +02:00
a19772e3a7 docs(changelog): note the duplicate-event action (#52)
All checks were successful
Translations / check (pull_request) Successful in 26s
CI / ci (pull_request) Successful in 9m15s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 19:11:34 +02:00
b3386eff43 Merge branch 'release/v2.15.0' into feat/duplicate-events 2026-07-15 19:10:29 +02:00
79e74d9995 feat(detail): duplicate an event as a new one (#52)
Add a Duplicate action to the event detail top bar that opens the shared
event form seeded with a copy of the event as a new, unsaved event, so a
non-recurring event can be re-created with just the day and time changed
instead of re-entering every field.

The copy reuses the existing prefilled-create overlay (createEvent), so it
becomes an independent event with the default reminder applied. Recurrence
is dropped — a duplicate is a single event; the edit form still exposes a
recurrence picker for anyone who wants a series. The occurrence's own times
carry over unchanged. Duplicate is offered for any loaded event, including
read-only ones (WebCal, birthdays): the source calendar is kept only when
it's writable, otherwise the copy resolves to the first writable calendar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 19:10:23 +02:00
396a5610aa fix(agenda): localize dates and refine the range bar
Localize every agenda date via a new localizedDateFormatter helper that lays a
field skeleton out in the locale's own order (Android best-pattern), instead of
a hardcoded day-month-year layout: the range-window summary, the screen's day
headers, and the widget's day headers. This also fixes the window mixing two
orders (e.g. "15 Jul – Aug 13, 2026").

Refine the range bar: the banner drops the range name (it already sits on the
selector button beside it) and shows just the concrete dates; the selector keeps
a subtle neutral surface tint — distinct from the top-bar view switcher's
secondary container so the two don't compete — and its right edge lines up with
the switcher. The anchored empty-today card takes a single event row's resting
corner radius.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 19:02:37 +02:00
b9329f6fb6 feat(agenda): anchor today with an empty-day card (#35)
Add an "Always show today" setting (Settings → Agenda, on by default) that
keeps today as the first entry in both the Agenda screen and its home-screen
widget even once nothing is left today. Under today's normal header a small
"No more events today" card appears — the coffee-cup empty-state motif in the
app, a rounded surface in the widget — so the first rows you see are clearly
today's rather than a future day's.

The anchor is a pure, JVM-tested helper (anchorTodayIfMissing) applied after
past-event filtering; in-app it only kicks in when the window starts on today,
never on a jumped-to date. The widget reads the pref reactively via per-instance
Glance state, mirroring the range/past-event settings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 18:39:27 +02:00
1b731a4ab0 docs(changelog): note the ACTION_EDIT intent and .ics MIME types
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 17:25:56 +02:00
ab631365b2 feat(intent): handle ACTION_EDIT and broaden .ics MIME types
Round out the calendar-intent surface toward AOSP/Etar parity — the app
already handled VIEW (date + event), INSERT, and .ics open/share, but was
missing the edit action and the alternate .ics MIME labels.

- ACTION_EDIT on content://com.android.calendar/events/<id> now opens the
  event in the edit form (previously only VIEW → read-only detail existed).
  An assistant, task app, or widget can hand an event to Calendula to edit.
  A bare EDIT URI with no occurrence extras falls back to the event row's
  own DTSTART/DTEND, mirroring the #48 view-event fallback.
- ACTION_EDIT with no event id (AOSP's "edit a new event") maps to the same
  prefilled create form as ACTION_INSERT.
- The .ics VIEW/SEND filters now also accept text/x-vcalendar (vCalendar
  1.0 / .vcs) and application/ics — the alternate labels the same calendar
  data arrives under from some file/mail apps (matches Etar's ImportActivity).

Deliberately excluded: webcal:// / http(s) remote-calendar subscription
(needs INTERNET, which the app doesn't have) and the Google-web-link handler
(Google-specific + network).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 17:23:40 +02:00
9aa370d583 Merge remote-tracking branch 'origin/main' into release/v2.15.0
# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/de/jeanlucmakiola/calendula/data/calendar/EventWriteMapper.kt
2026-07-15 17:23:31 +02:00
iker Contreras
8cd75716ae Translated using Weblate (Spanish)
All checks were successful
Translations / check (pull_request) Successful in 27s
CI / ci (pull_request) Successful in 10m52s
Currently translated at 100.0% (429 of 429 strings)

Translation: Calendula/Strings
Translate-URL: https://weblate.dev.jeanlucmakiola.de/projects/calendula/strings/es/
2026-07-14 02:41:02 +00:00
Thomas Tref
4feccf2008 Translated using Weblate (French)
All checks were successful
Translations / check (pull_request) Successful in 5s
CI / ci (pull_request) Successful in 4m14s
Currently translated at 30.5% (131 of 429 strings)

Translation: Calendula/Strings
Translate-URL: https://weblate.dev.jeanlucmakiola.de/projects/calendula/strings/fr/
2026-07-13 23:05:35 +00:00
23e9a5a35b Merge pull request 'fix(reminders): show the day for reminders on another day (#46)' (!75) from fix/reminder-day-context into release/v2.15.0
Reviewed-on: #75
2026-07-13 15:35:03 +00:00
38a35be0f0 fix(reminders): show the day for reminders on another day (#46)
All checks were successful
CI / ci (pull_request) Successful in 5m31s
Translations / check (pull_request) Successful in 5s
A reminder fired ahead of an event on a different day showed only the
event's time (e.g. "09:30 – 10:00"), making it look like it was
happening today. reminderTimeText now prefixes timed events with a
relative day: "Tomorrow"/"Yesterday", the short weekday for another day
this week, or the exact date for anything further out.

The this-week boundary honours the user's "week starts on" setting: the
resolved first day of week is threaded through from ReminderNotifier, so
e.g. a Sunday reads as next week under a Sunday-start locale. All-day
events keep their explicit date (never ambiguous), and cross-midnight
timed events keep both explicit dates.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 17:25:51 +02:00
f9f0572ec5 Merge pull request 'docs(changelog): add the custom snooze duration entry (#40)' (!74) from docs/changelog-snooze into release/v2.15.0
Reviewed-on: #74
2026-07-13 14:39:18 +00:00
0221972e6d docs(changelog): add the custom snooze duration entry (#40)
All checks were successful
Translations / check (pull_request) Successful in 4s
CI / ci (pull_request) Successful in 12s
The custom snooze duration shipped in 3c97673 but had no changelog entry;
add it to the Unreleased section so it's in the 2.15.0 release notes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 16:38:35 +02:00
86ea72d56f Merge pull request 'feat(edit): move an event to another calendar (#39)' (#73) from feat/change-event-calendar into release/v2.15.0 2026-07-13 14:30:07 +00:00
114db7939c ci(translations): run on every PR so the required check always reports
All checks were successful
Translations / check (pull_request) Successful in 5s
CI / ci (pull_request) Successful in 2m27s
The release/* branch protection requires the "Translations / check" status,
but the workflow was path-filtered to translation resources. A code-only PR
targeting a release branch never touches those, so the workflow never ran,
never posted its status, and the required check stayed pending forever —
permanently blocking the merge (only PRs that happened to change strings could
satisfy it).

Drop the path filter so it runs on every PR, mirroring the always-on `ci` job.
The parity check is SDK-free and passes when the committed translations are
consistent, so running it on unrelated PRs is effectively free.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 16:27:10 +02:00
4503847c0d feat(edit): move an event to another calendar (#39)
All checks were successful
CI / ci (pull_request) Successful in 8m40s
The calendar row in the editor is now tappable when editing an existing
event: picking a different calendar moves the event there on save, rather
than forcing a delete-and-recreate.

CALENDAR_ID is sync-adapter-owned and can't be updated in place, so the
move is copy+delete: the master row is re-inserted on the target calendar
(preserving UID_2445 so backup dedup and sync identity survive), its
reminders and editable guests are copied, and — for a recurring series —
every exception is replayed against the new master (modified occurrences
via CONTENT_EXCEPTION_URI, cancellations as STATUS_CANCELED). The user's
field edits are then applied with the normal series update. Everything on
the new side is built before the source is deleted (post-before-delete),
with a rollback of the copy on any failure, so a move is all-or-nothing.

A calendar change forces whole-series scope, so it skips the recurring
scope dialog. Managed special-dates calendars stay locked. Colour is not
carried across (a raw/keyed colour may be invalid on the target account),
matching the existing calendar-switch behaviour.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 16:11:32 +02:00
95c5fe76e6 Merge pull request 'Release 2.14.1' (!72) from release/v2.14.1 into main
All checks were successful
Release — F-Droid repo + Gitea release / detect (push) Successful in 6s
Release — F-Droid repo + Gitea release / release (push) Successful in 8m5s
Reviewed-on: #72
v2.14.1
2026-07-13 13:48:22 +00:00
34fa9c9c3e chore(release): cut 2.14.1
All checks were successful
Translations / check (pull_request) Successful in 5s
CI / ci (pull_request) Successful in 8m43s
Move the Unreleased fixes (#47, #48, #49, #22) under a 2.14.1 heading, bump
versionName/versionCode to 2.14.1/21401, and sync the F-Droid per-version
changelog.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 15:32:16 +02:00
53793bfb68 docs(changelog): describe the .ics reminder prompt accurately (#49)
The prompt is raised whenever a default is configured and the file's reminders
differ from it — including when the file carries none at all, not only when it
brings its own. On-device verified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 15:24:18 +02:00
9c7c8cb03a docs(changelog): note the #47 fix covers local calendars too
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 15:01:37 +02:00
4fea176e28 fix: drop occurrences via EXDATE on calendars with no _sync_id (#47)
The cancelled-exception fix works on synced calendars but not local ones. A
cancelled exception only attaches to its parent through ORIGINAL_SYNC_ID; a
local event has no _sync_id, so the link never forms and the provider's
expansion of the *parent* collapses — every other occurrence disappears, which
is the original #47 corruption, just on a different calendar type. Verified
on-device both ways: a DAVx5 series survives a single-occurrence delete, the
same series on a LOCAL calendar vanishes entirely.

deleteOccurrence now branches on _sync_id. Synced events keep the (verified)
exception path. Events without one — local calendars, and synced events not yet
pushed — add the occurrence to the master's EXDATE, which needs no parent link
and is the canonical iCalendar way to drop one; a sync adapter carries it
upstream unchanged if the calendar later syncs.

Two provider quirks shape the write (both observed on a Pixel):
- An EXDATE-only update is not treated as a recurrence change: the expanded
  Instances rows are left alone, so the occurrence stays visible. The
  time/recurrence set has to ride along to force re-expansion.
- DTSTART alone is worse — the provider then recomputes lastDate as if the event
  were a single instance and collapses the series to its first occurrence.
  DTSTART + DURATION + RRULE + zone together re-expand it correctly.

This path is reached in normal use: Calendula's own contact special-date
calendars are local and hold all-day yearly series, so deleting one birthday
occurrence went through it. All-day series take the VALUE=DATE EXDATE form.

Adds pure buildOccurrenceExdateValues + JVM tests (timed, append, duplicate
fold, all-day).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 15:01:11 +02:00
cf9492c7ba fix(detail): derive series length from DURATION when DTEND is null (#48)
A recurring series row carries DURATION, not DTEND, so EventDetailMapper's
end == begin fallback rendered it zero-length. That was harmless while every
caller supplied per-occurrence times from Instances, but the bare
content://…/events/<id> VIEW intent added in #48 names no occurrence and
keeps the row's own times — so a series opened from a third-party widget
without begin/end extras showed as "10:00 – 10:00".

Read DURATION in the detail projection and derive the end from it, the same
way SearchMapper and IcsExportMapper already do.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 12:55:05 +02:00
e6def9e5f7 docs(changelog): log the #47/#48/#49 fixes under Unreleased
The 2.14.1 branch carried four fixes but only the CalDAV colour picker
(#22) had a changelog entry. Adds the recurring single-instance delete
fix (#47), the external VIEW-intent handling (#48) and the default
reminder on ACTION_INSERT / .ics import prompt (#49), plus their issue
link definitions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 12:34:08 +02:00
974185f354 Merge pull request 'fix(edit): default reminder for ACTION_INSERT events + import prompt (Codeberg #49)' (!71) from fix/insert-intent-default-reminder into release/v2.14.1
Reviewed-on: #71
2026-07-12 10:03:36 +00:00
6aacdd9111 feat(edit): offer default reminder on .ics import instead of auto-applying (#49)
All checks were successful
Translations / check (pull_request) Successful in 5s
CI / ci (pull_request) Successful in 5m21s
Split the two prefill paths that share openImported(): an ACTION_INSERT
intent still auto-applies the settings default (it carries no reminder
semantics), but a .ics file — which owns its reminders — no longer silently
decides. It keeps the file's reminders and raises a one-time prompt
("This event was imported with N reminder(s) — apply your default?") so the
user chooses. The prompt is skipped when there's no real choice: no default
configured, or the file already carries exactly it.

openImported() now takes an ImportSource; CalendarHost tags the overlay
Insert vs File. Accepting swaps in the default and reveals the section;
declining (or dismissing) keeps the file's own reminders.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 11:57:38 +02:00
4f263d00fe fix(edit): apply default reminder to ACTION_INSERT events (#49)
External ACTION_INSERT launches (e.g. Google Maps' "add to calendar",
the Todo Agenda widget) share the single-event .ics prefill channel:
CalendarHost routes requestedInsertForm as importForm, so EventEditScreen
calls openImported(), which froze reminders as touched to respect a file's
own VALARMs. But an insert intent carries no reminders, so the empty freeze
just suppressed the configured settings default — the event opened (and
saved) with no reminder.

Make the freeze follow the source, not the path: a form that carries its
own reminders (an .ics with VALARMs) still freezes them; a form with none
(every insert intent, and an .ics without VALARMs) falls back to the
settings default via applyDefaultReminder(), exactly like openNew(). An
intent that did carry reminders still wins.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 11:46:51 +02:00
bda002684c Merge pull request 'fix(edit): curate the CalDAV colour picker (#22)' (!70) from fix/caldav-color-picker-v2 into release/v2.14.1
Reviewed-on: #70
2026-07-12 09:40:18 +00:00
b6bcd195b0 fix(edit): curate the CalDAV colour picker (#22)
All checks were successful
CI / ci (pull_request) Successful in 5m18s
CalDAV sync adapters (DAVx5) publish all ~147 CSS3 named colours into
CalendarContract.Colors, so the event-colour picker showed a full screen
of alphabetically-scrambled, partly duplicated swatches.

Curation now runs in the space the picker actually paints — every swatch
is softened through pastelize, which pins lightness and caps saturation,
so the raw palette's lightness axis is invisible on screen. Judging
distinctness there: colours that paint identically collapse to one
(folding aliases, dark/light shades of a hue, and the neutrals together),
oversized palettes drop washed-out neutral-origin tints and thin by CIE76
ΔE in painted Lab, and survivors sort continuously by painted hue with the
wheel cut at its single widest gap. The CSS3 dump lands at ~33 distinct,
rainbow-ordered swatches; small hand-picked palettes (Google's) pass
through untouched. Every surviving swatch keeps its provider colour key so
picks still round-trip through sync.

This revives work stranded on fix/caldav-color-picker (never merged) and
adapts it to the floret-kit extraction of pastelize: the curation's
painted-space transform now lives self-contained in domain/pastelArgb as a
mirror of floret's pastelize shaping, rather than the two sharing one
function.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 11:22:04 +02:00