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.
This commit is contained in:
Davide 2024-11-03 11:12:19 +01:00 committed by GitHub
parent 3f7ad5bf57
commit fff21c3250
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 40 additions and 21 deletions

View File

@ -23,12 +23,14 @@
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>. // along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
// //
import CommonUtils
import PassepartoutKit import PassepartoutKit
import SwiftUI import SwiftUI
extension ProviderEntityViewProviding where Self: ProviderCompatibleModule, EntityType.Configuration: ProviderConfigurationIdentifiable & Codable { extension ProviderEntityViewProviding where Self: ProviderCompatibleModule, EntityType.Configuration: ProviderConfigurationIdentifiable & Codable {
func vpnProviderEntityView( func vpnProviderEntityView(
with provider: ModuleMetadata.Provider, with provider: ModuleMetadata.Provider,
errorHandler: ErrorHandler,
onSelect: @escaping (any ProviderEntity & Encodable) async throws -> Void onSelect: @escaping (any ProviderEntity & Encodable) async throws -> Void
) -> some View { ) -> some View {
let selectedEntity = try? provider let selectedEntity = try? provider
@ -40,6 +42,7 @@ extension ProviderEntityViewProviding where Self: ProviderCompatibleModule, Enti
return VPNProviderServerCoordinator( return VPNProviderServerCoordinator(
moduleId: id, moduleId: id,
providerId: provider.id, providerId: provider.id,
errorHandler: errorHandler,
selectedEntity: selectedEntity, selectedEntity: selectedEntity,
onSelect: onSelect onSelect: onSelect
) )

View File

@ -23,6 +23,7 @@
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>. // along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
// //
import CommonUtils
import PassepartoutKit import PassepartoutKit
import SwiftUI import SwiftUI
@ -32,6 +33,7 @@ protocol ProviderEntityViewProviding {
@MainActor @MainActor
func providerEntityView( func providerEntityView(
with provider: ModuleMetadata.Provider, with provider: ModuleMetadata.Provider,
errorHandler: ErrorHandler,
onSelect: @escaping (any ProviderEntity & Encodable) async throws -> Void onSelect: @escaping (any ProviderEntity & Encodable) async throws -> Void
) -> EntityContent ) -> EntityContent
} }

View File

@ -25,6 +25,7 @@
import AppLibrary import AppLibrary
import CommonLibrary import CommonLibrary
import CommonUtils
import PassepartoutKit import PassepartoutKit
import SwiftUI import SwiftUI
@ -51,6 +52,9 @@ public struct AppCoordinator: View, AppCoordinatorConforming {
@State @State
private var profilePath = NavigationPath() private var profilePath = NavigationPath()
@StateObject
private var errorHandler: ErrorHandler = .default()
public init( public init(
profileManager: ProfileManager, profileManager: ProfileManager,
tunnel: ExtendedTunnel, tunnel: ExtendedTunnel,
@ -114,6 +118,7 @@ extension AppCoordinator {
tunnel: tunnel, tunnel: tunnel,
registry: registry, registry: registry,
isImporting: $isImporting, isImporting: $isImporting,
errorHandler: errorHandler,
flow: .init( flow: .init(
onEditProfile: { onEditProfile: {
guard let profile = profileManager.profile(withId: $0.id) else { guard let profile = profileManager.profile(withId: $0.id) else {
@ -167,7 +172,8 @@ extension AppCoordinator {
tunnel: tunnel, tunnel: tunnel,
profile: profile, profile: profile,
module: module, module: module,
provider: provider provider: provider,
errorHandler: errorHandler
) )
case .settings: case .settings:

View File

@ -40,14 +40,14 @@ struct ProfileContainerView: View, Routable {
@Binding @Binding
var isImporting: Bool var isImporting: Bool
@ObservedObject
var errorHandler: ErrorHandler
var flow: ProfileFlow? var flow: ProfileFlow?
@StateObject @StateObject
private var interactiveManager = InteractiveManager() private var interactiveManager = InteractiveManager()
@StateObject
private var errorHandler: ErrorHandler = .default()
var body: some View { var body: some View {
debugChanges() debugChanges()
return innerView return innerView
@ -150,7 +150,8 @@ private struct PreviewView: View {
profileManager: .mock, profileManager: .mock,
tunnel: .mock, tunnel: .mock,
registry: Registry(), registry: Registry(),
isImporting: .constant(false) isImporting: .constant(false),
errorHandler: .default()
) )
} }
.withMockEnvironment() .withMockEnvironment()

View File

@ -24,6 +24,7 @@
// //
import AppLibrary import AppLibrary
import CommonUtils
import PassepartoutKit import PassepartoutKit
import SwiftUI import SwiftUI
@ -44,9 +45,15 @@ struct ProviderEntitySelector: View {
let provider: ModuleMetadata.Provider let provider: ModuleMetadata.Provider
let errorHandler: ErrorHandler
var body: some View { var body: some View {
if let viewProvider = module as? any ProviderEntityViewProviding { if let viewProvider = module as? any ProviderEntityViewProviding {
AnyView(viewProvider.providerEntityView(with: provider, onSelect: onSelect)) AnyView(viewProvider.providerEntityView(
with: provider,
errorHandler: errorHandler,
onSelect: onSelect
))
} else { } else {
fatalError("Module got too far without being ProviderEntityViewProviding: \(module)") 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)") pp_log(.app, .info, "Select new provider entity: \(entity)")
var builder = profile.builder() var builder = profile.builder()
do {
try builder.setProviderEntity(entity, forModuleWithId: module.id) try builder.setProviderEntity(entity, forModuleWithId: module.id)
let newProfile = try builder.tryBuild() let newProfile = try builder.tryBuild()
try await profileManager.save(newProfile) try await profileManager.save(newProfile)
Task { // will reconnect via AppContext observation
do {
try await tunnel.connect(with: newProfile, processor: profileProcessor)
} catch { } catch {
pp_log(.app, .error, "Unable to connect with new provider entity: \(error)") pp_log(.app, .error, "Unable to save new provider entity: \(error)")
} throw error
} }
} }
} }

View File

@ -23,6 +23,7 @@
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>. // along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
// //
import CommonUtils
import PassepartoutKit import PassepartoutKit
import SwiftUI import SwiftUI
@ -35,8 +36,9 @@ extension OpenVPNModule.Builder: ModuleViewProviding {
extension OpenVPNModule: ProviderEntityViewProviding { extension OpenVPNModule: ProviderEntityViewProviding {
func providerEntityView( func providerEntityView(
with provider: ModuleMetadata.Provider, with provider: ModuleMetadata.Provider,
errorHandler: ErrorHandler,
onSelect: @escaping (any ProviderEntity & Encodable) async throws -> Void onSelect: @escaping (any ProviderEntity & Encodable) async throws -> Void
) -> some View { ) -> some View {
vpnProviderEntityView(with: provider, onSelect: onSelect) vpnProviderEntityView(with: provider, errorHandler: errorHandler, onSelect: onSelect)
} }
} }

View File

@ -36,13 +36,13 @@ struct VPNProviderServerCoordinator<Configuration>: View where Configuration: Pr
let providerId: ProviderID let providerId: ProviderID
@ObservedObject
var errorHandler: ErrorHandler
let selectedEntity: VPNEntity<Configuration>? let selectedEntity: VPNEntity<Configuration>?
let onSelect: (VPNEntity<Configuration>) async throws -> Void let onSelect: (VPNEntity<Configuration>) async throws -> Void
@StateObject
private var errorHandler: ErrorHandler = .default()
var body: some View { var body: some View {
NavigationStack { NavigationStack {
VPNProviderServerView( VPNProviderServerView(
@ -67,7 +67,6 @@ struct VPNProviderServerCoordinator<Configuration>: View where Configuration: Pr
} }
} }
} }
.withErrorHandler(errorHandler)
} }
} }
} }
@ -77,8 +76,8 @@ private extension VPNProviderServerCoordinator {
Task { Task {
do { do {
let entity = VPNEntity(server: server, preset: preset) let entity = VPNEntity(server: server, preset: preset)
try await onSelect(entity)
dismiss() dismiss()
try await onSelect(entity)
} catch { } catch {
pp_log(.app, .fault, "Unable to select server \(server.serverId) for provider \(server.provider.id): \(error)") pp_log(.app, .fault, "Unable to select server \(server.serverId) for provider \(server.provider.id): \(error)")
errorHandler.handle(error, title: Strings.Global.servers) errorHandler.handle(error, title: Strings.Global.servers)