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>
This commit is contained in:
@@ -153,10 +153,10 @@ class CalendarrAPI {
|
||||
/// Update a local calendar's birthday settings. `is_birthday` marks it as a
|
||||
/// birthday calendar; `notifyDaysBefore` sets the reminder (nil sends the -1
|
||||
/// sentinel to clear it server-side).
|
||||
func updateLocalCalendarBirthday(id: Int, isBirthday: Bool?, notifyDaysBefore: Int??) async throws {
|
||||
func updateLocalCalendarBirthday(id: Int, isBirthday: Bool? = nil, notifyDaysBefore: Int? = nil) async throws {
|
||||
var body: [String: Any] = [:]
|
||||
if let b = isBirthday { body["is_birthday"] = b }
|
||||
if let n = notifyDaysBefore { body["birthday_notify_days_before"] = n ?? -1 }
|
||||
if let n = notifyDaysBefore { body["birthday_notify_days_before"] = n } // -1 clears server-side
|
||||
guard !body.isEmpty else { return }
|
||||
_ = try await request("/api/local/calendars/\(id)", method: "PUT", body: body)
|
||||
}
|
||||
@@ -168,6 +168,14 @@ class CalendarrAPI {
|
||||
return (try? JSONDecoder().decode([BirthdayEntry].self, from: data)) ?? []
|
||||
}
|
||||
|
||||
/// Report that this device just synced its Contacts birthdays, so the web
|
||||
/// can show "birthdays come from these devices".
|
||||
func reportBirthdaySync(deviceId: String, deviceName: String, count: Int) async throws {
|
||||
_ = try await request("/api/birthdays/sync-report", method: "POST", body: [
|
||||
"device_id": deviceId, "device_name": deviceName, "count": count,
|
||||
])
|
||||
}
|
||||
|
||||
func getICalSubscriptions() async throws -> [ICalSubscription] {
|
||||
let data = try await request("/api/ical/subscriptions")
|
||||
return (try? JSONDecoder().decode([ICalSubscription].self, from: data)) ?? []
|
||||
|
||||
Reference in New Issue
Block a user