diff options
| author | mojganii <mojgan.jelodar@codic.se> | 2024-12-11 15:13:09 +0100 |
|---|---|---|
| committer | Jon Petersson <jon.petersson@mullvad.net> | 2025-01-13 09:34:43 +0100 |
| commit | a330c48b39db414b38332f5dd45de77337d53a89 (patch) | |
| tree | 34a784965804d4cd1de73db91e16adb9051eb84c /ios/MullvadVPN | |
| parent | cde728046d456248549f11a790a3a4916848bb18 (diff) | |
| download | mullvadvpn-a330c48b39db414b38332f5dd45de77337d53a89.tar.xz mullvadvpn-a330c48b39db414b38332f5dd45de77337d53a89.zip | |
Add FeatureIndicatorsView
Diffstat (limited to 'ios/MullvadVPN')
13 files changed, 437 insertions, 31 deletions
diff --git a/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift b/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift index d7e5dd557b..6fbb0690cb 100644 --- a/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift @@ -486,7 +486,8 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo private func makeTunnelCoordinator() -> TunnelCoordinator { let tunnelCoordinator = TunnelCoordinator( tunnelManager: tunnelManager, - outgoingConnectionService: outgoingConnectionService + outgoingConnectionService: outgoingConnectionService, + ipOverrideRepository: ipOverrideRepository ) tunnelCoordinator.showSelectLocationPicker = { [weak self] in diff --git a/ios/MullvadVPN/Coordinators/TunnelCoordinator.swift b/ios/MullvadVPN/Coordinators/TunnelCoordinator.swift index 42f8fba106..7a8145ddca 100644 --- a/ios/MullvadVPN/Coordinators/TunnelCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/TunnelCoordinator.swift @@ -6,6 +6,7 @@ // Copyright © 2023 Mullvad VPN AB. All rights reserved. // +import MullvadSettings import Routing import UIKit @@ -27,13 +28,15 @@ class TunnelCoordinator: Coordinator, Presenting { init( tunnelManager: TunnelManager, - outgoingConnectionService: OutgoingConnectionServiceHandling + outgoingConnectionService: OutgoingConnectionServiceHandling, + ipOverrideRepository: IPOverrideRepositoryProtocol ) { self.tunnelManager = tunnelManager let interactor = TunnelViewControllerInteractor( tunnelManager: tunnelManager, - outgoingConnectionService: outgoingConnectionService + outgoingConnectionService: outgoingConnectionService, + ipOverrideRepository: ipOverrideRepository ) controller = TunnelViewController(interactor: interactor) diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipFeatures.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipFeatures.swift new file mode 100644 index 0000000000..c005b3f080 --- /dev/null +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipFeatures.swift @@ -0,0 +1,88 @@ +// +// ChipFeatures.swift +// MullvadVPN +// +// Created by Mojgan on 2024-12-06. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// +import Foundation +import MullvadSettings +import SwiftUI + +protocol ChipFeature { + var isEnabled: Bool { get } + var name: LocalizedStringKey { get } +} + +struct DaitaFeature: ChipFeature { + let settings: LatestTunnelSettings + + var isEnabled: Bool { + settings.daita.daitaState.isEnabled + } + + var name: LocalizedStringKey { + LocalizedStringKey("DAITA") + } +} + +struct QuantumResistanceFeature: ChipFeature { + let settings: LatestTunnelSettings + var isEnabled: Bool { + settings.tunnelQuantumResistance.isEnabled + } + + var name: LocalizedStringKey { + LocalizedStringKey("Quantum resistance") + } +} + +struct MultihopFeature: ChipFeature { + let settings: LatestTunnelSettings + var isEnabled: Bool { + settings.tunnelMultihopState.isEnabled + } + + var name: LocalizedStringKey { + LocalizedStringKey("Multihop") + } +} + +struct ObfuscationFeature: ChipFeature { + let settings: LatestTunnelSettings + + var isEnabled: Bool { + settings.wireGuardObfuscation.state.isEnabled + } + + var name: LocalizedStringKey { + LocalizedStringKey("Obfuscation") + } +} + +struct DNSFeature: ChipFeature { + let settings: LatestTunnelSettings + + var isEnabled: Bool { + settings.dnsSettings.enableCustomDNS || !settings.dnsSettings.blockingOptions.isEmpty + } + + var name: LocalizedStringKey { + if !settings.dnsSettings.blockingOptions.isEmpty { + return LocalizedStringKey("DNS content blockers") + } + return LocalizedStringKey("Custom DNS") + } +} + +struct IPOverrideFeature: ChipFeature { + let overrides: [IPOverride] + + var isEnabled: Bool { + !overrides.isEmpty + } + + var name: LocalizedStringKey { + LocalizedStringKey("Server IP override") + } +} diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipContainerView.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipContainerView.swift new file mode 100644 index 0000000000..f06124ed48 --- /dev/null +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipContainerView.swift @@ -0,0 +1,95 @@ +// +// ChipContainerView.swift +// MullvadVPN +// +// Created by Mojgan on 2024-12-05. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import SwiftUI + +struct ChipContainerView<ViewModel>: View where ViewModel: ChipViewModelProtocol { + @ObservedObject var viewModel: ViewModel + + @State var chipHeight: CGFloat = 0 + @State var fullContainerHeight: CGFloat = 0 + @State var visibleContainerHeight: CGFloat = 0 + + var body: some View { + GeometryReader { geo in + let containerWidth = geo.size.width + let chipsOverflow = !viewModel.isExpanded && (fullContainerHeight > chipHeight) + let numberOfChips = chipsOverflow ? 2 : viewModel.chips.count + + HStack { + ZStack(alignment: .topLeading) { + createChipViews(chips: Array(viewModel.chips.prefix(numberOfChips)), containerWidth: containerWidth) + } + .sizeOfView { visibleContainerHeight = $0.height } + + if chipsOverflow { + Text(LocalizedStringKey("\(viewModel.chips.count - numberOfChips) more...")) + .font(.subheadline) + .lineLimit(1) + .foregroundStyle(UIColor.primaryTextColor.color) + .padding(.bottom, 12) + } + + Spacer() + } + .background(preRenderViewSize(containerWidth: containerWidth)) + }.frame(height: visibleContainerHeight) + } + + // Renders all chips on screen, in this case specifically to get their combined height. + // Used to determine if content would overflow if view was not expanded and should + // only be called from a background modifier. + private func preRenderViewSize(containerWidth: CGFloat) -> some View { + ZStack(alignment: .topLeading) { + createChipViews(chips: viewModel.chips, containerWidth: containerWidth) + } + .hidden() + .sizeOfView { fullContainerHeight = $0.height } + } + + private func createChipViews(chips: [ChipModel], containerWidth: CGFloat) -> some View { + var width = CGFloat.zero + var height = CGFloat.zero + + return ForEach(chips) { data in + ChipView(item: data) + .padding(EdgeInsets(top: 6, leading: 0, bottom: 6, trailing: 8)) + .alignmentGuide(.leading) { dimension in + if abs(width - dimension.width) > containerWidth { + width = 0 + height -= dimension.height + } + let result = width + if data.id == chips.last!.id { + width = 0 + } else { + width -= dimension.width + } + return result + } + .alignmentGuide(.top) { _ in + let result = height + if data.id == chips.last!.id { + height = 0 + } + return result + } + .sizeOfView { chipHeight = $0.height } + } + } +} + +#Preview("Normal") { + ChipContainerView(viewModel: MockFeatureIndicatorsViewModel()) + .background(UIColor.secondaryColor.color) +} + +#Preview("Expanded") { + ChipContainerView(viewModel: MockFeatureIndicatorsViewModel(isExpanded: true)) + .background(UIColor.secondaryColor.color) +} diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipModel.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipModel.swift new file mode 100644 index 0000000000..c1e990a1b1 --- /dev/null +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipModel.swift @@ -0,0 +1,15 @@ +// +// FeatureChipModel.swift +// MullvadVPN +// +// Created by Mojgan on 2024-12-05. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import SwiftUI + +struct ChipModel: Identifiable { + let id = UUID() + let name: LocalizedStringKey +} diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipView.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipView.swift new file mode 100644 index 0000000000..6d6614973f --- /dev/null +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipView.swift @@ -0,0 +1,40 @@ +// +// FeatureChipView.swift +// MullvadVPN +// +// Created by Mojgan on 2024-12-05. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import SwiftUI + +struct ChipView: View { + let item: ChipModel + var body: some View { + Text(item.name) + .font(.subheadline) + .lineLimit(1) + .foregroundStyle(UIColor.primaryTextColor.color) + .padding(.horizontal, 8) + .padding(.vertical, 4) + .background( + RoundedRectangle(cornerRadius: 8.0) + .stroke( + UIColor.primaryColor.color, + lineWidth: 1 + ) + .background( + RoundedRectangle(cornerRadius: 8.0) + .fill(UIColor.secondaryColor.color) + ) + ) + } +} + +#Preview { + ZStack { + ChipView(item: ChipModel(name: LocalizedStringKey("Example"))) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(UIColor.secondaryColor.color) +} diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipViewModelProtocol.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipViewModelProtocol.swift new file mode 100644 index 0000000000..65e3b0ccef --- /dev/null +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipViewModelProtocol.swift @@ -0,0 +1,32 @@ +// +// ChipViewModelProtocol.swift +// MullvadVPN +// +// Created by Mojgan on 2024-12-05. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import SwiftUI + +protocol ChipViewModelProtocol: ObservableObject { + var chips: [ChipModel] { get } + var isExpanded: Bool { get set } +} + +class MockFeatureIndicatorsViewModel: ChipViewModelProtocol { + @Published var chips: [ChipModel] = [ + ChipModel(name: LocalizedStringKey("DAITA")), + ChipModel(name: LocalizedStringKey("Obfuscation")), + ChipModel(name: LocalizedStringKey("Quantum resistance")), + ChipModel(name: LocalizedStringKey("Multihop")), + ChipModel(name: LocalizedStringKey("DNS content blockers")), + ChipModel(name: LocalizedStringKey("Custom DNS")), + ChipModel(name: LocalizedStringKey("Server IP override")), + ] + + @Published var isExpanded: Bool + + init(isExpanded: Bool = false) { + self.isExpanded = isExpanded + } +} diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView.swift index 03980fb361..3a1bf7d9af 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView.swift @@ -6,12 +6,14 @@ // Copyright © 2024 Mullvad VPN AB. All rights reserved. // +import MullvadSettings import SwiftUI typealias ButtonAction = (ConnectionViewViewModel.TunnelControlAction) -> Void struct ConnectionView: View { @StateObject var viewModel: ConnectionViewViewModel + @StateObject var indicatorsViewModel: FeatureIndicatorsViewModel var action: ButtonAction? var onContentUpdate: (() -> Void)? @@ -27,6 +29,11 @@ struct ConnectionView: View { VStack(alignment: .leading, spacing: 16) { ConnectionPanel(viewModel: viewModel) + + if !indicatorsViewModel.chips.isEmpty { + FeatureIndicatorsView(viewModel: indicatorsViewModel) + } + ButtonPanel(viewModel: viewModel, action: action) } .padding(16) @@ -34,17 +41,24 @@ struct ConnectionView: View { .cornerRadius(12) .padding(16) } - .onReceive(viewModel.$tunnelState, perform: { _ in + .padding(.bottom, 8) // Adding some spacing so to not overlap with the map legal link. + .onReceive( + indicatorsViewModel.$isExpanded + .combineLatest( + viewModel.$tunnelState, + viewModel.$showsActivityIndicator + ) + ) { _ in onContentUpdate?() - }) - .onReceive(viewModel.$showsActivityIndicator, perform: { _ in - onContentUpdate?() - }) + } } } #Preview { - ConnectionView(viewModel: ConnectionViewViewModel(tunnelState: .disconnected)) { action in + ConnectionView( + viewModel: ConnectionViewViewModel(tunnelState: .disconnected), + indicatorsViewModel: FeatureIndicatorsViewModel(tunnelSettings: LatestTunnelSettings(), ipOverrides: []) + ) { action in print(action) } .background(UIColor.secondaryColor.color) diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FI_TunnelViewController.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FI_TunnelViewController.swift index b70c3a9ffa..9aed890041 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FI_TunnelViewController.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FI_TunnelViewController.swift @@ -6,8 +6,10 @@ // Copyright © 2024 Mullvad VPN AB. All rights reserved. // +import Combine import MapKit import MullvadLogging +import MullvadSettings import MullvadTypes import SwiftUI @@ -17,7 +19,8 @@ class FI_TunnelViewController: UIViewController, RootContainment { private let logger = Logger(label: "TunnelViewController") private let interactor: TunnelViewControllerInteractor private var tunnelState: TunnelState = .disconnected - private var viewModel = ConnectionViewViewModel(tunnelState: .disconnected) + private var connectionViewViewModel: ConnectionViewViewModel + private var indicatorsViewViewModel: FeatureIndicatorsViewModel private var connectionView: ConnectionView private var connectionController: UIHostingController<ConnectionView>? @@ -48,7 +51,18 @@ class FI_TunnelViewController: UIViewController, RootContainment { init(interactor: TunnelViewControllerInteractor) { self.interactor = interactor - connectionView = ConnectionView(viewModel: self.viewModel) + + tunnelState = interactor.tunnelStatus.state + connectionViewViewModel = ConnectionViewViewModel(tunnelState: tunnelState) + indicatorsViewViewModel = FeatureIndicatorsViewModel( + tunnelSettings: interactor.tunnelSettings, + ipOverrides: interactor.ipOverrides + ) + + connectionView = ConnectionView( + viewModel: self.connectionViewViewModel, + indicatorsViewModel: self.indicatorsViewViewModel + ) super.init(nibName: nil, bundle: nil) @@ -73,10 +87,17 @@ class FI_TunnelViewController: UIViewController, RootContainment { interactor.didUpdateTunnelStatus = { [weak self] tunnelStatus in self?.setTunnelState(tunnelStatus.state, animated: true) - self?.viewModel.tunnelState = tunnelStatus.state self?.view.setNeedsLayout() } + interactor.didUpdateTunnelSettings = { [weak self] tunnelSettings in + self?.indicatorsViewViewModel.tunnelSettings = tunnelSettings + } + + interactor.didUpdateIpOverrides = { [weak self] overrides in + self?.indicatorsViewViewModel.ipOverrides = overrides + } + connectionView.action = { [weak self] action in switch action { case .connect: @@ -102,10 +123,6 @@ class FI_TunnelViewController: UIViewController, RootContainment { addMapController() addContentView() - - tunnelState = interactor.tunnelStatus.state - viewModel.tunnelState = tunnelState - updateMap(animated: false) } @@ -125,6 +142,8 @@ class FI_TunnelViewController: UIViewController, RootContainment { private func setTunnelState(_ tunnelState: TunnelState, animated: Bool) { self.tunnelState = tunnelState + connectionViewViewModel.tunnelState = tunnelState + setNeedsHeaderBarStyleAppearanceUpdate() guard isViewLoaded else { return } @@ -137,17 +156,17 @@ class FI_TunnelViewController: UIViewController, RootContainment { case let .connecting(tunnelRelays, _, _): mapViewController.removeLocationMarker() mapViewController.setCenter(tunnelRelays?.exit.location.geoCoordinate, animated: animated) - viewModel.showsActivityIndicator = true + connectionViewViewModel.showsActivityIndicator = true case let .reconnecting(tunnelRelays, _, _), let .negotiatingEphemeralPeer(tunnelRelays, _, _, _): mapViewController.removeLocationMarker() mapViewController.setCenter(tunnelRelays.exit.location.geoCoordinate, animated: animated) - viewModel.showsActivityIndicator = true + connectionViewViewModel.showsActivityIndicator = true case let .connected(tunnelRelays, _, _): let center = tunnelRelays.exit.location.geoCoordinate mapViewController.setCenter(center, animated: animated) { - self.viewModel.showsActivityIndicator = false + self.connectionViewViewModel.showsActivityIndicator = false // Connection can change during animation, so make sure we're still connected before adding marker. if case .connected = self.tunnelState { @@ -157,16 +176,16 @@ class FI_TunnelViewController: UIViewController, RootContainment { case .pendingReconnect: mapViewController.removeLocationMarker() - viewModel.showsActivityIndicator = true + connectionViewViewModel.showsActivityIndicator = true case .waitingForConnectivity, .error: mapViewController.removeLocationMarker() - viewModel.showsActivityIndicator = false + connectionViewViewModel.showsActivityIndicator = false case .disconnected, .disconnecting: mapViewController.removeLocationMarker() mapViewController.setCenter(nil, animated: animated) - viewModel.showsActivityIndicator = false + connectionViewViewModel.showsActivityIndicator = false } } diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FeatureIndicatorsView.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FeatureIndicatorsView.swift new file mode 100644 index 0000000000..eb1a29ea81 --- /dev/null +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FeatureIndicatorsView.swift @@ -0,0 +1,31 @@ +// +// FeaturesIndicatorsView.swift +// MullvadVPN +// +// Created by Mojgan on 2024-12-06. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import SwiftUI + +struct FeatureIndicatorsView<ViewModel>: View where ViewModel: ChipViewModelProtocol { + @ObservedObject var viewModel: ViewModel + + var body: some View { + VStack(alignment: .leading, spacing: 0) { + Text(LocalizedStringKey("Active features")) + .font(.footnote.weight(.semibold)) + .foregroundStyle(UIColor.primaryTextColor.color.opacity(0.6)) + + ChipContainerView(viewModel: viewModel) + .onTapGesture { + viewModel.isExpanded.toggle() + } + } + } +} + +#Preview("FeatureIndicatorsView") { + FeatureIndicatorsView(viewModel: MockFeatureIndicatorsViewModel(isExpanded: true)) + .background(UIColor.secondaryColor.color) +} diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FeatureIndicatorsViewModel.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FeatureIndicatorsViewModel.swift new file mode 100644 index 0000000000..42376b4560 --- /dev/null +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FeatureIndicatorsViewModel.swift @@ -0,0 +1,37 @@ +// +// FeatureIndicatorsViewModel.swift +// MullvadVPN +// +// Created by Mojgan on 2024-12-05. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import MullvadSettings + +class FeatureIndicatorsViewModel: ChipViewModelProtocol { + @Published var tunnelSettings: LatestTunnelSettings + @Published var ipOverrides: [IPOverride] + @Published var isExpanded = false + + init(tunnelSettings: LatestTunnelSettings, ipOverrides: [IPOverride], isExpanded: Bool = false) { + self.tunnelSettings = tunnelSettings + self.ipOverrides = ipOverrides + self.isExpanded = isExpanded + } + + var chips: [ChipModel] { + let features: [ChipFeature] = [ + DaitaFeature(settings: tunnelSettings), + QuantumResistanceFeature(settings: tunnelSettings), + MultihopFeature(settings: tunnelSettings), + ObfuscationFeature(settings: tunnelSettings), + DNSFeature(settings: tunnelSettings), + IPOverrideFeature(overrides: ipOverrides), + ] + + return features + .filter { $0.isEnabled } + .map { ChipModel(name: $0.name) } + } +} diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelViewControllerInteractor.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelViewControllerInteractor.swift index 47b75fd7d5..ef902e637a 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/TunnelViewControllerInteractor.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/TunnelViewControllerInteractor.swift @@ -6,7 +6,7 @@ // Copyright © 2022 Mullvad VPN AB. All rights reserved. // -import Foundation +import Combine import MullvadSettings import MullvadTypes @@ -15,9 +15,13 @@ final class TunnelViewControllerInteractor { private let outgoingConnectionService: OutgoingConnectionServiceHandling private var tunnelObserver: TunnelObserver? private var outgoingConnectionTask: Task<Void, Error>? + private var ipOverrideRepository: IPOverrideRepositoryProtocol + private var cancellables: Set<Combine.AnyCancellable> = [] var didUpdateTunnelStatus: ((TunnelStatus) -> Void)? var didUpdateDeviceState: ((_ deviceState: DeviceState, _ previousDeviceState: DeviceState) -> Void)? + var didUpdateTunnelSettings: ((LatestTunnelSettings) -> Void)? + var didUpdateIpOverrides: (([IPOverride]) -> Void)? var didGetOutGoingAddress: (@MainActor (OutgoingConnectionInfo) -> Void)? var tunnelStatus: TunnelStatus { @@ -28,16 +32,26 @@ final class TunnelViewControllerInteractor { tunnelManager.deviceState } + var tunnelSettings: LatestTunnelSettings { + tunnelManager.settings + } + + var ipOverrides: [IPOverride] { + ipOverrideRepository.fetchAll() + } + deinit { outgoingConnectionTask?.cancel() } init( tunnelManager: TunnelManager, - outgoingConnectionService: OutgoingConnectionServiceHandling + outgoingConnectionService: OutgoingConnectionServiceHandling, + ipOverrideRepository: IPOverrideRepositoryProtocol ) { self.tunnelManager = tunnelManager self.outgoingConnectionService = outgoingConnectionService + self.ipOverrideRepository = ipOverrideRepository let tunnelObserver = TunnelBlockObserver( didUpdateTunnelStatus: { [weak self] _, tunnelStatus in @@ -56,12 +70,21 @@ final class TunnelViewControllerInteractor { }, didUpdateDeviceState: { [weak self] _, deviceState, previousDeviceState in self?.didUpdateDeviceState?(deviceState, previousDeviceState) + }, + didUpdateTunnelSettings: { [weak self] _, tunnelSettings in + self?.didUpdateTunnelSettings?(tunnelSettings) } ) tunnelManager.addObserver(tunnelObserver) self.tunnelObserver = tunnelObserver + + ipOverrideRepository.overridesPublisher + .sink { [weak self] overrides in + self?.didUpdateIpOverrides?(overrides) + } + .store(in: &cancellables) } func startTunnel() { diff --git a/ios/MullvadVPN/Views/MainButtonStyle.swift b/ios/MullvadVPN/Views/MainButtonStyle.swift index f638c87ac2..f32a27fa06 100644 --- a/ios/MullvadVPN/Views/MainButtonStyle.swift +++ b/ios/MullvadVPN/Views/MainButtonStyle.swift @@ -22,16 +22,16 @@ struct MainButtonStyle: ButtonStyle { .padding(.horizontal, 8) .frame(height: 44) .foregroundColor( - configuration.isPressed - ? UIColor.secondaryTextColor.color - : disabled - ? UIColor.primaryTextColor.withAlphaComponent(0.2).color - : UIColor.primaryTextColor.color + disabled + ? UIColor.primaryTextColor.withAlphaComponent(0.2).color + : UIColor.primaryTextColor.color ) .background( disabled - ? style.color.darkened(by: 0.6) - : style.color + ? style.disabledColor + : configuration.isPressed + ? style.pressedColor + : style.color ) .font(.body.weight(.semibold)) } @@ -53,5 +53,13 @@ extension MainButtonStyle { Color(UIColor.successColor) } } + + var pressedColor: Color { + color.darkened(by: 0.4)! + } + + var disabledColor: Color { + color.darkened(by: 0.6)! + } } } |
