66 lines
2.8 KiB
Swift
66 lines
2.8 KiB
Swift
import Foundation
|
|
import OrgSocialKit
|
|
|
|
/// Orchestrates Guideline 5.1.1(v) account deletion.
|
|
///
|
|
/// - For vfile accounts, calls `POST /delete` on the host (irreversible
|
|
/// server-side removal of the social.org file).
|
|
/// - For every method, wipes all locally-stored credentials, drafts,
|
|
/// block / mute lists and cached app state from `UserDefaults`. The
|
|
/// only key preserved is `eulaAcceptedVersion`, since the EULA
|
|
/// acceptance is per-install, not per-account.
|
|
enum AccountDeletion {
|
|
|
|
/// Deletes the account on the host (vfile only) and then wipes every
|
|
/// `UserDefaults` key the app writes. Throws if the remote delete fails;
|
|
/// in that case the local data is left intact so the user can retry.
|
|
@MainActor
|
|
static func deleteCurrentAccount() async throws {
|
|
let defaults = UserDefaults.standard
|
|
let methodRaw = defaults.string(forKey: "uploadMethod") ?? UploadMethod.vfile.rawValue
|
|
let method = UploadMethod(rawValue: methodRaw) ?? .vfile
|
|
|
|
if method == .vfile,
|
|
let raw = defaults.string(forKey: "vfileURL"),
|
|
let url = URL(string: raw),
|
|
!raw.isEmpty {
|
|
try await HostDeleteClient().delete(vfileURL: url)
|
|
}
|
|
|
|
await PushRegistration.shared.unsubscribe()
|
|
wipeLocalState()
|
|
}
|
|
|
|
/// Resets every persisted UserDefaults key except `eulaAcceptedVersion`
|
|
/// and clears in-memory observable singletons. `eulaAcceptedVersion` is
|
|
/// per-install so the user is not re-prompted after deleting their
|
|
/// account on the same device.
|
|
///
|
|
/// The keys that drive `@AppStorage`-backed gates (`publicFeedURL`,
|
|
/// `vfileURL`, `githubToken`, `codebergToken`, `webdavURL`) are
|
|
/// explicitly written to "" first so the property wrappers publish a
|
|
/// change notification. `removeObject` alone is not always observed by
|
|
/// `@AppStorage` in iOS 17 / 18, which would leave the root view stuck
|
|
/// on the configured tabs after deletion.
|
|
@MainActor
|
|
static func wipeLocalState() {
|
|
let defaults = UserDefaults.standard
|
|
let credentialKeys: Set<String> = [
|
|
"publicFeedURL", "vfileURL",
|
|
"githubToken", "codebergToken", "webdavURL"
|
|
]
|
|
let dict = defaults.dictionaryRepresentation()
|
|
for key in dict.keys where key != "eulaAcceptedVersion" && !credentialKeys.contains(key) {
|
|
defaults.removeObject(forKey: key)
|
|
}
|
|
// Credential keys are written last with `set("")` so `@AppStorage`
|
|
// wrappers observing them publish a change. `removeObject` alone is
|
|
// not always observed by `@AppStorage` in iOS 17 / 18, which would
|
|
// leave the root view stuck on the configured tabs after deletion.
|
|
for key in credentialKeys {
|
|
defaults.set("", forKey: key)
|
|
}
|
|
BlockList.shared.reset()
|
|
}
|
|
}
|