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:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user