Files
Андрей Геращенко f52cdb51bc MALINKA-1095: Password logic refactoring
2023-03-10 11:40:54 +03:00

336 lines
12 KiB
Swift

//
// 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)
}
}
}