// // WalletPinControllern.swift // Wallet // // Created by Saveliy Stavitsky on 12/4/20. // Copyright © 2020 List. All rights reserved. // import UIKit import WalletKit final class WalletPinController: UIViewController { static func instantiate(viewModel: WalletPinViewModel? = nil) -> WalletPinController { let controller = WalletPinController(nibName: "WalletPinController", bundle: Bundle.main) controller.viewModel = viewModel ?? WalletPinViewModel() return controller } enum WalletPinControllerType { case old case new case protectionNew } private enum PinStackType { case error case empty case filled } private enum Constants { static let maxPinLength: Int = 4 static let maxPinInputTries: Int = 5 static let NoPinDigits: Int = 0 } @IBOutlet weak var logoTopConstraint: NSLayoutConstraint! @IBOutlet var headerTitleLabel: UILabel! @IBOutlet var headerDescriptionLabel: UILabel! @IBOutlet var pinStackView: UIStackView! @IBOutlet private weak var submitBtn: UIButton! @IBOutlet private weak var haveWalletButton: UIButton! @IBOutlet var leftButton: UIButton! @IBOutlet var errorTopConstraint: NSLayoutConstraint! @IBOutlet var errorTitleLabel: UILabel! @IBOutlet var errorTryLabel: UILabel! @IBOutlet var timeoutTitleLabel: UILabel! @IBOutlet var timeoutLabel: UILabel! @IBOutlet var outOfTriesErrorView: UIView! @IBOutlet var numbersStackView: UIStackView! @IBOutlet var numberButtons: [UIButton]! @IBOutlet private var warningView: CommonViewWarning! var viewModel: WalletPinViewModel! var type: WalletPinControllerType = .new var showBackButton = true var repeatPin: [Int] = [] var pin: [Int] = [Int]() { didSet { self.setupPinStack(type: .empty) self.errorTitleLabel.isHidden = true self.setupPinStack(type: .filled) if self.repeatPin.count == Constants.maxPinLength, self.pin.count == Constants.maxPinLength, self.repeatPin != self.pin { self.setupPinStack(type: .error) self.errorTitleLabel.isHidden = false } } } override func viewDidLoad() { super.viewDidLoad() self.timeoutTitleLabel.textAlignment = .center self.warningView.makeCritical() self.numbersStackView.spacing = UIScreen.main.bounds.height < 570 ? 8 : 24 if self.viewModel.validatePin.isExist { self.setupForPinValidation() } else { self.setupForPinSetting() } self.headerDescriptionLabel.font = .font(style: .regular, size: 14) for button in self.numberButtons { button.titleLabel?.font = .font(style: .regular, size: 28) button.setTitleColor(Asset.textGranite.color, for: .normal) } self.leftButton.titleLabel?.font = .font(style: .medium, size: 18) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) self.callBiometricsSignInIfEnabled() } private func callBiometricsSignInIfEnabled() { if self.viewModel.isBiometricksEnabled { self.viewModel.submitBiometric?() } } @IBAction private func onSubmit(_ : AnyObject?) { } @IBAction private func onUnderstand(_ : AnyObject?) { Account.Service.Authorize.shared.clearPinTries() self.repeatPin = [] self.pin = [] self.viewModel.validatePin = nil self.setupPinErrorsPresentation(isHidden: true) self.outOfTriesErrorView.isHidden = true self.setupForPinSetting() } @IBAction func haveWalletPrssed(_: AnyObject?) { Alert.hint(text: L10n.Account.Onboarding.Password.hint) } @IBAction func numberPressed(_ sender: Any) { guard let value = (sender as? UIButton)?.tag else { return } switch value { case -4: self.leftButton.isHidden = !self.viewModel.submitBiometric.isExist if !self.viewModel.submitBiometric.isExist { self.headerTitleLabel.text = L10n.Account.Pin.Enter.title } self.repeatPin = [] self.pin = [] self.viewModel.submitBiometric?() case -1: self.pin = self.pin.dropLast() default: self.pin += [value] } if self.pin.count == Constants.maxPinLength { self.pinValidation() } } private func setupForPinValidation() { self.title = "" // L10n.Account.Authorize.title self.logoTopConstraint.constant = -40 if self.viewModel.submitBiometric.isExist { self.headerTitleLabel.text = L10n.Account.Pin.Enter.title } else { self.headerTitleLabel.text = type == .new ? L10n.Account.Pin.Enter.titleNew : L10n.Account.Pin.Enter.titleOld } if self.showBackButton { self.navigationItem.leftBarButtonItem = .back { [weak self] in self?.navigationController?.dismiss(animated: true) } } // Clear the background image. self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default) // Clear the shadow image. self.navigationController?.navigationBar.shadowImage = UIImage() // Ensure the navigation bar is translucent. self.navigationController?.navigationBar.isTranslucent = true self.leftButton.isHidden = !(self.viewModel.isBiometricksEnabled) || !self.viewModel.submitBiometric.isExist self.leftButton.lzTitle = "" self.leftButton.setImage(Asset.accountPinFaceId.image, for: .normal) self.headerTitleLabel.font = .font(style: .regular, size: 14) self.headerTitleLabel.textColor = Asset.textGranite.color.withAlphaComponent(0.7) self.headerDescriptionLabel.isHidden = true } private func setupForPinSetting() { self.headerTitleLabel.font = .font(style: .bold, size: 24) self.headerTitleLabel.textColor = Asset.textGranite.color self.headerDescriptionLabel.isHidden = false self.leftButton.lzTitle = L10n.Common.Button.back if self.repeatPin.count == 0 { self.leftButton.isHidden = true } else { self.leftButton.isHidden = false self.headerTitleLabel.text = L10n.Account.Pin.Repeat.title } } private func pinValidation() { if let validatePin = self.viewModel.validatePin { let password = self.pin.map { "\($0)" }.joined() if validatePin(password) { self.viewModel.validatePinSuccess(from: self) } else { if Account.Service.Authorize.shared.getPinTries().count >= Constants.maxPinInputTries { self.outOfTriesErrorView.isHidden = false self.viewModel.clearAccountData(from: self) } self.setupPinErrorsPresentation(isHidden: false) } } else { let repeatPin = self.repeatPin.count let pinCount = self.pin.count if repeatPin == Constants.NoPinDigits, pinCount == Constants.maxPinLength { self.headerLabelRepeatSetup() self.repeatPin = self.pin self.pin = [] } else if repeatPin == Constants.maxPinLength, pinCount == Constants.maxPinLength, self.repeatPin == self.pin { let pinCode = self.pin.map { "\($0)" }.joined() Account.Service.Authorize.shared.update(password: pinCode) self.successShow() } } } private func headerLabelRepeatSetup() { leftButton.isHidden = false headerTitleLabel.text = L10n.Account.Pin.Repeat.title } private func setupPinErrorsPresentation(isHidden: Bool) { if !isHidden { self.setupPinStack(type: .error) self.pin = [] self.errorTryLabel.text = L10n.Account.Pin.tryCount(Constants.maxPinInputTries - Account.Service.Authorize.shared.getPinTries().count) } self.errorTryLabel.isHidden = isHidden self.errorTitleLabel.isHidden = isHidden self.warningView.isHidden = isHidden } private func setupPinStack(type: PinStackType) { var image: UIImage switch type { case .empty: image = Asset.accountPinStarEmpty.image case .error: image = Asset.accountPinStarError.image case .filled: image = Asset.accountPinStarFill.image let arrangedSubviews = self.pinStackView.arrangedSubviews for index in self.pin.indices where index < arrangedSubviews.count { if let stack = arrangedSubviews[index] as? UIStackView { (stack.arrangedSubviews.first as? UIImageView)?.image = image } } return } self.pinStackView.arrangedSubviews .compactMap { $0 as? UIStackView } .compactMap { $0.arrangedSubviews.first as? UIImageView } .forEach { $0.image = image } } private func successShow() { UserDefaults.standard.setValue(true, forKey: "isPinPwdMode") let password = self.pin.map { "\($0)" }.joined() self.viewModel.updatePassword(password, using: self) Popup.show( title: L10n.Account.Onboarding.Success.title, description: L10n.Account.Onboarding.Success.description, submit: Account.Service.isBiometricksAvailable ? L10n.Account.Onboarding.Success.submit : L10n.Common.Button.ok, in: self, { ctrl in if let popUp = ctrl as? CommonControllerPopup { popUp.muteCompletion = true } ctrl.dismiss(animated: true) guard Account.Service.isBiometricksAvailable else { self.viewModel.dismissController(self) return } let result: () -> Void = { self.viewModel.dismissController(self) } let submit: (UIViewController) -> Void = { ctrl in if let popUp = ctrl as? CommonControllerPopup { popUp.muteCompletion = true } ctrl.dismiss(animated: true) self.viewModel.checkBiometrics { success in success ? Alert.success() : Alert.error(text: L10n.Account.Onboarding.Bimetricks.error) self.viewModel.dismissController(self) } } if Account.Service.isFaceIdAvailable { Popup.show( title: L10n.Account.Onboarding.FaceId.title, image: Asset.commonFaceId.image, description: L10n.Account.Onboarding.FaceId.description, submit: L10n.Account.Onboarding.FaceId.submit, in: self, submit, result ) } else { Popup.show( title: L10n.Account.Onboarding.TouchId.title, image: Asset.commonTouchId.image, description: L10n.Account.Onboarding.TouchId.description, submit: L10n.Account.Onboarding.TouchId.submit, in: self, submit, result ) } } ) { self.viewModel.dismissController(self) } } }