Set duplicate as current inside ProfileManager
When setting duplicate as current, batch save original profile and duplicate in a single call via profilesToSave. This is to avoid a double call to willUpdateProfiles() when saving Core Data context. In order to set current profile to one that has not been persisted yet (the duplicate), we need to resort to a pendingProfiles map where to look the duplicate up when setting currentProfileId. Either way, iOS 14 cannot handle updating a "hot" change in a presented NavigationLink. Changing currentProfileId binding while in ProfileView messes up navigation completely (multiple push and pop events). Avoid.
This commit is contained in:
parent
943bce5515
commit
4cb18965c9
|
@ -105,7 +105,7 @@ extension OrganizerView {
|
||||||
private func profileMenu(forHeader header: Profile.Header) -> some View {
|
private func profileMenu(forHeader header: Profile.Header) -> some View {
|
||||||
ProfileView.DuplicateButton(
|
ProfileView.DuplicateButton(
|
||||||
header: header,
|
header: header,
|
||||||
switchCurrentProfile: false
|
setAsCurrent: false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ extension ProfileView {
|
||||||
)
|
)
|
||||||
DuplicateButton(
|
DuplicateButton(
|
||||||
header: currentProfile.value.header,
|
header: currentProfile.value.header,
|
||||||
switchCurrentProfile: true
|
setAsCurrent: true
|
||||||
)
|
)
|
||||||
uninstallVPNButton
|
uninstallVPNButton
|
||||||
Divider()
|
Divider()
|
||||||
|
@ -191,12 +191,12 @@ extension ProfileView {
|
||||||
|
|
||||||
private let header: Profile.Header
|
private let header: Profile.Header
|
||||||
|
|
||||||
private let switchCurrentProfile: Bool
|
private let setAsCurrent: Bool
|
||||||
|
|
||||||
init(header: Profile.Header, switchCurrentProfile: Bool) {
|
init(header: Profile.Header, setAsCurrent: Bool) {
|
||||||
profileManager = .shared
|
profileManager = .shared
|
||||||
self.header = header
|
self.header = header
|
||||||
self.switchCurrentProfile = switchCurrentProfile
|
self.setAsCurrent = setAsCurrent
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
@ -208,12 +208,7 @@ extension ProfileView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func duplicateProfile(withId id: UUID) {
|
private func duplicateProfile(withId id: UUID) {
|
||||||
guard let copy = profileManager.duplicateProfile(withId: id) else {
|
profileManager.duplicateProfile(withId: id, setAsCurrent: setAsCurrent)
|
||||||
return
|
|
||||||
}
|
|
||||||
if switchCurrentProfile {
|
|
||||||
profileManager.currentProfileId = copy.id
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,8 @@ public class ProfileManager: ObservableObject {
|
||||||
|
|
||||||
public let didCreateProfile = PassthroughSubject<Profile, Never>()
|
public let didCreateProfile = PassthroughSubject<Profile, Never>()
|
||||||
|
|
||||||
|
private var pendingProfiles: [UUID: Profile] = [:]
|
||||||
|
|
||||||
private var cancellables: Set<AnyCancellable> = []
|
private var cancellables: Set<AnyCancellable> = []
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
|
@ -172,9 +174,13 @@ extension ProfileManager {
|
||||||
|
|
||||||
// IMPORTANT: fetch live copy first (see intents)
|
// IMPORTANT: fetch live copy first (see intents)
|
||||||
if isCurrentProfile(id) {
|
if isCurrentProfile(id) {
|
||||||
pp_log.debug("Profile \(currentProfile.value.logDescription) found in memory")
|
pp_log.debug("Profile \(currentProfile.value.logDescription) found in memory (current profile)")
|
||||||
return currentProfile.value
|
return currentProfile.value
|
||||||
}
|
}
|
||||||
|
if let pending = pendingProfiles[id] {
|
||||||
|
pp_log.debug("Profile \(pending.logDescription) found in memory (pending profile)")
|
||||||
|
return pending
|
||||||
|
}
|
||||||
|
|
||||||
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")
|
||||||
|
@ -239,16 +245,34 @@ extension ProfileManager {
|
||||||
removeProfiles(withIds: ids)
|
removeProfiles(withIds: ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func duplicateProfile(withId id: UUID) -> Profile? {
|
public func duplicateProfile(withId id: UUID, setAsCurrent: Bool) {
|
||||||
guard let source = liveProfile(withId: id) else {
|
guard let source = liveProfile(withId: id) else {
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
let copy = source
|
let copy = source
|
||||||
.withNewId()
|
.withNewId()
|
||||||
.renamedUniquely(withLastUpdate: false)
|
.renamedUniquely(withLastUpdate: false)
|
||||||
|
|
||||||
saveProfile(copy, isActive: nil)
|
//
|
||||||
return copy
|
// XXX: we want to batch save the duplicate together with the former current
|
||||||
|
// profile, which is done in setCurrentProfile(). however, setting
|
||||||
|
// currentProfileId (for navigation), requires the profile ID to exist in
|
||||||
|
// the persistent store when looked up via liveProfile(withId:)
|
||||||
|
//
|
||||||
|
// the pendingProfiles workaround allows setting currentProfileId to a
|
||||||
|
// profile that has not been persisted yet
|
||||||
|
//
|
||||||
|
if setAsCurrent {
|
||||||
|
if #available(iOS 15, *) {
|
||||||
|
pendingProfiles[copy.id] = copy
|
||||||
|
currentProfileId = copy.id
|
||||||
|
pendingProfiles.removeValue(forKey: copy.id)
|
||||||
|
} else {
|
||||||
|
setCurrentProfile(copy)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
strategy.saveProfile(copy)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func persist() {
|
public func persist() {
|
||||||
|
|
Loading…
Reference in New Issue