TM-SGNL-iOS/Signal/Registration/UserInterface/RegistrationPhoneNumberDiscoverabilityViewController.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

278 lines
9 KiB
Swift

//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import SignalServiceKit
import SignalUI
protocol RegistrationPhoneNumberDiscoverabilityPresenter: AnyObject {
func setPhoneNumberDiscoverability(_ phoneNumberDiscoverability: PhoneNumberDiscoverability)
var presentedAsModal: Bool { get }
}
public struct RegistrationPhoneNumberDiscoverabilityState: Equatable {
let e164: E164
let phoneNumberDiscoverability: PhoneNumberDiscoverability
}
class RegistrationPhoneNumberDiscoverabilityViewController: OWSViewController {
private enum Constants {
static let continueButtonInsets: UIEdgeInsets = .init(
top: 16.0,
leading: 36.0,
bottom: 48,
trailing: 36.0
)
static let continueButtonEdgeInsets: UIEdgeInsets = .init(
hMargin: 0,
vMargin: 16
)
}
private let state: RegistrationPhoneNumberDiscoverabilityState
private weak var presenter: RegistrationPhoneNumberDiscoverabilityPresenter?
public init(
state: RegistrationPhoneNumberDiscoverabilityState,
presenter: RegistrationPhoneNumberDiscoverabilityPresenter
) {
self.state = state
self.presenter = presenter
self.phoneNumberDiscoverability = state.phoneNumberDiscoverability
super.init()
render()
}
@available(*, unavailable)
public override init() {
owsFail("This should not be called")
}
// MARK: State
private var phoneNumberDiscoverability: PhoneNumberDiscoverability {
didSet { render() }
}
// MARK: Rendering
private lazy var titleLabel: UILabel = {
let result = UILabel.titleLabelForRegistration(text: OWSLocalizedString(
"ONBOARDING_PHONE_NUMBER_DISCOVERABILITY_TITLE",
comment: "Title of the 'onboarding phone number discoverability' view."
))
result.accessibilityIdentifier = "registration.phoneNumberDiscoverability.titleLabel"
return result
}()
private lazy var explanationLabel: UILabel = {
let formattedPhoneNumber = state.e164.stringValue
let explanationTextFormat = OWSLocalizedString(
"ONBOARDING_PHONE_NUMBER_DISCOVERABILITY_EXPLANATION_FORMAT",
comment: "Explanation of the 'onboarding phone number discoverability' view. Embeds {user phone number}"
)
let result = UILabel.explanationLabelForRegistration(
text: String(format: explanationTextFormat, formattedPhoneNumber)
)
result.accessibilityIdentifier = "registration.phoneNumberDiscoverability.explanationLabel"
return result
}()
private lazy var everybodyButton: ButtonRow = createButtonForDiscoverability(.everybody)
private lazy var nobodyButton: ButtonRow = createButtonForDiscoverability(.nobody)
private func createButtonForDiscoverability(_ phoneNumberDiscoverability: PhoneNumberDiscoverability) -> ButtonRow {
let result = ButtonRow(title: phoneNumberDiscoverability.nameForDiscoverability)
result.handler = { [weak self] _ in
self?.phoneNumberDiscoverability = phoneNumberDiscoverability
}
return result
}
private lazy var selectionDescriptionLabel: UILabel = {
let result = UILabel()
result.font = .dynamicTypeCaption1Clamped
result.numberOfLines = 0
result.lineBreakMode = .byWordWrapping
return result
}()
private lazy var nextBarButton = UIBarButtonItem(
title: CommonStrings.nextButton,
style: .done,
target: self,
action: #selector(didTapSave),
accessibilityIdentifier: "registration.phoneNumberDiscoverability.nextButton"
)
private lazy var continueButton: UIView = {
let footerView = UIView()
let continueButton = OWSFlatButton.insetButton(
title: CommonStrings.continueButton,
font: UIFont.dynamicTypeBodyClamped.semibold(),
titleColor: .white,
backgroundColor: .ows_accentBlue,
target: self,
selector: #selector(didTapSave))
continueButton.accessibilityIdentifier = "registration.phoneNumberDiscoverability.saveButton"
footerView.addSubview(continueButton)
continueButton.contentEdgeInsets = Constants.continueButtonEdgeInsets
continueButton.autoPinEdgesToSuperviewEdges(with: Constants.continueButtonInsets)
return footerView
}()
public override func viewDidLoad() {
super.viewDidLoad()
initialRender()
}
public override func themeDidChange() {
super.themeDidChange()
render()
}
private func initialRender() {
navigationItem.setHidesBackButton(true, animated: false)
if !(presenter?.presentedAsModal ?? true) {
navigationItem.rightBarButtonItem = nextBarButton
}
let descriptionLabelContainer = UIView()
descriptionLabelContainer.addSubview(selectionDescriptionLabel)
selectionDescriptionLabel.autoPinEdgesToSuperviewMargins()
var stackSubviews = [
titleLabel,
.spacer(withHeight: 16),
explanationLabel,
.spacer(withHeight: 24),
everybodyButton,
nobodyButton,
.spacer(withHeight: 16),
descriptionLabelContainer,
.vStretchingSpacer(minHeight: 16)
]
if presenter?.presentedAsModal ?? false {
stackSubviews.append(contentsOf: [
continueButton,
.spacer(withHeight: 16)
])
}
let stackView = UIStackView(arrangedSubviews: stackSubviews)
stackView.axis = .vertical
view.addSubview(stackView)
stackView.autoPinEdgesToSuperviewMargins()
render()
}
private func render() {
everybodyButton.isSelected = phoneNumberDiscoverability == .everybody
everybodyButton.render()
nobodyButton.isSelected = phoneNumberDiscoverability == .nobody
nobodyButton.render()
selectionDescriptionLabel.text = phoneNumberDiscoverability.descriptionForDiscoverability
view.backgroundColor = Theme.backgroundColor
continueButton.tintColor = Theme.accentBlueColor
titleLabel.textColor = .colorForRegistrationTitleLabel
explanationLabel.textColor = .colorForRegistrationExplanationLabel
selectionDescriptionLabel.textColor = .colorForRegistrationExplanationLabel
}
// MARK: Events
@objc
private func didTapSave() {
presenter?.setPhoneNumberDiscoverability(phoneNumberDiscoverability)
}
}
// MARK: - ButtonRow
private class ButtonRow: UIButton {
var handler: ((ButtonRow) -> Void)?
private let selectedImageView = UIImageView()
static let vInset: CGFloat = 11
static var hInset: CGFloat { UIDevice.current.isPlusSizePhone ? 20 : 16 }
override var isSelected: Bool {
didSet {
selectedImageView.isHidden = !isSelected
}
}
private lazy var buttonLabel: UILabel = UILabel()
private lazy var divider: UIView = UIView()
init(title: String) {
super.init(frame: .zero)
addTarget(self, action: #selector(didTap), for: .touchUpInside)
buttonLabel.text = title
selectedImageView.isHidden = true
selectedImageView.contentMode = .scaleAspectFit
selectedImageView.autoSetDimension(.width, toSize: 24)
let stackView = UIStackView(arrangedSubviews: [buttonLabel, .hStretchingSpacer(), selectedImageView])
stackView.isUserInteractionEnabled = false
stackView.axis = .horizontal
stackView.spacing = 8
stackView.isLayoutMarginsRelativeArrangement = true
stackView.layoutMargins = UIEdgeInsets(
top: Self.vInset,
leading: Self.hInset,
bottom: Self.vInset,
trailing: Self.hInset
)
addSubview(stackView)
stackView.autoPinEdgesToSuperviewEdges()
stackView.autoSetDimension(.height, toSize: 44, relation: .greaterThanOrEqual)
addSubview(divider)
divider.autoSetDimension(.height, toSize: .hairlineWidth)
divider.autoPinEdge(toSuperviewEdge: .trailing)
divider.autoPinEdge(toSuperviewEdge: .bottom)
divider.autoPinEdge(toSuperviewEdge: .leading, withInset: Self.hInset)
render()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func render() {
setBackgroundImage(UIImage.image(color: Theme.cellSelectedColor), for: .highlighted)
setBackgroundImage(UIImage.image(color: Theme.backgroundColor), for: .normal)
buttonLabel.textColor = Theme.primaryTextColor
buttonLabel.font = .dynamicTypeBodyClamped
divider.backgroundColor = .ows_middleGray
selectedImageView.setTemplateImage(Theme.iconImage(.checkmark), tintColor: Theme.primaryIconColor)
}
@objc
func didTap() {
handler?(self)
}
}