Skip to content

Commit

Permalink
List only DAITA entry locations if multihop and DAITA are enabled
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon Petersson committed Aug 21, 2024
1 parent 207cc54 commit eabf71a
Show file tree
Hide file tree
Showing 13 changed files with 142 additions and 62 deletions.
6 changes: 6 additions & 0 deletions ios/MullvadVPN.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,8 @@
7A516C3C2B712F0B00BBD33D /* IPOverrideWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A516C3B2B712F0B00BBD33D /* IPOverrideWrapperTests.swift */; };
7A52F96A2C1735AE00B133B9 /* RelaySelectorStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FE25EF2AA77664003D1918 /* RelaySelectorStub.swift */; };
7A52F96C2C17450C00B133B9 /* RelaySelectorWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A52F96B2C17450C00B133B9 /* RelaySelectorWrapperTests.swift */; };
7A5468AC2C6A55B100590086 /* LocationRelays.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5468AB2C6A55B100590086 /* LocationRelays.swift */; };
7A5468AD2C6B5E4B00590086 /* LocationRelays.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5468AB2C6A55B100590086 /* LocationRelays.swift */; };
7A5869952B32E9C700640D27 /* LinkButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869942B32E9C700640D27 /* LinkButton.swift */; };
7A5869972B32EA4500640D27 /* AppButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869962B32EA4500640D27 /* AppButton.swift */; };
7A58699B2B482FE200640D27 /* UITableViewCell+Disable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A58699A2B482FE200640D27 /* UITableViewCell+Disable.swift */; };
Expand Down Expand Up @@ -1806,6 +1808,7 @@
7A516C392B7111A700BBD33D /* IPOverrideWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideWrapper.swift; sourceTree = "<group>"; };
7A516C3B2B712F0B00BBD33D /* IPOverrideWrapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideWrapperTests.swift; sourceTree = "<group>"; };
7A52F96B2C17450C00B133B9 /* RelaySelectorWrapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelaySelectorWrapperTests.swift; sourceTree = "<group>"; };
7A5468AB2C6A55B100590086 /* LocationRelays.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationRelays.swift; sourceTree = "<group>"; };
7A5869942B32E9C700640D27 /* LinkButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkButton.swift; sourceTree = "<group>"; };
7A5869962B32EA4500640D27 /* AppButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppButton.swift; sourceTree = "<group>"; };
7A58699A2B482FE200640D27 /* UITableViewCell+Disable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableViewCell+Disable.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2756,6 +2759,7 @@
F050AE5D2B739A73003F4EDB /* LocationDataSourceProtocol.swift */,
7A6652B62BB44B120042D848 /* LocationDiffableDataSourceProtocol.swift */,
7A6389F72B864CDF008E77E1 /* LocationNode.swift */,
7A5468AB2C6A55B100590086 /* LocationRelays.swift */,
F050AE512B70DFC0003F4EDB /* LocationSection.swift */,
F0BE65362B9F136A005CC385 /* LocationSectionHeaderView.swift */,
5888AD86227B17950051EB06 /* LocationViewController.swift */,
Expand Down Expand Up @@ -5251,6 +5255,7 @@
A9A5FA422ACB05D90083449F /* DeviceStateAccessorProtocol.swift in Sources */,
7A5869C32B5820CE00640D27 /* IPOverrideRepositoryTests.swift in Sources */,
A9A5FA392ACB05910083449F /* UIColor+Palette.swift in Sources */,
7A5468AD2C6B5E4B00590086 /* LocationRelays.swift in Sources */,
A9A5FA3A2ACB05910083449F /* UIEdgeInsets+Extensions.swift in Sources */,
A9A5FA3B2ACB05910083449F /* UIMetrics.swift in Sources */,
58B07C182AEFDD6C00A09625 /* StoreTransactionLog.swift in Sources */,
Expand Down Expand Up @@ -5542,6 +5547,7 @@
7A9CCCC42A96302800DD6A34 /* TunnelCoordinator.swift in Sources */,
5827B0A42B0F38FD00CCBBA1 /* EditAccessMethodInteractorProtocol.swift in Sources */,
586C0D852B03D31E00E7CDD7 /* SocksSectionHandler.swift in Sources */,
7A5468AC2C6A55B100590086 /* LocationRelays.swift in Sources */,
58BFA5CC22A7CE1F00A6173D /* ApplicationConfiguration.swift in Sources */,
5891BF5125E66B1E006D6FB0 /* UIBarButtonItem+KeyboardNavigation.swift in Sources */,
58E511E628DDDEAC00B0BCDE /* CodingErrors+CustomErrorDescription.swift in Sources */,
Expand Down
41 changes: 33 additions & 8 deletions ios/MullvadVPN/Coordinators/LocationCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class LocationCoordinator: Coordinator, Presentable, Presenting {
private let tunnelManager: TunnelManager
private let relayCacheTracker: RelayCacheTracker
private let customListRepository: CustomListRepositoryProtocol
private var cachedRelays: CachedRelays?
private var cachedRelays: LocationRelays?

let navigationController: UINavigationController

Expand Down Expand Up @@ -54,10 +54,16 @@ class LocationCoordinator: Coordinator, Presentable, Presenting {
}

func start() {
let startContext: LocationViewControllerWrapper.MultihopContext =
if case .noRelaysSatisfyingDaitaConstraints = tunnelManager.tunnelStatus.observedState
.blockedState?.reason { .entry } else { .exit }

let locationViewControllerWrapper = LocationViewControllerWrapper(
customListRepository: customListRepository,
constraints: tunnelManager.settings.relayConstraints,
multihopEnabled: tunnelManager.settings.tunnelMultihopState.isEnabled
multihopEnabled: tunnelManager.settings.tunnelMultihopState.isEnabled,
daitaEnabled: tunnelManager.settings.daita.state.isEnabled,
startContext: startContext
)
locationViewControllerWrapper.delegate = self

Expand All @@ -69,8 +75,13 @@ class LocationCoordinator: Coordinator, Presentable, Presenting {
relayCacheTracker.addObserver(self)

if let cachedRelays = try? relayCacheTracker.getCachedRelays() {
self.cachedRelays = cachedRelays
locationViewControllerWrapper.setCachedRelays(cachedRelays, filter: relayFilter)
let locationRelays = LocationRelays(
relays: cachedRelays.relays.wireguard.relays,
locations: cachedRelays.relays.locations
)
self.cachedRelays = locationRelays

locationViewControllerWrapper.setCachedRelays(locationRelays, filter: relayFilter)
}

navigationController.pushViewController(locationViewControllerWrapper, animated: false)
Expand All @@ -87,8 +98,14 @@ class LocationCoordinator: Coordinator, Presentable, Presenting {
)

relayFilterCoordinator.didFinish = { [weak self] coordinator, filter in
if let cachedRelays = self?.cachedRelays, let filter {
self?.locationViewControllerWrapper?.setCachedRelays(cachedRelays, filter: filter)
guard let self else { return }

if var cachedRelays, let filter {
cachedRelays.relays = cachedRelays.relays.filter { relay in
RelaySelector.relayMatchesFilter(relay, filter: filter)
}

locationViewControllerWrapper?.setCachedRelays(cachedRelays, filter: filter)
}

coordinator.dismiss(animated: true)
Expand Down Expand Up @@ -148,9 +165,13 @@ extension LocationCoordinator: RelayCacheTrackerObserver {
_ tracker: RelayCacheTracker,
didUpdateCachedRelays cachedRelays: CachedRelays
) {
self.cachedRelays = cachedRelays
let locationRelays = LocationRelays(
relays: cachedRelays.relays.wireguard.relays,
locations: cachedRelays.relays.locations
)
self.cachedRelays = locationRelays

locationViewControllerWrapper?.setCachedRelays(cachedRelays, filter: relayFilter)
locationViewControllerWrapper?.setCachedRelays(locationRelays, filter: relayFilter)
}
}

Expand Down Expand Up @@ -178,6 +199,10 @@ extension LocationCoordinator: LocationViewControllerWrapperDelegate {
relayConstraints.filter = .only(filter)

tunnelManager.updateSettings([.relayConstraints(relayConstraints)])

if let cachedRelays {
locationViewControllerWrapper?.setCachedRelays(cachedRelays, filter: filter)
}
}

func navigateToFilter() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,23 @@ class RelayFilterChipView: UIView {
return label
}()

let closeButton: IncreasedHitButton = {
let button = IncreasedHitButton()
button.setImage(
UIImage(resource: .iconCloseSml).withTintColor(.white.withAlphaComponent(0.6)),
for: .normal
)
button.accessibilityIdentifier = .relayFilterChipCloseButton
return button
}()

var didTapButton: (() -> Void)?

init() {
super.init(frame: .zero)

self.accessibilityIdentifier = .relayFilterChipView

let closeButton = IncreasedHitButton()
closeButton.setImage(
UIImage(named: "IconCloseSml")?.withTintColor(.white.withAlphaComponent(0.6)),
for: .normal
)
closeButton.accessibilityIdentifier = .relayFilterChipCloseButton
closeButton.addTarget(self, action: #selector(didTapButton(_:)), for: .touchUpInside)

let container = UIStackView(arrangedSubviews: [titleLabel, closeButton])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class RelayFilterView: UIView {

private let ownershipView = RelayFilterChipView()
private let providersView = RelayFilterChipView()
private let daitaView = RelayFilterChipView()
private var filter: RelayFilter?

var didUpdateFilter: ((RelayFilter) -> Void)?
Expand Down Expand Up @@ -71,14 +72,24 @@ class RelayFilterView: UIView {
}
}

func setDaita(_ enabled: Bool) {
daitaView.isHidden = !enabled
}

private func setUpViews() {
daitaView.setTitle(localizedDaitaText())
daitaView.isHidden = true
daitaView.closeButton.isHidden = true

ownershipView.isHidden = true
ownershipView.didTapButton = { [weak self] in
guard var filter = self?.filter else { return }

filter.ownership = .any
self?.didUpdateFilter?(filter)
}

providersView.isHidden = true
providersView.didTapButton = { [weak self] in
guard var filter = self?.filter else { return }

Expand All @@ -87,7 +98,7 @@ class RelayFilterView: UIView {
}

// Add a dummy view at the end to push content to the left.
let filterContainer = UIStackView(arrangedSubviews: [ownershipView, providersView, UIView()])
let filterContainer = UIStackView(arrangedSubviews: [daitaView, ownershipView, providersView, UIView()])
filterContainer.spacing = UIMetrics.FilterView.interChipViewSpacing

let contentContainer = UIStackView(arrangedSubviews: [titleLabel, filterContainer])
Expand All @@ -99,6 +110,15 @@ class RelayFilterView: UIView {
}
}

private func localizedDaitaText() -> String {
return NSLocalizedString(
"RELAY_FILTER_APPLIED_DAITA",
tableName: "RelayFilter",
value: "Setting: DAITA",
comment: ""
)
}

private func localizedOwnershipText(for string: String) -> String {
return NSLocalizedString(
"RELAY_FILTER_APPLIED_OWNERSHIP",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ class AllLocationDataSource: LocationDataSourceProtocol {
/// Constructs a collection of node trees from relays fetched from the API.
/// ``RelayLocation.city`` is of special import since we use it to get country
/// and city names.
func reload(_ response: REST.ServerRelaysResponse, relays: [REST.ServerRelay]) {
func reload(_ relays: LocationRelays) {
let rootNode = RootLocationNode()

for relay in relays {
for relay in relays.relays {
guard case
let .city(countryCode, cityCode) = RelayLocation(dashSeparatedString: relay.location),
let serverLocation = response.locations[relay.location]
let serverLocation = relays.locations[relay.location]
else { continue }

let relayLocation = RelayLocation.hostname(countryCode, cityCode, relay.hostname)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,17 @@ extension [LocationCellViewModel] {
}

extension LocationCellViewModel {
/* Exclusion of other locations in the same node tree as the currently excluded location
/* Exclusion of other locations in the same node tree (as the currently excluded location)
happens when there are no more hosts in that tree that can be selected.
We check this by doing the following, in order:

1. Count host names in the tree. More than one means that there are other locations than
1. Count hostnames in the tree. More than one means that there are other locations than
the excluded one for the relay selector to choose from. No exlusion.

2. Count host names in the excluded node. More than one means that there are multiple
locations for the relay selector to choose from. No exlusion.
2. Count hostnames in the excluded node. More than one means that there are multiple
locations for the relay selector to choose from. No exclusion.

3. Check existance of a location in the tree that match the currently excluded location.
3. Check existance of a location in the tree that matches the currently excluded location.
No match means no exclusion.
*/
func shouldExcludeLocation(_ excludedLocation: LocationCellViewModel?) -> Bool {
Expand All @@ -86,8 +86,8 @@ extension LocationCellViewModel {
if case .hostname = location { true } else { false }
}.count

// If the there are more than one selectable relay in the current node we don't need
// show this in the location tree and can return early.
// If the there's more than one selectable relay in the current node we don't need
// to show this in the location tree and can return early.
guard hostCount == 1 else { return false }

let proxyExcludedNode = RootLocationNode(children: [excludedLocation.node])
Expand All @@ -96,8 +96,8 @@ extension LocationCellViewModel {
if case .hostname = location { true } else { false }
}.count

// If the there are more than one selectable relay in the excluded node we don't need
// show this in the location tree and can return early.
// If the there's more than one selectable relay in the excluded node we don't need
// to show this in the location tree and can return early.
guard excludedHostCount == 1 else { return false }

var containsExcludedLocation = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,14 @@ final class LocationDataSource:
defaultRowAnimation = .fade
}

func setRelays(_ response: REST.ServerRelaysResponse, selectedRelays: RelaySelection, filter: RelayFilter) {
func setRelays(_ cachedRelays: LocationRelays, selectedRelays: RelaySelection) {
let allLocationsDataSource =
dataSources.first(where: { $0 is AllLocationDataSource }) as? AllLocationDataSource

let customListsDataSource =
dataSources.first(where: { $0 is CustomListsDataSource }) as? CustomListsDataSource

let relays = response.wireguard.relays.filter { relay in
RelaySelector.relayMatchesFilter(relay, filter: filter)
}

allLocationsDataSource?.reload(response, relays: relays)
allLocationsDataSource?.reload(cachedRelays)
customListsDataSource?.reload(allLocationNodes: allLocationsDataSource?.nodes ?? [])

setSelectedRelays(selectedRelays)
Expand Down Expand Up @@ -237,12 +233,6 @@ final class LocationDataSource:
)
}

private func nodeMatchesExcludedLocation(_ node: LocationNode) -> Bool {
// Compare nodes on name rather than whole node in order to match all items in both .customLists
// and .allLocations.
node.name == excludedLocation?.node.name
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = super.tableView(tableView, cellForRowAt: indexPath)
guard let cell = cell as? LocationCell, let item = itemIdentifier(for: indexPath) else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// LocationRelays.swift
// MullvadVPN
//
// Created by Jon Petersson on 2024-08-12.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

import MullvadREST

struct LocationRelays {
var relays: [REST.ServerRelay]
var locations: [String: REST.ServerLocation]
}
Loading

0 comments on commit eabf71a

Please sign in to comment.