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>
This commit is contained in:
Scarriffle
2026-07-12 21:58:31 +02:00
parent e55e6c7c92
commit cf990e4279
15 changed files with 527 additions and 19 deletions

View File

@@ -114,11 +114,17 @@ struct LocalCalendar: Codable, Identifiable {
var permission: String? = nil
var group: Bool = false
var remindersEnabled: Bool = true
// Birthday calendar: events are all-day yearly; server renders age + cake icon.
var isBirthday: Bool = false
// Days before a birthday to remind (0 = on the day). nil = no reminder.
var birthdayNotifyDaysBefore: Int? = nil
enum CodingKeys: String, CodingKey {
case id, name, color, enabled, owned, permission, group
case sharedBy = "shared_by"
case remindersEnabled = "reminders_enabled"
case isBirthday = "is_birthday"
case birthdayNotifyDaysBefore = "birthday_notify_days_before"
}
init(from decoder: Decoder) throws {
@@ -132,6 +138,8 @@ struct LocalCalendar: Codable, Identifiable {
permission = try c.decodeIfPresent(String.self, forKey: .permission)
group = try c.decodeIfPresent(Bool.self, forKey: .group) ?? false
remindersEnabled = try c.decodeIfPresent(Bool.self, forKey: .remindersEnabled) ?? true
isBirthday = try c.decodeIfPresent(Bool.self, forKey: .isBirthday) ?? false
birthdayNotifyDaysBefore = try c.decodeIfPresent(Int.self, forKey: .birthdayNotifyDaysBefore)
}
}