feat(settings): Preferences delete-account action
Danger subsection, confirmation dialog before calling users.delete (deleteAccount()), signs out locally on success. Closes out the Preferences page.
This commit is contained in:
@@ -36,6 +36,9 @@ struct SettingsView: View {
|
|||||||
@State private var languageErrorMessage: String?
|
@State private var languageErrorMessage: String?
|
||||||
@State private var isSavingPreferences = false
|
@State private var isSavingPreferences = false
|
||||||
@State private var preferencesErrorMessage: String?
|
@State private var preferencesErrorMessage: String?
|
||||||
|
@State private var isShowingDeleteAccountConfirmation = false
|
||||||
|
@State private var isDeletingAccount = false
|
||||||
|
@State private var deleteAccountErrorMessage: String?
|
||||||
|
|
||||||
/// Full Local Sync and cache-clearing both need a real connection to be
|
/// Full Local Sync and cache-clearing both need a real connection to be
|
||||||
/// safe — clearing while offline (or letting Full Local Sync think it
|
/// safe — clearing while offline (or letting Full Local Sync think it
|
||||||
@@ -302,9 +305,49 @@ struct SettingsView: View {
|
|||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundStyle(.red)
|
.foregroundStyle(.red)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preferencesSubsection("Danger") {
|
||||||
|
deleteAccountRow
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.frame(maxWidth: 480, alignment: .leading)
|
.frame(maxWidth: 480, alignment: .leading)
|
||||||
.task { await refreshProfile() }
|
.task { await refreshProfile() }
|
||||||
|
.confirmationDialog(
|
||||||
|
"Delete Account?",
|
||||||
|
isPresented: $isShowingDeleteAccountConfirmation,
|
||||||
|
titleVisibility: .visible
|
||||||
|
) {
|
||||||
|
Button("Delete Account", role: .destructive) { Task { await deleteAccount() } }
|
||||||
|
Button("Cancel", role: .cancel) {}
|
||||||
|
} message: {
|
||||||
|
Text("This is unrecoverable — everything associated with your account will be permanently deleted.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var deleteAccountRow: some View {
|
||||||
|
VStack(alignment: .leading, spacing: 6) {
|
||||||
|
HStack {
|
||||||
|
VStack(alignment: .leading, spacing: 2) {
|
||||||
|
Text("Delete account")
|
||||||
|
Text("You may delete your account at any time, note that this is unrecoverable.")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
if isDeletingAccount {
|
||||||
|
ProgressView().controlSize(.small)
|
||||||
|
} else {
|
||||||
|
Button("Delete…", role: .destructive) { isShowingDeleteAccountConfirmation = true }
|
||||||
|
.buttonStyle(.bordered)
|
||||||
|
.tint(.red)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let deleteAccountErrorMessage {
|
||||||
|
Text(deleteAccountErrorMessage)
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.red)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func preferencesSubsection<Content: View>(
|
private func preferencesSubsection<Content: View>(
|
||||||
@@ -407,6 +450,18 @@ struct SettingsView: View {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func deleteAccount() async {
|
||||||
|
guard let apiClient = session.apiClient else { return }
|
||||||
|
isDeletingAccount = true
|
||||||
|
defer { isDeletingAccount = false }
|
||||||
|
do {
|
||||||
|
try await apiClient.deleteAccount()
|
||||||
|
session.signOut()
|
||||||
|
} catch {
|
||||||
|
deleteAccountErrorMessage = outlineErrorMessage(error, fallback: "Couldn't delete your account.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func saveLanguage(_ code: String) async {
|
private func saveLanguage(_ code: String) async {
|
||||||
guard code != session.userLanguage, let apiClient = session.apiClient, let userId = session.userId else { return }
|
guard code != session.userLanguage, let apiClient = session.apiClient, let userId = session.userId else { return }
|
||||||
isSavingLanguage = true
|
isSavingLanguage = true
|
||||||
|
|||||||
Reference in New Issue
Block a user