Path: blob/main/Signal/src/ViewControllers/AppSettings/Payments/PaymentsViewPassphraseSplashViewController.swift
1 views
//
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
public import SignalServiceKit
public import SignalUI
public protocol PaymentsViewPassphraseDelegate: AnyObject {
func viewPassphraseDidCancel(viewController: PaymentsViewPassphraseSplashViewController)
func viewPassphraseDidComplete()
}
// MARK: -
public class PaymentsViewPassphraseSplashViewController: OWSViewController {
public enum Style: Int, CaseIterable {
/// From settings menu when user has not completed recovery phrase
case view
/// From settings menu after user has completed recovery phrase
case reviewed
/// When balance becomes non-zero for the first time
case fromBalance
/// From the help card on payments settings
case fromHelpCard
/// When dismissing the help card on payments settings
case fromHelpCardDismiss
}
public let style: Style
private let passphrase: PaymentsPassphrase
private weak var viewPassphraseDelegate: PaymentsViewPassphraseDelegate?
private let rootView = UIStackView()
public init(
passphrase: PaymentsPassphrase,
style: Style,
viewPassphraseDelegate: PaymentsViewPassphraseDelegate,
) {
self.passphrase = passphrase
self.style = style
self.viewPassphraseDelegate = viewPassphraseDelegate
super.init()
}
override public func viewDidLoad() {
super.viewDidLoad()
title = OWSLocalizedString(
"SETTINGS_PAYMENTS_VIEW_PASSPHRASE_TITLE",
comment: "Title for the 'view payments passphrase' view of the app settings.",
)
OWSTableViewController2.removeBackButtonText(viewController: self)
rootView.axis = .vertical
rootView.alignment = .fill
view.addSubview(rootView)
rootView.autoPin(toTopLayoutGuideOf: self, withInset: 0)
rootView.autoPin(toBottomLayoutGuideOf: self, withInset: 0)
rootView.autoPinWidthToSuperviewMargins()
updateContents()
updateNavbar()
}
override public func themeDidChange() {
super.themeDidChange()
updateContents()
}
private func updateNavbar() {
navigationItem.leftBarButtonItem = UIBarButtonItem(
image: Theme.iconImage(.buttonX),
style: .plain,
target: self,
action: #selector(didTapDismiss),
accessibilityIdentifier: "dismiss",
)
}
override public func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
updateContents()
updateNavbar()
}
private func updateContents() {
AssertIsOnMainThread()
view.backgroundColor = OWSTableViewController2.tableBackgroundColor(isUsingPresentedStyle: true)
let heroImage = UIImageView(image: UIImage(named: "recovery-phrase"))
let titleLabel = UILabel()
titleLabel.text = style.title
titleLabel.font = UIFont.dynamicTypeTitle2Clamped.semibold()
titleLabel.textColor = Theme.primaryTextColor
titleLabel.textAlignment = .center
let explanationLabel = PaymentsViewUtils.buildTextWithLearnMoreLinkTextView(
text: self.style.explanationText,
font: .dynamicTypeSubheadlineClamped,
learnMoreUrl: self.style.explanationUrl,
)
explanationLabel.textAlignment = .center
let topStack = UIStackView(arrangedSubviews: [
heroImage,
UIView.spacer(withHeight: 20),
titleLabel,
UIView.spacer(withHeight: 10),
explanationLabel,
])
topStack.axis = .vertical
topStack.alignment = .center
topStack.isLayoutMarginsRelativeArrangement = true
topStack.layoutMargins = UIEdgeInsets(hMargin: 20, vMargin: 0)
let nextButton = OWSFlatButton.insetButton(
title: CommonStrings.nextButton,
font: UIFont.dynamicTypeHeadline,
titleColor: .white,
backgroundColor: .ows_accentBlue,
target: self,
selector: #selector(didTapNextButton),
)
nextButton.autoSetHeightUsingFont()
nextButton.cornerRadius = 14
let cancelButton = OWSFlatButton.insetButton(
title: CommonStrings.notNowButton,
font: UIFont.dynamicTypeHeadline,
titleColor: .ows_accentBlue,
backgroundColor: .clear,
target: self,
selector: #selector(didTapDismiss),
)
cancelButton.autoSetHeightUsingFont()
let spacerFactory = SpacerFactory()
rootView.removeAllSubviews()
rootView.addArrangedSubviews([
spacerFactory.buildVSpacer(),
topStack,
spacerFactory.buildVSpacer(),
nextButton,
cancelButton,
UIView.spacer(withHeight: 8),
])
spacerFactory.finalizeSpacers()
}
func showDismissConfirmation() {
let actionSheet = ActionSheetController(
title: OWSLocalizedString(
"SETTINGS_PAYMENTS_PASSPHRASE_DISCARD_CONFIRMATION_TITLE",
comment: "Title of confirmation alert when discarding recovery phrase.",
),
message: OWSLocalizedString(
"SETTINGS_PAYMENTS_PASSPHRASE_DISCARD_CONFIRMATION_MESSAGE",
comment: "Message of confirmation alert when discarding recovery phrase.",
),
)
actionSheet.addAction(ActionSheetAction(
title: OWSLocalizedString(
"SETTINGS_PAYMENTS_PASSPHRASE_DISCARD_CONFIRMATION_BUTTON",
comment: "Button when discarding recovery phrase.",
),
style: .destructive,
handler: { [weak self] _ in
self?.notifyCancelled()
},
))
actionSheet.addAction(ActionSheetAction(
title: CommonStrings.cancelButton,
style: .cancel,
handler: nil,
))
self.presentActionSheet(actionSheet)
}
private func notifyCancelled() {
viewPassphraseDelegate?.viewPassphraseDidCancel(viewController: self)
}
// MARK: - Events
@objc
private func didTapDismiss() {
if style.shouldConfirmCancel {
showDismissConfirmation()
} else {
notifyCancelled()
}
}
@objc
private func didTapNextButton() {
AssertIsOnMainThread()
guard let viewPassphraseDelegate else {
dismiss(animated: false, completion: nil)
return
}
if SSKEnvironment.shared.owsPaymentsLockRef.isPaymentsLockEnabled() {
SSKEnvironment.shared.owsPaymentsLockRef.tryToUnlock { [weak self] outcome in
guard let self else { return }
guard outcome == OWSPaymentsLock.LocalAuthOutcome.success else {
PaymentActionSheets.showBiometryAuthFailedActionSheet { _ in
self.dismiss(animated: false, completion: nil)
}
return
}
let view = PaymentsViewPassphraseGridViewController(
passphrase: self.passphrase,
viewPassphraseDelegate: viewPassphraseDelegate,
)
self.navigationController?.pushViewController(view, animated: true)
}
} else {
let view = PaymentsViewPassphraseGridViewController(
passphrase: passphrase,
viewPassphraseDelegate: viewPassphraseDelegate,
)
navigationController?.pushViewController(view, animated: true)
}
}
}
extension PaymentsViewPassphraseSplashViewController.Style {
var title: String {
switch self {
case .reviewed:
return OWSLocalizedString(
"SETTINGS_PAYMENTS_VIEW_PASSPHRASE_START_TITLE",
comment: "Title for the first step of the 'view payments passphrase' views.",
)
case .fromBalance, .fromHelpCard, .fromHelpCardDismiss, .view:
return OWSLocalizedString(
"SETTINGS_PAYMENTS_SAVE_PASSPHRASE_START_TITLE",
comment: "Title for the first step of the 'save payments passphrase' views.",
)
}
}
var explanationText: String {
switch self {
case .view, .reviewed:
return OWSLocalizedString(
"SETTINGS_PAYMENTS_PASSPHRASE_EXPLANATION",
comment: "Explanation of the 'payments passphrase' in the 'view payments passphrase' settings.",
)
case .fromHelpCard, .fromHelpCardDismiss:
return OWSLocalizedString(
"SETTINGS_PAYMENTS_PASSPHRASE_EXPLANATION_FROM_HELP_CARD",
comment: "Explanation of the 'payments passphrase' when from the help card.",
)
case .fromBalance:
return OWSLocalizedString(
"SETTINGS_PAYMENTS_PASSPHRASE_EXPLANATION_FROM_BALANCE",
comment: "Explanation of the 'payments passphrase' when there is a balance.",
)
}
}
var explanationUrl: URL {
return URL.Support.Payments.walletViewPassphrase
}
var shouldConfirmCancel: Bool {
switch self {
case .reviewed:
return false
case .fromBalance, .fromHelpCard, .fromHelpCardDismiss, .view:
return true
}
}
var shouldShowHelpCardAfterCancel: Bool {
switch self {
case .reviewed, .fromHelpCardDismiss:
return false
case .fromBalance, .fromHelpCard, .view:
return true
}
}
}