Skip to content

Commit 7a8028d

Browse files
committed
Implement addLocationCell
1 parent ea3ad43 commit 7a8028d

File tree

4 files changed

+227
-22
lines changed

4 files changed

+227
-22
lines changed

ios/MullvadVPN.xcodeproj/project.pbxproj

+8
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,8 @@
817817
F0164ED12B4F2DCB0020268D /* AccessMethodIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0164ED02B4F2DCB0020268D /* AccessMethodIterator.swift */; };
818818
F028A56A2A34D4E700C0CAA3 /* RedeemVoucherViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F028A5692A34D4E700C0CAA3 /* RedeemVoucherViewController.swift */; };
819819
F028A56C2A34D8E600C0CAA3 /* AddCreditSucceededViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F028A56B2A34D8E600C0CAA3 /* AddCreditSucceededViewController.swift */; };
820+
F02F41A42B9723AF00625A4F /* AddLocationCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F02F419E2B9723AF00625A4F /* AddLocationCellViewModel.swift */; };
821+
F02F41A52B9723AF00625A4F /* AddLocationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F02F419F2B9723AF00625A4F /* AddLocationCell.swift */; };
820822
F03580252A13842C00E5DAFD /* IncreasedHitButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03580242A13842C00E5DAFD /* IncreasedHitButton.swift */; };
821823
F04413612BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F04413602BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift */; };
822824
F04413622BA45CE30018A6EE /* CustomListLocationNodeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F04413602BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift */; };
@@ -1965,6 +1967,8 @@
19651967
F0164ED02B4F2DCB0020268D /* AccessMethodIterator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessMethodIterator.swift; sourceTree = "<group>"; };
19661968
F028A5692A34D4E700C0CAA3 /* RedeemVoucherViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RedeemVoucherViewController.swift; sourceTree = "<group>"; };
19671969
F028A56B2A34D8E600C0CAA3 /* AddCreditSucceededViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddCreditSucceededViewController.swift; sourceTree = "<group>"; };
1970+
F02F419E2B9723AF00625A4F /* AddLocationCellViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddLocationCellViewModel.swift; sourceTree = "<group>"; };
1971+
F02F419F2B9723AF00625A4F /* AddLocationCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddLocationCell.swift; sourceTree = "<group>"; };
19681972
F03580242A13842C00E5DAFD /* IncreasedHitButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncreasedHitButton.swift; sourceTree = "<group>"; };
19691973
F04413602BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomListLocationNodeBuilder.swift; sourceTree = "<group>"; };
19701974
F04F95A02B21D24400431E08 /* shadowsocks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = shadowsocks.h; sourceTree = "<group>"; };
@@ -3492,6 +3496,8 @@
34923496
isa = PBXGroup;
34933497
children = (
34943498
7A6389D72B7E3BD6008E77E1 /* AddCustomListCoordinator.swift */,
3499+
F02F419F2B9723AF00625A4F /* AddLocationCell.swift */,
3500+
F02F419E2B9723AF00625A4F /* AddLocationCellViewModel.swift */,
34953501
7A6389D22B7E3BD6008E77E1 /* CustomListCellConfiguration.swift */,
34963502
7A6389D42B7E3BD6008E77E1 /* CustomListDataSourceConfiguration.swift */,
34973503
7A6389DA2B7E3BD6008E77E1 /* CustomListInteractor.swift */,
@@ -5151,6 +5157,7 @@
51515157
582BB1B1229569620055B6EF /* UINavigationBar+Appearance.swift in Sources */,
51525158
7A9FA1442A2E3FE5000B728D /* CheckableSettingsCell.swift in Sources */,
51535159
58ACF6492655365700ACE4B7 /* VPNSettingsViewController.swift in Sources */,
5160+
F02F41A52B9723AF00625A4F /* AddLocationCell.swift in Sources */,
51545161
7ABE318D2A1CDD4500DF4963 /* UIFont+Weight.swift in Sources */,
51555162
58C774BE29A7A249003A1A56 /* CustomNavigationController.swift in Sources */,
51565163
E1FD0DF528AA7CE400299DB4 /* StatusActivityView.swift in Sources */,
@@ -5405,6 +5412,7 @@
54055412
7A6389DB2B7E3BD6008E77E1 /* CustomListCellConfiguration.swift in Sources */,
54065413
F09A297C2A9F8A9B00EA3B6F /* VoucherTextField.swift in Sources */,
54075414
7A5869B72B56B41500640D27 /* IPOverrideTextViewController.swift in Sources */,
5415+
F02F41A42B9723AF00625A4F /* AddLocationCellViewModel.swift in Sources */,
54085416
58ACF64B26553C3F00ACE4B7 /* SettingsSwitchCell.swift in Sources */,
54095417
7AF9BE952A40461100DBFEDB /* RelayFilterView.swift in Sources */,
54105418
7A09C98129D99215000C2CAC /* String+FuzzyMatch.swift in Sources */,

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

-22
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
//
2+
// AddLocationCell.swift
3+
// MullvadVPN
4+
//
5+
// Created by Mojgan on 2024-02-29.
6+
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
protocol AddLocationCellDelegate: AnyObject {
12+
func toggleExpanding(cell: AddLocationCell)
13+
func toggleSelection(cell: AddLocationCell)
14+
}
15+
16+
class AddLocationCell: UITableViewCell {
17+
weak var delegate: AddLocationCellDelegate?
18+
19+
private let chevronDown = UIImage(resource: .iconChevronDown)
20+
private let chevronUp = UIImage(resource: .iconChevronUp)
21+
22+
private let locationLabel: UILabel = {
23+
let label = UILabel()
24+
label.font = UIFont.systemFont(ofSize: 17)
25+
label.textColor = .white
26+
label.numberOfLines = .zero
27+
label.lineBreakStrategy = []
28+
return label
29+
}()
30+
31+
private let checkboxButton: UIButton = {
32+
let button = UIButton()
33+
button.setImage(UIImage(systemName: "checkmark.square.fill"), for: .selected)
34+
button.setImage(UIImage(systemName: "square"), for: .normal)
35+
button.tintColor = .white
36+
return button
37+
}()
38+
39+
private let collapseButton: UIButton = {
40+
let button = UIButton(type: .custom)
41+
button.accessibilityIdentifier = .collapseButton
42+
button.isAccessibilityElement = false
43+
button.tintColor = .white
44+
return button
45+
}()
46+
47+
var isExpanded = false {
48+
didSet {
49+
updateCollapseImage()
50+
updateAccessibilityCustomActions()
51+
}
52+
}
53+
54+
var showsCollapseControl = false {
55+
didSet {
56+
collapseButton.isHidden = !showsCollapseControl
57+
updateAccessibilityCustomActions()
58+
}
59+
}
60+
61+
override var indentationLevel: Int {
62+
didSet {
63+
updateBackgroundColor()
64+
setLayoutMargins()
65+
}
66+
}
67+
68+
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
69+
super.init(style: style, reuseIdentifier: reuseIdentifier)
70+
setupCell()
71+
}
72+
73+
required init?(coder: NSCoder) {
74+
fatalError("init(coder:) has not been implemented")
75+
}
76+
77+
private func setLayoutMargins() {
78+
let indentation = CGFloat(indentationLevel) * indentationWidth
79+
80+
var contentMargins = UIMetrics.locationCellLayoutMargins
81+
contentMargins.leading += indentation
82+
83+
contentView.directionalLayoutMargins = contentMargins
84+
}
85+
86+
private func setupCell() {
87+
indentationWidth = UIMetrics.TableView.cellIndentationWidth
88+
89+
backgroundColor = .clear
90+
contentView.backgroundColor = .clear
91+
92+
backgroundView = UIView()
93+
94+
collapseButton.addTarget(self, action: #selector(handleCollapseButton(_:)), for: .touchUpInside)
95+
checkboxButton.addTarget(self, action: #selector(toggleCheckboxButton(_:)), for: .touchUpInside)
96+
97+
contentView.addConstrainedSubviews([checkboxButton, locationLabel, collapseButton]) {
98+
checkboxButton.pinEdgeToSuperviewMargin(.leading(.zero))
99+
checkboxButton.centerYAnchor.constraint(equalTo: contentView.centerYAnchor)
100+
checkboxButton.widthAnchor
101+
.constraint(
102+
equalToConstant: 44.0
103+
)
104+
checkboxButton.heightAnchor.constraint(equalTo: checkboxButton.widthAnchor, multiplier: 1, constant: 0)
105+
106+
locationLabel.leadingAnchor.constraint(
107+
equalTo: checkboxButton.trailingAnchor,
108+
constant: 12
109+
)
110+
111+
locationLabel.trailingAnchor.constraint(lessThanOrEqualTo: collapseButton.leadingAnchor)
112+
.withPriority(.defaultHigh)
113+
locationLabel.pinEdgesToSuperviewMargins(PinnableEdges([.top(.zero), .bottom(.zero)]))
114+
115+
collapseButton.widthAnchor
116+
.constraint(
117+
equalToConstant: UIMetrics.contentLayoutMargins.leading + UIMetrics
118+
.contentLayoutMargins.trailing + 24.0
119+
)
120+
collapseButton.pinEdgesToSuperviewMargins(.all().excluding(.leading))
121+
}
122+
123+
updateCollapseImage()
124+
updateAccessibilityCustomActions()
125+
updateBackgroundColor()
126+
setLayoutMargins()
127+
}
128+
129+
private func updateBackgroundColor() {
130+
backgroundView?.backgroundColor = backgroundColorForIdentationLevel()
131+
}
132+
133+
private func backgroundColorForIdentationLevel() -> UIColor {
134+
switch indentationLevel {
135+
case 1:
136+
return UIColor.Cell.Background.indentationLevelOne
137+
case 2:
138+
return UIColor.Cell.Background.indentationLevelTwo
139+
case 3:
140+
return UIColor.Cell.Background.indentationLevelThree
141+
default:
142+
return UIColor.Cell.Background.normal
143+
}
144+
}
145+
146+
private func updateCollapseImage() {
147+
let image = isExpanded ? chevronUp : chevronDown
148+
collapseButton.setImage(image, for: .normal)
149+
}
150+
151+
private func updateAccessibilityCustomActions() {
152+
if showsCollapseControl {
153+
let actionName = isExpanded
154+
? NSLocalizedString(
155+
"ADD_LOCATIONS_COLLAPSE_ACCESSIBILITY_ACTION",
156+
tableName: "AddLocationsLocation",
157+
value: "Collapse location",
158+
comment: ""
159+
)
160+
: NSLocalizedString(
161+
"ADD_LOCATIONS_EXPAND_ACCESSIBILITY_ACTION",
162+
tableName: "AddLocationsLocation",
163+
value: "Expand location",
164+
comment: ""
165+
)
166+
167+
accessibilityCustomActions = [
168+
UIAccessibilityCustomAction(
169+
name: actionName,
170+
target: self,
171+
selector: #selector(toggleCollapseAccessibilityAction)
172+
),
173+
]
174+
} else {
175+
accessibilityCustomActions = nil
176+
}
177+
}
178+
179+
// MARK: - actions
180+
181+
@objc private func handleCollapseButton(_ sender: UIControl) {
182+
delegate?.toggleExpanding(cell: self)
183+
}
184+
185+
@objc private func toggleCollapseAccessibilityAction() -> Bool {
186+
delegate?.toggleExpanding(cell: self)
187+
return true
188+
}
189+
190+
@objc private func toggleCheckboxButton(_ sender: UIControl) {
191+
delegate?.toggleSelection(cell: self)
192+
}
193+
}
194+
195+
extension AddLocationCell {
196+
func configure(item: AddLocationCellViewModel) {
197+
accessibilityIdentifier = item.node.name
198+
locationLabel.text = item.node.name
199+
showsCollapseControl = !item.node.children.isEmpty
200+
isExpanded = item.node.showsChildren
201+
checkboxButton.isSelected = item.isSelected
202+
checkboxButton.tintColor = item.isSelected ? .successColor : .white
203+
}
204+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//
2+
// AddLocationCellViewModel.swift
3+
// MullvadVPN
4+
//
5+
// Created by Mojgan on 2024-03-04.
6+
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
struct AddLocationCellViewModel: Hashable {
12+
let node: LocationNode
13+
var indentationLevel = 0
14+
var isSelected: Bool
15+
}

0 commit comments

Comments
 (0)