feat(sharing): hidden-profile toggle + hide non-sharing group members

- UserProfile decodes directory_hidden; updateProfile() sends it; Settings has
  a "hide my profile" toggle (excludes you from share/group pickers).
- GroupMember decodes shares_calendar; the group filter list now only shows
  members who actually share a calendar (no phantom empty rows).
- Robust custom decoders (decodeIfPresent) for the new fields.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-06 18:45:49 +02:00
parent 3f5de5cdfa
commit 32cb8e1689
5 changed files with 46 additions and 3 deletions

View File

@@ -223,6 +223,7 @@ struct UserProfile: Codable {
let isAdmin: Bool
let hasAvatar: Bool
let totpEnabled: Bool
var directoryHidden: Bool = false
enum CodingKeys: String, CodingKey {
case id, username, email
@@ -230,6 +231,19 @@ struct UserProfile: Codable {
case isAdmin = "is_admin"
case hasAvatar = "has_avatar"
case totpEnabled = "totp_enabled"
case directoryHidden = "directory_hidden"
}
init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
id = try c.decode(Int.self, forKey: .id)
username = try c.decode(String.self, forKey: .username)
displayName = try c.decodeIfPresent(String.self, forKey: .displayName)
email = try c.decodeIfPresent(String.self, forKey: .email)
isAdmin = try c.decodeIfPresent(Bool.self, forKey: .isAdmin) ?? false
hasAvatar = try c.decodeIfPresent(Bool.self, forKey: .hasAvatar) ?? false
totpEnabled = try c.decodeIfPresent(Bool.self, forKey: .totpEnabled) ?? false
directoryHidden = try c.decodeIfPresent(Bool.self, forKey: .directoryHidden) ?? false
}
}
@@ -258,9 +272,20 @@ struct GroupMember: Codable, Identifiable {
let displayName: String?
var role: String
var color: String?
var sharesCalendar: Bool = true
enum CodingKeys: String, CodingKey {
case id, role, color
case displayName = "display_name"
case sharesCalendar = "shares_calendar"
}
init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
id = try c.decode(Int.self, forKey: .id)
displayName = try c.decodeIfPresent(String.self, forKey: .displayName)
role = try c.decodeIfPresent(String.self, forKey: .role) ?? "member"
color = try c.decodeIfPresent(String.self, forKey: .color)
sharesCalendar = try c.decodeIfPresent(Bool.self, forKey: .sharesCalendar) ?? true
}
}

View File

@@ -132,6 +132,8 @@ private let strings: [String: [String: String]] = [
"settings.privacy": "Privatsphäre",
"settings.private_visibility": "Private Termine für Gruppen",
"settings.private_visibility.desc": "Wie private Termine für andere Gruppenmitglieder erscheinen",
"settings.directory_hidden": "Profil verbergen",
"settings.directory_hidden.desc": "Nicht in Teilen-/Gruppen-Auswahl anzeigen. In der Admin-Benutzerverwaltung bleibst du sichtbar.",
"settings.private.busy": "Als „Beschäftigt“",
"settings.private.hidden": "Ausblenden",
"settings.calendars": "Geteilter Kalender",
@@ -461,6 +463,8 @@ private let strings: [String: [String: String]] = [
"settings.privacy": "Privacy",
"settings.private_visibility": "Private events for groups",
"settings.private_visibility.desc": "How your private events appear to other group members",
"settings.directory_hidden": "Hide my profile",
"settings.directory_hidden.desc": "Don't show me in share/group pickers. You stay visible in the admin user management.",
"settings.private.busy": "Show as \"Busy\"",
"settings.private.hidden": "Hide",
"settings.calendars": "Shared calendar",