Commit Graph
453 Commits
Author SHA1 Message Date
Guido Schmit aeb50c593c chore: F-Droid recipe category -> Calendar & Agenda (valid category) 2026-07-16 20:07:09 +02:00
Guido Schmit 52dd6e7ff0 docs: F-Droid serving via existing reverse proxy (no nginx/TLS on the LXC) 2026-07-16 19:13:15 +02:00
Guido Schmit 42187b8254 docs: F-Droid own-repo hosting on a Proxmox LXC (build on Mac, index+serve on LXC) 2026-07-16 19:09:31 +02:00
Guido SchmitandClaude Opus 4.8 dd347a07f1 chore: GPLv3 license + F-Droid packaging (metadata, recipe, guide)
- LICENSE: GNU GPLv3
- fastlane/metadata (de-DE + en-US): title, descriptions, changelog, icon —
  used by both the own F-Droid repo and the official listing
- fdroid/com.scarriffle.calendarr.yml: ready-to-submit fdroiddata build recipe
- FDROID.md: how to run the own repo (fdroid.scarriffle.com) and submit to
  official F-Droid, incl. the signing-key caveat

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 19:03:04 +02:00
ScarriffleandClaude Opus 4.8 554ad425b1 Week view: render multi-day all-day events as continuous spanning bars
The all-day strip repeated a multi-day event once per day; now events span their
start→end columns and pack into lanes (like the month view) via a GeometryReader
layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 11:20:21 +02:00
Guido SchmitandClaude Opus 4.8 cc51ede8a5 fix: week/day all-day strip spans multi-day events as continuous bars
Lay out all-day events as bars across their start→end day columns, packed into
lanes, instead of listing a multi-day event separately in each day's column
(mirrors the month view and iOS).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 11:20:21 +02:00
ScarriffleandClaude Opus 4.8 12c869451b Never leak a shared calendar's real name to recipients
_cal_dict now labels shared (non-owned) calendars by the sharer/group name
(the value already computed for shared_by) instead of the owner's real calendar
name. Fixes recipients seeing the true name in calendar-management lists (and
anywhere else the name is shown); all clients pick it up automatically.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 11:15:35 +02:00
Guido SchmitandClaude Opus 4.8 101370719b Android: full-screen menu landing page + much denser settings
- gear now opens a full-screen Menu screen (Profile / Appearance / Accounts /
  Groups / Sync / Switch server / Logout), replacing the bottom-sheet menu that
  sat partly under the system nav buttons. Each entry opens its own full-screen
  page and returns to the menu on close.
- settings 'Darstellung' is the appearance table only again (no long scroll to
  reach navigation)
- much more compact settings: dropdowns use a small outlined chip instead of a
  full-height OutlinedTextField, smaller sync icons, bodyMedium row labels

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 20:31:21 +02:00
Guido SchmitandClaude Opus 4.8 e72ffab4f4 Android drawer/settings feedback: hamburger left, denser UI, full-screen settings
- move the top-bar hamburger to the LEFT (drawer opens from the left)
- much denser drawer: compact header, and calendar rows now use a small eye
  toggle instead of the bulky Switch, with tighter padding/text
- tapping the gear now opens the full-screen Settings directly (no bottom-sheet
  menu that sat under the system nav buttons, no redundant sync — that's in the
  drawer header). Accounts / Groups / Profile / Switch server / Logout moved
  into a 'More' section at the bottom of Settings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 20:02:08 +02:00
Guido SchmitandClaude Opus 4.8 3212fe2c6a feat: import birthdays from Android Contacts
Mirror the iOS Contacts birthday importer: read the address book's birthday
events and reconcile them into the user's birthday calendar, scoped to this
device via external_uid 'contact:<deviceId>:<contactId>' (add/update/delete),
then report the device.

- READ_CONTACTS permission + ContactsReader (Event TYPE_BIRTHDAY, handles
  year/no-year date formats)
- API: GET /birthdays, POST /birthdays/sync-report; local-event create/update
  now carry external_uid (+ update carries rrule/birth_year)
- CalendarViewModel.syncContactBirthdays reconciles like iOS; device-local
  enable flag + stable device id in SettingsStore
- birthday dialog gains an 'Import from contacts' action (requests permission);
  auto-syncs on launch when enabled

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 18:48:24 +02:00
Guido Schmit e042f92ec6 chore: bump version to 1.1.0 (versionCode 2) for Play Store testing 2026-07-15 18:38:06 +02:00
Guido SchmitandClaude Opus 4.8 749eccfaaa feat: left navigation drawer (calendars + groups + view + menu)
Mirror the iOS side drawer on Android with a ModalNavigationDrawer opened by the
hamburger or an edge-swipe (replaces the calendar filter bottom sheet):
- header with avatar/name/server + manual sync, full menu (gear), and close
- view + group switcher chips
- flat calendar list: tap toggles visibility, long-press opens a context menu
  (hide/banish + reminders), and a Sort toggle reveals up/down reordering.
  Order is device-local (SettingsStore.calendarOrder, mirrors the web).
- month-switching is now a named dropdown (scroll vs pages) like web/iOS
- new device-local 'Hide menu button' setting (drawer opens via edge-swipe)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 18:35:03 +02:00
ScarriffleandClaude Opus 4.8 7981ced5ba iOS drawer: declutter controls, bigger header, more top spacing
- remove the show-all / hide-all buttons (rarely needed with a few calendars;
  the web has none). The calendar section now shows a 'Calendars' header + a
  slim Sort toggle.
- larger header (avatar, name, action icons) with more padding
- more vertical breathing room between header / view switcher / groups / list

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 18:06:56 +02:00
ScarriffleandClaude Opus 4.8 6881c68935 iOS drawer: flat reorderable calendars, long-press menu, left-swipe close, header actions
Testing feedback:
- calendars are now one flat, drag-reorderable list (no per-source grouping);
  order is device-local (CalendarStore.calendarOrder, mirrors the web cal_order).
  Reordering is done via a 'Sort' toggle (native edit mode with drag handles).
- banish + reminder-mute moved from row swipe actions to a long-press context
  menu, which frees the horizontal swipe: swiping the drawer left closes it.
- the hamburger moved to the LEFT (drawer opens left); new device-local setting
  'Hide menu button' (open via edge-swipe only), not synced.
- the settings entry moved from the footer into the header as an icon, next to a
  restored manual-sync icon and the close button; the footer button is gone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 16:19:29 +02:00
ScarriffleandClaude Opus 4.8 daf0345120 iOS drawer: drop the grouped-section card behind show/hide-all
listRowBackground(.clear) so only the two bordered button pills show, without
the surrounding grouped-list capsule.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 06:46:32 +02:00
ScarriffleandClaude Opus 4.8 108503ad53 iOS: month-switching setting as a named dropdown (parity with web)
Replace the boolean "month view as pages" toggle with a Monatswechsel dropdown:
Continuous scroll vs. Page by page — same names/format as the web client.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 06:45:22 +02:00
ScarriffleandClaude Opus 4.8 a7802778b6 Web: expose month-navigation mode as a named dropdown
New 'Monatswechsel' setting (synced flag already existed as month_view_paged):
- Continuous scroll (week): current behaviour — wheel scrolls by a week,
  buttons/swipe jump 4 weeks.
- Page by page (month): wheel navigation is disabled; buttons and horizontal
  swipe jump a whole calendar month.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 06:26:02 +02:00
ScarriffleandClaude Opus 4.8 e008d5d98a iOS drawer: compact show/hide-all, single menu button, corner-safe footer
Testing feedback:
- show-all / hide-all are now side by side (bordered), not two big stacked rows
- the busy quick-access strip becomes a single "Einstellungen" button that
  opens the full menu (which already holds accounts/profile/groups/server/sync/
  logout)
- extra horizontal + bottom padding so the footer button is not clipped by the
  iPhone's rounded corners / home indicator

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 06:09:38 +02:00
ScarriffleandClaude Opus 4.8 d90a1a552c iOS: left side drawer (calendars + groups + nav) and opt-in top-bar colour
Replace the top-bar menu popup with an off-canvas left drawer, opened by the
hamburger or a narrow left-edge swipe (kept off the content area so month/week
swipe stay free). The drawer hosts the calendar visibility list, a view
switcher, group switching, and quick access to Settings/Accounts/Profile/
Groups/Server plus sync and logout.

- extract the filter list into reusable CalendarFilterContent (shared by the
  drawer and the still-present CalendarFilterSheet); the sheet is now a thin
  wrapper
- new CalendarDrawer view; CalendarHostView hosts it as a ZStack overlay with a
  scrim and edge-swipe gesture, and presents drawer destinations as sheets
- opt-in surface colour: surfaceColor (device-local) tints the flat top bar;
  empty keeps the translucent .bar material. Settings row with an Auto reset.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 22:24:24 +02:00
Guido SchmitandClaude Opus 4.8 fd804000c5 feat: per-setting sync table with dynamic 8-colour theme
Redesign the settings screen into uniform rows (sync icon | name | value):
dropdowns replace the chip pickers, every colour gets a reset to a canonical
default, a per-row sync icon toggles that setting's cross-device sync, and a
global switch flips all. Enabling a flag adopts this device's current value.

- AppSettings/SettingsStore: add text/bg/line colours, cache_months,
  month_view_paged, and the account-wide sync_flags map (+ DEFAULT_SYNC,
  effective-merge on pull)
- CalendarRepository.updateSettings: partial PUT of only the synced values plus
  the flag map (unsynced columns stay untouched server-side)
- Theme: fully dynamic — primary/accent tint, background/text/line drive
  background/onBackground/outline (matching web); today/divider/label unchanged
- add hide-profile (directory_hidden) to the profile block (parity with web/iOS)
- language + text/line contrast stay device-local

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 22:09:46 +02:00
ScarriffleandClaude Opus 4.8 7a89cc44f8 Web: settings page nav/header follow the surface colour too
The settings modal's own left nav and header were on --bg-app; point them at
--bg-sidebar / --bg-topbar so they track surface_color like the main sidebar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 22:08:22 +02:00
ScarriffleandClaude Opus 4.8 131f6b496d Add configurable surface/sidebar colour + clearer sync-icon state
- new user_settings.surface_color (nullable, device-local default, not synced):
  drives the web sidebar / top bar / surfaces; NULL derives from bg_color as
  before. Added to schema, migration, GET/PUT, NULLABLE_OVERRIDES, DEFAULT_SYNC.
- web: surface colour row in the settings table; applyTheme derives the whole
  surface family from it
- web: per-row sync icon now has real styling — ON shows the primary-tinted
  glyph on a pill, OFF shows a slashed, muted glyph (was previously unstyled,
  so synced/not-synced looked identical)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 22:01:16 +02:00
ScarriffleandClaude Opus 4.8 9a50f25f57 iOS: per-setting sync table with sync icons, dropdowns, colour resets
Generalise the two-tier SettingsSync into a server-driven flag map: each
syncable key has an account-wide flag (fetched from the server); pull applies
only on-flagged keys, push (read-modify-write) sends only on-flagged keys plus
the flag map. Per-row link icon toggles a setting's sync; a global switch flips
all. Enabling a flag adopts this device's current value as the shared one.

- AppSettings: fix bg_color mapping (was background_color), add cache_months,
  month_view_paged, sync_flags
- SettingsView: uniform rows (sync icon | name | value), ContrastSelector and
  the button pickers replaced by menu dropdowns, every colour gets a reset to a
  canonical default, global "sync all" toggle; account settings (privacy,
  group calendar, hide profile) kept in their own block; language and text/line
  contrast kept device-local (iOS-specific)
- unify default line colour to #3A3A52 across views

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 21:31:16 +02:00
ScarriffleandClaude Opus 4.8 4ab07ddcc3 Web polish from testing feedback + admin role toggle
- style the settings-table dropdowns for the dark theme (were white)
- share-icon picker on one even row (no lopsided wrap)
- more breathing room between colour hex and swatch
- user management: promote/demote admin (new PUT /users/{id}/admin,
  guarded against self-change and removing the last admin)
- calendar management table: clip/ellipsis cells so narrow columns no
  longer overlap (full-width detail rows still wrap)
- centralise default colours: utils.applyTheme now derives every fallback
  from settings-sync.DEFAULT_COLORS (single place for coders to edit)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 21:25:01 +02:00
ScarriffleandClaude Opus 4.8 137694ed98 Web: unified settings sync table with per-setting sync toggles
Replace the ad-hoc appearance panel with a uniform table (sync icon | name |
value): dropdowns instead of button groups, every colour with a reset to a
canonical default, a per-row sync toggle, and a global "sync all" master
switch.

- new settings-sync.js: canonical default colours, declarative table
  definition, browser-local value copy, effective-value resolution
- calendar.js: generalised sync engine (pull applies only synced keys from the
  server, save pushes only synced keys + the flag map), unified the two save
  buttons into one, dropped the dead text/line-contrast code, moved default
  duration into the appearance table, effective UI language on load
- account settings (private visibility, group calendar, hide profile) stay in a
  dedicated profile block, not the sync table
- app.css: settings table / sync toggle / sync icon styles
- i18n: sync-all / sync-this / share-icon strings; sw cache bump

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 21:07:26 +02:00
ScarriffleandClaude Opus 4.8 3d58e8fef8 Document the cross-device settings-sync contract
Shared reference for Web/iOS/Android: canonical keys, default flags, the
GET/PUT sync_flags API, and the pull/push rules each client implements.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 20:46:23 +02:00
ScarriffleandClaude Opus 4.8 91ae434e7c Add per-setting cross-device sync flags to user settings
Introduce an account-wide sync-flag map that is the single server-side
authority for which settings each client sends/fetches, plus value columns
for two newly-syncable device-local prefs.

- models.UserSettings: sync_flags (JSON), cache_months, month_view_paged
- main._migrate(): idempotent ALTER TABLE for the three new columns
- settings_router: DEFAULT_SYNC map + _resolve_sync_flags(); GET returns
  fully-resolved sync_flags (+ cache_months, month_view_paged); PUT accepts
  and merges a partial sync_flags map and the two new value fields

Rollout defaults: settings that already lived on the server sync ON; the
four newly-syncable prefs (language, share icon, cache range, month paging)
sync OFF until the user opts in.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 20:44:44 +02:00
ScarriffleandClaude Opus 4.8 1f1eb582ed Birthdays: explicit "Birthday calendar" add type + tidier table defaults (web)
- add "Geburtstagskalender" to the sidebar +calendar dropdown and a "+ Geburtstage"
  button to Settings > Calendars; both call ensureBirthdayCalendar (server enforces
  one per account) and toast "already exists" if present
- Settings birthday section: when none exists, just a hint pointing to "+ Geburtstage"
  (creation lives in the add area now, not a separate activate button)
- resizable calendar table: sensible default column widths instead of raw measured
- i18n de/en

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 21:34:38 +02:00
ScarriffleandClaude Opus 4.8 d4fd7beaf7 Birthdays: explicit calendar creation, sync never auto-creates (iOS)
- Contacts sync fills only an EXISTING birthday calendar (birthdayCalendar),
  never auto-creates it — deleting it no longer resurrects it on sync
- Accounts + menu gains "Geburtstagskalender" (only when none exists) to create
  the single birthday calendar explicitly
- the Birthdays-from-Contacts section shows a "create one first" hint when there
  is no birthday calendar; enabling sync only requests Contacts access

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 21:25:06 +02:00
ScarriffleandClaude Opus 4.8 29ddc5acbb Enforce a single birthday calendar per account (server-side)
- POST /local/calendars with is_birthday returns the existing birthday calendar
  if one exists (idempotent, no second created)
- PUT /local/calendars rejects (422) marking a second calendar as is_birthday

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 21:22:10 +02:00
ScarriffleandClaude Opus 4.8 0ce77ccdf8 Add drag-resizable columns to the calendar settings table
Column widths are draggable via a handle on each header's right edge and
persisted in localStorage (re-applied on re-render). Uses fixed table layout
locked to measured px widths so resizing one column doesn't reflow the others.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 21:06:36 +02:00
ScarriffleandClaude Opus 4.8 16ff434bef Make imported-event dedup bulletproof (server-side, id-based)
Repeated Contacts syncs must never create duplicate birthdays. Enforce it on
the server, independent of the client's reconcile:

- POST /local/events is now idempotent on external_uid: if an event with the
  same (calendar_id, external_uid) exists, update it in place instead of
  inserting a new row
- startup cleanup removes existing duplicates sharing the same
  (calendar_id, external_uid), keeping the earliest — auto-heals old data
- unique DB index on (calendar_id, external_uid) as a hard guarantee

external_uid is the stable "contact:<deviceId>:<contactId>", so the same contact
from the same device always maps to exactly one event.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 20:53:11 +02:00
ScarriffleandClaude Opus 4.8 1a30b4066a Fix: don't squeeze the Kalender settings table into the 680px column
The reading-column max-width added for the profile panel also constrained the
wide calendar table, forcing a half-width horizontal scroll. Exempt
#settings-panel-accounts so the table uses the full width.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 20:49:26 +02:00
Guido SchmitandClaude Opus 4.8 fdad9c9ba4 chore(android): target SDK 35 + version 1.0.0 for Play release
Play requires targeting a recent API level for new apps; bump compileSdk/
targetSdk to 35 and set the release version name to 1.0.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 19:30:27 +02:00
Guido SchmitandClaude Opus 4.8 1f90503104 chore(android): git-ignore keystore.properties + add example
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 19:16:42 +02:00
Guido SchmitandClaude Opus 4.8 486cc9c622 feat(android): single birthday-calendar model + group-visible exclusion
- one birthday calendar per user: BirthdayDialog targets the single calendar and
  offers activation when none exists; VM birthdayCalendar()/ensureBirthdayCalendar()
- removed the birthday toggle from the generic new-calendar dialog
- exclude the birthday calendar from the group-visible ("shared calendar") picker

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 19:08:19 +02:00
ScarriffleandClaude Opus 4.8 69ed8bf67d Birthdays: single calendar model + sync on server-sync (iOS)
- one birthday calendar per user; BirthdaysImporter auto-creates/targets it
  (no picker) and reconciles per-device (external_uid contact:<deviceId>:<id>)
  so multiple devices don't clash
- Contacts birthday sync now runs on every "Sync with server" (syncFromServer),
  not just app launch and the manual button
- report the device after each sync (POST /api/birthdays/sync-report) for the
  web device list
- Accounts: Birthdays-from-Contacts section drops the target picker, keeps the
  reminder picker; removed the birthday toggle from the generic new-calendar sheet
- New-birthday sheet targets the single calendar, offers activation if none
- exclude the birthday calendar from the group-visible ("shared calendar") picker

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 19:07:46 +02:00
ScarriffleandClaude Opus 4.8 aa12b83302 Birthday sync-device list + guard group-visible against birthday calendar
- new BirthdaySyncDevice table + /api/birthdays/sync-report & /devices so the
  web can show "birthdays come from these devices" (iOS reports its device on
  each Contacts sync)
- settings: reject setting a birthday calendar as the group-visible calendar
  (it can still be shared directly)
- web: Settings > Calendars > Birthdays shows the device list; exclude birthday
  calendars from the group-visible picker

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 18:52:02 +02:00
Guido SchmitandClaude Opus 4.8 7dbaad4b00 feat(android): birthday display, calendar creation and manual entry
- CalEvent parses is_birthday and exposes renderTitle (server age)
- EventLabel renders a cake icon + render title in month/week/day/agenda/preview
- LocalCalendar model + repository/api gain is_birthday, notify-days, rrule,
  birth_year; AccountsScreen can create a birthday calendar with a notify picker
- FAB long-press menu -> "New birthday" opens a minimal name+date dialog
  (year-unknown option) targeting a birthday calendar
- L10n strings

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 17:39:33 +02:00
Guido SchmitandClaude Opus 4.8 5b5a5d5cd8 chore(android): release signing via keystore.properties
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 17:39:33 +02:00
ScarriffleandClaude Opus 4.8 871fb833ba chore: sync Xcode project structure (Shared group + widget embedding)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 17:18:34 +02:00
ScarriffleandClaude Opus 4.8 19258096e2 Tidy settings layout (web)
- constrain settings panels to a 680px reading column so fields no longer
  stretch edge-to-edge on wide screens (main "looks bad" cause)
- fix the app-password "Erstellen" button rendering centred below the input:
  .app-pw-create inherited flex-direction:column from .form-group; force row so
  it sits inline to the right of the field

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 04:31:12 +02:00
ScarriffleandClaude Opus 4.8 6baa07379f Rework birthdays to a single calendar + UI fixes (web)
- one dedicated birthday calendar per user; enable it in Settings > Calendars
  (or from the New-birthday dialog) — it then shows in the sidebar like any
  calendar (colour/visibility)
- New-birthday dialog: no target picker; day/month selects + a year field that
  hides when "year unknown"; fixed the year-unknown checkbox layout
- create split-button is now one seamless pill; the caret opens the menu
- Settings > Calendars gains a Birthdays section (enable + notify-days-before)
- removed the birthday toggle from the generic new-calendar modal
- profile settings: right-align the section save button for consistency
- bump service-worker cache to v25 so assets refresh

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 04:20:49 +02:00
ScarriffleandClaude Opus 4.8 eb0684b99c Add birthday feature (web)
- views render display_title (age) and a cake icon for is_birthday events
  (month/week/agenda + quarter tooltip) via shared eventTitle/birthdayIconSvg
- create split-button caret opens a menu: new event / new birthday
- new birthday modal: name + date + "year unknown" + target birthday calendar,
  saved as an all-day FREQ=YEARLY local event with birth_year
- local-calendar modal gains a "birthday calendar" toggle + "notify N days before"
- i18n de/en strings

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:10:52 +02:00
ScarriffleandClaude Opus 4.8 cf990e4279 Add birthday feature (iOS)
- LocalCalendar/CalEvent gain is_birthday + birthday fields; API sends
  rrule/external_uid/birth_year and can create/update birthday calendars
- BirthdaysImporter: mirrors Contacts birthdays into a chosen birthday
  calendar, reconciling by external_uid (leaves manual entries untouched)
- AccountsView: "birthday calendar" toggle + notify-days when creating a
  calendar, and a "Birthdays from Contacts" section (enable + target + sync now)
- FAB long-press menu -> "New birthday" opens a minimal name+date mask
  (BirthdayEditorSheet) targeting birthday calendars, with year-unknown option
- Event bars render display_title (age) and a cake icon via EventLabel
- NSContactsUsageDescription added to both app build configs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 21:58:31 +02:00
ScarriffleandClaude Opus 4.8 ca09538971 Add birthday calendar backend support
Birthday calendars are ordinary local calendars flagged is_birthday, so they
flow to all clients via the merge read and inherit sharing/colors/reminders.

- models: LocalCalendar.is_birthday + birthday_notify_days_before;
  LocalEvent.external_uid (Contacts dedup) + birth_year
- build_local_event_dict: server-computed display_title "Name (age)" per
  occurrence, is_birthday flag for the client cake icon, and a reminder injected
  from birthday_notify_days_before so mobile schedulers fire it
- groups combined view keeps the birthday display_title instead of overwriting it
- local_router: calendar flags + event fields on create/update, plus
  GET /calendars/{id}/birthdays for importer reconcile by external_uid
- additive SQLite migrations

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 21:38:37 +02:00
ScarriffleandClaude Opus 4.8 e55e6c7c92 feat(ios): toggle month view between scroll feed and paged swipe
New device-local setting "Monatsansicht seitenweise wischen" (@AppStorage, no
server sync). Off keeps the continuous vertical scroll feed; on switches the
month view to a paged TabView — one month per screen, six height-filling week
rows, swipe left/right to change month (bounded ±monthsBack/Ahead range to keep
the TabView light). WeekRow gains a fillHeight flag so the same row renders at a
fixed height (scroll) or fills the page evenly (paged); its inner cells/divider
now size to the actual geometry. Paged selection is two-way bound to the store's
current date so prev/next/today and the title stay in sync.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 05:15:58 +02:00
Guido SchmitandClaude Opus 4.8 e49a2e027f feat(android): toggle month view between scroll feed and paged swipe
New device-local setting "Monatsansicht seitenweise wischen" (SettingsStore,
no server sync). When off, the month view stays the continuous vertical scroll
feed. When on, it becomes a HorizontalPager — one month per screen, six
height-filling week rows, swipe left/right to change month. The prev/next/today
signals route to the pager (animateScrollToPage) instead of the list, and the
visible month is derived from pagerState.currentPage. WeekRow now takes a
rowModifier so the same row renders at a fixed height (scroll) or weight(1f)
(paged). Setting is re-read when the settings screen closes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 22:47:26 +02:00
Guido SchmitandClaude Opus 4.8 8d7ac124ae fix(android): edge-to-edge insets + show all calendars in the filter
1) Burger menu (and other bottom sheets) had rows hidden behind the system
   navigation bar: MainActivity never called enableEdgeToEdge(), so the sheets'
   navigationBarsPadding resolved to 0. Enable edge-to-edge and place the insets
   ourselves — top bar statusBarsPadding, content/FAB navigationBarsPadding,
   auth screens systemBarsPadding — and add navigationBarsPadding (+ imePadding
   for the editor) to the Filter/Groups/Accounts/Editor sheets.

2) The calendar filter only listed calendars that had events in the loaded
   range, so empty calendars couldn't be toggled. Load the full calendar list
   from all sources (loadAllCalendars) and merge it with the event-derived
   entries — matching iOS, which already shows the complete list.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 22:29:07 +02:00
ScarriffleandClaude Opus 4.8 d426f8985c style(web): dark-mode the share modal's add-user search + permission select
The user-search input and permission dropdown in the share modal were raw white
browser controls. Style them like the rest of the app (dark bg, border, radius,
focus ring) and give the user checkboxes the primary accent colour. v80.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 13:25:03 +02:00