Added change PIN code feature from the settings

This commit is contained in:
Gil Eluard
2020-12-16 14:15:41 +01:00
parent 6d61d6d99b
commit 82fd55152e
9 changed files with 114 additions and 41 deletions

View File

@@ -216,6 +216,8 @@ final class EnterPinCodeViewController: UIViewController {
self.renderConfirmPinToDisable()
case .inactive:
self.renderInactive()
case .changePin:
self.renderChangePin()
}
}
@@ -336,6 +338,17 @@ final class EnterPinCodeViewController: UIViewController {
self.explanatoryLabel.isHidden = true
}
private func renderChangePin() {
self.inactiveView.isHidden = true
self.mainStackView.isHidden = false
self.logoImageView.isHidden = true
self.informationLabel.text = VectorL10n.pinProtectionConfirmPinToChange
self.explanatoryLabel.isHidden = true
self.forgotPinButton.isHidden = true
self.bottomView.isHidden = false
self.notAllowedPinView.isHidden = true
}
private func renderPlaceholdersCount(_ count: Int, error: Bool = false) {
UIView.animate(withDuration: 0.3) {
for case let imageView as UIImageView in self.placeholderStackView.arrangedSubviews {

View File

@@ -28,6 +28,7 @@ final class EnterPinCodeViewModel: EnterPinCodeViewModelType {
private var originalViewMode: SetPinCoordinatorViewMode
private var viewMode: SetPinCoordinatorViewMode
private var initialPin: String = ""
private var firstPin: String = ""
private var currentPin: String = "" {
didSet {
@@ -116,49 +117,12 @@ final class EnterPinCodeViewModel: EnterPinCodeViewModelType {
switch viewMode {
case .setPin, .setPinAfterLogin, .setPinAfterRegister:
// choosing pin
if firstPin.isEmpty {
// check if this PIN is allowed
if pinCodePreferences.notAllowedPINs.contains(currentPin) {
viewMode = .notAllowedPin
update(viewState: .notAllowedPin)
return
}
// go to next screen
firstPin = currentPin
currentPin.removeAll()
update(viewState: .confirmPin)
} else {
// check first and second pins
if firstPin == currentPin {
// complete with a little delay
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.coordinatorDelegate?.enterPinCodeViewModel(self, didCompleteWithPin: self.firstPin)
}
} else {
update(viewState: .pinsDontMatch)
}
}
updateAfterPinSet()
case .unlock, .confirmPinToDeactivate:
// unlocking
if currentPin != pinCodePreferences.pin {
// no match
numberOfFailuresDuringEnterPIN += 1
pinCodePreferences.numberOfPinFailures += 1
if viewMode == .unlock && localAuthenticationService.shouldLogOutUser() {
// log out user
self.coordinatorDelegate?.enterPinCodeViewModelDidCompleteWithReset(self, dueToTooManyErrors: true)
return
}
if numberOfFailuresDuringEnterPIN < pinCodePreferences.allowedNumberOfTrialsBeforeAlert {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.viewDelegate?.enterPinCodeViewModel(self, didUpdateViewState: .wrongPin)
self.currentPin.removeAll()
}
} else {
viewDelegate?.enterPinCodeViewModel(self, didUpdateViewState: .wrongPinTooManyTimes)
numberOfFailuresDuringEnterPIN = 0
currentPin.removeAll()
}
updateAfterUnlockFailed()
} else {
// match
// we can use biometrics anymore, if set
@@ -169,6 +133,23 @@ final class EnterPinCodeViewModel: EnterPinCodeViewModelType {
self.coordinatorDelegate?.enterPinCodeViewModelDidComplete(self)
}
}
case .changePin:
// unlocking
if initialPin.isEmpty && currentPin != pinCodePreferences.pin {
// no match
updateAfterUnlockFailed()
} else {
// match or already unlocked
if initialPin.isEmpty {
// the user can choose a new Pin code
initialPin = currentPin
currentPin.removeAll()
update(viewState: .choosePin)
} else {
// choosing pin
updateAfterPinSet()
}
}
default:
break
}
@@ -185,6 +166,8 @@ final class EnterPinCodeViewModel: EnterPinCodeViewModelType {
return .choosePinAfterLogin
case .setPinAfterRegister:
return .choosePinAfterRegister
case .changePin:
return .changePin
default:
return .inactive
}
@@ -201,6 +184,8 @@ final class EnterPinCodeViewModel: EnterPinCodeViewModelType {
update(viewState: .confirmPinToDisable)
case .inactive:
update(viewState: .inactive)
case .changePin:
update(viewState: .changePin)
default:
break
}
@@ -209,4 +194,49 @@ final class EnterPinCodeViewModel: EnterPinCodeViewModelType {
private func update(viewState: EnterPinCodeViewState) {
self.viewDelegate?.enterPinCodeViewModel(self, didUpdateViewState: viewState)
}
private func updateAfterUnlockFailed() {
numberOfFailuresDuringEnterPIN += 1
pinCodePreferences.numberOfPinFailures += 1
if viewMode == .unlock && localAuthenticationService.shouldLogOutUser() {
// log out user
self.coordinatorDelegate?.enterPinCodeViewModelDidCompleteWithReset(self, dueToTooManyErrors: true)
return
}
if numberOfFailuresDuringEnterPIN < pinCodePreferences.allowedNumberOfTrialsBeforeAlert {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.viewDelegate?.enterPinCodeViewModel(self, didUpdateViewState: .wrongPin)
self.currentPin.removeAll()
}
} else {
viewDelegate?.enterPinCodeViewModel(self, didUpdateViewState: .wrongPinTooManyTimes)
numberOfFailuresDuringEnterPIN = 0
currentPin.removeAll()
}
}
private func updateAfterPinSet() {
if firstPin.isEmpty {
// check if this PIN is allowed
if pinCodePreferences.notAllowedPINs.contains(currentPin) {
viewMode = .notAllowedPin
update(viewState: .notAllowedPin)
return
}
// go to next screen
firstPin = currentPin
currentPin.removeAll()
update(viewState: .confirmPin)
} else {
// check first and second pins
if firstPin == currentPin {
// complete with a little delay
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.coordinatorDelegate?.enterPinCodeViewModel(self, didCompleteWithPin: self.firstPin)
}
} else {
update(viewState: .pinsDontMatch)
}
}
}
}

View File

@@ -32,4 +32,5 @@ enum EnterPinCodeViewState {
case forgotPin // after pin has been set, user tapped forgot pin
case confirmPinToDisable // after pin has been set, confirm pin to disable pin
case inactive // inactive state, only used when app is not active
case changePin // pin is set, user tapped change pin from settings
}