diff --git a/DynamicIsland/Providers/CalendarServiceProviding.swift b/DynamicIsland/Providers/CalendarServiceProviding.swift index b33ae40..055de25 100644 --- a/DynamicIsland/Providers/CalendarServiceProviding.swift +++ b/DynamicIsland/Providers/CalendarServiceProviding.swift @@ -146,6 +146,7 @@ class CalendarService: CalendarServiceProviding { extension CalendarModel { init(from calendar: EKCalendar) { self.init( + accountName: calendar.accountTitle, id: calendar.calendarIdentifier, title: calendar.title, color: calendar.color, @@ -267,7 +268,7 @@ extension Priority { // MARK: - Helper Extensions -private extension EKCalendar { +extension EKCalendar { var accountTitle: String { switch source.sourceType { case .local, .subscribed, .birthdays: diff --git a/DynamicIsland/components/Settings/SettingsView.swift b/DynamicIsland/components/Settings/SettingsView.swift index ae7a5ec..a31e0c5 100644 --- a/DynamicIsland/components/Settings/SettingsView.swift +++ b/DynamicIsland/components/Settings/SettingsView.swift @@ -2905,18 +2905,48 @@ struct CalendarSettings: View { } Section(header: Text("Select Calendars")) { - ForEach(calendarManager.allCalendars, id: \.id) { calendar in - Toggle(isOn: Binding( - get: { calendarManager.getCalendarSelected(calendar) }, - set: { isSelected in - Task { - await calendarManager.setCalendarSelected(calendar, isSelected: isSelected) + let grouped = Dictionary(grouping: calendarManager.allCalendars, by: \.accountName) + let sortedAccounts = grouped.keys.sorted() + + ForEach(sortedAccounts, id: \.self) { account in + let accountCalendars = grouped[account] ?? [] + let allAccountSelected = accountCalendars.allSatisfy { calendarManager.getCalendarSelected($0) } + + Section(header: HStack { + Text(account) + Spacer() + Toggle("", isOn: Binding( + get: { allAccountSelected }, + set: { isSelected in + Task { + await calendarManager.setCalendarsSelected(accountCalendars, isSelected: isSelected) + } } + )) + .labelsHidden() + .toggleStyle(.switch) + .controlSize(.mini) + .disabled(!showCalendar) + }) { + ForEach(accountCalendars, id: \.id) { calendar in + Toggle(isOn: Binding( + get: { calendarManager.getCalendarSelected(calendar) }, + set: { isSelected in + Task { + await calendarManager.setCalendarSelected(calendar, isSelected: isSelected) + } + } + )) { + HStack(spacing: 8) { + Circle() + .fill(Color(calendar.color)) + .frame(width: 8, height: 8) + Text(calendar.title) + } + } + .disabled(!showCalendar) } - )) { - Text(calendar.title) } - .disabled(!showCalendar) } } } diff --git a/DynamicIsland/managers/CalendarManager.swift b/DynamicIsland/managers/CalendarManager.swift index 1d84c0b..42c66e3 100644 --- a/DynamicIsland/managers/CalendarManager.swift +++ b/DynamicIsland/managers/CalendarManager.swift @@ -382,6 +382,36 @@ class CalendarManager: ObservableObject { lastEventsFetchDate = Date() } + func setCalendarsSelected(_ calendars: [CalendarModel], isSelected: Bool) async { + var selectionState = Defaults[.calendarSelectionState] + let ids = Set(calendars.map { $0.id }) + + switch selectionState { + case .all: + if !isSelected { + let identifiers = Set(allCalendars.map { $0.id }).subtracting(ids) + selectionState = .selected(identifiers) + } + case .selected(var identifiers): + if isSelected { + identifiers.formUnion(ids) + } else { + identifiers.subtract(ids) + } + + if identifiers.isEmpty || identifiers.count == allCalendars.count { + selectionState = .all + } else { + selectionState = .selected(identifiers) + } + } + + Defaults[.calendarSelectionState] = selectionState + updateSelectedCalendars() + await updateEvents(force: true) + await updateLockScreenEvents(force: true) + } + func setReminderCompleted(reminderID: String, completed: Bool) async { await calendarService.setReminderCompleted(reminderID: reminderID, completed: completed) await updateEvents(force: true) diff --git a/DynamicIsland/managers/LockScreenWidgetPreviewManager.swift b/DynamicIsland/managers/LockScreenWidgetPreviewManager.swift index 9ac2c2a..7c3b5c0 100644 --- a/DynamicIsland/managers/LockScreenWidgetPreviewManager.swift +++ b/DynamicIsland/managers/LockScreenWidgetPreviewManager.swift @@ -302,6 +302,7 @@ final class LockScreenWidgetPreviewManager: ObservableObject { private func applyCalendarPreviewEvents() { let now = Date() let reminderCalendar = CalendarModel( + accountName: "Preview", id: "preview.reminders", title: "Reminders", color: .systemBlue, @@ -327,6 +328,7 @@ final class LockScreenWidgetPreviewManager: ObservableObject { ) let eventCalendar = CalendarModel( + accountName: "Preview", id: "preview.calendar", title: "Calendar", color: .systemOrange, diff --git a/DynamicIsland/models/CalendarModel.swift b/DynamicIsland/models/CalendarModel.swift index dd6e8b0..132ec3b 100644 --- a/DynamicIsland/models/CalendarModel.swift +++ b/DynamicIsland/models/CalendarModel.swift @@ -23,6 +23,7 @@ import Cocoa struct CalendarModel: Identifiable, Hashable { + let accountName: String let id: String let title: String let color: NSColor