Initial implementation for AppLockSetupFlowCoordinator. (#1949)

Rename AppLockSettingsScreen to AppLockSetupSettingsScreen.
Implement forced logout and persist PIN entry attempts.
This commit is contained in:
Doug
2023-10-25 12:03:37 +01:00
committed by GitHub
parent 30b18031a0
commit c6e4dc2a37
53 changed files with 773 additions and 234 deletions
@@ -21,6 +21,8 @@ struct AppLockSetupPINScreenCoordinatorParameters {
/// Whether the screen should start in create or unlock mode.
/// Specifying confirm here will raise a fatal error.
let initialMode: AppLockSetupPINScreenMode
/// Whether the screen is mandatory or can be cancelled by the user.
let isMandatory: Bool
let appLockService: AppLockServiceProtocol
}
@@ -46,6 +48,7 @@ final class AppLockSetupPINScreenCoordinator: CoordinatorProtocol {
self.parameters = parameters
viewModel = AppLockSetupPINScreenViewModel(initialMode: parameters.initialMode,
isMandatory: parameters.isMandatory,
appLockService: parameters.appLockService)
}
@@ -56,9 +59,9 @@ final class AppLockSetupPINScreenCoordinator: CoordinatorProtocol {
guard let self else { return }
switch action {
case .complete:
break
actionsSubject.send(.complete)
case .cancel:
break
actionsSubject.send(.cancel)
}
}
.store(in: &cancellables)
@@ -35,6 +35,8 @@ enum AppLockSetupPINScreenMode {
struct AppLockSetupPINScreenViewState: BindableState {
/// The current mode that the screen is in.
var mode: AppLockSetupPINScreenMode
/// Whether the screen is mandatory or can be cancelled.
let isMandatory: Bool
var title: String {
switch mode {
@@ -30,9 +30,9 @@ class AppLockSetupPINScreenViewModel: AppLockSetupPINScreenViewModelType, AppLoc
actionsSubject.eraseToAnyPublisher()
}
init(initialMode: AppLockSetupPINScreenMode, appLockService: AppLockServiceProtocol) {
init(initialMode: AppLockSetupPINScreenMode, isMandatory: Bool, appLockService: AppLockServiceProtocol) {
self.appLockService = appLockService
super.init(initialViewState: AppLockSetupPINScreenViewState(mode: initialMode, bindings: .init(pinCode: "")))
super.init(initialViewState: AppLockSetupPINScreenViewState(mode: initialMode, isMandatory: isMandatory, bindings: .init(pinCode: "")))
}
// MARK: - Public
@@ -21,6 +21,8 @@ import SwiftUI
struct AppLockSetupPINScreen: View {
@ObservedObject var context: AppLockSetupPINScreenViewModel.Context
@FocusState private var textFieldFocus
var body: some View {
ScrollView {
VStack(spacing: 48) {
@@ -28,6 +30,7 @@ struct AppLockSetupPINScreen: View {
PINTextField(pinCode: $context.pinCode,
isSecure: context.viewState.mode == .unlock)
.focused($textFieldFocus)
.onChange(of: context.pinCode) { newValue in
guard newValue.count == 4 else { return }
context.send(viewAction: .submitPINCode)
@@ -38,6 +41,12 @@ struct AppLockSetupPINScreen: View {
.frame(maxWidth: .infinity)
}
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
.toolbar { toolbar }
.toolbar(.visible, for: .navigationBar)
.navigationBarBackButtonHidden()
.interactiveDismissDisabled(context.viewState.isMandatory)
.alert(item: $context.alertInfo)
.onAppear { textFieldFocus = true }
}
var header: some View {
@@ -59,6 +68,17 @@ struct AppLockSetupPINScreen: View {
}
}
}
@ToolbarContentBuilder
var toolbar: some ToolbarContent {
if !context.viewState.isMandatory {
ToolbarItem(placement: .cancellationAction) {
Button(L10n.actionCancel) {
context.send(viewAction: .cancel)
}
}
}
}
}
// MARK: - Previews
@@ -66,9 +86,15 @@ struct AppLockSetupPINScreen: View {
struct AppLockSetupPINScreen_Previews: PreviewProvider, TestablePreview {
static let service = AppLockService(keychainController: KeychainControllerMock(),
appSettings: ServiceLocator.shared.settings)
static let createViewModel = AppLockSetupPINScreenViewModel(initialMode: .create, appLockService: service)
static let confirmViewModel = AppLockSetupPINScreenViewModel(initialMode: .confirm, appLockService: service)
static let unlockViewModel = AppLockSetupPINScreenViewModel(initialMode: .unlock, appLockService: service)
static let createViewModel = AppLockSetupPINScreenViewModel(initialMode: .create,
isMandatory: false,
appLockService: service)
static let confirmViewModel = AppLockSetupPINScreenViewModel(initialMode: .confirm,
isMandatory: false,
appLockService: service)
static let unlockViewModel = AppLockSetupPINScreenViewModel(initialMode: .unlock,
isMandatory: false,
appLockService: service)
static var previews: some View {
NavigationStack {