From fff21c3250dfdc6dfa2947ad7ecd15f49a371408 Mon Sep 17 00:00:00 2001 From: Davide Date: Sun, 3 Nov 2024 11:12:19 +0100 Subject: [PATCH] Late dismissal after changing active provider server (#804) The dismissal action waited until the current connection was disconnected. Consider that AppContext makes the explicit .connect() redundant, reconnection is already happening after saving a profile while connected. --- ...oviderEntityViewProviding+Extensions.swift | 3 +++ .../ProviderEntityViewProviding.swift | 2 ++ .../AppUIMain/Views/App/AppCoordinator.swift | 8 +++++- .../Views/App/ProfileContainerView.swift | 9 ++++--- .../Views/App/ProviderEntitySelector.swift | 26 ++++++++++++------- .../Extensions/OpenVPNModule+Extensions.swift | 4 ++- .../VPNProviderServerCoordinator.swift | 9 +++---- 7 files changed, 40 insertions(+), 21 deletions(-) diff --git a/Passepartout/Library/Sources/AppUIMain/Extensions/ProviderEntityViewProviding+Extensions.swift b/Passepartout/Library/Sources/AppUIMain/Extensions/ProviderEntityViewProviding+Extensions.swift index 6b5df32a..d14499d9 100644 --- a/Passepartout/Library/Sources/AppUIMain/Extensions/ProviderEntityViewProviding+Extensions.swift +++ b/Passepartout/Library/Sources/AppUIMain/Extensions/ProviderEntityViewProviding+Extensions.swift @@ -23,12 +23,14 @@ // along with Passepartout. If not, see . // +import CommonUtils import PassepartoutKit import SwiftUI extension ProviderEntityViewProviding where Self: ProviderCompatibleModule, EntityType.Configuration: ProviderConfigurationIdentifiable & Codable { func vpnProviderEntityView( with provider: ModuleMetadata.Provider, + errorHandler: ErrorHandler, onSelect: @escaping (any ProviderEntity & Encodable) async throws -> Void ) -> some View { let selectedEntity = try? provider @@ -40,6 +42,7 @@ extension ProviderEntityViewProviding where Self: ProviderCompatibleModule, Enti return VPNProviderServerCoordinator( moduleId: id, providerId: provider.id, + errorHandler: errorHandler, selectedEntity: selectedEntity, onSelect: onSelect ) diff --git a/Passepartout/Library/Sources/AppUIMain/Protocols/ProviderEntityViewProviding.swift b/Passepartout/Library/Sources/AppUIMain/Protocols/ProviderEntityViewProviding.swift index 6b6dd996..bc45486c 100644 --- a/Passepartout/Library/Sources/AppUIMain/Protocols/ProviderEntityViewProviding.swift +++ b/Passepartout/Library/Sources/AppUIMain/Protocols/ProviderEntityViewProviding.swift @@ -23,6 +23,7 @@ // along with Passepartout. If not, see . // +import CommonUtils import PassepartoutKit import SwiftUI @@ -32,6 +33,7 @@ protocol ProviderEntityViewProviding { @MainActor func providerEntityView( with provider: ModuleMetadata.Provider, + errorHandler: ErrorHandler, onSelect: @escaping (any ProviderEntity & Encodable) async throws -> Void ) -> EntityContent } diff --git a/Passepartout/Library/Sources/AppUIMain/Views/App/AppCoordinator.swift b/Passepartout/Library/Sources/AppUIMain/Views/App/AppCoordinator.swift index d52f9518..bc2389ae 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/App/AppCoordinator.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/App/AppCoordinator.swift @@ -25,6 +25,7 @@ import AppLibrary import CommonLibrary +import CommonUtils import PassepartoutKit import SwiftUI @@ -51,6 +52,9 @@ public struct AppCoordinator: View, AppCoordinatorConforming { @State private var profilePath = NavigationPath() + @StateObject + private var errorHandler: ErrorHandler = .default() + public init( profileManager: ProfileManager, tunnel: ExtendedTunnel, @@ -114,6 +118,7 @@ extension AppCoordinator { tunnel: tunnel, registry: registry, isImporting: $isImporting, + errorHandler: errorHandler, flow: .init( onEditProfile: { guard let profile = profileManager.profile(withId: $0.id) else { @@ -167,7 +172,8 @@ extension AppCoordinator { tunnel: tunnel, profile: profile, module: module, - provider: provider + provider: provider, + errorHandler: errorHandler ) case .settings: diff --git a/Passepartout/Library/Sources/AppUIMain/Views/App/ProfileContainerView.swift b/Passepartout/Library/Sources/AppUIMain/Views/App/ProfileContainerView.swift index e6955c9b..bb75cf53 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/App/ProfileContainerView.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/App/ProfileContainerView.swift @@ -40,14 +40,14 @@ struct ProfileContainerView: View, Routable { @Binding var isImporting: Bool + @ObservedObject + var errorHandler: ErrorHandler + var flow: ProfileFlow? @StateObject private var interactiveManager = InteractiveManager() - @StateObject - private var errorHandler: ErrorHandler = .default() - var body: some View { debugChanges() return innerView @@ -150,7 +150,8 @@ private struct PreviewView: View { profileManager: .mock, tunnel: .mock, registry: Registry(), - isImporting: .constant(false) + isImporting: .constant(false), + errorHandler: .default() ) } .withMockEnvironment() diff --git a/Passepartout/Library/Sources/AppUIMain/Views/App/ProviderEntitySelector.swift b/Passepartout/Library/Sources/AppUIMain/Views/App/ProviderEntitySelector.swift index 49d6a6a1..aea5b94d 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/App/ProviderEntitySelector.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/App/ProviderEntitySelector.swift @@ -24,6 +24,7 @@ // import AppLibrary +import CommonUtils import PassepartoutKit import SwiftUI @@ -44,9 +45,15 @@ struct ProviderEntitySelector: View { let provider: ModuleMetadata.Provider + let errorHandler: ErrorHandler + var body: some View { if let viewProvider = module as? any ProviderEntityViewProviding { - AnyView(viewProvider.providerEntityView(with: provider, onSelect: onSelect)) + AnyView(viewProvider.providerEntityView( + with: provider, + errorHandler: errorHandler, + onSelect: onSelect + )) } else { fatalError("Module got too far without being ProviderEntityViewProviding: \(module)") } @@ -58,16 +65,15 @@ private extension ProviderEntitySelector { pp_log(.app, .info, "Select new provider entity: \(entity)") var builder = profile.builder() - try builder.setProviderEntity(entity, forModuleWithId: module.id) - let newProfile = try builder.tryBuild() - try await profileManager.save(newProfile) + do { + try builder.setProviderEntity(entity, forModuleWithId: module.id) + let newProfile = try builder.tryBuild() + try await profileManager.save(newProfile) - Task { - do { - try await tunnel.connect(with: newProfile, processor: profileProcessor) - } catch { - pp_log(.app, .error, "Unable to connect with new provider entity: \(error)") - } + // will reconnect via AppContext observation + } catch { + pp_log(.app, .error, "Unable to save new provider entity: \(error)") + throw error } } } diff --git a/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/OpenVPNModule+Extensions.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/OpenVPNModule+Extensions.swift index 4016f7dc..2171c415 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/OpenVPNModule+Extensions.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/OpenVPNModule+Extensions.swift @@ -23,6 +23,7 @@ // along with Passepartout. If not, see . // +import CommonUtils import PassepartoutKit import SwiftUI @@ -35,8 +36,9 @@ extension OpenVPNModule.Builder: ModuleViewProviding { extension OpenVPNModule: ProviderEntityViewProviding { func providerEntityView( with provider: ModuleMetadata.Provider, + errorHandler: ErrorHandler, onSelect: @escaping (any ProviderEntity & Encodable) async throws -> Void ) -> some View { - vpnProviderEntityView(with: provider, onSelect: onSelect) + vpnProviderEntityView(with: provider, errorHandler: errorHandler, onSelect: onSelect) } } diff --git a/Passepartout/Library/Sources/AppUIMain/Views/Provider/VPNProviderServerCoordinator.swift b/Passepartout/Library/Sources/AppUIMain/Views/Provider/VPNProviderServerCoordinator.swift index 5f271c88..85ecb63b 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/Provider/VPNProviderServerCoordinator.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/Provider/VPNProviderServerCoordinator.swift @@ -36,13 +36,13 @@ struct VPNProviderServerCoordinator: View where Configuration: Pr let providerId: ProviderID + @ObservedObject + var errorHandler: ErrorHandler + let selectedEntity: VPNEntity? let onSelect: (VPNEntity) async throws -> Void - @StateObject - private var errorHandler: ErrorHandler = .default() - var body: some View { NavigationStack { VPNProviderServerView( @@ -67,7 +67,6 @@ struct VPNProviderServerCoordinator: View where Configuration: Pr } } } - .withErrorHandler(errorHandler) } } } @@ -77,8 +76,8 @@ private extension VPNProviderServerCoordinator { Task { do { let entity = VPNEntity(server: server, preset: preset) - try await onSelect(entity) dismiss() + try await onSelect(entity) } catch { pp_log(.app, .fault, "Unable to select server \(server.serverId) for provider \(server.provider.id): \(error)") errorHandler.handle(error, title: Strings.Global.servers)