Skip to content

Commit bf63d53

Browse files
author
Jon Petersson
committed
Allow deleting custom lists
1 parent 412d42a commit bf63d53

File tree

5 files changed

+223
-13
lines changed

5 files changed

+223
-13
lines changed

ios/MullvadSettings/CustomListRepository.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public enum CustomRelayListError: LocalizedError, Equatable {
1818
switch self {
1919
case .duplicateName:
2020
NSLocalizedString(
21-
"DUPLICATE_CUSTOM_LIST_ERROR",
21+
"DUPLICATE_CUSTOM_LISTS_ERROR",
2222
tableName: "CustomListRepository",
2323
value: "Name is already taken.",
2424
comment: ""

ios/MullvadVPN/Coordinators/CustomLists/AddCustomListCoordinator.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,14 @@ class AddCustomListCoordinator: Coordinator, Presentable, Presenting {
4242
controller.delegate = self
4343

4444
controller.navigationItem.title = NSLocalizedString(
45-
"CUSTOM_LIST_NAVIGATION_EDIT_TITLE",
45+
"CUSTOM_LISTS_NAVIGATION_EDIT_TITLE",
4646
tableName: "CustomLists",
4747
value: "New custom list",
4848
comment: ""
4949
)
5050

5151
controller.saveBarButton.title = NSLocalizedString(
52-
"CUSTOM_LIST_NAVIGATION_CREATE_BUTTON",
52+
"CUSTOM_LISTS_NAVIGATION_CREATE_BUTTON",
5353
tableName: "CustomLists",
5454
value: "Create",
5555
comment: ""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
//
2+
// CustomListViewController.swift
3+
// MullvadVPN
4+
//
5+
// Created by Jon Petersson on 2024-02-15.
6+
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
7+
//
8+
9+
import Combine
10+
import MullvadSettings
11+
import UIKit
12+
13+
protocol CustomListViewControllerDelegate: AnyObject {
14+
func customListDidSave()
15+
func customListDidDelete()
16+
func showLocations()
17+
}
18+
19+
class CustomListViewController: UIViewController {
20+
typealias DataSource = UITableViewDiffableDataSource<CustomListSectionIdentifier, CustomListItemIdentifier>
21+
22+
private let interactor: CustomListInteractorProtocol
23+
private let tableView = UITableView(frame: .zero, style: .insetGrouped)
24+
private let subject: CurrentValueSubject<CustomListViewModel, Never>
25+
private var cancellables = Set<AnyCancellable>()
26+
private var dataSource: DataSource?
27+
private let alertPresenter: AlertPresenter
28+
29+
private lazy var cellConfiguration: CustomListCellConfiguration = {
30+
CustomListCellConfiguration(tableView: tableView, subject: subject)
31+
}()
32+
33+
private lazy var dataSourceConfiguration: CustomListDataSourceConfiguration? = {
34+
dataSource.flatMap { dataSource in
35+
CustomListDataSourceConfiguration(dataSource: dataSource)
36+
}
37+
}()
38+
39+
lazy var saveBarButton: UIBarButtonItem = {
40+
let barButtonItem = UIBarButtonItem(
41+
title: NSLocalizedString(
42+
"CUSTOM_LIST_NAVIGATION_SAVE_BUTTON",
43+
tableName: "CustomLists",
44+
value: "Save",
45+
comment: ""
46+
),
47+
primaryAction: UIAction { _ in
48+
self.onSave()
49+
}
50+
)
51+
barButtonItem.style = .done
52+
53+
return barButtonItem
54+
}()
55+
56+
weak var delegate: CustomListViewControllerDelegate?
57+
58+
init(
59+
interactor: CustomListInteractorProtocol,
60+
subject: CurrentValueSubject<CustomListViewModel, Never>,
61+
alertPresenter: AlertPresenter
62+
) {
63+
self.subject = subject
64+
self.interactor = interactor
65+
self.alertPresenter = alertPresenter
66+
67+
super.init(nibName: nil, bundle: nil)
68+
}
69+
70+
required init?(coder: NSCoder) {
71+
fatalError("init(coder:) has not been implemented")
72+
}
73+
74+
override func viewDidLoad() {
75+
super.viewDidLoad()
76+
77+
view.directionalLayoutMargins = UIMetrics.contentLayoutMargins
78+
view.backgroundColor = .secondaryColor
79+
isModalInPresentation = true
80+
81+
addSubviews()
82+
configureNavigationItem()
83+
configureDataSource()
84+
configureTableView()
85+
86+
subject.sink { viewModel in
87+
self.saveBarButton.isEnabled = !viewModel.name.isEmpty
88+
}.store(in: &cancellables)
89+
}
90+
91+
private func configureNavigationItem() {
92+
navigationItem.leftBarButtonItem = UIBarButtonItem(
93+
systemItem: .cancel,
94+
primaryAction: UIAction(handler: { _ in
95+
self.dismiss(animated: true)
96+
})
97+
)
98+
99+
navigationItem.rightBarButtonItem = saveBarButton
100+
}
101+
102+
private func configureTableView() {
103+
tableView.delegate = dataSourceConfiguration
104+
tableView.backgroundColor = .secondaryColor
105+
tableView.registerReusableViews(from: CustomListItemIdentifier.CellIdentifier.self)
106+
}
107+
108+
private func configureDataSource() {
109+
cellConfiguration.onDelete = {
110+
self.onDelete()
111+
}
112+
113+
dataSource = DataSource(
114+
tableView: tableView,
115+
cellProvider: { _, indexPath, itemIdentifier in
116+
self.cellConfiguration.dequeueCell(at: indexPath, for: itemIdentifier)
117+
}
118+
)
119+
120+
dataSourceConfiguration?.didSelectItem = { item in
121+
self.view.endEditing(false)
122+
123+
switch item {
124+
case .name, .deleteList:
125+
break
126+
case .addLocations, .editLocations:
127+
self.delegate?.showLocations()
128+
}
129+
}
130+
131+
dataSourceConfiguration?.updateDataSource(sections: subject.value.tableSections, animated: false)
132+
}
133+
134+
private func addSubviews() {
135+
view.addConstrainedSubviews([tableView]) {
136+
tableView.pinEdgesToSuperview()
137+
}
138+
}
139+
140+
private func onSave() {
141+
do {
142+
try interactor.createCustomList(viewModel: subject.value)
143+
delegate?.customListDidSave()
144+
} catch {
145+
showSaveErrorAlert()
146+
}
147+
}
148+
149+
private func onDelete() {
150+
showDeleteAlert()
151+
}
152+
153+
private func showSaveErrorAlert() {
154+
let presentation = AlertPresentation(
155+
id: "api-custom-lists-save-list-alert",
156+
icon: .alert,
157+
message: NSLocalizedString(
158+
"CUSTOM_LISTS_SAVE_ERROR_PROMPT",
159+
tableName: "APIAccess",
160+
value: "List name is already taken.",
161+
comment: ""
162+
),
163+
buttons: [
164+
AlertAction(
165+
title: NSLocalizedString(
166+
"CUSTOM_LISTS_OK_BUTTON",
167+
tableName: "APIAccess",
168+
value: "Got it!",
169+
comment: ""
170+
),
171+
style: .default
172+
),
173+
]
174+
)
175+
176+
alertPresenter.showAlert(presentation: presentation, animated: true)
177+
}
178+
179+
private func showDeleteAlert() {
180+
let presentation = AlertPresentation(
181+
id: "api-custom-lists-delete-list-alert",
182+
icon: .alert,
183+
message: NSLocalizedString(
184+
"CUSTOM_LISTS_DELETE_PROMPT",
185+
tableName: "APIAccess",
186+
value: "Delete \(subject.value.name)?",
187+
comment: ""
188+
),
189+
buttons: [
190+
AlertAction(
191+
title: NSLocalizedString(
192+
"CUSTOM_LISTS_DELETE_BUTTON",
193+
tableName: "APIAccess",
194+
value: "Delete",
195+
comment: ""
196+
),
197+
style: .destructive,
198+
handler: {
199+
self.interactor.deleteCustomList(id: self.subject.value.id)
200+
self.dismiss(animated: true)
201+
self.delegate?.customListDidDelete()
202+
}
203+
),
204+
AlertAction(
205+
title: NSLocalizedString(
206+
"CUSTOM_LISTS_CANCEL_BUTTON",
207+
tableName: "APIAccess",
208+
value: "Cancel",
209+
comment: ""
210+
),
211+
style: .default
212+
),
213+
]
214+
)
215+
216+
alertPresenter.showAlert(presentation: presentation, animated: true)
217+
}
218+
}

ios/MullvadVPN/Coordinators/SelectLocationCoordinator.swift

+1-9
Original file line numberDiff line numberDiff line change
@@ -69,17 +69,9 @@ class SelectLocationCoordinator: Coordinator, Presentable, Presenting, RelayCach
6969
selectLocationViewController.navigateToFilter = { [weak self] in
7070
guard let self else { return }
7171

72-
// let coordinator = makeRelayFilterCoordinator(forModalPresentation: true)
73-
let coordinator = AddCustomListCoordinator(
74-
navigationController: CustomNavigationController(),
75-
customListInteractor: CustomListInteractor(repository: CustomListRepository())
76-
)
72+
let coordinator = makeRelayFilterCoordinator(forModalPresentation: true)
7773
coordinator.start()
7874

79-
coordinator.didFinish = {
80-
coordinator.dismiss(animated: true)
81-
}
82-
8375
presentChild(coordinator, animated: true)
8476
}
8577

ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodViewController.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ class EditAccessMethodViewController: UITableViewController {
333333
id: "api-access-methods-delete-method-alert",
334334
icon: .alert,
335335
message: NSLocalizedString(
336-
"METHOD_SETTINGS_SAVE_PROMPT",
336+
"METHOD_SETTINGS_DELETE_PROMPT",
337337
tableName: "APIAccess",
338338
value: "Delete \(methodName)?",
339339
comment: ""

0 commit comments

Comments
 (0)