|
|
|
@ -3,412 +3,208 @@
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
import UIKit
|
|
|
|
|
import PromiseKit
|
|
|
|
|
import WebKit
|
|
|
|
|
|
|
|
|
|
@objc
|
|
|
|
|
public class OnboardingCaptchaViewController: OnboardingBaseViewController {
|
|
|
|
|
|
|
|
|
|
// MARK: - Dependencies
|
|
|
|
|
|
|
|
|
|
private var tsAccountManager: TSAccountManager {
|
|
|
|
|
return TSAccountManager.sharedInstance()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: -
|
|
|
|
|
|
|
|
|
|
private let countryNameLabel = UILabel()
|
|
|
|
|
private let callingCodeLabel = UILabel()
|
|
|
|
|
private let phoneNumberTextField = UITextField()
|
|
|
|
|
private var nextButton: OWSFlatButton?
|
|
|
|
|
private var webView: WKWebView?
|
|
|
|
|
|
|
|
|
|
override public func loadView() {
|
|
|
|
|
super.loadView()
|
|
|
|
|
|
|
|
|
|
populateDefaults()
|
|
|
|
|
|
|
|
|
|
view.backgroundColor = Theme.backgroundColor
|
|
|
|
|
view.backgroundColor = .orange
|
|
|
|
|
view.layoutMargins = .zero
|
|
|
|
|
|
|
|
|
|
// TODO:
|
|
|
|
|
// navigationItem.title = NSLocalizedString("SETTINGS_BACKUP", comment: "Label for the backup view in app settings.")
|
|
|
|
|
|
|
|
|
|
let titleLabel = self.titleLabel(text: NSLocalizedString("ONBOARDING_PHONE_NUMBER_TITLE", comment: "Title of the 'onboarding phone number' view."))
|
|
|
|
|
|
|
|
|
|
// Country
|
|
|
|
|
let titleLabel = self.titleLabel(text: NSLocalizedString("ONBOARDING_CAPTCHA_TITLE", comment: "Title of the 'onboarding Captcha' view."))
|
|
|
|
|
|
|
|
|
|
let rowHeight: CGFloat = 40
|
|
|
|
|
|
|
|
|
|
countryNameLabel.textColor = Theme.primaryColor
|
|
|
|
|
countryNameLabel.font = UIFont.ows_dynamicTypeBody
|
|
|
|
|
countryNameLabel.setContentHuggingHorizontalLow()
|
|
|
|
|
countryNameLabel.setCompressionResistanceHorizontalLow()
|
|
|
|
|
|
|
|
|
|
let countryIcon = UIImage(named: (CurrentAppContext().isRTL
|
|
|
|
|
? "small_chevron_left"
|
|
|
|
|
: "small_chevron_right"))
|
|
|
|
|
let countryImageView = UIImageView(image: countryIcon?.withRenderingMode(.alwaysTemplate))
|
|
|
|
|
countryImageView.tintColor = Theme.placeholderColor
|
|
|
|
|
countryImageView.setContentHuggingHigh()
|
|
|
|
|
countryImageView.setCompressionResistanceHigh()
|
|
|
|
|
|
|
|
|
|
let countryRow = UIStackView(arrangedSubviews: [
|
|
|
|
|
countryNameLabel,
|
|
|
|
|
countryImageView
|
|
|
|
|
let titleRow = UIStackView(arrangedSubviews: [
|
|
|
|
|
titleLabel
|
|
|
|
|
])
|
|
|
|
|
countryRow.axis = .horizontal
|
|
|
|
|
countryRow.alignment = .center
|
|
|
|
|
countryRow.spacing = 10
|
|
|
|
|
countryRow.isUserInteractionEnabled = true
|
|
|
|
|
countryRow.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(countryRowTapped)))
|
|
|
|
|
countryRow.autoSetDimension(.height, toSize: rowHeight)
|
|
|
|
|
addBottomStroke(countryRow)
|
|
|
|
|
|
|
|
|
|
callingCodeLabel.textColor = Theme.primaryColor
|
|
|
|
|
callingCodeLabel.font = UIFont.ows_dynamicTypeBody
|
|
|
|
|
callingCodeLabel.setContentHuggingHorizontalHigh()
|
|
|
|
|
callingCodeLabel.setCompressionResistanceHorizontalHigh()
|
|
|
|
|
callingCodeLabel.isUserInteractionEnabled = true
|
|
|
|
|
callingCodeLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(countryCodeTapped)))
|
|
|
|
|
addBottomStroke(callingCodeLabel)
|
|
|
|
|
callingCodeLabel.autoSetDimension(.width, toSize: rowHeight, relation: .greaterThanOrEqual)
|
|
|
|
|
|
|
|
|
|
phoneNumberTextField.textAlignment = .left
|
|
|
|
|
phoneNumberTextField.delegate = self
|
|
|
|
|
phoneNumberTextField.keyboardType = .numberPad
|
|
|
|
|
phoneNumberTextField.textColor = Theme.primaryColor
|
|
|
|
|
phoneNumberTextField.font = UIFont.ows_dynamicTypeBody
|
|
|
|
|
phoneNumberTextField.setContentHuggingHorizontalLow()
|
|
|
|
|
phoneNumberTextField.setCompressionResistanceHorizontalLow()
|
|
|
|
|
|
|
|
|
|
addBottomStroke(phoneNumberTextField)
|
|
|
|
|
|
|
|
|
|
let phoneNumberRow = UIStackView(arrangedSubviews: [
|
|
|
|
|
callingCodeLabel,
|
|
|
|
|
phoneNumberTextField
|
|
|
|
|
])
|
|
|
|
|
phoneNumberRow.axis = .horizontal
|
|
|
|
|
phoneNumberRow.alignment = .fill
|
|
|
|
|
phoneNumberRow.spacing = 10
|
|
|
|
|
phoneNumberRow.autoSetDimension(.height, toSize: rowHeight)
|
|
|
|
|
callingCodeLabel.autoMatch(.height, to: .height, of: phoneNumberTextField)
|
|
|
|
|
|
|
|
|
|
// TODO: Finalize copy.
|
|
|
|
|
|
|
|
|
|
let nextButton = self.button(title: NSLocalizedString("BUTTON_NEXT",
|
|
|
|
|
comment: "Label for the 'next' button."),
|
|
|
|
|
selector: #selector(nextPressed))
|
|
|
|
|
self.nextButton = nextButton
|
|
|
|
|
let topSpacer = UIView.vStretchingSpacer()
|
|
|
|
|
let bottomSpacer = UIView.vStretchingSpacer()
|
|
|
|
|
titleRow.axis = .vertical
|
|
|
|
|
titleRow.alignment = .fill
|
|
|
|
|
titleRow.layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 0, right: 16)
|
|
|
|
|
titleRow.isLayoutMarginsRelativeArrangement = true
|
|
|
|
|
|
|
|
|
|
// We want the CAPTCHA web content to "fill the screen (honoring margins)".
|
|
|
|
|
// The way to do this with WKWebView is to inject a javascript snippet that
|
|
|
|
|
// manipulates the viewport.
|
|
|
|
|
let jscript = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);"
|
|
|
|
|
let userScript = WKUserScript(source: jscript, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
|
|
|
|
|
let wkUController = WKUserContentController()
|
|
|
|
|
wkUController.addUserScript(userScript)
|
|
|
|
|
let wkWebConfig = WKWebViewConfiguration()
|
|
|
|
|
wkWebConfig.userContentController = wkUController
|
|
|
|
|
let webView = WKWebView(frame: self.view.bounds, configuration: wkWebConfig)
|
|
|
|
|
self.webView = webView
|
|
|
|
|
webView.navigationDelegate = self
|
|
|
|
|
webView.allowsBackForwardNavigationGestures = false
|
|
|
|
|
webView.customUserAgent = "Signal iOS (+https://signal.org/download)"
|
|
|
|
|
webView.allowsLinkPreview = false
|
|
|
|
|
// webView.scrollView.contentInset = .zero
|
|
|
|
|
// webView.layoutMargins = .zero
|
|
|
|
|
|
|
|
|
|
let stackView = UIStackView(arrangedSubviews: [
|
|
|
|
|
titleLabel,
|
|
|
|
|
topSpacer,
|
|
|
|
|
countryRow,
|
|
|
|
|
UIView.spacer(withHeight: 8),
|
|
|
|
|
phoneNumberRow,
|
|
|
|
|
bottomSpacer,
|
|
|
|
|
nextButton
|
|
|
|
|
titleRow,
|
|
|
|
|
webView
|
|
|
|
|
])
|
|
|
|
|
stackView.axis = .vertical
|
|
|
|
|
stackView.alignment = .fill
|
|
|
|
|
stackView.layoutMargins = UIEdgeInsets(top: 32, left: 32, bottom: 32, right: 32)
|
|
|
|
|
stackView.layoutMargins = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
|
|
|
|
|
stackView.isLayoutMarginsRelativeArrangement = true
|
|
|
|
|
view.addSubview(stackView)
|
|
|
|
|
stackView.autoPinWidthToSuperviewMargins()
|
|
|
|
|
stackView.autoPinWidthToSuperviewMargins()
|
|
|
|
|
stackView.autoPin(toTopLayoutGuideOf: self, withInset: 0)
|
|
|
|
|
autoPinView(toBottomOfViewControllerOrKeyboard: stackView, avoidNotch: true)
|
|
|
|
|
|
|
|
|
|
// Ensure whitespace is balanced, so inputs are vertically centered.
|
|
|
|
|
topSpacer.autoMatch(.height, to: .height, of: bottomSpacer)
|
|
|
|
|
NotificationCenter.default.addObserver(self,
|
|
|
|
|
selector: #selector(didBecomeActive),
|
|
|
|
|
name: NSNotification.Name.OWSApplicationDidBecomeActive,
|
|
|
|
|
object: nil)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func addBottomStroke(_ view: UIView) {
|
|
|
|
|
let strokeView = UIView()
|
|
|
|
|
strokeView.backgroundColor = Theme.middleGrayColor
|
|
|
|
|
view.addSubview(strokeView)
|
|
|
|
|
strokeView.autoSetDimension(.height, toSize: CGHairlineWidth())
|
|
|
|
|
strokeView.autoPinWidthToSuperview()
|
|
|
|
|
strokeView.autoPinEdge(toSuperviewEdge: .bottom)
|
|
|
|
|
deinit {
|
|
|
|
|
NotificationCenter.default.removeObserver(self)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override func viewWillAppear(_ animated: Bool) {
|
|
|
|
|
super.viewWillAppear(animated)
|
|
|
|
|
|
|
|
|
|
self.navigationController?.isNavigationBarHidden = false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override func viewDidAppear(_ animated: Bool) {
|
|
|
|
|
super.viewDidAppear(animated)
|
|
|
|
|
|
|
|
|
|
self.navigationController?.isNavigationBarHidden = false
|
|
|
|
|
|
|
|
|
|
phoneNumberTextField.becomeFirstResponder()
|
|
|
|
|
|
|
|
|
|
if tsAccountManager.isReregistering() {
|
|
|
|
|
// If re-registering, pre-populate the country (country code, calling code, country name)
|
|
|
|
|
// and phone number state.
|
|
|
|
|
guard let phoneNumberE164 = tsAccountManager.reregisterationPhoneNumber() else {
|
|
|
|
|
owsFailDebug("Could not resume re-registration; missing phone number.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
tryToReregister(phoneNumberE164: phoneNumberE164)
|
|
|
|
|
}
|
|
|
|
|
loadContent()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func tryToReregister(phoneNumberE164: String) {
|
|
|
|
|
guard phoneNumberE164.count > 0 else {
|
|
|
|
|
owsFailDebug("Could not resume re-registration; invalid phoneNumberE164.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
guard let parsedPhoneNumber = PhoneNumber(fromE164: phoneNumberE164) else {
|
|
|
|
|
owsFailDebug("Could not resume re-registration; couldn't parse phoneNumberE164.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
guard let callingCode = parsedPhoneNumber.getCountryCode() else {
|
|
|
|
|
owsFailDebug("Could not resume re-registration; missing callingCode.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
let callingCodeText = "\(COUNTRY_CODE_PREFIX)\(callingCode)"
|
|
|
|
|
let countryCodes: [String] =
|
|
|
|
|
PhoneNumberUtil.sharedThreadLocal().countryCodes(fromCallingCode: callingCodeText)
|
|
|
|
|
guard let countryCode = countryCodes.first else {
|
|
|
|
|
owsFailDebug("Could not resume re-registration; unknown countryCode.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
guard let countryName = PhoneNumberUtil.countryName(fromCountryCode: countryCode) else {
|
|
|
|
|
owsFailDebug("Could not resume re-registration; unknown countryName.")
|
|
|
|
|
fileprivate let contentUrl = "https://signalcaptchas.org/registration/generate.html"
|
|
|
|
|
|
|
|
|
|
private func loadContent() {
|
|
|
|
|
guard let webView = webView else {
|
|
|
|
|
owsFailDebug("Missing webView.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if !phoneNumberE164.hasPrefix(callingCodeText) {
|
|
|
|
|
owsFailDebug("Could not resume re-registration; non-matching calling code.")
|
|
|
|
|
guard let url = URL(string: contentUrl) else {
|
|
|
|
|
owsFailDebug("Invalid URL.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
let phoneNumberWithoutCallingCode = phoneNumberE164.substring(from: callingCodeText.count)
|
|
|
|
|
|
|
|
|
|
onboardingController.update(withCountryName: countryName, callingCode: callingCodeText, countryCode: countryCode)
|
|
|
|
|
updateState()
|
|
|
|
|
|
|
|
|
|
phoneNumberTextField.text = phoneNumberWithoutCallingCode
|
|
|
|
|
// Don't let user edit their phone number while re-registering.
|
|
|
|
|
phoneNumberTextField.isEnabled = false
|
|
|
|
|
webView.load(URLRequest(url: url))
|
|
|
|
|
webView.scrollView.contentOffset = .zero
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: -
|
|
|
|
|
|
|
|
|
|
private var countryName: String {
|
|
|
|
|
get {
|
|
|
|
|
return onboardingController.state.countryName
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private var callingCode: String {
|
|
|
|
|
get {
|
|
|
|
|
AssertIsOnMainThread()
|
|
|
|
|
|
|
|
|
|
return onboardingController.state.callingCode
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private var countryCode: String {
|
|
|
|
|
get {
|
|
|
|
|
AssertIsOnMainThread()
|
|
|
|
|
public override func viewDidAppear(_ animated: Bool) {
|
|
|
|
|
super.viewDidAppear(animated)
|
|
|
|
|
|
|
|
|
|
return onboardingController.state.countryCode
|
|
|
|
|
}
|
|
|
|
|
self.navigationController?.isNavigationBarHidden = false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func populateDefaults() {
|
|
|
|
|
if let lastRegisteredPhoneNumber = OnboardingController.lastRegisteredPhoneNumber(),
|
|
|
|
|
lastRegisteredPhoneNumber.count > 0,
|
|
|
|
|
lastRegisteredPhoneNumber.hasPrefix(callingCode) {
|
|
|
|
|
phoneNumberTextField.text = lastRegisteredPhoneNumber.substring(from: callingCode.count)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateState()
|
|
|
|
|
}
|
|
|
|
|
// MARK: - Notifications
|
|
|
|
|
|
|
|
|
|
private func updateState() {
|
|
|
|
|
@objc func didBecomeActive() {
|
|
|
|
|
AssertIsOnMainThread()
|
|
|
|
|
|
|
|
|
|
countryNameLabel.text = countryName
|
|
|
|
|
callingCodeLabel.text = callingCode
|
|
|
|
|
|
|
|
|
|
self.phoneNumberTextField.placeholder = ViewControllerUtils.examplePhoneNumber(forCountryCode: countryCode, callingCode: callingCode)
|
|
|
|
|
loadContent()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - Events
|
|
|
|
|
|
|
|
|
|
@objc func countryRowTapped(sender: UIGestureRecognizer) {
|
|
|
|
|
guard sender.state == .recognized else {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
showCountryPicker()
|
|
|
|
|
}
|
|
|
|
|
private func didComplete(url: URL) {
|
|
|
|
|
Logger.info("")
|
|
|
|
|
|
|
|
|
|
@objc func countryCodeTapped(sender: UIGestureRecognizer) {
|
|
|
|
|
guard sender.state == .recognized else {
|
|
|
|
|
return
|
|
|
|
|
// signalcaptcha://03AF6jDqXgf1PocNNrWRJEENZ9l6RAMIsUoESi2dFKkxTgE2qjdZGVjEW6SZNFQqeRRTgGqOii6zHGG--uLyC1HnhSmRt8wHeKxHcg1hsK4ucTusANIeFXVB8wPPiV7U_0w2jUFVak5clMCvW9_JBfbfzj51_e9sou8DYfwc_R6THuTBTdpSV8Nh0yJalgget-nSukCxh6FPA6hRVbw7lP3r-me1QCykHOfh-V29UVaQ4Fs5upHvwB5rtiViqT_HN8WuGmdIdGcaWxaqy1lQTgFSs2Shdj593wZiXfhJnCWAw9rMn3jSgIZhkFxdXwKOmslQ2E_I8iWkm6
|
|
|
|
|
guard let host = url.host,
|
|
|
|
|
host.count > 0 else {
|
|
|
|
|
owsFailDebug("Missing host.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
showCountryPicker()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc func nextPressed() {
|
|
|
|
|
Logger.info("")
|
|
|
|
|
|
|
|
|
|
parseAndTryToRegister()
|
|
|
|
|
onboardingController.
|
|
|
|
|
}
|
|
|
|
|
//
|
|
|
|
|
// @objc func countryRowTapped(sender: UIGestureRecognizer) {
|
|
|
|
|
// guard sender.state == .recognized else {
|
|
|
|
|
// return
|
|
|
|
|
// }
|
|
|
|
|
// showCountryPicker()
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// @objc func countryCodeTapped(sender: UIGestureRecognizer) {
|
|
|
|
|
// guard sender.state == .recognized else {
|
|
|
|
|
// return
|
|
|
|
|
// }
|
|
|
|
|
// showCountryPicker()
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// @objc func nextPressed() {
|
|
|
|
|
// Logger.info("")
|
|
|
|
|
//
|
|
|
|
|
// parseAndTryToRegister()
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // MARK: -
|
|
|
|
|
//
|
|
|
|
|
// private func registrationSucceeded() {
|
|
|
|
|
// self.onboardingController.onboardingPhoneNumberDidComplete(viewController: self)
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: -
|
|
|
|
|
|
|
|
|
|
// MARK: - Country Picker
|
|
|
|
|
extension OnboardingCaptchaViewController: WKNavigationDelegate {
|
|
|
|
|
public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
|
|
|
|
Logger.verbose("navigationAction: \(String(describing: navigationAction.request.url))")
|
|
|
|
|
|
|
|
|
|
private func showCountryPicker() {
|
|
|
|
|
guard !tsAccountManager.isReregistering() else {
|
|
|
|
|
guard let url: URL = navigationAction.request.url else {
|
|
|
|
|
owsFailDebug("Missing URL.")
|
|
|
|
|
decisionHandler(.cancel)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if url.scheme == "signalcaptcha" {
|
|
|
|
|
decisionHandler(.cancel)
|
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
|
didComplete(url: url)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let countryCodeController = CountryCodeViewController()
|
|
|
|
|
countryCodeController.countryCodeDelegate = self
|
|
|
|
|
countryCodeController.interfaceOrientationMask = .portrait
|
|
|
|
|
let navigationController = OWSNavigationController(rootViewController: countryCodeController)
|
|
|
|
|
self.present(navigationController, animated: true, completion: nil)
|
|
|
|
|
decisionHandler(.allow)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - Register
|
|
|
|
|
|
|
|
|
|
private func parseAndTryToRegister() {
|
|
|
|
|
guard let phoneNumberText = phoneNumberTextField.text?.ows_stripped(),
|
|
|
|
|
phoneNumberText.count > 0 else {
|
|
|
|
|
OWSAlerts.showAlert(title:
|
|
|
|
|
NSLocalizedString("REGISTRATION_VIEW_NO_PHONE_NUMBER_ALERT_TITLE",
|
|
|
|
|
comment: "Title of alert indicating that users needs to enter a phone number to register."),
|
|
|
|
|
message:
|
|
|
|
|
NSLocalizedString("REGISTRATION_VIEW_NO_PHONE_NUMBER_ALERT_MESSAGE",
|
|
|
|
|
comment: "Message of alert indicating that users needs to enter a phone number to register."))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
public func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
|
|
|
|
|
Logger.verbose("navigationResponse: \(String(describing: navigationResponse))")
|
|
|
|
|
|
|
|
|
|
let phoneNumber = "\(callingCode)\(phoneNumberText)"
|
|
|
|
|
guard let localNumber = PhoneNumber.tryParsePhoneNumber(fromUserSpecifiedText: phoneNumber),
|
|
|
|
|
localNumber.toE164().count > 0,
|
|
|
|
|
PhoneNumberValidator().isValidForRegistration(phoneNumber: localNumber) else {
|
|
|
|
|
OWSAlerts.showAlert(title:
|
|
|
|
|
NSLocalizedString("REGISTRATION_VIEW_INVALID_PHONE_NUMBER_ALERT_TITLE",
|
|
|
|
|
comment: "Title of alert indicating that users needs to enter a valid phone number to register."),
|
|
|
|
|
message:
|
|
|
|
|
NSLocalizedString("REGISTRATION_VIEW_INVALID_PHONE_NUMBER_ALERT_MESSAGE",
|
|
|
|
|
comment: "Message of alert indicating that users needs to enter a valid phone number to register."))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
let parsedPhoneNumber = localNumber.toE164()
|
|
|
|
|
|
|
|
|
|
if UIDevice.current.isIPad {
|
|
|
|
|
let countryCode = self.countryCode
|
|
|
|
|
OWSAlerts.showConfirmationAlert(title: NSLocalizedString("REGISTRATION_IPAD_CONFIRM_TITLE",
|
|
|
|
|
comment: "alert title when registering an iPad"),
|
|
|
|
|
message: NSLocalizedString("REGISTRATION_IPAD_CONFIRM_BODY",
|
|
|
|
|
comment: "alert body when registering an iPad"),
|
|
|
|
|
proceedTitle: NSLocalizedString("REGISTRATION_IPAD_CONFIRM_BUTTON",
|
|
|
|
|
comment: "button text to proceed with registration when on an iPad"),
|
|
|
|
|
proceedAction: { (_) in
|
|
|
|
|
self.tryToRegister(parsedPhoneNumber: parsedPhoneNumber,
|
|
|
|
|
phoneNumberText: phoneNumberText,
|
|
|
|
|
countryCode: countryCode)
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
tryToRegister(parsedPhoneNumber: parsedPhoneNumber,
|
|
|
|
|
phoneNumberText: phoneNumberText,
|
|
|
|
|
countryCode: countryCode)
|
|
|
|
|
}
|
|
|
|
|
decisionHandler(.allow)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func tryToRegister(parsedPhoneNumber: String,
|
|
|
|
|
phoneNumberText: String,
|
|
|
|
|
countryCode: String) {
|
|
|
|
|
ModalActivityIndicatorViewController.present(fromViewController: self,
|
|
|
|
|
canCancel: true) { (modal) in
|
|
|
|
|
OnboardingController.setLastRegisteredCountryCode(value: countryCode)
|
|
|
|
|
OnboardingController.setLastRegisteredPhoneNumber(value: phoneNumberText)
|
|
|
|
|
|
|
|
|
|
self.tsAccountManager.register(withPhoneNumber: parsedPhoneNumber,
|
|
|
|
|
success: {
|
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
|
modal.dismiss(completion: {
|
|
|
|
|
self.registrationSucceeded()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}, failure: { (error) in
|
|
|
|
|
Logger.error("Error: \(error)")
|
|
|
|
|
|
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
|
modal.dismiss(completion: {
|
|
|
|
|
self.registrationFailed(error: error as NSError)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}, smsVerification: true)
|
|
|
|
|
}
|
|
|
|
|
public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
|
|
|
|
Logger.verbose("navigation: \(String(describing: navigation))")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func registrationSucceeded() {
|
|
|
|
|
self.onboardingController.onboardingPhoneNumberDidComplete(viewController: self)
|
|
|
|
|
public func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) {
|
|
|
|
|
Logger.verbose("navigation: \(String(describing: navigation))")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func registrationFailed(error: NSError) {
|
|
|
|
|
if error.code == 402 {
|
|
|
|
|
Logger.info("Captcha requested.")
|
|
|
|
|
|
|
|
|
|
self.onboardingController.onboardingPhoneNumberDidRequireCaptcha(viewController: self)
|
|
|
|
|
return
|
|
|
|
|
} else if error.code == 400 {
|
|
|
|
|
OWSAlerts.showAlert(title: NSLocalizedString("REGISTRATION_ERROR", comment: ""),
|
|
|
|
|
message: NSLocalizedString("REGISTRATION_NON_VALID_NUMBER", comment: ""))
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
OWSAlerts.showAlert(title: error.localizedDescription,
|
|
|
|
|
message: error.localizedRecoverySuggestion)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
phoneNumberTextField.becomeFirstResponder()
|
|
|
|
|
public func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
|
|
|
|
Logger.verbose("navigation: \(String(describing: navigation)), error: \(error)")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: -
|
|
|
|
|
|
|
|
|
|
extension OnboardingCaptchaViewController: UITextFieldDelegate {
|
|
|
|
|
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
|
|
|
|
// TODO: Fix auto-format of phone numbers.
|
|
|
|
|
ViewControllerUtils.phoneNumber(textField, shouldChangeCharactersIn: range, replacementString: string, countryCode: countryCode)
|
|
|
|
|
|
|
|
|
|
// Inform our caller that we took care of performing the change.
|
|
|
|
|
return false
|
|
|
|
|
public func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
|
|
|
|
|
Logger.verbose("navigation: \(String(describing: navigation))")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
|
|
|
|
parseAndTryToRegister()
|
|
|
|
|
textField.resignFirstResponder()
|
|
|
|
|
return false
|
|
|
|
|
public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
|
|
|
|
Logger.verbose("navigation: \(String(describing: navigation))")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: -
|
|
|
|
|
|
|
|
|
|
extension OnboardingCaptchaViewController: CountryCodeViewControllerDelegate {
|
|
|
|
|
public func countryCodeViewController(_ vc: CountryCodeViewController, didSelectCountryCode countryCode: String, countryName: String, callingCode: String) {
|
|
|
|
|
guard countryCode.count > 0 else {
|
|
|
|
|
owsFailDebug("Invalid country code.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
guard countryName.count > 0 else {
|
|
|
|
|
owsFailDebug("Invalid country name.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
guard callingCode.count > 0 else {
|
|
|
|
|
owsFailDebug("Invalid calling code.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
|
|
|
|
|
Logger.verbose("navigation: \(String(describing: navigation)), error: \(error)")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onboardingController.update(withCountryName: countryName, callingCode: callingCode, countryCode: countryCode)
|
|
|
|
|
updateState()
|
|
|
|
|
// public func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
|
|
|
|
|
|
|
|
|
|
// Trigger the formatting logic with a no-op edit.
|
|
|
|
|
_ = textField(phoneNumberTextField, shouldChangeCharactersIn: NSRange(location: 0, length: 0), replacementString: "")
|
|
|
|
|
public func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
|
|
|
|
|
Logger.verbose("")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|