diff --git a/Passepartout-iOS/Global/Theme+Cells.swift b/Passepartout-iOS/Global/Theme+Cells.swift index b19203cb..9ea26f8d 100644 --- a/Passepartout-iOS/Global/Theme+Cells.swift +++ b/Passepartout-iOS/Global/Theme+Cells.swift @@ -24,6 +24,7 @@ // import UIKit +import TunnelKit extension UITableViewCell { func applyChecked(_ checked: Bool, _ theme: Theme) { @@ -66,7 +67,7 @@ extension SettingTableViewCell { accessoryType = .none } - func applyVPN(_ theme: Theme, with vpnStatus: VPNStatus?) { + func applyVPN(_ theme: Theme, with vpnStatus: VPNStatus?, error: TunnelKitProvider.ProviderError?) { leftTextColor = theme.palette.colorPrimaryText guard let vpnStatus = vpnStatus else { rightText = L10n.Vpn.disabled @@ -83,13 +84,41 @@ extension SettingTableViewCell { rightText = L10n.Vpn.active rightTextColor = theme.palette.colorOn - case .disconnecting: - rightText = L10n.Vpn.disconnecting - rightTextColor = theme.palette.colorIndeterminate - - case .disconnected: - rightText = L10n.Vpn.inactive - rightTextColor = theme.palette.colorOff + case .disconnecting, .disconnected: + var disconnectionReason: String? + if let error = error { + switch error { + case .socketActivity, .timeout: + disconnectionReason = L10n.Vpn.Errors.timeout + + case .dnsFailure: + disconnectionReason = L10n.Vpn.Errors.dns + + case .tlsFailed: + disconnectionReason = L10n.Vpn.Errors.tls + + case .authenticationFailed: + disconnectionReason = L10n.Vpn.Errors.auth + + case .networkChanged: + disconnectionReason = L10n.Vpn.Errors.network + + default: + break + } + } + switch vpnStatus { + case .disconnecting: + rightText = disconnectionReason ?? L10n.Vpn.disconnecting + rightTextColor = theme.palette.colorIndeterminate + + case .disconnected: + rightText = disconnectionReason ?? L10n.Vpn.inactive + rightTextColor = theme.palette.colorOff + + default: + break + } } } } diff --git a/Passepartout-iOS/Scenes/ServiceViewController.swift b/Passepartout-iOS/Scenes/ServiceViewController.swift index bfac173e..40bf56ae 100644 --- a/Passepartout-iOS/Scenes/ServiceViewController.swift +++ b/Passepartout-iOS/Scenes/ServiceViewController.swift @@ -529,7 +529,7 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog } let cell = Cells.setting.dequeue(from: tableView, for: indexPath) - cell.applyVPN(Theme.current, with: vpn.isEnabled ? vpn.status : nil) + cell.applyVPN(Theme.current, with: vpn.isEnabled ? vpn.status : nil, error: service.vpnLastError) cell.leftText = L10n.Service.Cells.ConnectionStatus.caption cell.accessoryType = .none cell.isTappable = false diff --git a/Passepartout/Resources/en.lproj/Localizable.strings b/Passepartout/Resources/en.lproj/Localizable.strings index 362c2ec6..159cadba 100644 --- a/Passepartout/Resources/en.lproj/Localizable.strings +++ b/Passepartout/Resources/en.lproj/Localizable.strings @@ -161,6 +161,12 @@ "vpn.inactive" = "Inactive"; "vpn.disabled" = "Disabled"; +"vpn.errors.timeout" = "Timeout"; +"vpn.errors.auth" = "Auth failed"; +"vpn.errors.tls" = "TLS failed"; +"vpn.errors.dns" = "DNS failed"; +"vpn.errors.network" = "Network changed"; + "issue_reporter.title" = "Submit debug log"; "issue_reporter.message" = "The debug log of your latest connections is crucial to resolve your connectivity issues and is completely anonymous."; "issue_reporter.buttons.accept" = "I understand"; diff --git a/Passepartout/Sources/AppConstants.swift b/Passepartout/Sources/AppConstants.swift index 4e535325..991392b6 100644 --- a/Passepartout/Sources/AppConstants.swift +++ b/Passepartout/Sources/AppConstants.swift @@ -47,6 +47,7 @@ class AppConstants { // builder.debugLogFormat = "$DHH:mm:ss$d $N.$F:$l - $M" builder.debugLogFormat = Log.debugFormat builder.debugLogKey = "LastVPNLog" + builder.lastErrorKey = "LastVPNError" return builder.build() } diff --git a/Passepartout/Sources/Model/ConnectionService.swift b/Passepartout/Sources/Model/ConnectionService.swift index 404ff60b..bf207491 100644 --- a/Passepartout/Sources/Model/ConnectionService.swift +++ b/Passepartout/Sources/Model/ConnectionService.swift @@ -276,6 +276,16 @@ class ConnectionService: Codable { return lines.joined(separator: "\n") } + var vpnLastError: TunnelKitProvider.ProviderError? { + guard let key = tunnelConfiguration.lastErrorKey else { + return nil + } + guard let rawValue = defaults.string(forKey: key) else { + return nil + } + return TunnelKitProvider.ProviderError(rawValue: rawValue) + } + // func eraseVpnLog() { // defaults.removeObject(forKey: Keys.vpnLog) // } diff --git a/Passepartout/Sources/Model/Profiles/HostConnectionProfile.swift b/Passepartout/Sources/Model/Profiles/HostConnectionProfile.swift index 0e683e0c..3941f79f 100644 --- a/Passepartout/Sources/Model/Profiles/HostConnectionProfile.swift +++ b/Passepartout/Sources/Model/Profiles/HostConnectionProfile.swift @@ -60,6 +60,7 @@ class HostConnectionProfile: ConnectionProfile, Codable, Equatable { builder.shouldDebug = configuration.shouldDebug builder.debugLogFormat = configuration.debugLogFormat builder.debugLogKey = configuration.debugLogKey + builder.lastErrorKey = configuration.lastErrorKey return builder.build() } diff --git a/Passepartout/Sources/Model/Profiles/ProviderConnectionProfile.swift b/Passepartout/Sources/Model/Profiles/ProviderConnectionProfile.swift index 7e86c93c..0fdf25d4 100644 --- a/Passepartout/Sources/Model/Profiles/ProviderConnectionProfile.swift +++ b/Passepartout/Sources/Model/Profiles/ProviderConnectionProfile.swift @@ -121,6 +121,7 @@ class ProviderConnectionProfile: ConnectionProfile, Codable, Equatable { builder.shouldDebug = configuration.shouldDebug builder.debugLogFormat = configuration.debugLogFormat builder.debugLogKey = configuration.debugLogKey + builder.lastErrorKey = configuration.lastErrorKey if let address = manualAddress { builder.prefersResolvedAddresses = true diff --git a/Passepartout/Sources/SwiftGen+Strings.swift b/Passepartout/Sources/SwiftGen+Strings.swift index e3bbf1a1..18493a00 100644 --- a/Passepartout/Sources/SwiftGen+Strings.swift +++ b/Passepartout/Sources/SwiftGen+Strings.swift @@ -686,6 +686,19 @@ internal enum L10n { internal static let disconnecting = L10n.tr("Localizable", "vpn.disconnecting") /// Inactive internal static let inactive = L10n.tr("Localizable", "vpn.inactive") + + internal enum Errors { + /// Auth failed + internal static let auth = L10n.tr("Localizable", "vpn.errors.auth") + /// DNS failed + internal static let dns = L10n.tr("Localizable", "vpn.errors.dns") + /// Network changed + internal static let network = L10n.tr("Localizable", "vpn.errors.network") + /// Timeout + internal static let timeout = L10n.tr("Localizable", "vpn.errors.timeout") + /// TLS failed + internal static let tls = L10n.tr("Localizable", "vpn.errors.tls") + } } internal enum Wizards {