Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
signalapp
GitHub Repository: signalapp/Signal-iOS
Path: blob/main/Signal/src/ViewControllers/AppSettings/Payments/PaymentsViewPassphraseGridViewController.swift
1 views
//
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

public import SignalServiceKit
public import SignalUI

public class PaymentsViewPassphraseGridViewController: OWSTableViewController2 {

    private let passphrase: PaymentsPassphrase

    private weak var viewPassphraseDelegate: PaymentsViewPassphraseDelegate?

    private let bottomStack = UIStackView()

    override open var bottomFooter: UIView? {
        get { bottomStack }
        set {}
    }

    public init(
        passphrase: PaymentsPassphrase,
        viewPassphraseDelegate: PaymentsViewPassphraseDelegate,
    ) {
        self.passphrase = passphrase
        self.viewPassphraseDelegate = viewPassphraseDelegate

        super.init()

        self.shouldAvoidKeyboard = true
    }

    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.",
        )

        buildBottomView()
        updateTableContents()
    }

    override public func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        updateTableContents()
    }

    override public func themeDidChange() {
        super.themeDidChange()

        updateTableContents()
    }

    private func buildBottomView() {
        let nextButton = OWSFlatButton.insetButton(
            title: CommonStrings.nextButton,
            font: UIFont.dynamicTypeHeadline,
            titleColor: .white,
            backgroundColor: .ows_accentBlue,
            target: self,
            selector: #selector(didTapNextButton),
        )

        nextButton.autoSetHeightUsingFont()

        bottomStack.axis = .vertical
        bottomStack.alignment = .fill
        bottomStack.isLayoutMarginsRelativeArrangement = true
        bottomStack.layoutMargins = .init(top: 8, left: 20, bottom: 8, right: 20)
        bottomStack.addArrangedSubviews([
            nextButton,
        ])
    }

    private func updateTableContents() {
        AssertIsOnMainThread()

        let contents = OWSTableContents()

        let section = OWSTableSection()
        section.customHeaderView = buildHeader()
        section.customFooterView = buildFooter()
        section.hasBackground = false
        section.shouldDisableCellSelection = true

        let passphrase = self.passphrase
        section.add(OWSTableItem(
            customCellBlock: { [weak self] in
                let cell = OWSTableItem.newCell()
                guard let self else { return cell }
                let passphraseGrid = self.buildPassphraseGrid(passphrase: passphrase)
                cell.contentView.addSubview(passphraseGrid)
                passphraseGrid.autoPinEdgesToSuperviewEdges()
                return cell
            },
            actionBlock: nil,
        ))
        contents.add(section)

        self.contents = contents
    }

    private func buildPassphraseGrid(passphrase: PaymentsPassphrase) -> UIView {
        let copyToClipboardLabel = UILabel()
        copyToClipboardLabel.text = OWSLocalizedString(
            "SETTINGS_PAYMENTS_VIEW_PASSPHRASE_COPY_TO_CLIPBOARD",
            comment: "Label for the 'copy to clipboard' button in the 'view payments passphrase' views.",
        )
        copyToClipboardLabel.textColor = .ows_accentBlue
        copyToClipboardLabel.font = UIFont.dynamicTypeSubheadlineClamped.semibold()

        let copyToClipboardButton = OWSLayerView.pillView()
        copyToClipboardButton.backgroundColor = Theme.secondaryBackgroundColor
        copyToClipboardButton.addSubview(copyToClipboardLabel)
        copyToClipboardButton.layoutMargins = .init(hMargin: 16, vMargin: 4)
        copyToClipboardLabel.autoPinEdgesToSuperviewMargins()
        copyToClipboardButton.isUserInteractionEnabled = true
        copyToClipboardButton.addGestureRecognizer(UITapGestureRecognizer(
            target: self,
            action: #selector(showCopyToClipboardConfirmUI),
        ))

        return PaymentsViewUtils.buildPassphraseGrid(
            passphrase: passphrase,
            footerButton: copyToClipboardButton,
        )
    }

    private func buildHeader() -> UIView {
        let explanationLabel = UILabel()
        explanationLabel.text = OWSLocalizedString(
            "SETTINGS_PAYMENTS_VIEW_PASSPHRASE_WORDS_EXPLANATION",
            comment: "Header text for the 'review payments passphrase words' step in the 'view payments passphrase' settings.",
        )
        explanationLabel.font = .dynamicTypeSubheadlineClamped
        explanationLabel.textColor = Theme.secondaryTextAndIconColor
        explanationLabel.textAlignment = .center
        explanationLabel.numberOfLines = 0
        explanationLabel.lineBreakMode = .byWordWrapping

        let topStack = UIStackView(arrangedSubviews: [
            explanationLabel,
        ])
        topStack.axis = .vertical
        topStack.alignment = .center
        topStack.isLayoutMarginsRelativeArrangement = true
        topStack.layoutMargins = .init(top: 32, left: 20, bottom: 40, right: 20)
        return topStack
    }

    private func buildFooter() -> UIView {
        let explanationLabel = UILabel()
        explanationLabel.text = OWSLocalizedString(
            "SETTINGS_PAYMENTS_VIEW_PASSPHRASE_WORDS_FOOTER_2",
            comment: "Footer text for the 'review payments passphrase words' step in the 'view payments passphrase' settings.",
        )
        explanationLabel.font = .dynamicTypeSubheadlineClamped
        explanationLabel.textColor = Theme.secondaryTextAndIconColor
        explanationLabel.textAlignment = .center
        explanationLabel.numberOfLines = 0
        explanationLabel.lineBreakMode = .byWordWrapping

        let topStack = UIStackView(arrangedSubviews: [
            explanationLabel,
        ])
        topStack.axis = .vertical
        topStack.alignment = .center
        topStack.isLayoutMarginsRelativeArrangement = true
        topStack.layoutMargins = .init(hMargin: 20, vMargin: 16)
        return topStack
    }

    // MARK: - Events

    @objc
    private func didTapNextButton() {
        guard let viewPassphraseDelegate else {
            dismiss(animated: false, completion: nil)
            return
        }
        let view = PaymentsViewPassphraseConfirmViewController(
            passphrase: passphrase,
            viewPassphraseDelegate: viewPassphraseDelegate,
        )
        navigationController?.pushViewController(view, animated: true)
    }

    @objc
    private func showCopyToClipboardConfirmUI() {

        let actionSheet = ActionSheetController(
            title: OWSLocalizedString(
                "SETTINGS_PAYMENTS_VIEW_PASSPHRASE_COPY_TO_CLIPBOARD_CONFIRM_TITLE",
                comment: "Title for the 'copy recovery passphrase to clipboard confirm' alert in the payment settings.",
            ),
            message: OWSLocalizedString(
                "SETTINGS_PAYMENTS_VIEW_PASSPHRASE_COPY_TO_CLIPBOARD_CONFIRM_MESSAGE",
                comment: "Message for the 'copy recovery passphrase to clipboard confirm' alert in the payment settings.",
            ),
        )

        actionSheet.addAction(ActionSheetAction(
            title: CommonStrings.copyButton,
            style: .default,
        ) { [weak self] _ in
            self?.didTapCopyToClipboard()
        })

        actionSheet.addAction(OWSActionSheets.cancelAction)

        presentActionSheet(actionSheet)
    }

    @objc
    private func didTapCopyToClipboard() {
        // Ensure that passphrase only resides in pasteboard for short window of time.
        let pasteboardDuration: TimeInterval = .second * 30
        let expireDate = Date().addingTimeInterval(pasteboardDuration)
        UIPasteboard.general.setItems(
            [[UIPasteboard.typeAutomatic: passphrase.asPassphrase]],
            options: [.expirationDate: expireDate],
        )

        self.presentToast(
            text: OWSLocalizedString(
                "SETTINGS_PAYMENTS_VIEW_PASSPHRASE_COPIED_TO_CLIPBOARD",
                comment: "Indicator that the payments passphrase has been copied to the clipboard in the 'view payments passphrase' views.",
            ),
            image: .copy,
            extraVInset: bottomStack.height,
        )
    }
}