Skip to content

Commit ed147a2

Browse files
author
Jon Petersson
committed
Combine parts of LocationDataSource and AddLocationDataSource to one
1 parent 563ce04 commit ed147a2

21 files changed

+283
-299
lines changed

ios/MullvadVPN.xcodeproj/project.pbxproj

+4
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@
531531
7A6389EB2B7FAD7A008E77E1 /* SettingsFieldValidationErrorContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6389EA2B7FAD7A008E77E1 /* SettingsFieldValidationErrorContentView.swift */; };
532532
7A6389ED2B7FADA1008E77E1 /* SettingsFieldValidationErrorConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6389EC2B7FADA1008E77E1 /* SettingsFieldValidationErrorConfiguration.swift */; };
533533
7A6389F82B864CDF008E77E1 /* LocationNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6389F72B864CDF008E77E1 /* LocationNode.swift */; };
534+
7A6652B82BB44C3E0042D848 /* LocationDiffableDataSourceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6652B62BB44B120042D848 /* LocationDiffableDataSourceProtocol.swift */; };
534535
7A6B4F592AB8412E00123853 /* TunnelMonitorTimings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6B4F582AB8412E00123853 /* TunnelMonitorTimings.swift */; };
535536
7A6F2FA52AFA3CB2006D0856 /* AccountExpiryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FA42AFA3CB2006D0856 /* AccountExpiryTests.swift */; };
536537
7A6F2FA72AFBB9AE006D0856 /* AccountExpiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FA62AFBB9AE006D0856 /* AccountExpiry.swift */; };
@@ -1787,6 +1788,7 @@
17871788
7A6389EA2B7FAD7A008E77E1 /* SettingsFieldValidationErrorContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFieldValidationErrorContentView.swift; sourceTree = "<group>"; };
17881789
7A6389EC2B7FADA1008E77E1 /* SettingsFieldValidationErrorConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFieldValidationErrorConfiguration.swift; sourceTree = "<group>"; };
17891790
7A6389F72B864CDF008E77E1 /* LocationNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationNode.swift; sourceTree = "<group>"; };
1791+
7A6652B62BB44B120042D848 /* LocationDiffableDataSourceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationDiffableDataSourceProtocol.swift; sourceTree = "<group>"; };
17901792
7A6B4F582AB8412E00123853 /* TunnelMonitorTimings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelMonitorTimings.swift; sourceTree = "<group>"; };
17911793
7A6F2FA42AFA3CB2006D0856 /* AccountExpiryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountExpiryTests.swift; sourceTree = "<group>"; };
17921794
7A6F2FA62AFBB9AE006D0856 /* AccountExpiry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountExpiry.swift; sourceTree = "<group>"; };
@@ -2432,6 +2434,7 @@
24322434
F050AE4D2B70D7F8003F4EDB /* LocationCellViewModel.swift */,
24332435
583DA21325FA4B5C00318683 /* LocationDataSource.swift */,
24342436
F050AE5D2B739A73003F4EDB /* LocationDataSourceProtocol.swift */,
2437+
7A6652B62BB44B120042D848 /* LocationDiffableDataSourceProtocol.swift */,
24352438
7A6389F72B864CDF008E77E1 /* LocationNode.swift */,
24362439
F050AE512B70DFC0003F4EDB /* LocationSection.swift */,
24372440
F0BE65362B9F136A005CC385 /* LocationSectionHeaderView.swift */,
@@ -5321,6 +5324,7 @@
53215324
58607A4D2947287800BC467D /* AccountExpiryInAppNotificationProvider.swift in Sources */,
53225325
58C8191829FAA2C400DEB1B4 /* NotificationConfiguration.swift in Sources */,
53235326
58FF9FE82B07650A00E4C97D /* ButtonCellContentConfiguration.swift in Sources */,
5327+
7A6652B82BB44C3E0042D848 /* LocationDiffableDataSourceProtocol.swift in Sources */,
53245328
5827B0A82B0F49EF00CCBBA1 /* ProxyConfigurationInteractorProtocol.swift in Sources */,
53255329
7A5869B92B56E7F000640D27 /* IPOverrideViewControllerDelegate.swift in Sources */,
53265330
586C0D7A2B039CE300E7CDD7 /* ShadowsocksCipherPicker.swift in Sources */,

ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
{
2-
"originHash" : "c15149b2d59d9e9c72375f65339c04f41a19943e1117e682df27fc9f943fdc56",
32
"pins" : [
43
{
54
"identity" : "swift-log",
@@ -19,5 +18,5 @@
1918
}
2019
}
2120
],
22-
"version" : 3
21+
"version" : 2
2322
}

ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift

+1-9
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,6 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo
4343
*/
4444
private let secondaryNavigationContainer = RootContainerViewController()
4545

46-
private var customListRepository: CustomListRepositoryProtocol {
47-
#if DEBUG
48-
InMemoryCustomListRepository()
49-
#else
50-
CustomListRepository()
51-
#endif
52-
}
53-
5446
/// Posts `preferredAccountNumber` notification when user inputs the account number instead of voucher code
5547
private let preferredAccountNumberSubject = PassthroughSubject<String, Never>()
5648

@@ -719,7 +711,7 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo
719711
navigationController: navigationController,
720712
tunnelManager: tunnelManager,
721713
relayCacheTracker: relayCacheTracker,
722-
customListRepository: customListRepository
714+
customListRepository: CustomListRepository()
723715
)
724716

725717
locationCoordinator.didFinish = { [weak self] _ in

ios/MullvadVPN/Coordinators/CustomLists/AddLocationsCoordinator.swift

-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
77
//
88

9-
import Combine
10-
import Foundation
119
import MullvadSettings
1210
import MullvadTypes
1311
import Routing

ios/MullvadVPN/Coordinators/CustomLists/AddLocationsDataSource.swift

+42-99
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,40 @@
66
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
77
//
88

9-
import Foundation
109
import MullvadSettings
1110
import MullvadTypes
1211
import UIKit
1312

14-
class AddLocationsDataSource: UITableViewDiffableDataSource<LocationSection, LocationCellViewModel> {
15-
private let tableView: UITableView
16-
private let nodes: [LocationNode]
13+
class AddLocationsDataSource:
14+
UITableViewDiffableDataSource<LocationSection, LocationCellViewModel>,
15+
LocationDiffableDataSourceProtocol {
1716
private var customListLocationNode: CustomListLocationNode
17+
private let nodes: [LocationNode]
1818
var didUpdateCustomList: ((CustomListLocationNode) -> Void)?
19+
let tableView: UITableView
20+
let sections: [LocationSection]
1921

2022
init(
2123
tableView: UITableView,
22-
allLocations: [LocationNode],
24+
allLocationNodes: [LocationNode],
2325
customList: CustomList
2426
) {
2527
self.tableView = tableView
26-
self.nodes = allLocations
28+
self.nodes = allLocationNodes
2729

2830
self.customListLocationNode = CustomListLocationNodeBuilder(
2931
customList: customList,
3032
allLocations: self.nodes
3133
).customListLocationNode
3234

35+
let sections: [LocationSection] = [.customLists]
36+
self.sections = sections
37+
3338
super.init(tableView: tableView) { _, indexPath, itemIdentifier in
3439
let cell = tableView.dequeueReusableView(
35-
withIdentifier: LocationSection.allCases[indexPath.section],
40+
withIdentifier: sections[indexPath.section],
3641
for: indexPath
37-
// swiftlint:disable:next force_cast
38-
) as! LocationCell
42+
) as! LocationCell // swiftlint:disable:this force_cast
3943
cell.configure(item: itemIdentifier, behavior: .add)
4044
cell.selectionStyle = .none
4145
return cell
@@ -47,6 +51,14 @@ class AddLocationsDataSource: UITableViewDiffableDataSource<LocationSection, Loc
4751
reloadWithSelectedLocations()
4852
}
4953

54+
func nodeShowsChildren(_ node: LocationNode) -> Bool {
55+
isLocationInCustomList(node: node)
56+
}
57+
58+
func nodeShouldBeSelected(_ node: LocationNode) -> Bool {
59+
customListLocationNode.children.contains(node)
60+
}
61+
5062
private func reloadWithSelectedLocations() {
5163
var locationsList: [LocationCellViewModel] = []
5264
nodes.forEach { node in
@@ -62,9 +74,13 @@ class AddLocationsDataSource: UITableViewDiffableDataSource<LocationSection, Loc
6274
return
6375
}
6476

65-
// Walk tree backwards to determine which nodes should be expanded.
66-
node.forEachAncestor { node in
67-
node.showsChildren = true
77+
// Only parents with partially selected children should be expanded.
78+
node.forEachDescendant { descendantNode in
79+
if customListLocationNode.children.contains(descendantNode) {
80+
descendantNode.forEachAncestor { descendantParentNode in
81+
descendantParentNode.showsChildren = true
82+
}
83+
}
6884
}
6985

7086
locationsList.append(contentsOf: recursivelyCreateCellViewModelTree(
@@ -73,53 +89,7 @@ class AddLocationsDataSource: UITableViewDiffableDataSource<LocationSection, Loc
7389
indentationLevel: 1
7490
))
7591
}
76-
updateDataSnapshot(with: locationsList)
77-
}
78-
79-
private func updateDataSnapshot(
80-
with list: [LocationCellViewModel],
81-
animated: Bool = false,
82-
completion: (() -> Void)? = nil
83-
) {
84-
var snapshot = NSDiffableDataSourceSnapshot<LocationSection, LocationCellViewModel>()
85-
86-
snapshot.appendSections([.customLists])
87-
snapshot.appendItems(list, toSection: .customLists)
88-
89-
apply(snapshot, animatingDifferences: animated, completion: completion)
90-
}
91-
92-
private func recursivelyCreateCellViewModelTree(
93-
for node: LocationNode,
94-
in section: LocationSection,
95-
indentationLevel: Int
96-
) -> [LocationCellViewModel] {
97-
var viewModels = [LocationCellViewModel]()
98-
for childNode in node.children {
99-
viewModels.append(
100-
LocationCellViewModel(
101-
section: .customLists,
102-
node: childNode,
103-
indentationLevel: indentationLevel,
104-
isSelected: customListLocationNode.children.contains(childNode)
105-
)
106-
)
107-
108-
let indentationLevel = indentationLevel + 1
109-
110-
// Walk tree forward to determine which nodes should be expanded.
111-
if isLocationInCustomList(node: childNode) {
112-
viewModels.append(
113-
contentsOf: recursivelyCreateCellViewModelTree(
114-
for: childNode,
115-
in: section,
116-
indentationLevel: indentationLevel
117-
)
118-
)
119-
}
120-
}
121-
122-
return viewModels
92+
updateDataSnapshot(with: [locationsList])
12393
}
12494

12595
private func isLocationInCustomList(node: LocationNode) -> Bool {
@@ -146,26 +116,23 @@ extension AddLocationsDataSource: UITableViewDelegate {
146116

147117
extension AddLocationsDataSource: LocationCellDelegate {
148118
func toggleExpanding(cell: LocationCell) {
149-
guard let indexPath = tableView.indexPath(for: cell),
150-
let item = itemIdentifier(for: indexPath) else { return }
151-
let isExpanded = item.node.showsChildren
152-
153-
item.node.showsChildren = !isExpanded
154-
155-
var locationList = snapshot().itemIdentifiers
156-
157-
if !isExpanded {
158-
locationList.addSubNodes(from: item, at: indexPath)
159-
} else {
160-
locationList.removeSubNodes(from: item.node)
119+
let items = toggledItems(for: cell).first!.map { item in
120+
var item = item
121+
if containsChild(parent: customListLocationNode, child: item.node) {
122+
item.isSelected = true
123+
}
124+
return item
161125
}
162126

163-
updateDataSnapshot(with: locationList, animated: true, completion: {
164-
self.scroll(to: item, animated: true)
127+
updateDataSnapshot(with: [items], reloadExisting: true, completion: {
128+
if let indexPath = self.tableView.indexPath(for: cell),
129+
let item = self.itemIdentifier(for: indexPath) {
130+
self.scroll(to: item, animated: true)
131+
}
165132
})
166133
}
167134

168-
func toggleSelection(cell: LocationCell) {
135+
func toggleSelecting(cell: LocationCell) {
169136
guard let index = tableView.indexPath(for: cell)?.row else { return }
170137

171138
var locationList = snapshot().itemIdentifiers
@@ -181,36 +148,12 @@ extension AddLocationsDataSource: LocationCellDelegate {
181148
} else {
182149
customListLocationNode.remove(selectedLocation: item.node, with: locationList)
183150
}
184-
updateDataSnapshot(with: locationList, completion: {
151+
updateDataSnapshot(with: [locationList], completion: {
185152
self.didUpdateCustomList?(self.customListLocationNode)
186153
})
187154
}
188155
}
189156

190-
extension AddLocationsDataSource {
191-
private func scroll(to item: LocationCellViewModel, animated: Bool) {
192-
guard
193-
let visibleIndexPaths = tableView.indexPathsForVisibleRows,
194-
let indexPath = indexPath(for: item)
195-
else { return }
196-
197-
if item.node.children.count > visibleIndexPaths.count {
198-
tableView.scrollToRow(at: indexPath, at: .top, animated: animated)
199-
} else {
200-
if let last = item.node.children.last {
201-
if let lastInsertedIndexPath = self.indexPath(for: LocationCellViewModel(
202-
section: .customLists,
203-
node: last
204-
)),
205-
let lastVisibleIndexPath = visibleIndexPaths.last,
206-
lastInsertedIndexPath >= lastVisibleIndexPath {
207-
tableView.scrollToRow(at: lastInsertedIndexPath, at: .bottom, animated: animated)
208-
}
209-
}
210-
}
211-
}
212-
}
213-
214157
// MARK: - Toggle selection in table view
215158

216159
fileprivate extension [LocationCellViewModel] {

ios/MullvadVPN/Coordinators/CustomLists/AddLocationsViewController.swift

+9-21
Original file line numberDiff line numberDiff line change
@@ -28,28 +28,9 @@ class AddLocationsViewController: UIViewController {
2828
tableView.rowHeight = 56
2929
tableView.indicatorStyle = .white
3030
tableView.accessibilityIdentifier = .addLocationsView
31-
tableView.allowsMultipleSelection = true
32-
tableView.tableHeaderView = nil
33-
tableView.sectionHeaderHeight = .zero
3431
return tableView
3532
}()
3633

37-
private lazy var backBarButton: UIBarButtonItem = {
38-
let backBarButton = UIBarButtonItem(
39-
primaryAction: UIAction(
40-
image: UIImage(resource: .iconBack),
41-
handler: { [weak self] _ in
42-
guard let self else { return }
43-
delegate?.didBack()
44-
navigationController?.popViewController(animated: true)
45-
}
46-
)
47-
)
48-
backBarButton.style = .done
49-
50-
return backBarButton
51-
}()
52-
5334
init(
5435
allLocationsNodes: [LocationNode],
5536
customList: CustomList
@@ -67,11 +48,18 @@ class AddLocationsViewController: UIViewController {
6748
super.viewDidLoad()
6849
tableView.backgroundColor = view.backgroundColor
6950
view.backgroundColor = .secondaryColor
70-
navigationItem.leftBarButtonItem = backBarButton
7151
addConstraints()
7252
setUpDataSource()
7353
}
7454

55+
override func didMove(toParent parent: UIViewController?) {
56+
super.didMove(toParent: parent)
57+
58+
if parent == nil {
59+
delegate?.didBack()
60+
}
61+
}
62+
7563
private func addConstraints() {
7664
view.addConstrainedSubviews([tableView]) {
7765
tableView.pinEdgesToSuperview()
@@ -81,7 +69,7 @@ class AddLocationsViewController: UIViewController {
8169
private func setUpDataSource() {
8270
dataSource = AddLocationsDataSource(
8371
tableView: tableView,
84-
allLocations: nodes.copy(),
72+
allLocationNodes: nodes.copy(),
8573
customList: customList
8674
)
8775

ios/MullvadVPN/Coordinators/CustomLists/CustomListDataSourceConfiguration.swift

+15
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,21 @@ class CustomListDataSourceConfiguration: NSObject {
6363
}
6464

6565
extension CustomListDataSourceConfiguration: UITableViewDelegate {
66+
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
67+
return nil
68+
}
69+
70+
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
71+
let sectionIdentifier = dataSource.snapshot().sectionIdentifiers[section]
72+
73+
return switch sectionIdentifier {
74+
case .name:
75+
16
76+
default:
77+
UITableView.automaticDimension
78+
}
79+
}
80+
6681
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
6782
UIMetrics.SettingsCell.customListsCellHeight
6883
}

ios/MullvadVPN/Coordinators/CustomLists/EditCustomListCoordinator.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,6 @@ extension EditCustomListCoordinator: CustomListViewControllerDelegate {
9494

9595
coordinator.start()
9696

97-
coordinator.addChild(coordinator)
97+
addChild(coordinator)
9898
}
9999
}

ios/MullvadVPN/Coordinators/CustomLists/EditLocationsCoordinator.swift

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
77
//
88

9-
import Foundation
109
import MullvadSettings
1110
import MullvadTypes
1211
import Routing

ios/MullvadVPN/Coordinators/CustomLists/ListCustomListCoordinator.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ class ListCustomListCoordinator: Coordinator, Presentable, Presenting {
9292
}
9393

9494
tunnelManager.updateSettings([.relayConstraints(relayConstraints)]) { [weak self] in
95-
self?.tunnelManager.startTunnel()
95+
self?.tunnelManager.reconnectTunnel(selectNewRelay: true)
9696
}
9797
}
9898

0 commit comments

Comments
 (0)