diff --git a/Examples/xx-messenger/Sources/BackupFeature/BackupFeature.swift b/Examples/xx-messenger/Sources/BackupFeature/BackupFeature.swift index f672b312a1867790a10ffb54f84cd683084a1b22..431dd8ea7c546a4ac05715f6a90c59ddde85a04b 100644 --- a/Examples/xx-messenger/Sources/BackupFeature/BackupFeature.swift +++ b/Examples/xx-messenger/Sources/BackupFeature/BackupFeature.swift @@ -7,6 +7,10 @@ import XXMessengerClient import XXModels public struct BackupState: Equatable { + public enum Field: String, Hashable { + case passphrase + } + public enum Error: String, Swift.Error, Equatable { case dbContactNotFound case dbContactUsernameMissing @@ -19,6 +23,7 @@ public struct BackupState: Equatable { isStopping: Bool = false, backup: BackupStorage.Backup? = nil, alert: AlertState<BackupAction>? = nil, + focusedField: Field? = nil, passphrase: String = "", isExporting: Bool = false, exportData: Data? = nil @@ -29,6 +34,7 @@ public struct BackupState: Equatable { self.isStopping = isStopping self.backup = backup self.alert = alert + self.focusedField = focusedField self.passphrase = passphrase self.isExporting = isExporting self.exportData = exportData @@ -40,6 +46,7 @@ public struct BackupState: Equatable { public var isStopping: Bool public var backup: BackupStorage.Backup? public var alert: AlertState<BackupAction>? + @BindableState public var focusedField: Field? @BindableState public var passphrase: String @BindableState public var isExporting: Bool public var exportData: Data? @@ -119,6 +126,7 @@ public let backupReducer = Reducer<BackupState, BackupAction, BackupEnvironment> case .startTapped: state.isStarting = true + state.focusedField = nil return Effect.run { [state] subscriber in do { let e2e: E2E = try env.messenger.e2e.tryGet() diff --git a/Examples/xx-messenger/Sources/BackupFeature/BackupView.swift b/Examples/xx-messenger/Sources/BackupFeature/BackupView.swift index 54fc6567cd07eac6c5649fa5a35e6b28a91fe3eb..89510b2fbf2c993afa1438fd62408dc925d1c30e 100644 --- a/Examples/xx-messenger/Sources/BackupFeature/BackupView.swift +++ b/Examples/xx-messenger/Sources/BackupFeature/BackupView.swift @@ -8,6 +8,7 @@ public struct BackupView: View { } let store: Store<BackupState, BackupAction> + @FocusState var focusedField: BackupState.Field? struct ViewState: Equatable { struct Backup: Equatable { @@ -23,6 +24,7 @@ public struct BackupView: View { backup = state.backup.map { backup in Backup(date: backup.date, size: backup.data.count) } + focusedField = state.focusedField passphrase = state.passphrase isExporting = state.isExporting exportData = state.exportData @@ -34,6 +36,7 @@ public struct BackupView: View { var isStopping: Bool var isLoading: Bool { isStarting || isResuming || isStopping } var backup: Backup? + var focusedField: BackupState.Field? var passphrase: String var isExporting: Bool var exportData: Data? @@ -57,9 +60,9 @@ public struct BackupView: View { ) } .navigationTitle("Backup") - .task { - await viewStore.send(.task).finish() - } + .task { await viewStore.send(.task).finish() } + .onChange(of: viewStore.focusedField) { focusedField = $0 } + .onChange(of: focusedField) { viewStore.send(.set(\.$focusedField, $0)) } } } @@ -75,6 +78,11 @@ public struct BackupView: View { prompt: Text("Backup passphrase"), label: { Text("Backup passphrase") } ) + .textContentType(.password) + .textInputAutocapitalization(.never) + .disableAutocorrection(true) + .focused($focusedField, equals: .passphrase) + Button { viewStore.send(.startTapped) } label: { @@ -91,6 +99,7 @@ public struct BackupView: View { } header: { Text("New backup") } + .disabled(viewStore.isStarting) } @ViewBuilder func backupSection( diff --git a/Examples/xx-messenger/Tests/BackupFeatureTests/BackupFeatureTests.swift b/Examples/xx-messenger/Tests/BackupFeatureTests/BackupFeatureTests.swift index 21afbd5409a40c956cce46cc40726996f741cb70..b916ddc0719ad73eaf9c0fa04eb6ab52250a0350 100644 --- a/Examples/xx-messenger/Tests/BackupFeatureTests/BackupFeatureTests.swift +++ b/Examples/xx-messenger/Tests/BackupFeatureTests/BackupFeatureTests.swift @@ -101,6 +101,9 @@ final class BackupFeatureTests: XCTestCase { } actions = [] + store.send(.set(\.$focusedField, .passphrase)) { + $0.focusedField = .passphrase + } store.send(.set(\.$passphrase, passphrase)) { $0.passphrase = passphrase } @@ -110,6 +113,7 @@ final class BackupFeatureTests: XCTestCase { actions = [] store.send(.startTapped) { $0.isStarting = true + $0.focusedField = nil } XCTAssertNoDifference(actions, [