278 lines
9 KiB
Swift
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)
|
|
}
|
|
}
|