feat: hide individual member calendars in the group view (iOS)

The calendar filter, when a group overlay is active, now lists the group's
members (+ the shared group calendar) and lets you hide each one individually
(Outlook-style). Filtering is client-side via CalendarStore.hiddenGroupKeys
(per-member gm:<id> / group-calendar gc keys), reset when switching groups.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-06-01 18:00:30 +02:00
parent b9547c15f9
commit b61a90d960
4 changed files with 95 additions and 3 deletions

View File

@@ -70,6 +70,15 @@ class CalendarStore {
/// show/hide list. Re-activation happens in AccountsView.
var banishedCalendarKeys: Set<String> = CalendarStore.loadBanishedKeys()
/// Group-overlay visibility: which members' calendars (`gm:<userId>`) and the
/// group calendar (`gc`) are hidden in the combined view like hiding
/// individual people in Outlook. In-memory; resets when leaving/switching a
/// group (the per-calendar hide/banish sets are for the personal view only).
var hiddenGroupKeys: Set<String> = []
static func groupMemberKey(_ ownerId: Int) -> String { "gm:\(ownerId)" }
static let groupCalendarKey = "gc"
// Cache bookkeeping
private var cachedStart: Date? = nil
private var cachedEnd: Date? = nil
@@ -114,6 +123,19 @@ class CalendarStore {
publishWidgetSnapshot()
}
/// Toggle / replace group-overlay visibility (members or the group calendar).
func setGroupKeyHidden(_ key: String, hidden: Bool) {
if hidden { hiddenGroupKeys.insert(key) } else { hiddenGroupKeys.remove(key) }
let (s, e) = rangeForCurrentView()
refreshFromCache(start: s, end: e)
}
func setHiddenGroupKeys(_ keys: Set<String>) {
hiddenGroupKeys = keys
let (s, e) = rangeForCurrentView()
refreshFromCache(start: s, end: e)
}
static func calendarKey(source: String, calendarId: String) -> String {
// The events API returns `calendar_id` inconsistently: a raw numeric for
// CalDAV, but "<source>-<id>" for local / ical / google / homeassistant
@@ -219,10 +241,18 @@ class CalendarStore {
/// `start` / `end` are kept in the signature for call-site clarity.
func refreshFromCache(start: Date, end: Date) {
_ = (start, end)
// In group overlay mode show everything (the per-calendar hide/banish
// toggles are for the personal view only).
// In group overlay mode the per-calendar hide/banish toggles don't apply;
// instead honour the per-member / group-calendar toggles (hiddenGroupKeys).
if activeGroup != nil {
events = allCachedEvents
if hiddenGroupKeys.isEmpty {
events = allCachedEvents
} else {
events = allCachedEvents.filter { ev in
if ev.isGroupEvent { return !hiddenGroupKeys.contains(Self.groupCalendarKey) }
if let o = ev.owner { return !hiddenGroupKeys.contains(Self.groupMemberKey(o.id ?? -1)) }
return true
}
}
return
}
events = allCachedEvents.filter { ev in

View File

@@ -164,6 +164,7 @@ private let strings: [String: [String: String]] = [
"group.name": "Name",
"group.icon": "Icon",
"group.members": "Mitglieder",
"group.calendar": "Gruppenkalender",
"group.member_colors": "Farben der Mitglieder",
"group.delete": "Gruppe löschen",
@@ -465,6 +466,7 @@ private let strings: [String: [String: String]] = [
"group.name": "Name",
"group.icon": "Icon",
"group.members": "Members",
"group.calendar": "Group calendar",
"group.member_colors": "Member colours",
"group.delete": "Delete group",