// // Copyright 2023 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import SafariServices import SignalServiceKit import SignalUI public struct RegistrationPinAttemptsExhaustedViewState: Equatable { public enum Mode: Equatable { /// We've already registered and were attempting to restore backups from kbs /// but ran out of guesses; we can proceed without backups. case restoringBackup /// We were attempting to use the PIN to bypass sms-based registration. /// We may or may not need the PIN for reglock later; for now we can fall back /// to sms based verification. case restoringRegistrationRecoveryPassword } public let mode: Mode } // MARK: - RegistrationPinAttemptsExhaustedAndMustCreateNewPinPresenter protocol RegistrationPinAttemptsExhaustedAndMustCreateNewPinPresenter: AnyObject { func acknowledgePinGuessesExhausted() } // MARK: - RegistrationPinAttemptsExhaustedAndMustCreateNewPinViewController class RegistrationPinAttemptsExhaustedAndMustCreateNewPinViewController: OWSViewController { private var state: RegistrationPinAttemptsExhaustedViewState public init( state: RegistrationPinAttemptsExhaustedViewState, presenter: RegistrationPinAttemptsExhaustedAndMustCreateNewPinPresenter ) { self.state = state self.presenter = presenter super.init() } public func updateState(_ newState: RegistrationPinAttemptsExhaustedViewState) { self.state = newState self.render() } @available(*, unavailable) public override init() { owsFail("This should not be called") } // MARK: Internal state private weak var presenter: RegistrationPinAttemptsExhaustedAndMustCreateNewPinPresenter? public override func viewDidLoad() { super.viewDidLoad() initialRender() } public override func themeDidChange() { super.themeDidChange() render() } // MARK: Rendering private lazy var titleLabel: UILabel = { let result = UILabel.titleLabelForRegistration(text: OWSLocalizedString( "ONBOARDING_PIN_ATTEMPTS_EXHAUSTED_TITLE", comment: "Title of the 'onboarding pin attempts exhausted' view when reglock is disabled." )) result.accessibilityIdentifier = "registration.pinAttemptsExhausted.titleLabel" return result }() private lazy var explanationLabel: UILabel = { let result = UILabel.explanationLabelForRegistration(text: "") result.accessibilityIdentifier = "registration.pinAttemptsExhausted.explanationLabel" return result }() private lazy var continueButton: OWSButton = { let result = OWSButton(title: "") { [weak self] in self?.presenter?.acknowledgePinGuessesExhausted() } result.dimsWhenHighlighted = true result.layer.cornerRadius = 8 result.backgroundColor = .ows_accentBlue result.titleLabel?.font = UIFont.dynamicTypeBody.semibold() result.titleLabel?.numberOfLines = 0 result.ows_contentEdgeInsets = .init(margin: 14) result.autoSetDimension(.height, toSize: 48, relation: .greaterThanOrEqual) return result }() private lazy var learnMoreButton: OWSFlatButton = { let result = OWSFlatButton.button( title: OWSLocalizedString( "ONBOARDING_PIN_ATTEMPTS_EXHAUSTED_LEARN_MORE", comment: "Label for the 'learn more' link when reglock is disabled in the 'onboarding pin attempts exhausted' view." ), font: UIFont.dynamicTypeBody.semibold(), titleColor: Theme.accentBlueColor, backgroundColor: .clear, target: self, selector: #selector(didTapLearnMoreButton) ) result.accessibilityIdentifier = "registration.pinAttemptsExhausted.learnMoreButton" return result }() private func initialRender() { navigationItem.setHidesBackButton(true, animated: false) let stackView = UIStackView() stackView.axis = .vertical stackView.directionalLayoutMargins = .layoutMarginsForRegistration(traitCollection.horizontalSizeClass) stackView.isLayoutMarginsRelativeArrangement = true view.addSubview(stackView) stackView.autoPinEdgesToSuperviewMargins() stackView.addArrangedSubview(titleLabel) stackView.setCustomSpacing(12, after: titleLabel) stackView.addArrangedSubview(explanationLabel) stackView.addArrangedSubview(UIView.vStretchingSpacer()) stackView.addArrangedSubview(continueButton) stackView.setCustomSpacing(24, after: continueButton) stackView.addArrangedSubview(learnMoreButton) render() } private func render() { view.backgroundColor = Theme.backgroundColor titleLabel.textColor = Theme.primaryTextColor explanationLabel.textColor = Theme.secondaryTextAndIconColor learnMoreButton.setTitleColor(Theme.accentBlueColor) switch state.mode { case .restoringBackup: explanationLabel.text = OWSLocalizedString( "ONBOARDING_PIN_ATTEMPTS_EXHAUSTED_EXPLANATION", comment: "Explanation of the 'onboarding pin attempts exhausted' view when reglock is disabled." ) continueButton.setTitle( OWSLocalizedString( "ONBOARDING_2FA_CREATE_NEW_PIN", comment: "Label for the 'create new pin' button when reglock is disabled during onboarding." ), for: .normal ) case .restoringRegistrationRecoveryPassword: explanationLabel.text = OWSLocalizedString( "ONBOARDING_PIN_ATTEMPTS_EXHAUSTED_WITH_UNKNOWN_REGLOCK_EXPLANATION", comment: "Explanation of the 'onboarding pin attempts exhausted' view when it is unknown if reglock is enabled." ) continueButton.setTitle(CommonStrings.continueButton, for: .normal) } } // MARK: Events @objc private func didTapLearnMoreButton() { present(SFSafariViewController(url: URL.Support.pin), animated: true) } }