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:
parent
18161ed1f1
commit
5d85699ce4
|
@ -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) {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -53,8 +53,6 @@ struct ProfileView: View {
|
||||||
@State private var modalType: ModalType?
|
@State private var modalType: ModalType?
|
||||||
|
|
||||||
@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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue