Skip to content

Commit d1a6063

Browse files
committed
Make account number in welcome view copyable
1 parent 319589b commit d1a6063

File tree

3 files changed

+66
-3
lines changed

3 files changed

+66
-3
lines changed

ios/MullvadVPN/Classes/AccessbilityIdentifier.swift

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public enum AccessibilityIdentifier: Equatable {
3232
case revokedDeviceLoginButton
3333
case dnsSettingsEditButton
3434
case infoButton
35+
case copyButton
3536
case learnAboutPrivacyButton
3637
case logOutDeviceConfirmButton
3738
case logOutDeviceCancelButton

ios/MullvadVPN/View controllers/CreationAccount/Welcome/WelcomeContentView.swift

+61-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import UIKit
1111
protocol WelcomeContentViewDelegate: AnyObject, Sendable {
1212
func didTapPurchaseButton(welcomeContentView: WelcomeContentView, button: AppButton)
1313
func didTapInfoButton(welcomeContentView: WelcomeContentView, button: UIButton)
14+
func didTapCopyButton(welcomeContentView: WelcomeContentView, button: UIButton)
1415
}
1516

1617
struct WelcomeViewModel: Sendable {
@@ -19,6 +20,8 @@ struct WelcomeViewModel: Sendable {
1920
}
2021

2122
final class WelcomeContentView: UIView, Sendable {
23+
private var revertCopyImageWorkItem: DispatchWorkItem?
24+
2225
private let titleLabel: UILabel = {
2326
let label = UILabel()
2427
label.font = .preferredFont(forTextStyle: .largeTitle, weight: .bold)
@@ -62,6 +65,14 @@ final class WelcomeContentView: UIView, Sendable {
6265
return label
6366
}()
6467

68+
private let copyButton: UIButton = {
69+
let button = UIButton(type: .system)
70+
button.setAccessibilityIdentifier(.copyButton)
71+
button.tintColor = .white
72+
button.setContentHuggingPriority(.defaultHigh, for: .horizontal)
73+
return button
74+
}()
75+
6576
private let deviceNameLabel: UILabel = {
6677
let label = UILabel()
6778
label.adjustsFontForContentSizeCategory = true
@@ -124,6 +135,14 @@ final class WelcomeContentView: UIView, Sendable {
124135
return stackView
125136
}()
126137

138+
private let accountRowStackView: UIStackView = {
139+
let stackView = UIStackView()
140+
stackView.axis = .horizontal
141+
stackView.distribution = .fill
142+
stackView.spacing = UIMetrics.padding8
143+
return stackView
144+
}()
145+
127146
private let deviceRowStackView: UIStackView = {
128147
let stackView = UIStackView()
129148
stackView.axis = .horizontal
@@ -175,12 +194,16 @@ final class WelcomeContentView: UIView, Sendable {
175194
}
176195

177196
private func configureUI() {
197+
accountRowStackView.addArrangedSubview(accountNumberLabel)
198+
accountRowStackView.addArrangedSubview(copyButton)
199+
accountRowStackView.addArrangedSubview(UIView()) // To push content to the left.
200+
178201
textsStackView.addArrangedSubview(titleLabel)
179202
textsStackView.setCustomSpacing(UIMetrics.padding8, after: titleLabel)
180203
textsStackView.addArrangedSubview(subtitleLabel)
181204
textsStackView.setCustomSpacing(UIMetrics.padding16, after: subtitleLabel)
182-
textsStackView.addArrangedSubview(accountNumberLabel)
183-
textsStackView.setCustomSpacing(UIMetrics.padding16, after: accountNumberLabel)
205+
textsStackView.addArrangedSubview(accountRowStackView)
206+
textsStackView.setCustomSpacing(UIMetrics.padding16, after: accountRowStackView)
184207

185208
deviceRowStackView.addArrangedSubview(deviceNameLabel)
186209
deviceRowStackView.setCustomSpacing(UIMetrics.padding8, after: deviceNameLabel)
@@ -196,6 +219,8 @@ final class WelcomeContentView: UIView, Sendable {
196219
addSubview(textsStackView)
197220
addSubview(buttonsStackView)
198221
addConstraints()
222+
223+
showCheckmark(false)
199224
}
200225

201226
private func addConstraints() {
@@ -209,7 +234,7 @@ final class WelcomeContentView: UIView, Sendable {
209234
}
210235

211236
private func addActions() {
212-
[purchaseButton, infoButton].forEach {
237+
[purchaseButton, infoButton, copyButton].forEach {
213238
$0.addTarget(self, action: #selector(tapped(button:)), for: .touchUpInside)
214239
}
215240
}
@@ -220,7 +245,40 @@ final class WelcomeContentView: UIView, Sendable {
220245
delegate?.didTapPurchaseButton(welcomeContentView: self, button: button)
221246
case AccessibilityIdentifier.infoButton.asString:
222247
delegate?.didTapInfoButton(welcomeContentView: self, button: button)
248+
case AccessibilityIdentifier.copyButton.asString:
249+
didTapCopyAccountNumber()
223250
default: return
224251
}
225252
}
253+
254+
private func showCheckmark(_ showCheckmark: Bool) {
255+
if showCheckmark {
256+
let tickIcon = UIImage(named: "IconTick")
257+
258+
copyButton.setImage(tickIcon, for: .normal)
259+
copyButton.tintColor = .successColor
260+
} else {
261+
let copyIcon = UIImage(named: "IconCopy")
262+
263+
copyButton.setImage(copyIcon, for: .normal)
264+
copyButton.tintColor = .white
265+
}
266+
}
267+
268+
@objc private func didTapCopyAccountNumber() {
269+
let delayedWorkItem = DispatchWorkItem { [weak self] in
270+
self?.showCheckmark(false)
271+
}
272+
273+
revertCopyImageWorkItem?.cancel()
274+
revertCopyImageWorkItem = delayedWorkItem
275+
276+
showCheckmark(true)
277+
delegate?.didTapCopyButton(welcomeContentView: self, button: copyButton)
278+
279+
DispatchQueue.main.asyncAfter(
280+
deadline: .now() + .seconds(2),
281+
execute: delayedWorkItem
282+
)
283+
}
226284
}

ios/MullvadVPN/View controllers/CreationAccount/Welcome/WelcomeViewController.swift

+4
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,8 @@ extension WelcomeViewController: @preconcurrency WelcomeContentViewDelegate {
8383
func didTapPurchaseButton(welcomeContentView: WelcomeContentView, button: AppButton) {
8484
delegate?.didRequestToViewPurchaseOptions(accountNumber: interactor.accountNumber)
8585
}
86+
87+
func didTapCopyButton(welcomeContentView: WelcomeContentView, button: UIButton) {
88+
UIPasteboard.general.string = interactor.accountNumber
89+
}
8690
}

0 commit comments

Comments
 (0)