Delete profile from Organizer

Swipe to delete. Address a couple things on iPad though:

1. Also check provider availability before showing view

fatalError hit on iPad when navigating from a ready provider
profile to a non-ready one. Similar to when navigating between
different VPN protocols.

2. Suppress assertion on deleted profile

Deleting current profile via swipe seems to re-render a new
NavigationLink with the deleted profile, which results in
loading a deleted profile and hitting the assertion. Not sure if
this is a programming error or a glitch in ForEach.
This commit is contained in:
Davide De Rosa 2022-04-19 08:33:22 +02:00
parent 18161ed1f1
commit 5d85699ce4
4 changed files with 20 additions and 48 deletions

View File

@ -78,6 +78,7 @@ extension OrganizerView {
List { List {
Section { Section {
ForEach(headers.sorted(), content: navigationLink(forHeader:)) ForEach(headers.sorted(), content: navigationLink(forHeader:))
.onDelete(perform: removeProfiles)
.onAppear(perform: selectActiveProfile) .onAppear(perform: selectActiveProfile)
} }
} }
@ -157,6 +158,15 @@ extension OrganizerView.ProfilesList {
} }
} }
private func removeProfiles(_ indexSet: IndexSet) {
let headers = profileManager.headers.sorted()
var toDelete: [UUID] = []
indexSet.forEach {
toDelete.append(headers[$0].id)
}
profileManager.removeProfiles(withIds: toDelete)
}
private func dismissSelectionIfDeleted(headers: [Profile.Header]) { private func dismissSelectionIfDeleted(headers: [Profile.Header]) {
if let selectedProfileId = selectedProfileId, if let selectedProfileId = selectedProfileId,
!profileManager.isExistingProfile(withId: selectedProfileId) { !profileManager.isExistingProfile(withId: selectedProfileId) {

View File

@ -44,7 +44,7 @@ extension ProfileView {
var body: some View { var body: some View {
debugChanges() debugChanges()
return Group { return Group {
if !isEmpty { if canDisplay {
mainView mainView
} else { } else {
EmptyView() EmptyView()
@ -52,8 +52,14 @@ extension ProfileView {
} }
} }
private var isEmpty: Bool { private var canDisplay: Bool {
currentProfile.value.isPlaceholder || !currentProfile.value.isProvider guard !currentProfile.value.isPlaceholder else {
return false
}
guard let providerName = currentProfile.value.header.providerName else {
return false
}
return providerManager.isAvailable(providerName, vpnProtocol: currentProfile.value.currentVPNProtocol)
} }
private var mainView: some View { private var mainView: some View {

View File

@ -54,8 +54,6 @@ struct ProfileView: View {
@State private var isLoaded = false @State private var isLoaded = false
@State private var isAskingRemoveProfile = false
init(header: Profile.Header?) { init(header: Profile.Header?) {
let profileManager: ProfileManager = .shared let profileManager: ProfileManager = .shared
@ -98,7 +96,6 @@ struct ProfileView: View {
) )
ExtraSection(currentProfile: profileManager.currentProfile) ExtraSection(currentProfile: profileManager.currentProfile)
DiagnosticsSection(currentProfile: profileManager.currentProfile) DiagnosticsSection(currentProfile: profileManager.currentProfile)
removeProfileSection
UninstallVPNSection() UninstallVPNSection()
} }
@ -152,47 +149,6 @@ struct ProfileView: View {
} }
} }
private var removeProfileSection: some View {
Section {
Button {
isAskingRemoveProfile = true
} label: {
Label(L10n.Organizer.Alerts.RemoveProfile.title, systemImage: themeDeleteImage)
}.foregroundColor(themeErrorColor)
.actionSheet(isPresented: $isAskingRemoveProfile) {
ActionSheet(
title: Text(L10n.Organizer.Alerts.RemoveProfile.message(header.name)),
message: nil,
buttons: [
.destructive(Text(L10n.Organizer.Alerts.RemoveProfile.title), action: confirmRemoveProfile),
.cancel(Text(L10n.Global.Strings.cancel))
]
)
}
}
}
private func confirmRemoveProfile() {
withAnimation {
removeProfile()
}
}
private func removeProfile() {
guard profileManager.isExistingProfile(withId: header.id) else {
assertionFailure("Deleting non-existent profile \(header.name)")
return
}
IntentDispatcher.forgetProfile(withHeader: header)
profileManager.removeProfiles(withIds: [header.id])
// XXX: iOS 14, NavigationLink removal via header removal in OrganizerView+Profiles doesn't pop
if #available(iOS 15, *) {
} else {
presentationMode.wrappedValue.dismiss()
}
}
private func loadProfileIfNeeded() { private func loadProfileIfNeeded() {
guard !isLoaded else { guard !isLoaded else {
return return

View File

@ -162,7 +162,7 @@ extension ProfileManager {
} }
guard let profile = strategy.profile(withId: id) else { guard let profile = strategy.profile(withId: id) else {
assertionFailure("Profile in headers yet not found in persistent store") // assertionFailure("Profile in headers yet not found in persistent store")
return nil return nil
} }
guard availabilityFilter?(profile.header) ?? true else { guard availabilityFilter?(profile.header) ?? true else {