Resolve issues when toggling/switching profiles (#747)
- Drop logic behind connection button tasks, let the library handle concurrency - Drop AppContext observation of saved profiles for reconnection, let save() actively decide - NETunnelStrategy and NETunnelManagerRepository are now a single entity - Avoid flickering when toggling same profile
This commit is contained in:
parent
696f076ac5
commit
39bdf145e8
|
@ -41,7 +41,7 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "git@github.com:passepartoutvpn/passepartoutkit-source",
|
"location" : "git@github.com:passepartoutvpn/passepartoutkit-source",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "ead8d1652fc6875f8865a1c8610b0cc35828dee6"
|
"revision" : "288ccbf1e1a984c8dc3a7cd2fd23d3d3fc4464f6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,7 +28,7 @@ let package = Package(
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", from: "0.9.0"),
|
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", from: "0.9.0"),
|
||||||
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "ead8d1652fc6875f8865a1c8610b0cc35828dee6"),
|
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "288ccbf1e1a984c8dc3a7cd2fd23d3d3fc4464f6"),
|
||||||
// .package(path: "../../../passepartoutkit-source"),
|
// .package(path: "../../../passepartoutkit-source"),
|
||||||
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", from: "0.9.1"),
|
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", from: "0.9.1"),
|
||||||
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", revision: "031863a1cd683962a7dfe68e20b91fa820a1ecce"),
|
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", revision: "031863a1cd683962a7dfe68e20b91fa820a1ecce"),
|
||||||
|
|
|
@ -65,13 +65,10 @@ public final class NEProfileRepository: ProfileRepository {
|
||||||
profilesSubject.eraseToAnyPublisher()
|
profilesSubject.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
// unused in app, rely on Tunnel.prepare()
|
|
||||||
public func loadProfiles(purge: Bool) async throws {
|
|
||||||
try await repository.load(purge: purge)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func saveProfile(_ profile: Profile) async throws {
|
public func saveProfile(_ profile: Profile) async throws {
|
||||||
try await repository.save(profile, connect: false, title: title)
|
|
||||||
|
// FIXME: #379, save + reconnect in some scenarios
|
||||||
|
try await repository.save(profile, forConnecting: false, title: title)
|
||||||
if let index = profilesSubject.value.firstIndex(where: { $0.id == profile.id }) {
|
if let index = profilesSubject.value.firstIndex(where: { $0.id == profile.id }) {
|
||||||
profilesSubject.value[index] = profile
|
profilesSubject.value[index] = profile
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -115,9 +115,6 @@ private extension AppContext {
|
||||||
try await tunnel.disconnect()
|
try await tunnel.disconnect()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if tunnel.status == .active {
|
|
||||||
try await tunnel.connect(with: profile, processor: profileProcessor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,9 +41,6 @@ struct TunnelRestartButton<Label>: View where Label: View {
|
||||||
|
|
||||||
let label: () -> Label
|
let label: () -> Label
|
||||||
|
|
||||||
@State
|
|
||||||
private var pendingTask: Task<Void, Error>?
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Button {
|
Button {
|
||||||
guard let profile else {
|
guard let profile else {
|
||||||
|
@ -52,10 +49,11 @@ struct TunnelRestartButton<Label>: View where Label: View {
|
||||||
guard tunnel.status == .active else {
|
guard tunnel.status == .active else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pendingTask?.cancel()
|
Task {
|
||||||
pendingTask = Task {
|
|
||||||
do {
|
do {
|
||||||
try await tunnel.connect(with: profile, processor: profileProcessor)
|
try await tunnel.connect(with: profile, processor: profileProcessor)
|
||||||
|
} catch is CancellationError {
|
||||||
|
//
|
||||||
} catch {
|
} catch {
|
||||||
errorHandler.handle(
|
errorHandler.handle(
|
||||||
error,
|
error,
|
||||||
|
|
|
@ -62,9 +62,6 @@ struct TunnelToggleButton<Label>: View, TunnelContextProviding, ThemeProviding w
|
||||||
|
|
||||||
let label: (Bool) -> Label
|
let label: (Bool) -> Label
|
||||||
|
|
||||||
@State
|
|
||||||
private var pendingTask: Task<Void, Error>?
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Button(action: tryPerform) {
|
Button(action: tryPerform) {
|
||||||
label(canConnect)
|
label(canConnect)
|
||||||
|
@ -100,12 +97,13 @@ private extension TunnelToggleButton {
|
||||||
|
|
||||||
private extension TunnelToggleButton {
|
private extension TunnelToggleButton {
|
||||||
func tryPerform() {
|
func tryPerform() {
|
||||||
pendingTask?.cancel()
|
Task {
|
||||||
pendingTask = Task {
|
|
||||||
guard let profile else {
|
guard let profile else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !isInstalled {
|
||||||
nextProfileId = profile.id
|
nextProfileId = profile.id
|
||||||
|
}
|
||||||
defer {
|
defer {
|
||||||
if nextProfileId == profile.id {
|
if nextProfileId == profile.id {
|
||||||
nextProfileId = nil
|
nextProfileId = nil
|
||||||
|
@ -137,6 +135,8 @@ private extension TunnelToggleButton {
|
||||||
} else {
|
} else {
|
||||||
try await tunnel.connect(with: profile, processor: profileProcessor)
|
try await tunnel.connect(with: profile, processor: profileProcessor)
|
||||||
}
|
}
|
||||||
|
} catch is CancellationError {
|
||||||
|
//
|
||||||
} catch {
|
} catch {
|
||||||
errorHandler.handle(
|
errorHandler.handle(
|
||||||
error,
|
error,
|
||||||
|
|
|
@ -182,19 +182,19 @@ private extension ProfileManager {
|
||||||
|
|
||||||
extension Tunnel {
|
extension Tunnel {
|
||||||
static let shared = Tunnel(
|
static let shared = Tunnel(
|
||||||
strategy: NETunnelStrategy(repository: ProfileManager.neRepository)
|
strategy: ProfileManager.neStrategy
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension ProfileManager {
|
private extension ProfileManager {
|
||||||
static let localProfileRepository: ProfileRepository = {
|
static let localProfileRepository: ProfileRepository = {
|
||||||
NEProfileRepository(repository: neRepository) {
|
NEProfileRepository(repository: neStrategy) {
|
||||||
ProfileManager.sharedTitle($0)
|
ProfileManager.sharedTitle($0)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
static let neRepository: NETunnelManagerRepository = {
|
static let neStrategy: NETunnelStrategy = {
|
||||||
NETunnelManagerRepository(
|
NETunnelStrategy(
|
||||||
bundleIdentifier: BundleConfiguration.mainString(for: .tunnelId),
|
bundleIdentifier: BundleConfiguration.mainString(for: .tunnelId),
|
||||||
coder: Registry.sharedProtocolCoder,
|
coder: Registry.sharedProtocolCoder,
|
||||||
environment: .shared
|
environment: .shared
|
||||||
|
|
Loading…
Reference in New Issue