Skip to content

Commit 4d11f05

Browse files
author
Jon Petersson
committed
Add UI for creating and editing a custom list
1 parent 1a6cc86 commit 4d11f05

16 files changed

+661
-22
lines changed

ios/MullvadSettings/CustomList.swift

+4-2
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ import MullvadTypes
1212
public struct CustomList: Codable, Equatable {
1313
public let id: UUID
1414
public var name: String
15-
public var list: [RelayLocation] = []
16-
public init(id: UUID, name: String) {
15+
public var locations: [RelayLocation]
16+
17+
public init(id: UUID = UUID(), name: String, locations: [RelayLocation]) {
1718
self.id = id
1819
self.name = name
20+
self.locations = locations
1921
}
2022
}

ios/MullvadSettings/CustomListRepository.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ public struct CustomListRepository: CustomListRepositoryProtocol {
4141

4242
public init() {}
4343

44-
public func create(_ name: String) throws -> CustomList {
44+
public func create(_ name: String, locations: [RelayLocation]) throws -> CustomList {
4545
var lists = fetchAll()
4646
if lists.contains(where: { $0.name == name }) {
4747
throw CustomRelayListError.duplicateName
4848
} else {
49-
let item = CustomList(id: UUID(), name: name)
49+
let item = CustomList(id: UUID(), name: name, locations: locations)
5050
lists.append(item)
5151
try write(lists)
5252
return item

ios/MullvadSettings/CustomListRepositoryProtocol.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ public protocol CustomListRepositoryProtocol {
2828

2929
/// Create a custom list by unique name.
3030
/// - Parameter name: a custom list name.
31+
/// - Parameter locations: locations in a custom list.
3132
/// - Returns: a persistent custom list model upon success, otherwise throws `Error`.
32-
func create(_ name: String) throws -> CustomList
33+
func create(_ name: String, locations: [RelayLocation]) throws -> CustomList
3334

3435
/// Fetch all custom list.
3536
/// - Returns: all custom list model .

ios/MullvadVPN.xcodeproj/project.pbxproj

+45-1
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,15 @@
518518
7A6000F92B6273A4001CF0D9 /* AccessMethodViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586C0D7B2B03BDD100E7CDD7 /* AccessMethodViewModel.swift */; };
519519
7A6000FC2B628DF6001CF0D9 /* ListCellContentConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6000FB2B628DF6001CF0D9 /* ListCellContentConfiguration.swift */; };
520520
7A6000FE2B628E9F001CF0D9 /* ListCellContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6000FD2B628E9F001CF0D9 /* ListCellContentView.swift */; };
521+
7A6389DB2B7E3BD6008E77E1 /* CustomListCellConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6389D22B7E3BD6008E77E1 /* CustomListCellConfiguration.swift */; };
522+
7A6389DC2B7E3BD6008E77E1 /* CustomListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6389D32B7E3BD6008E77E1 /* CustomListViewModel.swift */; };
523+
7A6389DD2B7E3BD6008E77E1 /* CustomListDataSourceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6389D42B7E3BD6008E77E1 /* CustomListDataSourceConfiguration.swift */; };
524+
7A6389DE2B7E3BD6008E77E1 /* CustomListItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6389D52B7E3BD6008E77E1 /* CustomListItemIdentifier.swift */; };
525+
7A6389DF2B7E3BD6008E77E1 /* AddCustomListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6389D72B7E3BD6008E77E1 /* AddCustomListCoordinator.swift */; };
526+
7A6389E12B7E3BD6008E77E1 /* CustomListSectionIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6389D92B7E3BD6008E77E1 /* CustomListSectionIdentifier.swift */; };
527+
7A6389E22B7E3BD6008E77E1 /* CustomListInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6389DA2B7E3BD6008E77E1 /* CustomListInteractor.swift */; };
528+
7A6389E52B7E4247008E77E1 /* EditCustomListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6389E42B7E4247008E77E1 /* EditCustomListCoordinator.swift */; };
529+
7A6389E72B7E42BE008E77E1 /* CustomListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6389E62B7E42BE008E77E1 /* CustomListViewController.swift */; };
521530
7A6B4F592AB8412E00123853 /* TunnelMonitorTimings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6B4F582AB8412E00123853 /* TunnelMonitorTimings.swift */; };
522531
7A6F2FA52AFA3CB2006D0856 /* AccountExpiryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FA42AFA3CB2006D0856 /* AccountExpiryTests.swift */; };
523532
7A6F2FA72AFBB9AE006D0856 /* AccountExpiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FA62AFBB9AE006D0856 /* AccountExpiry.swift */; };
@@ -1727,6 +1736,15 @@
17271736
7A6000F52B60092F001CF0D9 /* AccessMethodViewModelEditing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessMethodViewModelEditing.swift; sourceTree = "<group>"; };
17281737
7A6000FB2B628DF6001CF0D9 /* ListCellContentConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCellContentConfiguration.swift; sourceTree = "<group>"; };
17291738
7A6000FD2B628E9F001CF0D9 /* ListCellContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCellContentView.swift; sourceTree = "<group>"; };
1739+
7A6389D22B7E3BD6008E77E1 /* CustomListCellConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomListCellConfiguration.swift; sourceTree = "<group>"; };
1740+
7A6389D32B7E3BD6008E77E1 /* CustomListViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomListViewModel.swift; sourceTree = "<group>"; };
1741+
7A6389D42B7E3BD6008E77E1 /* CustomListDataSourceConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomListDataSourceConfiguration.swift; sourceTree = "<group>"; };
1742+
7A6389D52B7E3BD6008E77E1 /* CustomListItemIdentifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomListItemIdentifier.swift; sourceTree = "<group>"; };
1743+
7A6389D72B7E3BD6008E77E1 /* AddCustomListCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddCustomListCoordinator.swift; sourceTree = "<group>"; };
1744+
7A6389D92B7E3BD6008E77E1 /* CustomListSectionIdentifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomListSectionIdentifier.swift; sourceTree = "<group>"; };
1745+
7A6389DA2B7E3BD6008E77E1 /* CustomListInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomListInteractor.swift; sourceTree = "<group>"; };
1746+
7A6389E42B7E4247008E77E1 /* EditCustomListCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditCustomListCoordinator.swift; sourceTree = "<group>"; };
1747+
7A6389E62B7E42BE008E77E1 /* CustomListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomListViewController.swift; sourceTree = "<group>"; };
17301748
7A6B4F582AB8412E00123853 /* TunnelMonitorTimings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelMonitorTimings.swift; sourceTree = "<group>"; };
17311749
7A6F2FA42AFA3CB2006D0856 /* AccountExpiryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountExpiryTests.swift; sourceTree = "<group>"; };
17321750
7A6F2FA62AFBB9AE006D0856 /* AccountExpiry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountExpiry.swift; sourceTree = "<group>"; };
@@ -3003,6 +3021,8 @@
30033021
58CAF9F22983D32200BE19F7 /* Coordinators */ = {
30043022
isa = PBXGroup;
30053023
children = (
3024+
7A6389D12B7E3BD6008E77E1 /* CustomLists */,
3025+
58EFC76F2AFB3FA800E9F4CB /* Settings */,
30063026
7A9CCCAF2A96302800DD6A34 /* AccountCoordinator.swift */,
30073027
7A9CCCAC2A96302800DD6A34 /* AccountDeletionCoordinator.swift */,
30083028
7A9CCCA32A96302700DD6A34 /* AddCreditSucceededCoordinator.swift */,
@@ -3018,7 +3038,6 @@
30183038
7A9CCCA52A96302700DD6A34 /* RevokedCoordinator.swift */,
30193039
7A9CCCB02A96302800DD6A34 /* SafariCoordinator.swift */,
30203040
7A9CCCA72A96302700DD6A34 /* SelectLocationCoordinator.swift */,
3021-
58EFC76F2AFB3FA800E9F4CB /* Settings */,
30223041
7A9CCCA62A96302700DD6A34 /* SetupAccountCompletedCoordinator.swift */,
30233042
7A9CCCA22A96302700DD6A34 /* TermsOfServiceCoordinator.swift */,
30243043
7A9CCCB22A96302800DD6A34 /* TunnelCoordinator.swift */,
@@ -3381,6 +3400,22 @@
33813400
path = IPOverride;
33823401
sourceTree = "<group>";
33833402
};
3403+
7A6389D12B7E3BD6008E77E1 /* CustomLists */ = {
3404+
isa = PBXGroup;
3405+
children = (
3406+
7A6389D72B7E3BD6008E77E1 /* AddCustomListCoordinator.swift */,
3407+
7A6389D22B7E3BD6008E77E1 /* CustomListCellConfiguration.swift */,
3408+
7A6389D42B7E3BD6008E77E1 /* CustomListDataSourceConfiguration.swift */,
3409+
7A6389DA2B7E3BD6008E77E1 /* CustomListInteractor.swift */,
3410+
7A6389D52B7E3BD6008E77E1 /* CustomListItemIdentifier.swift */,
3411+
7A6389D92B7E3BD6008E77E1 /* CustomListSectionIdentifier.swift */,
3412+
7A6389E62B7E42BE008E77E1 /* CustomListViewController.swift */,
3413+
7A6389D32B7E3BD6008E77E1 /* CustomListViewModel.swift */,
3414+
7A6389E42B7E4247008E77E1 /* EditCustomListCoordinator.swift */,
3415+
);
3416+
path = CustomLists;
3417+
sourceTree = "<group>";
3418+
};
33843419
7A83C3FC2A55B39500DFB83A /* TestPlans */ = {
33853420
isa = PBXGroup;
33863421
children = (
@@ -4908,6 +4943,7 @@
49084943
isa = PBXSourcesBuildPhase;
49094944
buildActionMask = 2147483647;
49104945
files = (
4946+
7A6389DC2B7E3BD6008E77E1 /* CustomListViewModel.swift in Sources */,
49114947
7A9CCCC42A96302800DD6A34 /* TunnelCoordinator.swift in Sources */,
49124948
5827B0A42B0F38FD00CCBBA1 /* EditAccessMethodInteractorProtocol.swift in Sources */,
49134949
586C0D852B03D31E00E7CDD7 /* SocksSectionHandler.swift in Sources */,
@@ -5071,6 +5107,7 @@
50715107
58E25F812837BBBB002CFB2C /* SceneDelegate.swift in Sources */,
50725108
7A1A26492A29D48A00B978AA /* RelayFilterCellFactory.swift in Sources */,
50735109
5867771629097C5B006F721F /* ProductState.swift in Sources */,
5110+
7A6389DE2B7E3BD6008E77E1 /* CustomListItemIdentifier.swift in Sources */,
50745111
58C76A082A33850E00100D75 /* ApplicationTarget.swift in Sources */,
50755112
58CEB3042AFD36CE00E6E088 /* SwitchCellContentView.swift in Sources */,
50765113
F07BF2622A26279100042943 /* RedeemVoucherOperation.swift in Sources */,
@@ -5086,8 +5123,11 @@
50865123
7AF9BE8E2A331C7B00DBFEDB /* RelayFilterViewModel.swift in Sources */,
50875124
58F3C0A4249CB069003E76BE /* HeaderBarView.swift in Sources */,
50885125
5864AF0829C78849005B0CD9 /* CellFactoryProtocol.swift in Sources */,
5126+
7A6389E22B7E3BD6008E77E1 /* CustomListInteractor.swift in Sources */,
50895127
F07CFF2029F2720E008C0343 /* RegisteredDeviceInAppNotificationProvider.swift in Sources */,
5128+
7A6389E12B7E3BD6008E77E1 /* CustomListSectionIdentifier.swift in Sources */,
50905129
58CEB2F32AFD0BA100E6E088 /* TextCellContentView.swift in Sources */,
5130+
7A6389E72B7E42BE008E77E1 /* CustomListViewController.swift in Sources */,
50915131
586C0D7C2B03BDD100E7CDD7 /* AccessMethodViewModel.swift in Sources */,
50925132
587A01FC23F1F0BE00B68763 /* SimulatorTunnelProviderHost.swift in Sources */,
50935133
7A6F2FA72AFBB9AE006D0856 /* AccountExpiry.swift in Sources */,
@@ -5107,6 +5147,7 @@
51075147
5807E2C02432038B00F5FF30 /* String+Split.swift in Sources */,
51085148
58B26E242943520C00D5980C /* NotificationProviderProtocol.swift in Sources */,
51095149
5877F94E2A0A59AA0052D9E9 /* NotificationResponse.swift in Sources */,
5150+
7A6389E52B7E4247008E77E1 /* EditCustomListCoordinator.swift in Sources */,
51105151
58677712290976FB006F721F /* SettingsInteractor.swift in Sources */,
51115152
58EF875D2B1638BF00C098B2 /* ProxyConfigurationTesterProtocol.swift in Sources */,
51125153
58CE5E66224146200008646E /* LoginViewController.swift in Sources */,
@@ -5167,6 +5208,7 @@
51675208
581DA2752A1E283E0046ED47 /* WgKeyRotation.swift in Sources */,
51685209
5827B0BB2B14A28300CCBBA1 /* MethodTestingStatusCellContentView.swift in Sources */,
51695210
7A83C4022A57FAA800DFB83A /* SettingsDNSInfoCell.swift in Sources */,
5211+
7A6389DF2B7E3BD6008E77E1 /* AddCustomListCoordinator.swift in Sources */,
51705212
586C0D952B03D92100E7CDD7 /* SocksItemIdentifier.swift in Sources */,
51715213
F0C6A8432AB08E54000777A8 /* RedeemVoucherViewConfiguration.swift in Sources */,
51725214
7AF10EB42ADE85BC00C090B9 /* RelayFilterCoordinator.swift in Sources */,
@@ -5203,6 +5245,7 @@
52035245
584D26C4270C855B004EA533 /* PreferencesDataSource.swift in Sources */,
52045246
F0D8825B2B04F53600D3EF9A /* OutgoingConnectionData.swift in Sources */,
52055247
7A6F2FAF2AFE36E7006D0856 /* PreferencesInfoButtonItem.swift in Sources */,
5248+
7A6389DD2B7E3BD6008E77E1 /* CustomListDataSourceConfiguration.swift in Sources */,
52065249
5827B0BF2B14B37D00CCBBA1 /* Publisher+PreviousValue.swift in Sources */,
52075250
7A9CCCB62A96302800DD6A34 /* OutOfTimeCoordinator.swift in Sources */,
52085251
5827B0AA2B0F4C9100CCBBA1 /* EditAccessMethodViewControllerDelegate.swift in Sources */,
@@ -5221,6 +5264,7 @@
52215264
7A21DACF2A30AA3700A787A9 /* UITextField+Appearance.swift in Sources */,
52225265
585B1FF02AB09F97008AD470 /* VPNConnectionProtocol.swift in Sources */,
52235266
58C3A4B222456F1B00340BDB /* AccountInputGroupView.swift in Sources */,
5267+
7A6389DB2B7E3BD6008E77E1 /* CustomListCellConfiguration.swift in Sources */,
52245268
F09A297C2A9F8A9B00EA3B6F /* VoucherTextField.swift in Sources */,
52255269
7A5869B72B56B41500640D27 /* IPOverrideTextViewController.swift in Sources */,
52265270
58ACF64B26553C3F00ACE4B7 /* SettingsSwitchCell.swift in Sources */,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//
2+
// AddCustomListCoordinator.swift
3+
// MullvadVPN
4+
//
5+
// Created by Jon Petersson on 2024-02-14.
6+
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
7+
//
8+
9+
import Combine
10+
import MullvadSettings
11+
import Routing
12+
import UIKit
13+
14+
class AddCustomListCoordinator: Coordinator, Presentable, Presenting {
15+
let navigationController: UINavigationController
16+
let customListInteractor: CustomListInteractorProtocol
17+
18+
var presentedViewController: UIViewController {
19+
navigationController
20+
}
21+
22+
var didFinish: (() -> Void)?
23+
24+
init(
25+
navigationController: UINavigationController,
26+
customListInteractor: CustomListInteractorProtocol
27+
) {
28+
self.navigationController = navigationController
29+
self.customListInteractor = customListInteractor
30+
}
31+
32+
func start() {
33+
let subject = CurrentValueSubject<CustomListViewModel, Never>(
34+
CustomListViewModel(id: UUID(), name: "", locations: [], tableSections: [.name, .addLocations])
35+
)
36+
37+
let controller = CustomListViewController(
38+
interactor: customListInteractor,
39+
subject: subject,
40+
alertPresenter: AlertPresenter(context: self)
41+
)
42+
controller.delegate = self
43+
44+
controller.navigationItem.title = NSLocalizedString(
45+
"CUSTOM_LIST_NAVIGATION_EDIT_TITLE",
46+
tableName: "CustomLists",
47+
value: "New custom list",
48+
comment: ""
49+
)
50+
51+
controller.saveBarButton.title = NSLocalizedString(
52+
"CUSTOM_LIST_NAVIGATION_CREATE_BUTTON",
53+
tableName: "CustomLists",
54+
value: "Create",
55+
comment: ""
56+
)
57+
58+
navigationController.pushViewController(controller, animated: false)
59+
}
60+
}
61+
62+
extension AddCustomListCoordinator: CustomListViewControllerDelegate {
63+
func customListDidSave() {
64+
didFinish?()
65+
}
66+
67+
func customListDidDelete() {
68+
// No op.
69+
}
70+
71+
func showLocations() {
72+
// TODO: Show view controller for locations.
73+
}
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//
2+
// CustomListCellConfiguration.swift
3+
// MullvadVPN
4+
//
5+
// Created by Jon Petersson on 2024-02-14.
6+
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
7+
//
8+
9+
import Combine
10+
import UIKit
11+
12+
struct CustomListCellConfiguration {
13+
let tableView: UITableView
14+
let subject: CurrentValueSubject<CustomListViewModel, Never>
15+
16+
var onDelete: (() -> Void)?
17+
18+
func dequeueCell(at indexPath: IndexPath, for itemIdentifier: CustomListItemIdentifier) -> UITableViewCell {
19+
let cell = tableView.dequeueReusableView(withIdentifier: itemIdentifier.cellIdentifier, for: indexPath)
20+
21+
configureBackground(cell: cell, itemIdentifier: itemIdentifier)
22+
23+
switch itemIdentifier {
24+
case .name:
25+
configureName(cell, itemIdentifier: itemIdentifier)
26+
case .addLocations, .editLocations:
27+
configureLocations(cell, itemIdentifier: itemIdentifier)
28+
case .deleteList:
29+
configureDelete(cell, itemIdentifier: itemIdentifier)
30+
}
31+
32+
return cell
33+
}
34+
35+
private func configureBackground(cell: UITableViewCell, itemIdentifier: CustomListItemIdentifier) {
36+
guard let cell = cell as? DynamicBackgroundConfiguration else { return }
37+
cell.setAutoAdaptingBackgroundConfiguration(.mullvadListGroupedCell(), selectionType: .dimmed)
38+
}
39+
40+
private func configureName(_ cell: UITableViewCell, itemIdentifier: CustomListItemIdentifier) {
41+
var contentConfiguration = TextCellContentConfiguration()
42+
43+
contentConfiguration.text = itemIdentifier.text
44+
contentConfiguration.setPlaceholder(type: .required)
45+
contentConfiguration.textFieldProperties = .withSmartFeaturesDisabled()
46+
contentConfiguration.inputText = subject.value.name
47+
contentConfiguration.editingEvents.onChange = subject.bindTextAction(to: \.name)
48+
49+
cell.contentConfiguration = contentConfiguration
50+
}
51+
52+
private func configureLocations(_ cell: UITableViewCell, itemIdentifier: CustomListItemIdentifier) {
53+
var contentConfiguration = UIListContentConfiguration.mullvadValueCell(tableStyle: tableView.style)
54+
55+
contentConfiguration.text = itemIdentifier.text
56+
cell.contentConfiguration = contentConfiguration
57+
58+
if let cell = cell as? CustomCellDisclosureHandling {
59+
cell.disclosureType = .chevron
60+
}
61+
}
62+
63+
private func configureDelete(_ cell: UITableViewCell, itemIdentifier: CustomListItemIdentifier) {
64+
var contentConfiguration = ButtonCellContentConfiguration()
65+
66+
contentConfiguration.style = .tableInsetGroupedDanger
67+
contentConfiguration.text = itemIdentifier.text
68+
contentConfiguration.primaryAction = UIAction { _ in
69+
onDelete?()
70+
}
71+
72+
cell.contentConfiguration = contentConfiguration
73+
}
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//
2+
// CustomListDataSourceConfigurationv.swift
3+
// MullvadVPN
4+
//
5+
// Created by Jon Petersson on 2024-02-14.
6+
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
class CustomListDataSourceConfiguration: NSObject {
12+
let dataSource: UITableViewDiffableDataSource<CustomListSectionIdentifier, CustomListItemIdentifier>
13+
14+
var didSelectItem: ((CustomListItemIdentifier) -> Void)?
15+
16+
init(dataSource: UITableViewDiffableDataSource<CustomListSectionIdentifier, CustomListItemIdentifier>) {
17+
self.dataSource = dataSource
18+
}
19+
20+
func updateDataSource(
21+
sections: [CustomListSectionIdentifier],
22+
animated: Bool,
23+
completion: (() -> Void)? = nil
24+
) {
25+
var snapshot = NSDiffableDataSourceSnapshot<CustomListSectionIdentifier, CustomListItemIdentifier>()
26+
27+
sections.forEach { section in
28+
switch section {
29+
case .name:
30+
snapshot.appendSections([.name])
31+
snapshot.appendItems([.name], toSection: .name)
32+
case .addLocations:
33+
snapshot.appendSections([.addLocations])
34+
snapshot.appendItems([.addLocations], toSection: .addLocations)
35+
case .editLocations:
36+
snapshot.appendSections([.editLocations])
37+
snapshot.appendItems([.editLocations], toSection: .editLocations)
38+
case .deleteList:
39+
snapshot.appendSections([.deleteList])
40+
snapshot.appendItems([.deleteList], toSection: .deleteList)
41+
}
42+
}
43+
44+
dataSource.apply(snapshot, animatingDifferences: animated)
45+
}
46+
}
47+
48+
extension CustomListDataSourceConfiguration: UITableViewDelegate {
49+
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
50+
UIMetrics.SettingsCell.customListsCellHeight
51+
}
52+
53+
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
54+
tableView.deselectRow(at: indexPath, animated: false)
55+
56+
dataSource.itemIdentifier(for: indexPath).flatMap { item in
57+
didSelectItem?(item)
58+
}
59+
}
60+
}

0 commit comments

Comments
 (0)