diff options
| author | Jon Petersson <jon.petersson@mullvad.net> | 2025-03-05 10:24:31 +0100 |
|---|---|---|
| committer | Jon Petersson <jon.petersson@mullvad.net> | 2025-03-06 10:36:05 +0100 |
| commit | d1a60639626773ad495d8a5c90cd51dd473aa7f3 (patch) | |
| tree | 2beab96e83da6699e6040d01c11a7bf526ffd62a | |
| parent | 319589b4ec1020b4da217bbc0ae5456ec889b7ab (diff) | |
| download | mullvadvpn-d1a60639626773ad495d8a5c90cd51dd473aa7f3.tar.xz mullvadvpn-d1a60639626773ad495d8a5c90cd51dd473aa7f3.zip | |
Make account number in welcome view copyable
3 files changed, 66 insertions, 3 deletions
diff --git a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift index ea585cebcf..c94c188e34 100644 --- a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift +++ b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift @@ -32,6 +32,7 @@ public enum AccessibilityIdentifier: Equatable { case revokedDeviceLoginButton case dnsSettingsEditButton case infoButton + case copyButton case learnAboutPrivacyButton case logOutDeviceConfirmButton case logOutDeviceCancelButton diff --git a/ios/MullvadVPN/View controllers/CreationAccount/Welcome/WelcomeContentView.swift b/ios/MullvadVPN/View controllers/CreationAccount/Welcome/WelcomeContentView.swift index a892e15baf..5ed49896a0 100644 --- a/ios/MullvadVPN/View controllers/CreationAccount/Welcome/WelcomeContentView.swift +++ b/ios/MullvadVPN/View controllers/CreationAccount/Welcome/WelcomeContentView.swift @@ -11,6 +11,7 @@ import UIKit protocol WelcomeContentViewDelegate: AnyObject, Sendable { func didTapPurchaseButton(welcomeContentView: WelcomeContentView, button: AppButton) func didTapInfoButton(welcomeContentView: WelcomeContentView, button: UIButton) + func didTapCopyButton(welcomeContentView: WelcomeContentView, button: UIButton) } struct WelcomeViewModel: Sendable { @@ -19,6 +20,8 @@ struct WelcomeViewModel: Sendable { } final class WelcomeContentView: UIView, Sendable { + private var revertCopyImageWorkItem: DispatchWorkItem? + private let titleLabel: UILabel = { let label = UILabel() label.font = .preferredFont(forTextStyle: .largeTitle, weight: .bold) @@ -62,6 +65,14 @@ final class WelcomeContentView: UIView, Sendable { return label }() + private let copyButton: UIButton = { + let button = UIButton(type: .system) + button.setAccessibilityIdentifier(.copyButton) + button.tintColor = .white + button.setContentHuggingPriority(.defaultHigh, for: .horizontal) + return button + }() + private let deviceNameLabel: UILabel = { let label = UILabel() label.adjustsFontForContentSizeCategory = true @@ -124,6 +135,14 @@ final class WelcomeContentView: UIView, Sendable { return stackView }() + private let accountRowStackView: UIStackView = { + let stackView = UIStackView() + stackView.axis = .horizontal + stackView.distribution = .fill + stackView.spacing = UIMetrics.padding8 + return stackView + }() + private let deviceRowStackView: UIStackView = { let stackView = UIStackView() stackView.axis = .horizontal @@ -175,12 +194,16 @@ final class WelcomeContentView: UIView, Sendable { } private func configureUI() { + accountRowStackView.addArrangedSubview(accountNumberLabel) + accountRowStackView.addArrangedSubview(copyButton) + accountRowStackView.addArrangedSubview(UIView()) // To push content to the left. + textsStackView.addArrangedSubview(titleLabel) textsStackView.setCustomSpacing(UIMetrics.padding8, after: titleLabel) textsStackView.addArrangedSubview(subtitleLabel) textsStackView.setCustomSpacing(UIMetrics.padding16, after: subtitleLabel) - textsStackView.addArrangedSubview(accountNumberLabel) - textsStackView.setCustomSpacing(UIMetrics.padding16, after: accountNumberLabel) + textsStackView.addArrangedSubview(accountRowStackView) + textsStackView.setCustomSpacing(UIMetrics.padding16, after: accountRowStackView) deviceRowStackView.addArrangedSubview(deviceNameLabel) deviceRowStackView.setCustomSpacing(UIMetrics.padding8, after: deviceNameLabel) @@ -196,6 +219,8 @@ final class WelcomeContentView: UIView, Sendable { addSubview(textsStackView) addSubview(buttonsStackView) addConstraints() + + showCheckmark(false) } private func addConstraints() { @@ -209,7 +234,7 @@ final class WelcomeContentView: UIView, Sendable { } private func addActions() { - [purchaseButton, infoButton].forEach { + [purchaseButton, infoButton, copyButton].forEach { $0.addTarget(self, action: #selector(tapped(button:)), for: .touchUpInside) } } @@ -220,7 +245,40 @@ final class WelcomeContentView: UIView, Sendable { delegate?.didTapPurchaseButton(welcomeContentView: self, button: button) case AccessibilityIdentifier.infoButton.asString: delegate?.didTapInfoButton(welcomeContentView: self, button: button) + case AccessibilityIdentifier.copyButton.asString: + didTapCopyAccountNumber() default: return } } + + private func showCheckmark(_ showCheckmark: Bool) { + if showCheckmark { + let tickIcon = UIImage(named: "IconTick") + + copyButton.setImage(tickIcon, for: .normal) + copyButton.tintColor = .successColor + } else { + let copyIcon = UIImage(named: "IconCopy") + + copyButton.setImage(copyIcon, for: .normal) + copyButton.tintColor = .white + } + } + + @objc private func didTapCopyAccountNumber() { + let delayedWorkItem = DispatchWorkItem { [weak self] in + self?.showCheckmark(false) + } + + revertCopyImageWorkItem?.cancel() + revertCopyImageWorkItem = delayedWorkItem + + showCheckmark(true) + delegate?.didTapCopyButton(welcomeContentView: self, button: copyButton) + + DispatchQueue.main.asyncAfter( + deadline: .now() + .seconds(2), + execute: delayedWorkItem + ) + } } diff --git a/ios/MullvadVPN/View controllers/CreationAccount/Welcome/WelcomeViewController.swift b/ios/MullvadVPN/View controllers/CreationAccount/Welcome/WelcomeViewController.swift index e112d7cb29..a1deac16c0 100644 --- a/ios/MullvadVPN/View controllers/CreationAccount/Welcome/WelcomeViewController.swift +++ b/ios/MullvadVPN/View controllers/CreationAccount/Welcome/WelcomeViewController.swift @@ -83,4 +83,8 @@ extension WelcomeViewController: @preconcurrency WelcomeContentViewDelegate { func didTapPurchaseButton(welcomeContentView: WelcomeContentView, button: AppButton) { delegate?.didRequestToViewPurchaseOptions(accountNumber: interactor.accountNumber) } + + func didTapCopyButton(welcomeContentView: WelcomeContentView, button: UIButton) { + UIPasteboard.general.string = interactor.accountNumber + } } |
