Better account deletion / logout handling of local data
This commit is contained in:
24
apple/OmnivoreKit/Sources/App/Views/DeleteAccountView.swift
Normal file
24
apple/OmnivoreKit/Sources/App/Views/DeleteAccountView.swift
Normal file
@ -0,0 +1,24 @@
|
||||
import Models
|
||||
import Services
|
||||
import SwiftUI
|
||||
import Utils
|
||||
import Views
|
||||
|
||||
struct DeleteAccountView: View {
|
||||
@EnvironmentObject var dataService: DataService
|
||||
@EnvironmentObject var authenticator: Authenticator
|
||||
|
||||
public var body: some View {
|
||||
VStack(alignment: .center) {
|
||||
Text("Deleting account...")
|
||||
ProgressView()
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.task {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {
|
||||
authenticator.logout(dataService: dataService, isAccountDeletion: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
33
apple/OmnivoreKit/Sources/App/Views/LogoutView.swift
Normal file
33
apple/OmnivoreKit/Sources/App/Views/LogoutView.swift
Normal file
@ -0,0 +1,33 @@
|
||||
import Models
|
||||
import Services
|
||||
import SwiftUI
|
||||
import Utils
|
||||
import Views
|
||||
|
||||
struct LogoutView: View {
|
||||
@EnvironmentObject var dataService: DataService
|
||||
@EnvironmentObject var authenticator: Authenticator
|
||||
@Environment(\.openURL) var openURL
|
||||
|
||||
let deletedAccountConfirmationMessage = "Your account has been deleted. Additional steps may be needed if Sign in with Apple was used to register."
|
||||
|
||||
public var body: some View {
|
||||
VStack(alignment: .center) {
|
||||
Text("Logging out...")
|
||||
ProgressView()
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.task {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {
|
||||
authenticator.logout(dataService: dataService)
|
||||
}
|
||||
}
|
||||
.alert(deletedAccountConfirmationMessage, isPresented: $authenticator.showAppleRevokeTokenAlert) {
|
||||
Button("View Details") {
|
||||
openURL(URL(string: "https://support.apple.com/en-us/HT210426")!)
|
||||
}
|
||||
Button(LocalText.dismissButton) { self.authenticator.showAppleRevokeTokenAlert = false }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -196,7 +196,7 @@ struct ProfileView: View {
|
||||
primaryButton: .destructive(Text(LocalText.genericConfirm)) {
|
||||
dismiss()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
|
||||
authenticator.logout(dataService: dataService)
|
||||
authenticator.beginLogout()
|
||||
}
|
||||
},
|
||||
secondaryButton: .cancel()
|
||||
|
||||
@ -52,9 +52,13 @@ struct InnerRootView: View {
|
||||
if authenticator.isLoggedIn {
|
||||
PrimaryContentView()
|
||||
} else {
|
||||
WelcomeView()
|
||||
.accessibilityElement()
|
||||
.accessibilityIdentifier("welcomeView")
|
||||
if authenticator.isLoggingOut {
|
||||
LogoutView()
|
||||
} else {
|
||||
WelcomeView()
|
||||
.accessibilityElement()
|
||||
.accessibilityIdentifier("welcomeView")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import CoreData
|
||||
import Foundation
|
||||
import GoogleSignIn
|
||||
import Models
|
||||
@ -18,6 +19,7 @@ public final class Authenticator: ObservableObject {
|
||||
}
|
||||
|
||||
@Published public internal(set) var isLoggedIn: Bool
|
||||
@Published public internal(set) var isLoggingOut = false
|
||||
@Published public var showAppleRevokeTokenAlert = false
|
||||
|
||||
let networker: Networker
|
||||
@ -37,15 +39,21 @@ public final class Authenticator: ObservableObject {
|
||||
ValetKey.authToken.value()
|
||||
}
|
||||
|
||||
public func beginLogout() {
|
||||
isLoggingOut = true
|
||||
isLoggedIn = false
|
||||
}
|
||||
|
||||
public func logout(dataService: DataService, isAccountDeletion: Bool = false) {
|
||||
dataService.resetLocalStorage()
|
||||
|
||||
clearCreds()
|
||||
Authenticator.unregisterIntercomUser?()
|
||||
isLoggedIn = false
|
||||
showAppleRevokeTokenAlert = isAccountDeletion
|
||||
EventTracker.reset()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {
|
||||
dataService.resetLocalStorage()
|
||||
}
|
||||
|
||||
isLoggedIn = false
|
||||
isLoggingOut = false
|
||||
}
|
||||
|
||||
public func clearCreds() {
|
||||
|
||||
@ -128,6 +128,17 @@ public final class DataService: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
func deleteAllEntities(entityName: String, inContext context: NSManagedObjectContext) {
|
||||
let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
|
||||
let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)
|
||||
do {
|
||||
try context.execute(deleteRequest)
|
||||
try context.save()
|
||||
} catch {
|
||||
print("Error deleting all \(entityName) items.", error)
|
||||
}
|
||||
}
|
||||
|
||||
private func clearDownloadedFiles() {
|
||||
let relevantTypes = ["pdf", "mp3", "speechMarks"]
|
||||
let fileMgr = FileManager()
|
||||
@ -176,6 +187,15 @@ public final class DataService: ObservableObject {
|
||||
}
|
||||
|
||||
public func resetLocalStorage() {
|
||||
viewContext.perform {
|
||||
// We want to specify the order of deleting items to better handle relationships
|
||||
let entities = ["LibraryItem", "Viewer", "Filter", "Highlight", "NewsletterEmail",
|
||||
"LinkedItemLabel", "RecentSearchItem", "Recommendation", "RecommendationGroup", "UserProfile"]
|
||||
entities.forEach { entityName in
|
||||
self.deleteAllEntities(entityName: entityName, inContext: self.viewContext)
|
||||
}
|
||||
}
|
||||
|
||||
lastItemSyncTime = Date(timeIntervalSinceReferenceDate: 0)
|
||||
|
||||
clearCoreData()
|
||||
|
||||
Reference in New Issue
Block a user