diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2021-10-19 14:05:30 +0200 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2021-11-04 10:54:05 +0100 |
| commit | 9879cb94b2b6a0d9ee5c873e8b46bc726ea88dc1 (patch) | |
| tree | 1437278383a69a42160405a498cd245dc1e32e75 /ios | |
| parent | 198861449e9aab65e6de496c3aa3eeaa4a89aefa (diff) | |
| download | mullvadvpn-9879cb94b2b6a0d9ee5c873e8b46bc726ea88dc1.tar.xz mullvadvpn-9879cb94b2b6a0d9ee5c873e8b46bc726ea88dc1.zip | |
Settings: implement data source
Diffstat (limited to 'ios')
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.pbxproj | 14 | ||||
| -rw-r--r-- | ios/MullvadVPN/EmptyTableViewHeaderFooterView.swift | 2 | ||||
| -rw-r--r-- | ios/MullvadVPN/SettingsDataSource.swift | 210 | ||||
| -rw-r--r-- | ios/MullvadVPN/SettingsDataSourceDelegate.swift | 13 | ||||
| -rw-r--r-- | ios/MullvadVPN/SettingsViewController.swift | 148 | ||||
| -rw-r--r-- | ios/MullvadVPN/StaticTableViewDataSource.swift | 127 |
6 files changed, 259 insertions, 255 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index ec6cdb1084..2657cd46b5 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -48,7 +48,6 @@ 5819C2142726CC8D00D6EC38 /* DataSourceSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5819C2132726CC8D00D6EC38 /* DataSourceSnapshotTests.swift */; }; 5819C2152726CC9400D6EC38 /* DataSourceSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587EB66F27143B6500123C75 /* DataSourceSnapshot.swift */; }; 5819C2172729595500D6EC38 /* SettingsAddDNSEntryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5819C2162729595500D6EC38 /* SettingsAddDNSEntryCell.swift */; }; - 581CBCEE229826FD00727D7F /* StaticTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581CBCED229826FD00727D7F /* StaticTableViewDataSource.swift */; }; 581FC4FA2695ACE100AA97BA /* Account.strings in Resources */ = {isa = PBXBuildFile; fileRef = 581FC4F82695ACE100AA97BA /* Account.strings */; }; 5820674926E63EC900655B05 /* Promise+BackgroundTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5820674826E63EC800655B05 /* Promise+BackgroundTask.swift */; }; 5820674E26E6510200655B05 /* REST.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5820674D26E6510200655B05 /* REST.swift */; }; @@ -259,6 +258,8 @@ 58E1336E26D2BE7500CC316B /* AnyResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E1336C26D2BE7500CC316B /* AnyResult.swift */; }; 58E1336F26D2BE7500CC316B /* AnyResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E1336C26D2BE7500CC316B /* AnyResult.swift */; }; 58E6771F24ADFE7800AA26E7 /* SettingsNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E6771E24ADFE7800AA26E7 /* SettingsNavigationController.swift */; }; + 58EE2E3A272FF814003BFF93 /* SettingsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EE2E38272FF814003BFF93 /* SettingsDataSource.swift */; }; + 58EE2E3B272FF814003BFF93 /* SettingsDataSourceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EE2E39272FF814003BFF93 /* SettingsDataSourceDelegate.swift */; }; 58EF580B25D69D7A00AEBA94 /* ProblemReportSubmissionOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EF580A25D69D7A00AEBA94 /* ProblemReportSubmissionOverlayView.swift */; }; 58EF581125D69DB400AEBA94 /* StatusImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EF581025D69DB400AEBA94 /* StatusImageView.swift */; }; 58F19E35228C15BA00C7710B /* SpinnerActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F19E34228C15BA00C7710B /* SpinnerActivityIndicatorView.swift */; }; @@ -365,7 +366,6 @@ 581503A524D6F4AE00C9C50E /* Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = "<group>"; }; 5819C2132726CC8D00D6EC38 /* DataSourceSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSourceSnapshotTests.swift; sourceTree = "<group>"; }; 5819C2162729595500D6EC38 /* SettingsAddDNSEntryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAddDNSEntryCell.swift; sourceTree = "<group>"; }; - 581CBCED229826FD00727D7F /* StaticTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticTableViewDataSource.swift; sourceTree = "<group>"; }; 581FC4F92695ACE100AA97BA /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Account.strings; sourceTree = "<group>"; }; 5820674826E63EC800655B05 /* Promise+BackgroundTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Promise+BackgroundTask.swift"; sourceTree = "<group>"; }; 5820674D26E6510200655B05 /* REST.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = REST.swift; sourceTree = "<group>"; }; @@ -523,6 +523,8 @@ 58E6771E24ADFE7800AA26E7 /* SettingsNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsNavigationController.swift; sourceTree = "<group>"; }; 58E973DD24850EB600096F90 /* AsyncOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncOperation.swift; sourceTree = "<group>"; }; 58ECD29123F178FD004298B6 /* Screenshots.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Screenshots.xcconfig; sourceTree = "<group>"; }; + 58EE2E38272FF814003BFF93 /* SettingsDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsDataSource.swift; sourceTree = "<group>"; }; + 58EE2E39272FF814003BFF93 /* SettingsDataSourceDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsDataSourceDelegate.swift; sourceTree = "<group>"; }; 58EF580A25D69D7A00AEBA94 /* ProblemReportSubmissionOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProblemReportSubmissionOverlayView.swift; sourceTree = "<group>"; }; 58EF581025D69DB400AEBA94 /* StatusImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusImageView.swift; sourceTree = "<group>"; }; 58F19E34228C15BA00C7710B /* SpinnerActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpinnerActivityIndicatorView.swift; sourceTree = "<group>"; }; @@ -881,7 +883,10 @@ 5857F24624C882D700CF6F47 /* SelectLocationNavigationController.swift */, 5888AD86227B17950051EB06 /* SelectLocationViewController.swift */, 582BB1B2229574F40055B6EF /* SettingsAccountCell.swift */, + 5819C2162729595500D6EC38 /* SettingsAddDNSEntryCell.swift */, 582BB1AE229566420055B6EF /* SettingsCell.swift */, + 58EE2E38272FF814003BFF93 /* SettingsDataSource.swift */, + 58EE2E39272FF814003BFF93 /* SettingsDataSourceDelegate.swift */, 584D26C5270C8741004EA533 /* SettingsDNSTextCell.swift */, 58E6771E24ADFE7800AA26E7 /* SettingsNavigationController.swift */, 584D26C1270C8542004EA533 /* SettingsStaticTextFooterView.swift */, @@ -891,7 +896,6 @@ 587A01FB23F1F0BE00B68763 /* SimulatorTunnelProviderHost.swift */, 58FD5BEF24238EB300112C88 /* SKProduct+Formatting.swift */, 58F19E34228C15BA00C7710B /* SpinnerActivityIndicatorView.swift */, - 581CBCED229826FD00727D7F /* StaticTableViewDataSource.swift */, 58EF581025D69DB400AEBA94 /* StatusImageView.swift */, 5807E2BF2432038B00F5FF30 /* String+Split.swift */, 5871FB8225498CA20051A0A4 /* Swizzle.swift */, @@ -908,7 +912,6 @@ 58B8743122B25A7600015324 /* WireguardAssociatedAddresses.swift */, 58F7CA872692E34000FC59FD /* WireguardKeysContentView.swift */, 5877152F23981F7B001F8237 /* WireguardKeysViewController.swift */, - 5819C2162729595500D6EC38 /* SettingsAddDNSEntryCell.swift */, ); path = MullvadVPN; sourceTree = "<group>"; @@ -1329,6 +1332,7 @@ 5871FB8325498CA20051A0A4 /* Swizzle.swift in Sources */, 58EF581125D69DB400AEBA94 /* StatusImageView.swift in Sources */, 58907D9524D17B4E00CFC3F5 /* DisconnectSplitButton.swift in Sources */, + 58EE2E3B272FF814003BFF93 /* SettingsDataSourceDelegate.swift in Sources */, 5823FA5426CE49F700283BF8 /* TunnelObserver.swift in Sources */, 5888AD87227B17950051EB06 /* SelectLocationViewController.swift in Sources */, 5820676026E75A4D00655B05 /* Promise+Delay.swift in Sources */, @@ -1390,11 +1394,11 @@ 5815039724D6ECAE00C9C50E /* CustomFormatLogHandler.swift in Sources */, 5820675E26E6839900655B05 /* PresentAlertOperation.swift in Sources */, 5815039D24D6ECE600C9C50E /* TextFileOutputStream.swift in Sources */, - 581CBCEE229826FD00727D7F /* StaticTableViewDataSource.swift in Sources */, 58CE5E64224146200008646E /* AppDelegate.swift in Sources */, 588DD76B26FCB49E006F6233 /* Cancellable.swift in Sources */, 58ACF64F26567A7100ACE4B7 /* CustomSwitchContainer.swift in Sources */, 5857F24324C8662600CF6F47 /* SelectLocationHeaderView.swift in Sources */, + 58EE2E3A272FF814003BFF93 /* SettingsDataSource.swift in Sources */, 58AEEF652344A36000C9BBD5 /* KeychainError.swift in Sources */, 581503A624D6F4AE00C9C50E /* Logging.swift in Sources */, 58CCA01222424D11004F3011 /* SettingsViewController.swift in Sources */, diff --git a/ios/MullvadVPN/EmptyTableViewHeaderFooterView.swift b/ios/MullvadVPN/EmptyTableViewHeaderFooterView.swift index 0d047ab045..78c30b5e15 100644 --- a/ios/MullvadVPN/EmptyTableViewHeaderFooterView.swift +++ b/ios/MullvadVPN/EmptyTableViewHeaderFooterView.swift @@ -10,8 +10,6 @@ import UIKit class EmptyTableViewHeaderFooterView: UITableViewHeaderFooterView { - static var reuseIdentifier = "EmptyTableViewHeaderFooterView" - override init(reuseIdentifier: String?) { super.init(reuseIdentifier: reuseIdentifier) diff --git a/ios/MullvadVPN/SettingsDataSource.swift b/ios/MullvadVPN/SettingsDataSource.swift new file mode 100644 index 0000000000..9d1d48be6b --- /dev/null +++ b/ios/MullvadVPN/SettingsDataSource.swift @@ -0,0 +1,210 @@ +// +// SettingsDataSource.swift +// MullvadVPN +// +// Created by pronebird on 19/10/2021. +// Copyright © 2021 Mullvad VPN AB. All rights reserved. +// + +import UIKit + +class SettingsDataSource: NSObject, AccountObserver, UITableViewDataSource, UITableViewDelegate { + private enum CellReuseIdentifiers: String, CaseIterable { + case accountCell + case basicCell + + var reusableViewClass: AnyClass { + switch self { + case .accountCell: + return SettingsAccountCell.self + case .basicCell: + return SettingsCell.self + } + } + } + + private enum HeaderFooterReuseIdentifier: String, CaseIterable { + case spacer + + var reusableViewClass: AnyClass { + switch self { + case .spacer: + return EmptyTableViewHeaderFooterView.self + } + } + } + + enum Section: String { + case main + case version + case problemReport + } + + enum Item: String { + case account + case preferences + case wireguardKey + case version + case problemReport + } + + private var snapshot = DataSourceSnapshot<Section, Item>() + + weak var delegate: SettingsDataSourceDelegate? + + weak var tableView: UITableView? { + didSet { + tableView?.delegate = self + tableView?.dataSource = self + + registerClasses() + } + } + + override init() { + super.init() + + Account.shared.addObserver(self) + updateDataSnapshot() + } + + private func registerClasses() { + CellReuseIdentifiers.allCases.forEach { cellIdentifier in + tableView?.register(cellIdentifier.reusableViewClass, forCellReuseIdentifier: cellIdentifier.rawValue) + } + + HeaderFooterReuseIdentifier.allCases.forEach { reuseIdentifier in + tableView?.register(reuseIdentifier.reusableViewClass, forHeaderFooterViewReuseIdentifier: reuseIdentifier.rawValue) + } + } + + private func updateDataSnapshot() { + var newSnapshot = DataSourceSnapshot<Section, Item>() + + if Account.shared.isLoggedIn { + newSnapshot.appendSections([.main]) + newSnapshot.appendItems([.account, .preferences, .wireguardKey], in: .main) + } + + newSnapshot.appendSections([.version, .problemReport]) + newSnapshot.appendItems([.version], in: .version) + newSnapshot.appendItems([.problemReport], in: .problemReport) + + snapshot = newSnapshot + } + + // MARK: - UITableViewDataSource + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + let sectionIdentifier = snapshot.section(at: section)! + + return snapshot.numberOfItems(in: sectionIdentifier) ?? 0 + } + + func numberOfSections(in tableView: UITableView) -> Int { + return snapshot.numberOfSections() + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let item = snapshot.itemForIndexPath(indexPath)! + + switch item { + case .account: + let cell = tableView.dequeueReusableCell(withIdentifier: CellReuseIdentifiers.accountCell.rawValue, for: indexPath) as! SettingsAccountCell + cell.titleLabel.text = NSLocalizedString("ACCOUNT_CELL_LABEL", tableName: "Settings", value: "Account", comment: "") + cell.accountExpiryDate = Account.shared.expiry + cell.accessibilityIdentifier = "AccountCell" + cell.accessoryType = .disclosureIndicator + + return cell + + case .preferences: + let cell = tableView.dequeueReusableCell(withIdentifier: CellReuseIdentifiers.basicCell.rawValue, for: indexPath) as! SettingsCell + cell.titleLabel.text = NSLocalizedString("PREFERENCES_CELL_LABEL", tableName: "Settings", value: "Preferences", comment: "") + cell.detailTitleLabel.text = nil + cell.accessibilityIdentifier = nil + cell.accessoryType = .disclosureIndicator + + return cell + + case .wireguardKey: + let cell = tableView.dequeueReusableCell(withIdentifier: CellReuseIdentifiers.basicCell.rawValue, for: indexPath) as! SettingsCell + cell.titleLabel.text = NSLocalizedString("WIREGUARD_KEY_CELL_LABEL", tableName: "Settings", value: "WireGuard key", comment: "") + cell.detailTitleLabel.text = nil + cell.accessibilityIdentifier = "WireGuardKeyCell" + cell.accessoryType = .disclosureIndicator + + return cell + + case .version: + let cell = tableView.dequeueReusableCell(withIdentifier: CellReuseIdentifiers.basicCell.rawValue, for: indexPath) as! SettingsCell + cell.titleLabel.text = NSLocalizedString("APP_VERSION_CELL_LABEL", tableName: "Settings", value: "App version", comment: "") + cell.detailTitleLabel.text = Bundle.main.productVersion + cell.accessibilityIdentifier = nil + cell.accessoryType = .none + + return cell + + case .problemReport: + let cell = tableView.dequeueReusableCell(withIdentifier: CellReuseIdentifiers.basicCell.rawValue, for: indexPath) as! SettingsCell + cell.titleLabel.text = NSLocalizedString("REPORT_PROBLEM_CELL_LABEL", tableName: "Settings", value: "Report a problem", comment: "") + cell.detailTitleLabel.text = nil + cell.accessibilityIdentifier = nil + cell.accessoryType = .disclosureIndicator + + return cell + } + } + + // MARK: - UITableViewDelegate + + func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool { + if case .version = snapshot.itemForIndexPath(indexPath) { + return false + } else { + return true + } + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + guard let item = snapshot.itemForIndexPath(indexPath) else { return } + + delegate?.settingsDataSource(self, didSelectItem: item) + } + + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + return tableView.dequeueReusableHeaderFooterView(withIdentifier: HeaderFooterReuseIdentifier.spacer.rawValue) + } + + func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + return nil + } + + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return UIMetrics.sectionSpacing + } + + func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + return 0 + } + + // MARK: - AccountObserver + + func account(_ account: Account, didUpdateExpiry expiry: Date) { + tableView?.performBatchUpdates { + if let indexPath = snapshot.indexPathForItem(.version) { + tableView?.reloadRows(at: [indexPath], with: .none) + } + } + } + + func account(_ account: Account, didLoginWithToken token: String, expiry: Date) { + updateDataSnapshot() + tableView?.reloadData() + } + + func accountDidLogout(_ account: Account) { + updateDataSnapshot() + tableView?.reloadData() + } +} diff --git a/ios/MullvadVPN/SettingsDataSourceDelegate.swift b/ios/MullvadVPN/SettingsDataSourceDelegate.swift new file mode 100644 index 0000000000..739c6d4552 --- /dev/null +++ b/ios/MullvadVPN/SettingsDataSourceDelegate.swift @@ -0,0 +1,13 @@ +// +// SettingsDataSourceDelegate.swift +// MullvadVPN +// +// Created by pronebird on 19/10/2021. +// Copyright © 2021 Mullvad VPN AB. All rights reserved. +// + +import UIKit + +protocol SettingsDataSourceDelegate: AnyObject { + func settingsDataSource(_ dataSource: SettingsDataSource, didSelectItem item: SettingsDataSource.Item) +} diff --git a/ios/MullvadVPN/SettingsViewController.swift b/ios/MullvadVPN/SettingsViewController.swift index a2a7f91208..80bf44338a 100644 --- a/ios/MullvadVPN/SettingsViewController.swift +++ b/ios/MullvadVPN/SettingsViewController.swift @@ -13,23 +13,11 @@ protocol SettingsViewControllerDelegate: AnyObject { func settingsViewControllerDidFinish(_ controller: SettingsViewController) } -class SettingsViewController: UITableViewController, AccountObserver { +class SettingsViewController: UITableViewController, SettingsDataSourceDelegate { weak var delegate: SettingsViewControllerDelegate? - private enum CellIdentifier: String { - case accountCell = "AccountCell" - case basicCell = "BasicCell" - } - - private let staticDataSource = SettingsTableViewDataSource() - - private weak var accountRow: StaticTableViewRow? - private var accountExpiryObserver: NSObjectProtocol? - - private var settingsNavigationController: SettingsNavigationController? { - return self.navigationController as? SettingsNavigationController - } + private let dataSource = SettingsDataSource() init() { super.init(style: .grouped) @@ -42,42 +30,17 @@ class SettingsViewController: UITableViewController, AccountObserver { override func viewDidLoad() { super.viewDidLoad() - tableView.backgroundColor = .secondaryColor - tableView.separatorColor = .secondaryColor - tableView.rowHeight = UITableView.automaticDimension - tableView.estimatedRowHeight = 60 - tableView.sectionHeaderHeight = UIMetrics.sectionSpacing - tableView.sectionFooterHeight = 0 - - tableView.dataSource = staticDataSource - tableView.delegate = staticDataSource - - tableView.register(SettingsAccountCell.self, forCellReuseIdentifier: CellIdentifier.accountCell.rawValue) - tableView.register(SettingsCell.self, forCellReuseIdentifier: CellIdentifier.basicCell.rawValue) - tableView.register(EmptyTableViewHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: EmptyTableViewHeaderFooterView.reuseIdentifier) - navigationItem.title = NSLocalizedString("NAVIGATION_TITLE", tableName: "Settings", comment: "Navigation title") navigationItem.largeTitleDisplayMode = .always navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleDismiss)) - Account.shared.addObserver(self) - setupDataSource() - } - - // MARK: - AccountObserver - - func account(_ account: Account, didUpdateExpiry expiry: Date) { - guard let accountRow = accountRow else { return } - - staticDataSource.reloadRows([accountRow], with: .none) - } - - func account(_ account: Account, didLoginWithToken token: String, expiry: Date) { - // no-op - } + tableView.backgroundColor = .secondaryColor + tableView.separatorColor = .secondaryColor + tableView.rowHeight = UITableView.automaticDimension + tableView.estimatedRowHeight = 60 - func accountDidLogout(_ account: Account) { - // no-op + dataSource.tableView = tableView + dataSource.delegate = self } // MARK: - IBActions @@ -86,88 +49,31 @@ class SettingsViewController: UITableViewController, AccountObserver { delegate?.settingsViewControllerDidFinish(self) } - // MARK: - Private + // MARK: - SettingsDataSourceDelegate - private func setupDataSource() { - if Account.shared.isLoggedIn { - let topSection = StaticTableViewSection() - let accountRow = StaticTableViewRow(reuseIdentifier: CellIdentifier.accountCell.rawValue) { (_, cell) in - let cell = cell as! SettingsAccountCell + func settingsDataSource(_ dataSource: SettingsDataSource, didSelectItem item: SettingsDataSource.Item) { + guard let route = item.navigationRoute else { return } - cell.titleLabel.text = NSLocalizedString("ACCOUNT_CELL_LABEL", tableName: "Settings", comment: "") - cell.accountExpiryDate = Account.shared.expiry - cell.accessibilityIdentifier = "AccountCell" - cell.accessoryType = .disclosureIndicator - } + let settingsNavigationController = navigationController as? SettingsNavigationController - accountRow.actionBlock = { [weak self] (indexPath) in - self?.settingsNavigationController?.navigate(to: .account, animated: true) - } - - let preferencesRow = StaticTableViewRow(reuseIdentifier: CellIdentifier.basicCell.rawValue) { (_, cell) in - let cell = cell as! SettingsCell - cell.titleLabel.text = NSLocalizedString("PREFERENCES_CELL_LABEL", tableName: "Settings", comment: "") - cell.accessoryType = .disclosureIndicator - } - - preferencesRow.actionBlock = { [weak self] (indexPath) in - self?.settingsNavigationController?.navigate(to: .preferences, animated: true) - } - - let wireguardKeyRow = StaticTableViewRow(reuseIdentifier: CellIdentifier.basicCell.rawValue) { (_, cell) in - let cell = cell as! SettingsCell - - cell.titleLabel.text = NSLocalizedString("WIREGUARD_KEY_CELL_LABEL", tableName: "Settings", comment: "") - cell.accessibilityIdentifier = "WireGuardKeyCell" - cell.accessoryType = .disclosureIndicator - } - - wireguardKeyRow.actionBlock = { [weak self] (indexPath) in - self?.settingsNavigationController?.navigate(to: .wireguardKeys, animated: true) - } - - self.accountRow = accountRow - - topSection.addRows([accountRow, preferencesRow, wireguardKeyRow]) - staticDataSource.addSections([topSection]) - } - - let middleSection = StaticTableViewSection() - let versionRow = StaticTableViewRow(reuseIdentifier: CellIdentifier.basicCell.rawValue) { (_, cell) in - let cell = cell as! SettingsCell - cell.titleLabel.text = NSLocalizedString("APP_VERSION_CELL_LABEL", tableName: "Settings", comment: "") - cell.detailTitleLabel.text = Bundle.main.productVersion - } - versionRow.isSelectable = false - - middleSection.addRows([versionRow]) - staticDataSource.addSections([middleSection]) - - let bottomSection = StaticTableViewSection() - - let problemReportRow = StaticTableViewRow(reuseIdentifier: CellIdentifier.basicCell.rawValue) { (indexPath, cell) in - let cell = cell as! SettingsCell - - cell.titleLabel.text = NSLocalizedString("REPORT_PROBLEM_CELL_LABEL", tableName: "Settings", comment: "") - cell.accessoryType = .disclosureIndicator - } - - problemReportRow.actionBlock = { [weak self] (indexPath) in - self?.settingsNavigationController?.navigate(to: .problemReport, animated: true) - } - - bottomSection.addRows([problemReportRow]) - staticDataSource.addSections([bottomSection]) + settingsNavigationController?.navigate(to: route, animated: true) } } -class SettingsTableViewDataSource: StaticTableViewDataSource { - - // MARK: - UITableViewDelegate - - func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - return tableView.dequeueReusableHeaderFooterView(withIdentifier: EmptyTableViewHeaderFooterView.reuseIdentifier) +extension SettingsDataSource.Item { + var navigationRoute: SettingsNavigationRoute? { + switch self { + case .account: + return .account + case .preferences: + return .preferences + case .wireguardKey: + return .wireguardKeys + case .version: + return nil + case .problemReport: + return .problemReport + } } - } diff --git a/ios/MullvadVPN/StaticTableViewDataSource.swift b/ios/MullvadVPN/StaticTableViewDataSource.swift deleted file mode 100644 index 36cef2adaa..0000000000 --- a/ios/MullvadVPN/StaticTableViewDataSource.swift +++ /dev/null @@ -1,127 +0,0 @@ -// -// StaticTableViewDataSource.swift -// MullvadVPN -// -// Created by pronebird on 24/05/2019. -// Copyright © 2019 Mullvad VPN AB. All rights reserved. -// - -import UIKit - -class StaticTableViewRow { - typealias ConfigurationBlock = (IndexPath, UITableViewCell) -> Void - typealias ActionBlock = (IndexPath) -> Void - - let reuseIdentifier: String - let configurationBlock: ConfigurationBlock - - var isSelectable = true - var isHidden = false - var actionBlock: ActionBlock? - - init(reuseIdentifier: String, configurationBlock: @escaping ConfigurationBlock) { - self.reuseIdentifier = reuseIdentifier - self.configurationBlock = configurationBlock - } -} - -class StaticTableViewSection { - private(set) var rows = [StaticTableViewRow]() - - var isHidden: Bool { - return rows.allSatisfy({ $0.isHidden }) - } - - func addRows(_ rows: [StaticTableViewRow]) { - self.rows.append(contentsOf: rows) - } -} - -class StaticTableViewDataSource: NSObject, UITableViewDataSource, UITableViewDelegate { - - @IBOutlet weak var tableView: UITableView? - - private(set) var sections = [StaticTableViewSection]() - - func addSections(_ sections: [StaticTableViewSection]) { - self.sections.append(contentsOf: sections) - } - - func reloadRows(_ rows: [StaticTableViewRow], with animation: UITableView.RowAnimation) { - let indexPaths = rows.compactMap { indexPathForRow($0) } - - tableView?.reloadRows(at: indexPaths, with: animation) - } - - func indexPathForRow(_ searchRow: StaticTableViewRow) -> IndexPath? { - var sectionIndex = 0 - - for section in sections { - let visibleRows = section.rows.filter { !$0.isHidden } - - // skip incrementing the section index since invisible sections are normally collapsed - guard visibleRows.count > 0 else { - continue - } - - if let rowIndex = visibleRows.firstIndex(where: { $0 === searchRow }) { - return IndexPath(row: rowIndex, section: sectionIndex) - } - - sectionIndex += 1 - } - - return nil - } - - // MARK: - UITableViewDelegate - - func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool { - let row = self.row(for: indexPath) - - return row.isSelectable - } - - // MARK: - UITableViewDataSource - - func numberOfSections(in tableView: UITableView) -> Int { - return sections.reduce(0, { $1.isHidden ? $0 : $0 + 1 }) - } - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return sections[section].rows.count - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let row = self.row(for: indexPath) - let reuseIdentifier = row.reuseIdentifier - - let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) - - row.configurationBlock(indexPath, cell) - - return cell - } - - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let row = self.row(for: indexPath) - - row.actionBlock?(indexPath) - } - - // MARK: - Private - - private func row(for indexPath: IndexPath) -> StaticTableViewRow { - let section = self.section(for: indexPath) - let row = section.rows.compactMap({ $0.isHidden ? nil : $0 }) - - return row[indexPath.row] - } - - private func section(for indexPath: IndexPath) -> StaticTableViewSection { - let visibleSections = sections.compactMap({ $0.isHidden ? nil : $0 }) - - return visibleSections[indexPath.section] - } - -} |
