Signal-iOS/Signal/Registration/UserInterface/RegistrationReglockTimeoutViewController.swift
Igor Solomennikov b260cc7842
Update other misc view controllers used in registration.
• adopt dynamic colors
• use system-provided layout margins
• use shared button styles
• migrate away from PureLayout
• added Preview for some view controllers
2025-10-08 15:11:15 -07:00

231 lines
7.7 KiB
Swift

//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import SafariServices
import SignalServiceKit
import SignalUI
// MARK: - RegistrationReglockTimeoutAcknowledgeAction
public enum RegistrationReglockTimeoutAcknowledgeAction {
case resetPhoneNumber
case close
// Unable to do anything, just stuck here.
case none
}
// MARK: - RegistrationReglockTimeoutState
public struct RegistrationReglockTimeoutState: Equatable {
let reglockExpirationDate: Date
let acknowledgeAction: RegistrationReglockTimeoutAcknowledgeAction
}
// MARK: - RegistrationReglockTimeoutPresenter
protocol RegistrationReglockTimeoutPresenter: AnyObject {
func acknowledgeReglockTimeout()
}
// MARK: - RegistrationReglockTimeoutViewController
class RegistrationReglockTimeoutViewController: OWSViewController {
private let oneMinute: TimeInterval = 60
public init(
state: RegistrationReglockTimeoutState,
presenter: RegistrationReglockTimeoutPresenter
) {
self.state = state
self.presenter = presenter
super.init()
navigationItem.hidesBackButton = true
}
@available(*, unavailable)
public override init() {
owsFail("This should not be called")
}
deinit {
explanationLabelTimer?.invalidate()
explanationLabelTimer = nil
}
// MARK: Internal state
private let state: RegistrationReglockTimeoutState
private weak var presenter: RegistrationReglockTimeoutPresenter?
private var explanationLabelTimer: Timer?
public override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .Signal.background
let titleLabel = UILabel.titleLabelForRegistration(text: OWSLocalizedString(
"REGISTRATION_LOCK_TIMEOUT_TITLE",
comment: "Registration Lock can prevent users from registering in some cases, and they'll have to wait. This is the title of that screen."
))
titleLabel.accessibilityIdentifier = "registration.reglockTimeout.titleLabel"
let okayButtonTitle: String
switch state.acknowledgeAction {
case .resetPhoneNumber:
okayButtonTitle = OWSLocalizedString(
"REGISTRATION_LOCK_TIMEOUT_RESET_PHONE_NUMBER_BUTTON",
comment: "Registration Lock can prevent users from registering in some cases, and they'll have to wait. This button appears on that screen. Tapping it will bump the user back, earlier in registration, so they can register with a different phone number."
)
case .close, .none:
okayButtonTitle = CommonStrings.okayButton
}
let okayButton = UIButton(
configuration: .largePrimary(title: okayButtonTitle),
primaryAction: UIAction { [weak self] _ in
self?.presenter?.acknowledgeReglockTimeout()
}
)
let learnMoreButton = UIButton(
configuration: .largeSecondary(title: OWSLocalizedString(
"REGISTRATION_LOCK_TIMEOUT_LEARN_MORE_BUTTON",
comment: "Registration Lock can prevent users from registering in some cases, and they'll have to wait. This button appears on that screen. Tapping it will tell the user more information."
)),
primaryAction: UIAction { [weak self] _ in
self?.didTapLearnMoreButton()
}
)
learnMoreButton.accessibilityIdentifier = "registration.reglockTimeout.learnMoreButton"
let buttonContainer = UIStackView(arrangedSubviews: [ okayButton, learnMoreButton ])
buttonContainer.axis = .vertical
buttonContainer.spacing = 12
buttonContainer.alignment = .fill
buttonContainer.isLayoutMarginsRelativeArrangement = true
buttonContainer.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 0, leading: 22, bottom: 16, trailing: 22)
let stackView = UIStackView(arrangedSubviews: [
titleLabel,
explanationLabel,
.vStretchingSpacer(minHeight: 36),
buttonContainer,
])
stackView.axis = .vertical
stackView.spacing = 12
stackView.preservesSuperviewLayoutMargins = true
stackView.isLayoutMarginsRelativeArrangement = true
view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
])
updateExplanationLabelText()
explanationLabelTimer = Timer.scheduledTimer(
withTimeInterval: oneMinute,
repeats: true
) { [weak self] _ in
self?.updateExplanationLabelText()
}
}
// MARK: UI
private var explanationLabelText: String {
let format = OWSLocalizedString(
"REGISTRATION_LOCK_TIMEOUT_DESCRIPTION_FORMAT",
comment: "Registration Lock can prevent users from registering in some cases, and they'll have to wait. This is the description on that screen, explaining what's going on. Embeds {{ duration }}, such as \"7 days\"."
)
let remainingSeconds: UInt32 = {
let result = state.reglockExpirationDate.timeIntervalSince(Date())
return UInt32(result <= oneMinute ? oneMinute : result)
}()
return String(
format: format,
DateUtil.formatDuration(seconds: remainingSeconds, useShortFormat: false)
)
}
private lazy var explanationLabel: UILabel = {
let result = UILabel.explanationLabelForRegistration(text: explanationLabelText)
result.accessibilityIdentifier = "registration.reglockTimeout.explanationLabel"
return result
}()
private func updateExplanationLabelText() {
explanationLabel.text = explanationLabelText
}
// MARK: Events
private func didTapLearnMoreButton() {
present(SFSafariViewController(url: URL.Support.pin), animated: true)
}
}
// MARK: -
#if DEBUG
private class PreviewRegistrationReglockTimeoutPresenter: RegistrationReglockTimeoutPresenter {
func acknowledgeReglockTimeout() {
print("acknowledgeReglockTimeout")
}
}
@available(iOS 17, *)
#Preview {
let presenter = PreviewRegistrationReglockTimeoutPresenter()
UINavigationController(
rootViewController: RegistrationReglockTimeoutViewController(
state: RegistrationReglockTimeoutState(
reglockExpirationDate: Date.now.addingTimeInterval(1000),
acknowledgeAction: .resetPhoneNumber
),
presenter: presenter
)
)
}
@available(iOS 17, *)
#Preview {
let presenter = PreviewRegistrationReglockTimeoutPresenter()
UINavigationController(
rootViewController: RegistrationReglockTimeoutViewController(
state: RegistrationReglockTimeoutState(
reglockExpirationDate: Date.now.addingTimeInterval(1000),
acknowledgeAction: .close
),
presenter: presenter
)
)
}
@available(iOS 17, *)
#Preview {
let presenter = PreviewRegistrationReglockTimeoutPresenter()
UINavigationController(
rootViewController: RegistrationReglockTimeoutViewController(
state: RegistrationReglockTimeoutState(
reglockExpirationDate: Date.now.addingTimeInterval(1000),
acknowledgeAction: .none
),
presenter: presenter
)
)
}
#endif