From b2b508d289d05d78eeb756ec3dc700f3b9626edd Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Wed, 7 Sep 2022 13:26:26 +0200
Subject: [PATCH] Add Send Request screen

---
 Examples/xx-messenger/Package.swift           |  1 +
 .../AppFeature/AppEnvironment+Live.swift      |  5 ++-
 .../ContactFeature/ContactFeature.swift       | 31 ++++++++++++++--
 .../ContactSendRequestFeature.swift           | 28 +++++++++++++++
 .../ContactSendRequestView.swift              | 36 +++++++++++++++++++
 .../Sources/ContactFeature/ContactView.swift  |  9 +++++
 .../ContactFeatureTests.swift                 |  8 ++++-
 .../ContactSendRequestFeatureTests.swift      | 15 ++++++++
 8 files changed, 128 insertions(+), 5 deletions(-)
 create mode 100644 Examples/xx-messenger/Sources/ContactFeature/ContactSendRequestFeature.swift
 create mode 100644 Examples/xx-messenger/Sources/ContactFeature/ContactSendRequestView.swift
 create mode 100644 Examples/xx-messenger/Tests/ContactFeatureTests/ContactSendRequestFeatureTests.swift

diff --git a/Examples/xx-messenger/Package.swift b/Examples/xx-messenger/Package.swift
index e991313f..79123e05 100644
--- a/Examples/xx-messenger/Package.swift
+++ b/Examples/xx-messenger/Package.swift
@@ -95,6 +95,7 @@ let package = Package(
       dependencies: [
         .target(name: "AppCore"),
         .product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
+        .product(name: "ComposablePresentation", package: "swift-composable-presentation"),
         .product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"),
         .product(name: "XXModels", package: "client-ios-db"),
       ],
diff --git a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift
index 8e3f48dc..514f56bd 100644
--- a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift
+++ b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift
@@ -60,7 +60,10 @@ extension AppEnvironment {
                   messenger: messenger,
                   db: dbManager.getDB,
                   mainQueue: mainQueue,
-                  bgQueue: bgQueue
+                  bgQueue: bgQueue,
+                  sendRequest: {
+                    ContactSendRequestEnvironment()
+                  }
                 )
               }
             )
diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift
index 7bbb5f3c..76715592 100644
--- a/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift
+++ b/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift
@@ -1,5 +1,6 @@
 import AppCore
 import ComposableArchitecture
+import ComposablePresentation
 import Foundation
 import XCTestDynamicOverlay
 import XXClient
@@ -10,16 +11,19 @@ public struct ContactState: Equatable {
   public init(
     id: Data,
     dbContact: XXModels.Contact? = nil,
-    xxContact: XXClient.Contact? = nil
+    xxContact: XXClient.Contact? = nil,
+    sendRequest: ContactSendRequestState? = nil
   ) {
     self.id = id
     self.dbContact = dbContact
     self.xxContact = xxContact
+    self.sendRequest = sendRequest
   }
 
   public var id: Data
   public var dbContact: XXModels.Contact?
   public var xxContact: XXClient.Contact?
+  public var sendRequest: ContactSendRequestState?
 }
 
 public enum ContactAction: Equatable {
@@ -27,6 +31,8 @@ public enum ContactAction: Equatable {
   case dbContactFetched(XXModels.Contact?)
   case saveFactsTapped
   case sendRequestTapped
+  case sendRequestDismissed
+  case sendRequest(ContactSendRequestAction)
 }
 
 public struct ContactEnvironment {
@@ -34,18 +40,21 @@ public struct ContactEnvironment {
     messenger: Messenger,
     db: DBManagerGetDB,
     mainQueue: AnySchedulerOf<DispatchQueue>,
-    bgQueue: AnySchedulerOf<DispatchQueue>
+    bgQueue: AnySchedulerOf<DispatchQueue>,
+    sendRequest: @escaping () -> ContactSendRequestEnvironment
   ) {
     self.messenger = messenger
     self.db = db
     self.mainQueue = mainQueue
     self.bgQueue = bgQueue
+    self.sendRequest = sendRequest
   }
 
   public var messenger: Messenger
   public var db: DBManagerGetDB
   public var mainQueue: AnySchedulerOf<DispatchQueue>
   public var bgQueue: AnySchedulerOf<DispatchQueue>
+  public var sendRequest: () -> ContactSendRequestEnvironment
 }
 
 #if DEBUG
@@ -54,7 +63,8 @@ extension ContactEnvironment {
     messenger: .unimplemented,
     db: .unimplemented,
     mainQueue: .unimplemented,
-    bgQueue: .unimplemented
+    bgQueue: .unimplemented,
+    sendRequest: { .unimplemented }
   )
 }
 #endif
@@ -93,6 +103,21 @@ public let contactReducer = Reducer<ContactState, ContactAction, ContactEnvironm
     .eraseToEffect()
 
   case .sendRequestTapped:
+    state.sendRequest = ContactSendRequestState()
+    return .none
+
+  case .sendRequestDismissed:
+    state.sendRequest = nil
+    return .none
+
+  case .sendRequest(_):
     return .none
   }
 }
+.presenting(
+  contactSendRequestReducer,
+  state: .keyPath(\.sendRequest),
+  id: .notNil(),
+  action: /ContactAction.sendRequest,
+  environment: { $0.sendRequest() }
+)
diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactSendRequestFeature.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactSendRequestFeature.swift
new file mode 100644
index 00000000..a0a474e5
--- /dev/null
+++ b/Examples/xx-messenger/Sources/ContactFeature/ContactSendRequestFeature.swift
@@ -0,0 +1,28 @@
+import ComposableArchitecture
+import XCTestDynamicOverlay
+
+public struct ContactSendRequestState: Equatable {
+  public init() {}
+}
+
+public enum ContactSendRequestAction: Equatable {
+  case start
+}
+
+public struct ContactSendRequestEnvironment {
+  public init() {}
+}
+
+#if DEBUG
+extension ContactSendRequestEnvironment {
+  public static let unimplemented = ContactSendRequestEnvironment()
+}
+#endif
+
+public let contactSendRequestReducer = Reducer<ContactSendRequestState, ContactSendRequestAction, ContactSendRequestEnvironment>
+{ state, action, env in
+  switch action {
+  case .start:
+    return .none
+  }
+}
diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactSendRequestView.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactSendRequestView.swift
new file mode 100644
index 00000000..4f5048d3
--- /dev/null
+++ b/Examples/xx-messenger/Sources/ContactFeature/ContactSendRequestView.swift
@@ -0,0 +1,36 @@
+import ComposableArchitecture
+import SwiftUI
+
+public struct ContactSendRequestView: View {
+  public init(store: Store<ContactSendRequestState, ContactSendRequestAction>) {
+    self.store = store
+  }
+
+  let store: Store<ContactSendRequestState, ContactSendRequestAction>
+
+  struct ViewState: Equatable {
+    init(state: ContactSendRequestState) {}
+  }
+
+  public var body: some View {
+    WithViewStore(store.scope(state: ViewState.init)) { viewStore in
+      Form {
+
+      }
+      .navigationTitle("Send Request")
+      .task { viewStore.send(.start) }
+    }
+  }
+}
+
+#if DEBUG
+public struct ContactSendRequestView_Previews: PreviewProvider {
+  public static var previews: some View {
+    ContactSendRequestView(store: Store(
+      initialState: ContactSendRequestState(),
+      reducer: .empty,
+      environment: ()
+    ))
+  }
+}
+#endif
diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
index 6bf50150..44a46c56 100644
--- a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
+++ b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
@@ -1,5 +1,6 @@
 import AppCore
 import ComposableArchitecture
+import ComposablePresentation
 import SwiftUI
 import XXClient
 import XXModels
@@ -160,6 +161,14 @@ public struct ContactView: View {
       }
       .navigationTitle("Contact")
       .task { viewStore.send(.start) }
+      .background(NavigationLinkWithStore(
+        store.scope(
+          state: \.sendRequest,
+          action: ContactAction.sendRequest
+        ),
+        onDeactivate: { viewStore.send(.sendRequestDismissed) },
+        destination: ContactSendRequestView.init(store:)
+      ))
     }
   }
 }
diff --git a/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift
index f14c9e39..64d0d72a 100644
--- a/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift
@@ -103,6 +103,12 @@ final class ContactFeatureTests: XCTestCase {
       environment: .unimplemented
     )
 
-    store.send(.sendRequestTapped)
+    store.send(.sendRequestTapped) {
+      $0.sendRequest = ContactSendRequestState()
+    }
+
+    store.send(.sendRequestDismissed) {
+      $0.sendRequest = nil
+    }
   }
 }
diff --git a/Examples/xx-messenger/Tests/ContactFeatureTests/ContactSendRequestFeatureTests.swift b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactSendRequestFeatureTests.swift
new file mode 100644
index 00000000..8d4eb040
--- /dev/null
+++ b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactSendRequestFeatureTests.swift
@@ -0,0 +1,15 @@
+import ComposableArchitecture
+import XCTest
+@testable import ContactFeature
+
+final class ContactSendRequestFeatureTests: XCTestCase {
+  func testStart() {
+    let store = TestStore(
+      initialState: ContactSendRequestState(),
+      reducer: contactSendRequestReducer,
+      environment: .unimplemented
+    )
+
+    store.send(.start)
+  }
+}
-- 
GitLab