Skip to content

Commit

Permalink
Split select location view into two sections
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon Petersson committed Feb 29, 2024
1 parent b74b165 commit 8c2109a
Show file tree
Hide file tree
Showing 24 changed files with 943 additions and 472 deletions.
1 change: 1 addition & 0 deletions ios/.swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ disabled_rules:
- type_body_length
- opening_brace # Differs from Google swift guidelines enforced by swiftformat
- trailing_comma
- switch_case_alignment # Enables expressions such as [return switch location {}]

Check warning on line 10 in ios/.swiftlint.yml

View workflow job for this annotation

GitHub Actions / check-formatting

10:27 [comments] too few spaces before comment
opt_in_rules:
- empty_count

Expand Down
74 changes: 49 additions & 25 deletions ios/MullvadVPN.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo
}()

private var splitTunnelCoordinator: TunnelCoordinator?
private var splitLocationCoordinator: SelectLocationCoordinator?
private var splitLocationCoordinator: LocationCoordinator?

private let tunnelManager: TunnelManager
private let storePaymentManager: StorePaymentManager
Expand Down Expand Up @@ -703,11 +703,11 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo
}

private func makeSelectLocationCoordinator(forModalPresentation isModalPresentation: Bool)
-> SelectLocationCoordinator {
-> LocationCoordinator {
let navigationController = CustomNavigationController()
navigationController.isNavigationBarHidden = !isModalPresentation

let selectLocationCoordinator = SelectLocationCoordinator(
let selectLocationCoordinator = LocationCoordinator(
navigationController: navigationController,
tunnelManager: tunnelManager,
relayCacheTracker: relayCacheTracker
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// SelectLocationCoordinator.swift
// LocationCoordinator.swift
// MullvadVPN
//
// Created by pronebird on 29/01/2023.
Expand All @@ -13,7 +13,7 @@ import UIKit

import MullvadSettings

class SelectLocationCoordinator: Coordinator, Presentable, Presenting, RelayCacheTrackerObserver {
class LocationCoordinator: Coordinator, Presentable, Presenting, RelayCacheTrackerObserver {
private let tunnelManager: TunnelManager
private let relayCacheTracker: RelayCacheTracker
private var cachedRelays: CachedRelays?
Expand All @@ -24,10 +24,10 @@ class SelectLocationCoordinator: Coordinator, Presentable, Presenting, RelayCach
navigationController
}

var selectLocationViewController: SelectLocationViewController? {
var selectLocationViewController: LocationViewController? {
return navigationController.viewControllers.first {
$0 is SelectLocationViewController
} as? SelectLocationViewController
$0 is LocationViewController
} as? LocationViewController
}

var relayFilter: RelayFilter {
Expand All @@ -39,7 +39,7 @@ class SelectLocationCoordinator: Coordinator, Presentable, Presenting, RelayCach
}
}

var didFinish: ((SelectLocationCoordinator, RelayLocation?) -> Void)?
var didFinish: ((LocationCoordinator, [RelayLocation]) -> Void)?

init(
navigationController: UINavigationController,
Expand All @@ -52,22 +52,22 @@ class SelectLocationCoordinator: Coordinator, Presentable, Presenting, RelayCach
}

func start() {
let selectLocationViewController = SelectLocationViewController()
let selectLocationViewController = LocationViewController()

selectLocationViewController.didSelectRelay = { [weak self] relay in
selectLocationViewController.didSelectRelays = { [weak self] locations, customListId in
guard let self else { return }

var relayConstraints = tunnelManager.settings.relayConstraints
relayConstraints.locations = .only(RelayLocations(
locations: [relay],
customListId: nil
locations: locations,
customListId: customListId
))

tunnelManager.updateSettings([.relayConstraints(relayConstraints)]) {
self.tunnelManager.startTunnel()
}

didFinish?(self, relay)
didFinish?(self, locations)
}

selectLocationViewController.navigateToFilter = { [weak self] in
Expand All @@ -91,7 +91,7 @@ class SelectLocationCoordinator: Coordinator, Presentable, Presenting, RelayCach
selectLocationViewController.didFinish = { [weak self] in
guard let self else { return }

didFinish?(self, nil)
didFinish?(self, [])
}

relayCacheTracker.addObserver(self)
Expand All @@ -101,8 +101,7 @@ class SelectLocationCoordinator: Coordinator, Presentable, Presenting, RelayCach
selectLocationViewController.setCachedRelays(cachedRelays, filter: relayFilter)
}

selectLocationViewController.relayLocation =
tunnelManager.settings.relayConstraints.locations.value?.locations.first
selectLocationViewController.relayLocations = tunnelManager.settings.relayConstraints.locations.value

navigationController.pushViewController(selectLocationViewController, animated: false)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// SettingsValidationErrorConfiguration.swift
// MullvadVPN
//
// Created by Jon Petersson on 2024-02-16.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

import UIKit

struct SettingsValidationErrorConfiguration: UIContentConfiguration, Equatable {
var errors: [CustomListFieldValidationError] = []
var directionalLayoutMargins: NSDirectionalEdgeInsets = UIMetrics.SettingsCell.settingsValidationErrorLayoutMargins

func makeContentView() -> UIView & UIContentView {
return SettingsValidationErrorContentView(configuration: self)
}

func updated(for state: UIConfigurationState) -> Self {
return self
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//
// SettingsValidationErrorContentView.swift
// MullvadVPN
//
// Created by Jon Petersson on 2024-02-16.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

import UIKit

class SettingsValidationErrorContentView: UIView, UIContentView {
let contentView = UIStackView()

var icon: UIImageView {
let view = UIImageView(image: UIImage(resource: .iconAlert).withTintColor(.dangerColor))
view.heightAnchor.constraint(equalToConstant: 14).isActive = true
view.widthAnchor.constraint(equalTo: view.heightAnchor, multiplier: 1).isActive = true
return view
}

var configuration: UIContentConfiguration {
get {
actualConfiguration
}
set {
guard let newConfiguration = newValue as? SettingsValidationErrorConfiguration else { return }

let previousConfiguration = actualConfiguration
actualConfiguration = newConfiguration

configureSubviews(previousConfiguration: previousConfiguration)
}
}

private var actualConfiguration: SettingsValidationErrorConfiguration

func supports(_ configuration: UIContentConfiguration) -> Bool {
configuration is SettingsValidationErrorConfiguration
}

init(configuration: SettingsValidationErrorConfiguration) {
actualConfiguration = configuration

super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 44))

addSubviews()
configureSubviews()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

private func addSubviews() {
contentView.axis = .vertical
contentView.spacing = 6

addConstrainedSubviews([contentView]) {
contentView.pinEdgesToSuperviewMargins()
}
}

private func configureSubviews(previousConfiguration: SettingsValidationErrorConfiguration? = nil) {
guard actualConfiguration != previousConfiguration else { return }

configureLayoutMargins()

contentView.arrangedSubviews.forEach { view in
view.removeFromSuperview()
}

actualConfiguration.errors.forEach { error in
let label = UILabel()
label.text = error.errorDescription
label.numberOfLines = 0
label.font = .systemFont(ofSize: 13)
label.textColor = .white.withAlphaComponent(0.6)

let stackView = UIStackView(arrangedSubviews: [icon, label])
stackView.alignment = .top
stackView.spacing = 6

contentView.addArrangedSubview(stackView)
}
}

private func configureLayoutMargins() {
directionalLayoutMargins = actualConfiguration.directionalLayoutMargins
}
}
4 changes: 4 additions & 0 deletions ios/MullvadVPN/UI appearance/UIColor+Palette.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ extension UIColor {
static let backgroundColor = UIColor(red: 0.13, green: 0.20, blue: 0.30, alpha: 1.0)
}

enum SubSubSubCell {
static let backgroundColor = UIColor(red: 0.11, green: 0.17, blue: 0.27, alpha: 1.0)
}

enum HeaderBar {
static let defaultBackgroundColor = primaryColor
static let unsecuredBackgroundColor = dangerColor
Expand Down
2 changes: 1 addition & 1 deletion ios/MullvadVPN/UI appearance/UIMetrics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ extension UIMetrics {
static let contentInsets = UIEdgeInsets(top: 24, left: 24, bottom: 24, right: 24)

/// Common layout margins for location cell presentation
static let selectLocationCellLayoutMargins = NSDirectionalEdgeInsets(top: 16, leading: 28, bottom: 16, trailing: 12)
static let locationCellLayoutMargins = NSDirectionalEdgeInsets(top: 16, leading: 28, bottom: 16, trailing: 12)

/// Layout margins used by content heading displayed below the large navigation title.
static let contentHeadingLayoutMargins = NSDirectionalEdgeInsets(top: 8, leading: 24, bottom: 24, trailing: 24)
Expand Down
Loading

0 comments on commit 8c2109a

Please sign in to comment.