Revisit overuse of EnvironmentObject (#794)
The biggest issue is the hidden and scattered use of both Tunnel and ConnectionObserver. Only use the latter, and rename it to ExtendedTunnel for being now a full wrapper around Tunnel (e.g. for .connectionStatus). In general, restrict the use of EnvironmentObject to: - Theme - IAPManager - ProfileProcessor - ProviderManager Always be explicit about: - ProfileManager - ExtendedTunnel Contextually, move some UI entities to the base AppUI target.
This commit is contained in:
parent
33d238270e
commit
590b2790fa
|
@ -83,10 +83,14 @@ extension PassepartoutApp {
|
|||
.withEnvironment(from: context, theme: theme)
|
||||
}
|
||||
MenuBarExtra {
|
||||
AppMenu()
|
||||
AppMenu(
|
||||
profileManager: context.profileManager,
|
||||
profileProcessor: context.profileProcessor,
|
||||
tunnel: context.tunnel
|
||||
)
|
||||
.withEnvironment(from: context, theme: theme)
|
||||
} label: {
|
||||
AppMenuImage(connectionObserver: context.connectionObserver)
|
||||
AppMenuImage(tunnel: context.tunnel)
|
||||
.environmentObject(theme)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,12 +38,10 @@ public final class AppContext: ObservableObject {
|
|||
|
||||
public let profileProcessor: ProfileProcessor
|
||||
|
||||
public let tunnel: Tunnel
|
||||
public let tunnel: ExtendedTunnel
|
||||
|
||||
public let tunnelEnvironment: TunnelEnvironment
|
||||
|
||||
public let connectionObserver: ConnectionObserver
|
||||
|
||||
public let registry: Registry
|
||||
|
||||
public let providerManager: ProviderManager
|
||||
|
@ -65,9 +63,8 @@ public final class AppContext: ObservableObject {
|
|||
self.iapManager = iapManager
|
||||
self.profileManager = profileManager
|
||||
self.profileProcessor = profileProcessor
|
||||
self.tunnel = tunnel
|
||||
self.tunnelEnvironment = tunnelEnvironment
|
||||
connectionObserver = ConnectionObserver(
|
||||
self.tunnel = ExtendedTunnel(
|
||||
tunnel: tunnel,
|
||||
environment: tunnelEnvironment,
|
||||
interval: constants.tunnel.refreshInterval
|
||||
|
@ -79,7 +76,7 @@ public final class AppContext: ObservableObject {
|
|||
|
||||
Task {
|
||||
await iapManager.reloadReceipt()
|
||||
connectionObserver.observeObjects()
|
||||
self.tunnel.observeObjects()
|
||||
profileManager.observeObjects()
|
||||
observeObjects()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// ConnectionObserver.swift
|
||||
// ExtendedTunnel.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 9/7/24.
|
||||
|
@ -29,8 +29,8 @@ import Foundation
|
|||
import PassepartoutKit
|
||||
|
||||
@MainActor
|
||||
public final class ConnectionObserver: ObservableObject {
|
||||
public let tunnel: Tunnel
|
||||
public final class ExtendedTunnel: ObservableObject {
|
||||
private let tunnel: Tunnel
|
||||
|
||||
private let environment: TunnelEnvironment
|
||||
|
||||
|
@ -40,14 +40,10 @@ public final class ConnectionObserver: ObservableObject {
|
|||
environment.environmentValue(forKey: key)
|
||||
}
|
||||
|
||||
public var connectionStatus: ConnectionStatus? {
|
||||
value(forKey: TunnelEnvironmentKeys.connectionStatus)
|
||||
}
|
||||
|
||||
@Published
|
||||
public private(set) var lastErrorCode: PassepartoutError.Code? {
|
||||
didSet {
|
||||
pp_log(.app, .info, "ConnectionObserver.lastErrorCode -> \(lastErrorCode?.rawValue ?? "nil")")
|
||||
pp_log(.app, .info, "ExtendedTunnel.lastErrorCode -> \(lastErrorCode?.rawValue ?? "nil")")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,3 +99,63 @@ public final class ConnectionObserver: ObservableObject {
|
|||
.store(in: &subscriptions)
|
||||
}
|
||||
}
|
||||
|
||||
extension ExtendedTunnel {
|
||||
public var status: TunnelStatus {
|
||||
tunnel.status
|
||||
}
|
||||
|
||||
public var connectionStatus: TunnelStatus {
|
||||
var status = tunnel.status
|
||||
if status == .active, let environmentConnectionStatus {
|
||||
if environmentConnectionStatus == .connected {
|
||||
status = .active
|
||||
} else {
|
||||
status = .activating
|
||||
}
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
private var environmentConnectionStatus: ConnectionStatus? {
|
||||
value(forKey: TunnelEnvironmentKeys.connectionStatus)
|
||||
}
|
||||
}
|
||||
|
||||
extension ExtendedTunnel {
|
||||
public var currentProfile: TunnelCurrentProfile? {
|
||||
tunnel.currentProfile
|
||||
}
|
||||
|
||||
public func prepare(purge: Bool) async throws {
|
||||
try await tunnel.prepare(purge: purge)
|
||||
}
|
||||
|
||||
public func install(_ profile: Profile, processor: ProfileProcessor) async throws {
|
||||
let newProfile = try processor.processed(profile)
|
||||
try await tunnel.install(newProfile, connect: false, title: processor.title)
|
||||
}
|
||||
|
||||
public func connect(with profile: Profile, processor: ProfileProcessor) async throws {
|
||||
let newProfile = try processor.processed(profile)
|
||||
try await tunnel.install(newProfile, connect: true, title: processor.title)
|
||||
}
|
||||
|
||||
public func disconnect() async throws {
|
||||
try await tunnel.disconnect()
|
||||
}
|
||||
|
||||
public func currentLog(parameters: Constants.Log) async -> [String] {
|
||||
let output = try? await tunnel.sendMessage(.localLog(
|
||||
sinceLast: parameters.sinceLast,
|
||||
maxLevel: parameters.maxLevel
|
||||
))
|
||||
switch output {
|
||||
case .debugLog(let log):
|
||||
return log.lines.map(parameters.formatter.formattedLine)
|
||||
|
||||
default:
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,23 +27,26 @@ import Foundation
|
|||
import PassepartoutKit
|
||||
|
||||
@MainActor
|
||||
final class InteractiveManager: ObservableObject {
|
||||
typealias CompletionBlock = (Profile) async throws -> Void
|
||||
public final class InteractiveManager: ObservableObject {
|
||||
public typealias CompletionBlock = (Profile) async throws -> Void
|
||||
|
||||
@Published
|
||||
var isPresented = false
|
||||
public var isPresented = false
|
||||
|
||||
private(set) var editor = ProfileEditor()
|
||||
public private(set) var editor = ProfileEditor()
|
||||
|
||||
private var onComplete: CompletionBlock?
|
||||
|
||||
func present(with profile: Profile, onComplete: CompletionBlock?) {
|
||||
public init() {
|
||||
}
|
||||
|
||||
public func present(with profile: Profile, onComplete: CompletionBlock?) {
|
||||
editor = ProfileEditor(profile: profile)
|
||||
self.onComplete = onComplete
|
||||
isPresented = true
|
||||
}
|
||||
|
||||
func complete() async throws {
|
||||
public func complete() async throws {
|
||||
isPresented = false
|
||||
let newProfile = try editor.build()
|
||||
try await onComplete?(newProfile)
|
|
@ -1,55 +0,0 @@
|
|||
//
|
||||
// Tunnel+Extensions.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 8/11/24.
|
||||
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
//
|
||||
// This file is part of Passepartout.
|
||||
//
|
||||
// Passepartout is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Passepartout is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import CommonLibrary
|
||||
import Foundation
|
||||
import PassepartoutKit
|
||||
|
||||
@MainActor
|
||||
extension Tunnel {
|
||||
public func install(_ profile: Profile, processor: ProfileProcessor) async throws {
|
||||
let newProfile = try processor.processed(profile)
|
||||
try await install(newProfile, connect: false, title: processor.title)
|
||||
}
|
||||
|
||||
public func connect(with profile: Profile, processor: ProfileProcessor) async throws {
|
||||
let newProfile = try processor.processed(profile)
|
||||
try await install(newProfile, connect: true, title: processor.title)
|
||||
}
|
||||
|
||||
public func currentLog(parameters: Constants.Log) async -> [String] {
|
||||
let output = try? await sendMessage(.localLog(
|
||||
sinceLast: parameters.sinceLast,
|
||||
maxLevel: parameters.maxLevel
|
||||
))
|
||||
switch output {
|
||||
case .debugLog(let log):
|
||||
return log.lines.map(parameters.formatter.formattedLine)
|
||||
|
||||
default:
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,12 +30,9 @@ import SwiftUI
|
|||
extension View {
|
||||
public func withEnvironment(from context: AppContext, theme: Theme) -> some View {
|
||||
environmentObject(theme)
|
||||
.environmentObject(context.connectionObserver)
|
||||
.environmentObject(context.iapManager)
|
||||
.environmentObject(context.profileManager)
|
||||
.environmentObject(context.profileProcessor)
|
||||
.environmentObject(context.providerManager)
|
||||
.environmentObject(context.tunnel)
|
||||
}
|
||||
|
||||
public func withMockEnvironment() -> some View {
|
||||
|
|
|
@ -88,18 +88,12 @@ extension ProfileProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
extension Tunnel {
|
||||
public static var mock: Tunnel {
|
||||
extension ExtendedTunnel {
|
||||
public static var mock: ExtendedTunnel {
|
||||
AppContext.mock.tunnel
|
||||
}
|
||||
}
|
||||
|
||||
extension ConnectionObserver {
|
||||
public static var mock: ConnectionObserver {
|
||||
AppContext.mock.connectionObserver
|
||||
}
|
||||
}
|
||||
|
||||
extension ProviderManager {
|
||||
public static var mock: ProviderManager {
|
||||
AppContext.mock.providerManager
|
||||
|
|
|
@ -30,7 +30,7 @@ import PassepartoutKit
|
|||
public protocol AppCoordinatorConforming {
|
||||
init(
|
||||
profileManager: ProfileManager,
|
||||
tunnel: Tunnel,
|
||||
tunnel: ExtendedTunnel,
|
||||
registry: Registry
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
//
|
||||
// TunnelContextProviding.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 9/5/24.
|
||||
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
//
|
||||
// This file is part of Passepartout.
|
||||
//
|
||||
// Passepartout is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Passepartout is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import PassepartoutKit
|
||||
|
||||
public protocol TunnelContextProviding {
|
||||
var connectionObserver: ConnectionObserver { get }
|
||||
}
|
||||
|
||||
@MainActor
|
||||
extension TunnelContextProviding {
|
||||
public var tunnelConnectionStatus: TunnelStatus {
|
||||
var status = connectionObserver.tunnel.status
|
||||
if status == .active, let connectionStatus = connectionObserver.connectionStatus {
|
||||
if connectionStatus == .connected {
|
||||
status = .active
|
||||
} else {
|
||||
status = .activating
|
||||
}
|
||||
}
|
||||
return status
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ import PassepartoutKit
|
|||
public protocol TunnelInstallationProviding {
|
||||
var profileManager: ProfileManager { get }
|
||||
|
||||
var tunnel: Tunnel { get }
|
||||
var tunnel: ExtendedTunnel { get }
|
||||
}
|
||||
|
||||
@MainActor
|
||||
|
|
|
@ -27,32 +27,34 @@ import Foundation
|
|||
import PassepartoutKit
|
||||
import SwiftUI
|
||||
|
||||
struct ConnectionStatusView: View, TunnelContextProviding, ThemeProviding {
|
||||
public struct ConnectionStatusView: View, ThemeProviding {
|
||||
|
||||
@EnvironmentObject
|
||||
var theme: Theme
|
||||
|
||||
@EnvironmentObject
|
||||
var connectionObserver: ConnectionObserver
|
||||
public var theme: Theme
|
||||
|
||||
@ObservedObject
|
||||
var tunnel: Tunnel
|
||||
private var tunnel: ExtendedTunnel
|
||||
|
||||
var body: some View {
|
||||
public init(tunnel: ExtendedTunnel) {
|
||||
self.tunnel = tunnel
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
Text(statusDescription)
|
||||
.foregroundStyle(tunnelStatusColor)
|
||||
.font(.headline)
|
||||
.foregroundStyle(tunnel.statusColor(theme))
|
||||
}
|
||||
}
|
||||
|
||||
private extension ConnectionStatusView {
|
||||
var statusDescription: String {
|
||||
if let lastErrorCode = connectionObserver.lastErrorCode {
|
||||
if let lastErrorCode = tunnel.lastErrorCode {
|
||||
return lastErrorCode.localizedDescription
|
||||
}
|
||||
let status = tunnelConnectionStatus
|
||||
let status = tunnel.connectionStatus
|
||||
switch status {
|
||||
case .active:
|
||||
if let dataCount = connectionObserver.dataCount {
|
||||
if let dataCount = tunnel.dataCount {
|
||||
let down = dataCount.received.descriptionAsDataUnit
|
||||
let up = dataCount.sent.descriptionAsDataUnit
|
||||
return "↓\(down) ↑\(up)"
|
||||
|
@ -75,7 +77,7 @@ private extension ConnectionStatusView {
|
|||
#Preview("Connected") {
|
||||
ConnectionStatusView(tunnel: .mock)
|
||||
.task {
|
||||
try? await Tunnel.mock.connect(with: .mock, processor: .mock)
|
||||
try? await ExtendedTunnel.mock.connect(with: .mock, processor: .mock)
|
||||
}
|
||||
.frame(width: 100, height: 100)
|
||||
.withMockEnvironment()
|
||||
|
@ -94,7 +96,7 @@ private extension ConnectionStatusView {
|
|||
}
|
||||
return ConnectionStatusView(tunnel: .mock)
|
||||
.task {
|
||||
try? await Tunnel.mock.connect(with: profile, processor: .mock)
|
||||
try? await ExtendedTunnel.mock.connect(with: profile, processor: .mock)
|
||||
}
|
||||
.frame(width: 100, height: 100)
|
||||
.withMockEnvironment()
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// TunnelContextProviding+Theme.swift
|
||||
// ExtendedTunnel+Theme.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 9/6/24.
|
||||
|
@ -26,11 +26,12 @@
|
|||
import PassepartoutKit
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
extension TunnelContextProviding where Self: ThemeProviding {
|
||||
var tunnelStatusColor: Color {
|
||||
if connectionObserver.lastErrorCode != nil {
|
||||
switch connectionObserver.tunnel.status {
|
||||
extension ExtendedTunnel {
|
||||
|
||||
@MainActor
|
||||
public func statusColor(_ theme: Theme) -> Color {
|
||||
if lastErrorCode != nil {
|
||||
switch status {
|
||||
case .inactive:
|
||||
return theme.inactiveColor
|
||||
|
||||
|
@ -38,7 +39,7 @@ extension TunnelContextProviding where Self: ThemeProviding {
|
|||
return theme.errorColor
|
||||
}
|
||||
}
|
||||
switch tunnelConnectionStatus {
|
||||
switch connectionStatus {
|
||||
case .active:
|
||||
return theme.activeColor
|
||||
|
|
@ -27,18 +27,15 @@ import PassepartoutKit
|
|||
import SwiftUI
|
||||
import UtilsLibrary
|
||||
|
||||
struct TunnelToggleButton<Label>: View, TunnelContextProviding, ThemeProviding where Label: View {
|
||||
enum Style {
|
||||
public struct TunnelToggleButton<Label>: View, ThemeProviding where Label: View {
|
||||
public enum Style {
|
||||
case plain
|
||||
|
||||
case color
|
||||
}
|
||||
|
||||
@EnvironmentObject
|
||||
var theme: Theme
|
||||
|
||||
@EnvironmentObject
|
||||
var connectionObserver: ConnectionObserver
|
||||
public var theme: Theme
|
||||
|
||||
@EnvironmentObject
|
||||
private var iapManager: IAPManager
|
||||
|
@ -46,25 +43,45 @@ struct TunnelToggleButton<Label>: View, TunnelContextProviding, ThemeProviding w
|
|||
@EnvironmentObject
|
||||
private var profileProcessor: ProfileProcessor
|
||||
|
||||
var style: Style = .plain
|
||||
private let style: Style
|
||||
|
||||
@ObservedObject
|
||||
var tunnel: Tunnel
|
||||
private var tunnel: ExtendedTunnel
|
||||
|
||||
let profile: Profile?
|
||||
private let profile: Profile?
|
||||
|
||||
@Binding
|
||||
var nextProfileId: Profile.ID?
|
||||
private var nextProfileId: Profile.ID?
|
||||
|
||||
let interactiveManager: InteractiveManager
|
||||
private let interactiveManager: InteractiveManager
|
||||
|
||||
let errorHandler: ErrorHandler
|
||||
private let errorHandler: ErrorHandler
|
||||
|
||||
var onProviderEntityRequired: ((Profile) -> Void)?
|
||||
private let onProviderEntityRequired: ((Profile) -> Void)?
|
||||
|
||||
let label: (Bool) -> Label
|
||||
private let label: (Bool) -> Label
|
||||
|
||||
var body: some View {
|
||||
public init(
|
||||
style: Style = .plain,
|
||||
tunnel: ExtendedTunnel,
|
||||
profile: Profile?,
|
||||
nextProfileId: Binding<Profile.ID?>,
|
||||
interactiveManager: InteractiveManager,
|
||||
errorHandler: ErrorHandler,
|
||||
onProviderEntityRequired: ((Profile) -> Void)? = nil,
|
||||
label: @escaping (Bool) -> Label
|
||||
) {
|
||||
self.style = style
|
||||
self.tunnel = tunnel
|
||||
self.profile = profile
|
||||
_nextProfileId = nextProfileId
|
||||
self.interactiveManager = interactiveManager
|
||||
self.errorHandler = errorHandler
|
||||
self.onProviderEntityRequired = onProviderEntityRequired
|
||||
self.label = label
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
Button(action: tryPerform) {
|
||||
label(canConnect)
|
||||
}
|
||||
|
@ -92,7 +109,7 @@ private extension TunnelToggleButton {
|
|||
return .primary
|
||||
|
||||
case .color:
|
||||
return tunnelStatusColor
|
||||
return tunnel.statusColor(theme)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -39,7 +39,7 @@ extension Issue {
|
|||
|
||||
let purchasedProducts: Set<AppProduct>
|
||||
|
||||
let tunnel: Tunnel
|
||||
let tunnel: ExtendedTunnel
|
||||
|
||||
let urlForTunnelLog: URL
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ import UtilsLibrary
|
|||
struct InstalledProfileView: View, Routable {
|
||||
|
||||
@EnvironmentObject
|
||||
var theme: Theme
|
||||
private var theme: Theme
|
||||
|
||||
let layout: ProfilesLayout
|
||||
|
||||
|
@ -39,7 +39,8 @@ struct InstalledProfileView: View, Routable {
|
|||
|
||||
let profile: Profile?
|
||||
|
||||
let tunnel: Tunnel
|
||||
@ObservedObject
|
||||
var tunnel: ExtendedTunnel
|
||||
|
||||
let interactiveManager: InteractiveManager
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ import UtilsLibrary
|
|||
struct ProfileContextMenu: View, Routable {
|
||||
let profileManager: ProfileManager
|
||||
|
||||
let tunnel: Tunnel
|
||||
let tunnel: ExtendedTunnel
|
||||
|
||||
let header: ProfileHeader
|
||||
|
||||
|
|
|
@ -28,21 +28,18 @@ import PassepartoutKit
|
|||
import SwiftUI
|
||||
import UtilsLibrary
|
||||
|
||||
struct ProfileRowView: View, Routable, TunnelContextProviding {
|
||||
struct ProfileRowView: View, Routable {
|
||||
|
||||
@EnvironmentObject
|
||||
private var theme: Theme
|
||||
|
||||
@EnvironmentObject
|
||||
var connectionObserver: ConnectionObserver
|
||||
|
||||
let style: ProfileCardView.Style
|
||||
|
||||
@ObservedObject
|
||||
var profileManager: ProfileManager
|
||||
|
||||
@ObservedObject
|
||||
var tunnel: Tunnel
|
||||
var tunnel: ExtendedTunnel
|
||||
|
||||
let header: ProfileHeader
|
||||
|
||||
|
@ -116,7 +113,7 @@ private extension ProfileRowView {
|
|||
}
|
||||
|
||||
var statusImage: Theme.ImageName {
|
||||
switch tunnelConnectionStatus {
|
||||
switch tunnel.connectionStatus {
|
||||
case .active:
|
||||
return .marked
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ struct TunnelRestartButton<Label>: View where Label: View {
|
|||
private var profileProcessor: ProfileProcessor
|
||||
|
||||
@ObservedObject
|
||||
var tunnel: Tunnel
|
||||
var tunnel: ExtendedTunnel
|
||||
|
||||
let profile: Profile?
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ struct AboutRouterView: View {
|
|||
|
||||
let profileManager: ProfileManager
|
||||
|
||||
let tunnel: Tunnel
|
||||
let tunnel: ExtendedTunnel
|
||||
|
||||
@State
|
||||
var navigationRoute: NavigationRoute?
|
||||
|
@ -63,7 +63,10 @@ extension AboutRouterView {
|
|||
DonateView()
|
||||
|
||||
case .diagnostics:
|
||||
DiagnosticsView()
|
||||
DiagnosticsView(
|
||||
profileManager: profileManager,
|
||||
tunnel: tunnel
|
||||
)
|
||||
|
||||
case .appDebugLog(let title):
|
||||
DebugLogView.withApp(parameters: Constants.shared.log)
|
||||
|
|
|
@ -35,7 +35,7 @@ public struct AppCoordinator: View, AppCoordinatorConforming {
|
|||
|
||||
private let profileManager: ProfileManager
|
||||
|
||||
private let tunnel: Tunnel
|
||||
private let tunnel: ExtendedTunnel
|
||||
|
||||
private let registry: Registry
|
||||
|
||||
|
@ -53,7 +53,7 @@ public struct AppCoordinator: View, AppCoordinatorConforming {
|
|||
|
||||
public init(
|
||||
profileManager: ProfileManager,
|
||||
tunnel: Tunnel,
|
||||
tunnel: ExtendedTunnel,
|
||||
registry: Registry
|
||||
) {
|
||||
self.profileManager = profileManager
|
||||
|
|
|
@ -28,12 +28,12 @@ import PassepartoutKit
|
|||
import SwiftUI
|
||||
import UtilsLibrary
|
||||
|
||||
struct ProfileContainerView: View, Routable, TunnelInstallationProviding {
|
||||
struct ProfileContainerView: View, Routable {
|
||||
let layout: ProfilesLayout
|
||||
|
||||
let profileManager: ProfileManager
|
||||
|
||||
let tunnel: Tunnel
|
||||
let tunnel: ExtendedTunnel
|
||||
|
||||
let registry: Registry
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ struct ProfileGridView: View, Routable, TunnelInstallationProviding {
|
|||
var profileManager: ProfileManager
|
||||
|
||||
@ObservedObject
|
||||
var tunnel: Tunnel
|
||||
var tunnel: ExtendedTunnel
|
||||
|
||||
let interactiveManager: InteractiveManager
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ struct ProfileListView: View, Routable, TunnelInstallationProviding {
|
|||
var profileManager: ProfileManager
|
||||
|
||||
@ObservedObject
|
||||
var tunnel: Tunnel
|
||||
var tunnel: ExtendedTunnel
|
||||
|
||||
let interactiveManager: InteractiveManager
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ struct ProviderEntitySelector: View {
|
|||
var profileManager: ProfileManager
|
||||
|
||||
@ObservedObject
|
||||
var tunnel: Tunnel
|
||||
var tunnel: ExtendedTunnel
|
||||
|
||||
let profile: Profile
|
||||
|
||||
|
|
|
@ -32,19 +32,22 @@ import SwiftUI
|
|||
|
||||
public struct AppMenu: View {
|
||||
|
||||
@EnvironmentObject
|
||||
@ObservedObject
|
||||
private var profileManager: ProfileManager
|
||||
|
||||
@EnvironmentObject
|
||||
@ObservedObject
|
||||
private var profileProcessor: ProfileProcessor
|
||||
|
||||
@EnvironmentObject
|
||||
private var tunnel: Tunnel
|
||||
@ObservedObject
|
||||
private var tunnel: ExtendedTunnel
|
||||
|
||||
@StateObject
|
||||
private var model = Model()
|
||||
|
||||
public init() {
|
||||
public init(profileManager: ProfileManager, profileProcessor: ProfileProcessor, tunnel: ExtendedTunnel) {
|
||||
self.profileManager = profileManager
|
||||
self.profileProcessor = profileProcessor
|
||||
self.tunnel = tunnel
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
|
|
|
@ -28,17 +28,17 @@
|
|||
import PassepartoutKit
|
||||
import SwiftUI
|
||||
|
||||
public struct AppMenuImage: View, TunnelContextProviding {
|
||||
public struct AppMenuImage: View {
|
||||
|
||||
@ObservedObject
|
||||
public var connectionObserver: ConnectionObserver
|
||||
private var tunnel: ExtendedTunnel
|
||||
|
||||
public init(connectionObserver: ConnectionObserver) {
|
||||
self.connectionObserver = connectionObserver
|
||||
public init(tunnel: ExtendedTunnel) {
|
||||
self.tunnel = tunnel
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
ThemeMenuImage(tunnelConnectionStatus.imageName)
|
||||
ThemeMenuImage(tunnel.connectionStatus.imageName)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ extension DebugLogView {
|
|||
}
|
||||
}
|
||||
|
||||
static func withTunnel(_ tunnel: Tunnel, parameters: Constants.Log) -> DebugLogView {
|
||||
static func withTunnel(_ tunnel: ExtendedTunnel, parameters: Constants.Log) -> DebugLogView {
|
||||
DebugLogView {
|
||||
await tunnel.currentLog(parameters: parameters)
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import AppLibrary
|
||||
import CommonLibrary
|
||||
import PassepartoutKit
|
||||
import SwiftUI
|
||||
|
@ -41,15 +42,16 @@ struct DiagnosticsView: View {
|
|||
@EnvironmentObject
|
||||
private var theme: Theme
|
||||
|
||||
@EnvironmentObject
|
||||
private var connectionObserver: ConnectionObserver
|
||||
|
||||
@EnvironmentObject
|
||||
private var iapManager: IAPManager
|
||||
|
||||
@AppStorage(AppPreference.logsPrivateData.key, store: .appGroup)
|
||||
private var logsPrivateData = false
|
||||
|
||||
let profileManager: ProfileManager
|
||||
|
||||
let tunnel: ExtendedTunnel
|
||||
|
||||
var availableTunnelLogs: () async -> [LogEntry] = {
|
||||
await Task.detached {
|
||||
PassepartoutConfiguration.shared.availableLogs(at: BundleConfiguration.urlForTunnelLog)
|
||||
|
@ -128,7 +130,7 @@ private extension DiagnosticsView {
|
|||
}
|
||||
|
||||
var openVPNSection: some View {
|
||||
connectionObserver.value(forKey: TunnelEnvironmentKeys.OpenVPN.serverConfiguration)
|
||||
tunnel.value(forKey: TunnelEnvironmentKeys.OpenVPN.serverConfiguration)
|
||||
.map { cfg in
|
||||
Group {
|
||||
NavigationLink(Strings.Views.Diagnostics.Openvpn.Rows.serverConfiguration) {
|
||||
|
@ -143,7 +145,8 @@ private extension DiagnosticsView {
|
|||
var reportIssueSection: some View {
|
||||
Section {
|
||||
ReportIssueButton(
|
||||
tunnel: connectionObserver.tunnel,
|
||||
profileManager: profileManager,
|
||||
tunnel: tunnel,
|
||||
title: Strings.Views.Diagnostics.ReportIssue.title,
|
||||
purchasedProducts: iapManager.purchasedProducts,
|
||||
isUnableToEmail: $isPresentingUnableToEmail
|
||||
|
@ -190,7 +193,7 @@ private extension DiagnosticsView {
|
|||
}
|
||||
|
||||
#Preview {
|
||||
DiagnosticsView {
|
||||
DiagnosticsView(profileManager: .mock, tunnel: .mock) {
|
||||
[
|
||||
.init(date: Date(), url: URL(string: "http://one.com")!),
|
||||
.init(date: Date().addingTimeInterval(-60), url: URL(string: "http://two.com")!),
|
||||
|
|
|
@ -29,13 +29,13 @@ import SwiftUI
|
|||
|
||||
struct ReportIssueButton {
|
||||
|
||||
@EnvironmentObject
|
||||
private var profileManager: ProfileManager
|
||||
|
||||
@EnvironmentObject
|
||||
private var providerManager: ProviderManager
|
||||
|
||||
let tunnel: Tunnel
|
||||
@ObservedObject
|
||||
var profileManager: ProfileManager
|
||||
|
||||
let tunnel: ExtendedTunnel
|
||||
|
||||
let title: String
|
||||
|
||||
|
|
|
@ -29,10 +29,10 @@ import SwiftUI
|
|||
struct ProviderContentModifier<Entity, ProviderRows>: ViewModifier where Entity: ProviderEntity, Entity.Configuration: ProviderConfigurationIdentifiable & Codable, ProviderRows: View {
|
||||
|
||||
@EnvironmentObject
|
||||
private var providerManager: ProviderManager
|
||||
private var iapManager: IAPManager
|
||||
|
||||
@EnvironmentObject
|
||||
private var iapManager: IAPManager
|
||||
private var providerManager: ProviderManager
|
||||
|
||||
let apis: [APIMapper]
|
||||
|
||||
|
|
|
@ -32,11 +32,11 @@ import SwiftUI
|
|||
public struct AppCoordinator: View, AppCoordinatorConforming {
|
||||
private let profileManager: ProfileManager
|
||||
|
||||
private let tunnel: Tunnel
|
||||
private let tunnel: ExtendedTunnel
|
||||
|
||||
private let registry: Registry
|
||||
|
||||
public init(profileManager: ProfileManager, tunnel: Tunnel, registry: Registry) {
|
||||
public init(profileManager: ProfileManager, tunnel: ExtendedTunnel, registry: Registry) {
|
||||
self.profileManager = profileManager
|
||||
self.tunnel = tunnel
|
||||
self.registry = registry
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// ConnectionObserverTests.swift
|
||||
// ExtendedTunnelTests.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 9/12/24.
|
||||
|
@ -28,15 +28,15 @@ import Foundation
|
|||
import PassepartoutKit
|
||||
import XCTest
|
||||
|
||||
final class ConnectionObserverTests: XCTestCase {
|
||||
final class ExtendedTunnelTests: XCTestCase {
|
||||
}
|
||||
|
||||
@MainActor
|
||||
extension ConnectionObserverTests {
|
||||
extension ExtendedTunnelTests {
|
||||
func test_givenTunnel_whenDisconnectWithError_thenPublishesLastErrorCode() async throws {
|
||||
let env = InMemoryEnvironment()
|
||||
let tunnel = Tunnel(strategy: FakeTunnelStrategy(environment: env))
|
||||
let sut = ConnectionObserver(tunnel: tunnel, environment: env, interval: 0.1)
|
||||
let sut = ExtendedTunnel(tunnel: tunnel, environment: env, interval: 0.1)
|
||||
sut.observeObjects()
|
||||
|
||||
let profile = try Profile.Builder().tryBuild()
|
||||
|
@ -51,7 +51,7 @@ extension ConnectionObserverTests {
|
|||
func test_givenTunnel_whenConnect_thenPublishesDataCount() async throws {
|
||||
let env = InMemoryEnvironment()
|
||||
let tunnel = Tunnel(strategy: FakeTunnelStrategy(environment: env))
|
||||
let sut = ConnectionObserver(tunnel: tunnel, environment: env, interval: 0.1)
|
||||
let sut = ExtendedTunnel(tunnel: tunnel, environment: env, interval: 0.1)
|
||||
sut.observeObjects()
|
||||
|
||||
let profile = try Profile.Builder().tryBuild()
|
Loading…
Reference in New Issue