From 7ba09aa7456dde371df389f0fd3dd255e7ac7916 Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Tue, 13 Sep 2022 23:03:58 +0200
Subject: [PATCH] Present Chat from Contact

---
 Examples/xx-messenger/Package.swift           |  2 ++
 .../AppFeature/AppEnvironment+Live.swift      |  4 +++
 .../ContactFeature/ContactFeature.swift       | 34 ++++++++++++++++---
 .../Sources/ContactFeature/ContactView.swift  | 23 +++++++++++++
 .../ContactFeatureTests.swift                 | 31 +++++++++++++++++
 5 files changed, 90 insertions(+), 4 deletions(-)

diff --git a/Examples/xx-messenger/Package.swift b/Examples/xx-messenger/Package.swift
index 30f5cd4e..2d5fbb9f 100644
--- a/Examples/xx-messenger/Package.swift
+++ b/Examples/xx-messenger/Package.swift
@@ -77,6 +77,7 @@ let package = Package(
       name: "AppFeature",
       dependencies: [
         .target(name: "AppCore"),
+        .target(name: "ChatFeature"),
         .target(name: "CheckContactAuthFeature"),
         .target(name: "ConfirmRequestFeature"),
         .target(name: "ContactFeature"),
@@ -152,6 +153,7 @@ let package = Package(
       name: "ContactFeature",
       dependencies: [
         .target(name: "AppCore"),
+        .target(name: "ChatFeature"),
         .target(name: "CheckContactAuthFeature"),
         .target(name: "ConfirmRequestFeature"),
         .target(name: "SendRequestFeature"),
diff --git a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift
index 7adbcae8..9050fb3e 100644
--- a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift
+++ b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift
@@ -1,4 +1,5 @@
 import AppCore
+import ChatFeature
 import CheckContactAuthFeature
 import ConfirmRequestFeature
 import ContactFeature
@@ -64,6 +65,9 @@ extension AppEnvironment {
           mainQueue: mainQueue,
           bgQueue: bgQueue
         )
+      },
+      chat: {
+        ChatEnvironment()
       }
     )
 
diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift
index 545cb8fb..993796e4 100644
--- a/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift
+++ b/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift
@@ -1,4 +1,5 @@
 import AppCore
+import ChatFeature
 import CheckContactAuthFeature
 import ComposableArchitecture
 import ComposablePresentation
@@ -22,7 +23,8 @@ public struct ContactState: Equatable {
     sendRequest: SendRequestState? = nil,
     verifyContact: VerifyContactState? = nil,
     confirmRequest: ConfirmRequestState? = nil,
-    checkAuth: CheckContactAuthState? = nil
+    checkAuth: CheckContactAuthState? = nil,
+    chat: ChatState? = nil
   ) {
     self.id = id
     self.dbContact = dbContact
@@ -34,6 +36,7 @@ public struct ContactState: Equatable {
     self.verifyContact = verifyContact
     self.confirmRequest = confirmRequest
     self.checkAuth = checkAuth
+    self.chat = chat
   }
 
   public var id: Data
@@ -46,6 +49,7 @@ public struct ContactState: Equatable {
   public var verifyContact: VerifyContactState?
   public var confirmRequest: ConfirmRequestState?
   public var checkAuth: CheckContactAuthState?
+  public var chat: ChatState?
 }
 
 public enum ContactAction: Equatable, BindableAction {
@@ -64,6 +68,9 @@ public enum ContactAction: Equatable, BindableAction {
   case confirmRequestTapped
   case confirmRequestDismissed
   case confirmRequest(ConfirmRequestAction)
+  case chatTapped
+  case chatDismissed
+  case chat(ChatAction)
   case binding(BindingAction<ContactState>)
 }
 
@@ -76,7 +83,8 @@ public struct ContactEnvironment {
     sendRequest: @escaping () -> SendRequestEnvironment,
     verifyContact: @escaping () -> VerifyContactEnvironment,
     confirmRequest: @escaping () -> ConfirmRequestEnvironment,
-    checkAuth: @escaping () -> CheckContactAuthEnvironment
+    checkAuth: @escaping () -> CheckContactAuthEnvironment,
+    chat: @escaping () -> ChatEnvironment
   ) {
     self.messenger = messenger
     self.db = db
@@ -86,6 +94,7 @@ public struct ContactEnvironment {
     self.verifyContact = verifyContact
     self.confirmRequest = confirmRequest
     self.checkAuth = checkAuth
+    self.chat = chat
   }
 
   public var messenger: Messenger
@@ -96,6 +105,7 @@ public struct ContactEnvironment {
   public var verifyContact: () -> VerifyContactEnvironment
   public var confirmRequest: () -> ConfirmRequestEnvironment
   public var checkAuth: () -> CheckContactAuthEnvironment
+  public var chat: () -> ChatEnvironment
 }
 
 #if DEBUG
@@ -108,7 +118,8 @@ extension ContactEnvironment {
     sendRequest: { .unimplemented },
     verifyContact: { .unimplemented },
     confirmRequest: { .unimplemented },
-    checkAuth: { .unimplemented }
+    checkAuth: { .unimplemented },
+    chat: { .unimplemented }
   )
 }
 #endif
@@ -204,7 +215,15 @@ public let contactReducer = Reducer<ContactState, ContactAction, ContactEnvironm
     state.confirmRequest = nil
     return .none
 
-  case .binding(_), .sendRequest(_), .verifyContact(_), .confirmRequest(_), .checkAuth(_):
+  case .chatTapped:
+    state.chat = ChatState(id: .contact(state.id))
+    return .none
+
+  case .chatDismissed:
+    state.chat = nil
+    return .none
+
+  case .binding(_), .sendRequest(_), .verifyContact(_), .confirmRequest(_), .checkAuth(_), .chat(_):
     return .none
   }
 }
@@ -237,3 +256,10 @@ public let contactReducer = Reducer<ContactState, ContactAction, ContactEnvironm
   action: /ContactAction.checkAuth,
   environment: { $0.checkAuth() }
 )
+.presenting(
+  chatReducer,
+  state: .keyPath(\.chat),
+  id: .keyPath(\.?.id),
+  action: /ContactAction.chat,
+  environment: { $0.chat() }
+)
diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
index 08ae7eb8..d775df01 100644
--- a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
+++ b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
@@ -1,4 +1,5 @@
 import AppCore
+import ChatFeature
 import CheckContactAuthFeature
 import ComposableArchitecture
 import ComposablePresentation
@@ -148,6 +149,20 @@ public struct ContactView: View {
             Text("Auth")
           }
           .animation(.default, value: viewStore.dbContact?.authStatus)
+
+          Section {
+            Button {
+              viewStore.send(.chatTapped)
+            } label: {
+              HStack {
+                Text("Chat")
+                Spacer()
+                Image(systemName: "chevron.forward")
+              }
+            }
+          } header: {
+            Text("Chat")
+          }
         }
       }
       .navigationTitle("Contact")
@@ -185,6 +200,14 @@ public struct ContactView: View {
         onDeactivate: { viewStore.send(.checkAuthDismissed) },
         destination: CheckContactAuthView.init(store:)
       ))
+      .background(NavigationLinkWithStore(
+        store.scope(
+          state: \.chat,
+          action: ContactAction.chat
+        ),
+        onDeactivate: { viewStore.send(.chatDismissed) },
+        destination: ChatView.init(store:)
+      ))
     }
   }
 }
diff --git a/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift
index afc146b1..11bfe8a7 100644
--- a/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift
@@ -1,3 +1,4 @@
+import ChatFeature
 import CheckContactAuthFeature
 import Combine
 import ComposableArchitecture
@@ -280,4 +281,34 @@ final class ContactFeatureTests: XCTestCase {
       $0.confirmRequest = nil
     }
   }
+
+  func testChatTapped() {
+    let contactId = "contact-id".data(using: .utf8)!
+    let store = TestStore(
+      initialState: ContactState(
+        id: contactId
+      ),
+      reducer: contactReducer,
+      environment: .unimplemented
+    )
+
+    store.send(.chatTapped) {
+      $0.chat = ChatState(id: .contact(contactId))
+    }
+  }
+
+  func testChatDismissed() {
+    let store = TestStore(
+      initialState: ContactState(
+        id: "contact-id".data(using: .utf8)!,
+        chat: ChatState(id: .contact("contact-id".data(using: .utf8)!))
+      ),
+      reducer: contactReducer,
+      environment: .unimplemented
+    )
+
+    store.send(.chatDismissed) {
+      $0.chat = nil
+    }
+  }
 }
-- 
GitLab