336 lines
12 KiB
Swift
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)
|
|
}
|
|
}
|
|
}
|