From 73602d1adede2af1cebd8ad5c648394ba7bfa85d Mon Sep 17 00:00:00 2001 From: psavarmattas Date: Sat, 15 Aug 2026 20:30:31 +0100 Subject: [PATCH] feat(settings): shared save path for preference toggles savePreference(_:) reads the current OutlineUserPreferences, flips one field, sends the whole object via updateUserPreferences. Every Behavior/ Display toggle added next reuses this instead of its own copy of the same read-modify-write. --- Outpost/Features/Account/SettingsView.swift | 48 +++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/Outpost/Features/Account/SettingsView.swift b/Outpost/Features/Account/SettingsView.swift index 27e6920..5002e7d 100644 --- a/Outpost/Features/Account/SettingsView.swift +++ b/Outpost/Features/Account/SettingsView.swift @@ -34,6 +34,8 @@ struct SettingsView: View { @State private var nameErrorMessage: String? @State private var isSavingLanguage = false @State private var languageErrorMessage: String? + @State private var isSavingPreferences = false + @State private var preferencesErrorMessage: String? /// Full Local Sync and cache-clearing both need a real connection to be /// safe — clearing while offline (or letting Full Local Sync think it @@ -248,6 +250,12 @@ struct SettingsView: View { Divider() appearanceRow } + + if let preferencesErrorMessage { + Text(preferencesErrorMessage) + .font(.caption) + .foregroundStyle(.red) + } } .frame(maxWidth: 480, alignment: .leading) .task { await refreshProfile() } @@ -338,6 +346,46 @@ struct SettingsView: View { } } + /// Every toggle in the Preferences page shares this: read + /// `session.userPreferences` (or an empty object if nothing's been set + /// yet), flip the one field the caller cares about, send the *whole* + /// object back — see `UpdateUserPreferencesRequest`'s own doc comment + /// for why the full object rather than a partial diff. + private func savePreference(_ update: (inout OutlineUserPreferences) -> Void) async { + guard let apiClient = session.apiClient, let userId = session.userId else { return } + var preferences = session.userPreferences ?? OutlineUserPreferences() + update(&preferences) + isSavingPreferences = true + defer { isSavingPreferences = false } + do { + let updated = try await apiClient.updateUserPreferences( + UpdateUserPreferencesRequest(id: userId, preferences: preferences) + ) + session.applyUpdatedProfile(updated) + preferencesErrorMessage = nil + } catch { + preferencesErrorMessage = outlineErrorMessage(error, fallback: "Couldn't update your preferences.") + } + } + + private func preferenceToggleRow( + _ title: String, + description: String, + isOn: Bool, + onChange: @escaping (Bool) -> Void + ) -> some View { + VStack(alignment: .leading, spacing: 2) { + Toggle(isOn: Binding(get: { isOn }, set: onChange)) { + VStack(alignment: .leading, spacing: 2) { + Text(title) + Text(description) + .font(.caption) + .foregroundStyle(.secondary) + } + } + } + } + // MARK: - Offline & Sync private var offlineSyncDetail: some View {