From 3b6d4db726a7c753de4c8113e5ba61a35ac52da9 Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Fri, 30 Sep 2022 12:44:30 +0200
Subject: [PATCH] Improve form on BackupView

---
 .../Sources/BackupFeature/BackupFeature.swift     |  8 ++++++++
 .../Sources/BackupFeature/BackupView.swift        | 15 ++++++++++++---
 .../BackupFeatureTests/BackupFeatureTests.swift   |  4 ++++
 3 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/Examples/xx-messenger/Sources/BackupFeature/BackupFeature.swift b/Examples/xx-messenger/Sources/BackupFeature/BackupFeature.swift
index f672b312..431dd8ea 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 54fc6567..89510b2f 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 21afbd54..b916ddc0 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, [
-- 
GitLab