From d107bb5820e4524b366091945b222db090180ea2 Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Thu, 8 Sep 2022 12:18:18 +0200
Subject: [PATCH 1/5] Remove UserSearchResultFeature

---
 .../AppFeature/AppEnvironment+Live.swift      |  3 -
 .../UserSearchFeature/UserSearchFeature.swift | 54 ++++++++++----
 .../UserSearchResultFeature.swift             | 55 --------------
 .../UserSearchResultView.swift                | 74 -------------------
 .../UserSearchFeature/UserSearchView.swift    | 37 ++++++++--
 .../UserSearchFeatureTests.swift              | 37 ++++++++--
 .../UserSearchResultFeatureTests.swift        | 46 ------------
 7 files changed, 100 insertions(+), 206 deletions(-)
 delete mode 100644 Examples/xx-messenger/Sources/UserSearchFeature/UserSearchResultFeature.swift
 delete mode 100644 Examples/xx-messenger/Sources/UserSearchFeature/UserSearchResultView.swift
 delete mode 100644 Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchResultFeatureTests.swift

diff --git a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift
index 298cb9f6..01e31519 100644
--- a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift
+++ b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift
@@ -53,9 +53,6 @@ extension AppEnvironment {
               messenger: messenger,
               mainQueue: mainQueue,
               bgQueue: bgQueue,
-              result: {
-                UserSearchResultEnvironment()
-              },
               contact: {
                 ContactEnvironment(
                   messenger: messenger,
diff --git a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchFeature.swift b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchFeature.swift
index 1b10c1b3..d49b4440 100644
--- a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchFeature.swift
+++ b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchFeature.swift
@@ -13,12 +13,38 @@ public struct UserSearchState: Equatable {
     case phone
   }
 
+  public struct Result: Equatable, Identifiable {
+    public init(
+      id: Data,
+      xxContact: XXClient.Contact,
+      username: String? = nil,
+      email: String? = nil,
+      phone: String? = nil
+    ) {
+      self.id = id
+      self.xxContact = xxContact
+      self.username = username
+      self.email = email
+      self.phone = phone
+    }
+
+    public var id: Data
+    public var xxContact: XXClient.Contact
+    public var username: String?
+    public var email: String?
+    public var phone: String?
+
+    public var hasFacts: Bool {
+      username != nil || email != nil || phone != nil
+    }
+  }
+
   public init(
     focusedField: Field? = nil,
     query: MessengerSearchUsers.Query = .init(),
     isSearching: Bool = false,
     failure: String? = nil,
-    results: IdentifiedArrayOf<UserSearchResultState> = [],
+    results: IdentifiedArrayOf<Result> = [],
     contact: ContactState? = nil
   ) {
     self.focusedField = focusedField
@@ -33,7 +59,7 @@ public struct UserSearchState: Equatable {
   @BindableState public var query: MessengerSearchUsers.Query
   public var isSearching: Bool
   public var failure: String?
-  public var results: IdentifiedArrayOf<UserSearchResultState>
+  public var results: IdentifiedArrayOf<Result>
   public var contact: ContactState?
 }
 
@@ -42,8 +68,8 @@ public enum UserSearchAction: Equatable, BindableAction {
   case didFail(String)
   case didSucceed([Contact])
   case didDismissContact
+  case resultTapped(id: Data)
   case binding(BindingAction<UserSearchState>)
-  case result(id: UserSearchResultState.ID, action: UserSearchResultAction)
   case contact(ContactAction)
 }
 
@@ -52,20 +78,17 @@ public struct UserSearchEnvironment {
     messenger: Messenger,
     mainQueue: AnySchedulerOf<DispatchQueue>,
     bgQueue: AnySchedulerOf<DispatchQueue>,
-    result: @escaping () -> UserSearchResultEnvironment,
     contact: @escaping () -> ContactEnvironment
   ) {
     self.messenger = messenger
     self.mainQueue = mainQueue
     self.bgQueue = bgQueue
-    self.result = result
     self.contact = contact
   }
 
   public var messenger: Messenger
   public var mainQueue: AnySchedulerOf<DispatchQueue>
   public var bgQueue: AnySchedulerOf<DispatchQueue>
-  public var result: () -> UserSearchResultEnvironment
   public var contact: () -> ContactEnvironment
 }
 
@@ -75,7 +98,6 @@ extension UserSearchEnvironment {
     messenger: .unimplemented,
     mainQueue: .unimplemented,
     bgQueue: .unimplemented,
-    result: { .unimplemented },
     contact: { .unimplemented }
   )
 }
@@ -105,7 +127,13 @@ public let userSearchReducer = Reducer<UserSearchState, UserSearchAction, UserSe
     state.failure = nil
     state.results = IdentifiedArray(uniqueElements: contacts.compactMap { contact in
       guard let id = try? contact.getId() else { return nil }
-      return UserSearchResultState(id: id, xxContact: contact)
+      return UserSearchState.Result(
+        id: id,
+        xxContact: contact,
+        username: try? contact.getFact(.username)?.fact,
+        email: try? contact.getFact(.email)?.fact,
+        phone: try? contact.getFact(.phone)?.fact
+      )
     })
     return .none
 
@@ -119,24 +147,18 @@ public let userSearchReducer = Reducer<UserSearchState, UserSearchAction, UserSe
     state.contact = nil
     return .none
 
-  case .result(let id, action: .tapped):
+  case .resultTapped(let id):
     state.contact = ContactState(
       id: id,
       xxContact: state.results[id: id]?.xxContact
     )
     return .none
 
-  case .binding(_), .result(_, _), .contact(_):
+  case .binding(_), .contact(_):
     return .none
   }
 }
 .binding()
-.presenting(
-  forEach: userSearchResultReducer,
-  state: \.results,
-  action: /UserSearchAction.result(id:action:),
-  environment: { $0.result() }
-)
 .presenting(
   contactReducer,
   state: .keyPath(\.contact),
diff --git a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchResultFeature.swift b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchResultFeature.swift
deleted file mode 100644
index 839e6886..00000000
--- a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchResultFeature.swift
+++ /dev/null
@@ -1,55 +0,0 @@
-import ComposableArchitecture
-import Foundation
-import XCTestDynamicOverlay
-import XXClient
-
-public struct UserSearchResultState: Equatable, Identifiable {
-  public init(
-    id: Data,
-    xxContact: XXClient.Contact,
-    username: String? = nil,
-    email: String? = nil,
-    phone: String? = nil
-  ) {
-    self.id = id
-    self.xxContact = xxContact
-    self.username = username
-    self.email = email
-    self.phone = phone
-  }
-
-  public var id: Data
-  public var xxContact: XXClient.Contact
-  public var username: String?
-  public var email: String?
-  public var phone: String?
-}
-
-public enum UserSearchResultAction: Equatable {
-  case start
-  case tapped
-}
-
-public struct UserSearchResultEnvironment {
-  public init() {}
-}
-
-#if DEBUG
-extension UserSearchResultEnvironment {
-  public static let unimplemented = UserSearchResultEnvironment()
-}
-#endif
-
-public let userSearchResultReducer = Reducer<UserSearchResultState, UserSearchResultAction, UserSearchResultEnvironment>
-{ state, action, env in
-  switch action {
-  case .start:
-    state.username = try? state.xxContact.getFact(.username)?.fact
-    state.email = try? state.xxContact.getFact(.email)?.fact
-    state.phone = try? state.xxContact.getFact(.phone)?.fact
-    return .none
-
-  case .tapped:
-    return .none
-  }
-}
diff --git a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchResultView.swift b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchResultView.swift
deleted file mode 100644
index fd29a84f..00000000
--- a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchResultView.swift
+++ /dev/null
@@ -1,74 +0,0 @@
-import ComposableArchitecture
-import SwiftUI
-import XXModels
-
-public struct UserSearchResultView: View {
-  public init(store: Store<UserSearchResultState, UserSearchResultAction>) {
-    self.store = store
-  }
-
-  let store: Store<UserSearchResultState, UserSearchResultAction>
-
-  struct ViewState: Equatable {
-    var username: String?
-    var email: String?
-    var phone: String?
-
-    init(state: UserSearchResultState) {
-      username = state.username
-      email = state.email
-      phone = state.phone
-    }
-
-    var isEmpty: Bool {
-      username == nil && email == nil && phone == nil
-    }
-  }
-
-  public var body: some View {
-    WithViewStore(store.scope(state: ViewState.init)) { viewStore in
-      Section {
-        Button {
-          viewStore.send(.tapped)
-        } label: {
-          HStack {
-            VStack {
-              if viewStore.isEmpty {
-                Image(systemName: "questionmark")
-                  .frame(maxWidth: .infinity)
-              } else {
-                if let username = viewStore.username {
-                  Text(username)
-                }
-                if let email = viewStore.email {
-                  Text(email)
-                }
-                if let phone = viewStore.phone {
-                  Text(phone)
-                }
-              }
-            }
-            Spacer()
-            Image(systemName: "chevron.forward")
-          }
-        }
-      }
-      .task { viewStore.send(.start) }
-    }
-  }
-}
-
-#if DEBUG
-public struct UserSearchResultView_Previews: PreviewProvider {
-  public static var previews: some View {
-    UserSearchResultView(store: Store(
-      initialState: UserSearchResultState(
-        id: "contact-id".data(using: .utf8)!,
-        xxContact: .unimplemented("contact-data".data(using: .utf8)!)
-      ),
-      reducer: .empty,
-      environment: ()
-    ))
-  }
-}
-#endif
diff --git a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchView.swift b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchView.swift
index f0416b3a..9f76ed0a 100644
--- a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchView.swift
+++ b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchView.swift
@@ -17,12 +17,14 @@ public struct UserSearchView: View {
     var query: MessengerSearchUsers.Query
     var isSearching: Bool
     var failure: String?
+    var results: IdentifiedArrayOf<UserSearchState.Result>
 
     init(state: UserSearchState) {
       focusedField = state.focusedField
       query = state.query
       isSearching = state.isSearching
       failure = state.failure
+      results = state.results
     }
   }
 
@@ -87,13 +89,34 @@ public struct UserSearchView: View {
           }
         }
 
-        ForEachStore(
-          store.scope(
-            state: \.results,
-            action: UserSearchAction.result(id:action:)
-          ),
-          content: UserSearchResultView.init(store:)
-        )
+        ForEach(viewStore.results) { result in
+          Section {
+            Button {
+              viewStore.send(.resultTapped(id: result.id))
+            } label: {
+              HStack {
+                VStack {
+                  if result.hasFacts {
+                    if let username = result.username {
+                      Text(username)
+                    }
+                    if let email = result.email {
+                      Text(email)
+                    }
+                    if let phone = result.phone {
+                      Text(phone)
+                    }
+                  } else {
+                    Image(systemName: "questionmark")
+                      .frame(maxWidth: .infinity)
+                  }
+                }
+                Spacer()
+                Image(systemName: "chevron.forward")
+              }
+            }
+          }
+        }
       }
       .onChange(of: viewStore.focusedField) { focusedField = $0 }
       .onChange(of: focusedField) { viewStore.send(.set(\.$focusedField, $0)) }
diff --git a/Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchFeatureTests.swift b/Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchFeatureTests.swift
index c457327c..f8f482f6 100644
--- a/Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchFeatureTests.swift
@@ -20,12 +20,26 @@ final class UserSearchFeatureTests: XCTestCase {
 
     var contact1 = Contact.unimplemented("contact-1".data(using: .utf8)!)
     contact1.getIdFromContact.run = { _ in "contact-1-id".data(using: .utf8)! }
-    var contact2 = Contact.unimplemented("contact-1".data(using: .utf8)!)
+    contact1.getFactsFromContact.run = { _ in
+      [
+        Fact(type: .username, value: "contact-1-username"),
+        Fact(type: .email, value: "contact-1-email"),
+        Fact(type: .phone, value: "contact-1-phone"),
+      ]
+    }
+    var contact2 = Contact.unimplemented("contact-2".data(using: .utf8)!)
     contact2.getIdFromContact.run = { _ in "contact-2-id".data(using: .utf8)! }
+    contact2.getFactsFromContact.run = { _ in
+      [
+        Fact(type: .username, value: "contact-2-username"),
+      ]
+    }
     var contact3 = Contact.unimplemented("contact-3".data(using: .utf8)!)
     contact3.getIdFromContact.run = { _ in throw GetIdFromContactError() }
+    contact3.getFactsFromContact.run = { _ in [] }
     var contact4 = Contact.unimplemented("contact-4".data(using: .utf8)!)
     contact4.getIdFromContact.run = { _ in "contact-4-id".data(using: .utf8)! }
+    contact4.getFactsFromContact.run = { _ in throw GetFactsFromContactError() }
     let contacts = [contact1, contact2, contact3, contact4]
 
     store.environment.bgQueue = .immediate
@@ -54,9 +68,22 @@ final class UserSearchFeatureTests: XCTestCase {
       $0.isSearching = false
       $0.failure = nil
       $0.results = [
-        .init(id: "contact-1-id".data(using: .utf8)!, xxContact: contact1),
-        .init(id: "contact-2-id".data(using: .utf8)!, xxContact: contact2),
-        .init(id: "contact-4-id".data(using: .utf8)!, xxContact: contact4)
+        .init(
+          id: "contact-1-id".data(using: .utf8)!,
+          xxContact: contact1,
+          username: "contact-1-username",
+          email: "contact-1-email",
+          phone: "contact-1-phone"
+        ),
+        .init(
+          id: "contact-2-id".data(using: .utf8)!,
+          xxContact: contact2,
+          username: "contact-2-username"
+        ),
+        .init(
+          id: "contact-4-id".data(using: .utf8)!,
+          xxContact: contact4
+        )
       ]
     }
   }
@@ -103,7 +130,7 @@ final class UserSearchFeatureTests: XCTestCase {
       environment: .unimplemented
     )
 
-    store.send(.result(id: "contact-id".data(using: .utf8)!, action: .tapped)) {
+    store.send(.resultTapped(id: "contact-id".data(using: .utf8)!)) {
       $0.contact = ContactState(
         id: "contact-id".data(using: .utf8)!,
         xxContact: .unimplemented("contact-data".data(using: .utf8)!)
diff --git a/Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchResultFeatureTests.swift b/Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchResultFeatureTests.swift
deleted file mode 100644
index c8f2a99b..00000000
--- a/Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchResultFeatureTests.swift
+++ /dev/null
@@ -1,46 +0,0 @@
-import ComposableArchitecture
-import XCTest
-import XCTestDynamicOverlay
-import XXClient
-@testable import UserSearchFeature
-
-final class UserSearchResultFeatureTests: XCTestCase {
-  func testStart() {
-    var contact = Contact.unimplemented("contact-data".data(using: .utf8)!)
-    contact.getFactsFromContact.run = { _ in
-      [
-        Fact(fact: "contact-username", type: 0),
-        Fact(fact: "contact-email", type: 1),
-        Fact(fact: "contact-phone", type: 2),
-      ]
-    }
-
-    let store = TestStore(
-      initialState: UserSearchResultState(
-        id: "contact-id".data(using: .utf8)!,
-        xxContact: contact
-      ),
-      reducer: userSearchResultReducer,
-      environment: .unimplemented
-    )
-
-    store.send(.start) {
-      $0.username = "contact-username"
-      $0.email = "contact-email"
-      $0.phone = "contact-phone"
-    }
-  }
-
-  func testTapped() {
-    let store = TestStore(
-      initialState: UserSearchResultState(
-        id: "contact-id".data(using: .utf8)!,
-        xxContact: .unimplemented("contact-data".data(using: .utf8)!)
-      ),
-      reducer: userSearchResultReducer,
-      environment: .unimplemented
-    )
-
-    store.send(.tapped)
-  }
-}
-- 
GitLab


From 2797bde11e43b3e7a40ae55da28457e056ab8175 Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Thu, 8 Sep 2022 12:21:26 +0200
Subject: [PATCH 2/5] Extract ContactAuthStatusView

---
 .../SharedUI/ContactAuthStatusView.swift      | 94 +++++++++++++++++++
 .../Sources/ContactFeature/ContactView.swift  | 82 +---------------
 2 files changed, 95 insertions(+), 81 deletions(-)
 create mode 100644 Examples/xx-messenger/Sources/AppCore/SharedUI/ContactAuthStatusView.swift

diff --git a/Examples/xx-messenger/Sources/AppCore/SharedUI/ContactAuthStatusView.swift b/Examples/xx-messenger/Sources/AppCore/SharedUI/ContactAuthStatusView.swift
new file mode 100644
index 00000000..355749c2
--- /dev/null
+++ b/Examples/xx-messenger/Sources/AppCore/SharedUI/ContactAuthStatusView.swift
@@ -0,0 +1,94 @@
+import SwiftUI
+import XXModels
+
+public struct ContactAuthStatusView: View {
+  public init(_ authStatus: Contact.AuthStatus) {
+    self.authStatus = authStatus
+  }
+
+  public var authStatus: Contact.AuthStatus
+
+  public var body: some View {
+    switch authStatus {
+    case .stranger:
+      HStack {
+        Text("Stranger")
+        Spacer()
+        Image(systemName: "person.fill.questionmark")
+      }
+
+    case .requesting:
+      HStack {
+        Text("Sending auth request")
+        Spacer()
+        ProgressView()
+      }
+
+    case .requested:
+      HStack {
+        Text("Request sent")
+        Spacer()
+        Image(systemName: "paperplane")
+      }
+
+    case .requestFailed:
+      HStack {
+        Text("Sending request failed")
+        Spacer()
+        Image(systemName: "xmark.diamond.fill")
+          .foregroundColor(.red)
+      }
+
+    case .verificationInProgress:
+      HStack {
+        Text("Verification is progress")
+        Spacer()
+        ProgressView()
+      }
+
+    case .verified:
+      HStack {
+        Text("Verified")
+        Spacer()
+        Image(systemName: "person.fill.checkmark")
+      }
+
+    case .verificationFailed:
+      HStack {
+        Text("Verification failed")
+        Spacer()
+        Image(systemName: "xmark.diamond.fill")
+          .foregroundColor(.red)
+      }
+
+    case .confirming:
+      HStack {
+        Text("Confirming auth request")
+        Spacer()
+        ProgressView()
+      }
+
+    case .confirmationFailed:
+      HStack {
+        Text("Confirmation failed")
+        Spacer()
+        Image(systemName: "xmark.diamond.fill")
+          .foregroundColor(.red)
+      }
+
+    case .friend:
+      HStack {
+        Text("Friend")
+        Spacer()
+        Image(systemName: "person.fill.checkmark")
+      }
+
+    case .hidden:
+      HStack {
+        Text("Hidden")
+        Spacer()
+        Image(systemName: "eye.slash")
+      }
+    }
+  }
+}
diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
index 48743b07..252a1fd1 100644
--- a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
+++ b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
@@ -100,87 +100,7 @@ public struct ContactView: View {
           }
 
           Section {
-            switch dbContact.authStatus {
-            case .stranger:
-              HStack {
-                Text("Stranger")
-                Spacer()
-                Image(systemName: "person.fill.questionmark")
-              }
-
-            case .requesting:
-              HStack {
-                Text("Sending auth request")
-                Spacer()
-                ProgressView()
-              }
-
-            case .requested:
-              HStack {
-                Text("Request sent")
-                Spacer()
-                Image(systemName: "paperplane")
-              }
-
-            case .requestFailed:
-              HStack {
-                Text("Sending request failed")
-                Spacer()
-                Image(systemName: "xmark.diamond.fill")
-                  .foregroundColor(.red)
-              }
-
-            case .verificationInProgress:
-              HStack {
-                Text("Verification is progress")
-                Spacer()
-                ProgressView()
-              }
-
-            case .verified:
-              HStack {
-                Text("Verified")
-                Spacer()
-                Image(systemName: "person.fill.checkmark")
-              }
-
-            case .verificationFailed:
-              HStack {
-                Text("Verification failed")
-                Spacer()
-                Image(systemName: "xmark.diamond.fill")
-                  .foregroundColor(.red)
-              }
-
-            case .confirming:
-              HStack {
-                Text("Confirming auth request")
-                Spacer()
-                ProgressView()
-              }
-
-            case .confirmationFailed:
-              HStack {
-                Text("Confirmation failed")
-                Spacer()
-                Image(systemName: "xmark.diamond.fill")
-                  .foregroundColor(.red)
-              }
-
-            case .friend:
-              HStack {
-                Text("Friend")
-                Spacer()
-                Image(systemName: "person.fill.checkmark")
-              }
-
-            case .hidden:
-              HStack {
-                Text("Hidden")
-                Spacer()
-                Image(systemName: "eye.slash")
-              }
-            }
+            ContactAuthStatusView(dbContact.authStatus)
             Button {
               viewStore.send(.sendRequestTapped)
             } label: {
-- 
GitLab


From 06f7ff828b3e12321f4313eb3685cd3a5352b4be Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Thu, 8 Sep 2022 12:30:44 +0200
Subject: [PATCH 3/5] Update deprecations

---
 .../ContactFeature/ContactFeature.swift       |  6 ++---
 .../Sources/ContactFeature/ContactView.swift  |  6 ++---
 .../Sources/HomeFeature/HomeFeature.swift     |  2 +-
 .../SendRequestFeature/SendRequestView.swift  | 24 +++++++++----------
 .../UserSearchFeature/UserSearchFeature.swift |  6 ++---
 5 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift
index 8126b792..9f6ef294 100644
--- a/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift
+++ b/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift
@@ -105,13 +105,13 @@ public let contactReducer = Reducer<ContactState, ContactAction, ContactEnvironm
       var dbContact = state.dbContact ?? XXModels.Contact(id: state.id)
       dbContact.marshaled = xxContact.data
       if state.importUsername {
-        dbContact.username = try? xxContact.getFact(.username)?.fact
+        dbContact.username = try? xxContact.getFact(.username)?.value
       }
       if state.importEmail {
-        dbContact.email = try? xxContact.getFact(.email)?.fact
+        dbContact.email = try? xxContact.getFact(.email)?.value
       }
       if state.importPhone {
-        dbContact.phone = try? xxContact.getFact(.phone)?.fact
+        dbContact.phone = try? xxContact.getFact(.phone)?.value
       }
       _ = try! env.db().saveContact(dbContact)
     }
diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
index 252a1fd1..a35e2074 100644
--- a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
+++ b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
@@ -26,9 +26,9 @@ public struct ContactView: View {
     init(state: ContactState) {
       dbContact = state.dbContact
       xxContactIsSet = state.xxContact != nil
-      xxContactUsername = try? state.xxContact?.getFact(.username)?.fact
-      xxContactEmail = try? state.xxContact?.getFact(.email)?.fact
-      xxContactPhone = try? state.xxContact?.getFact(.phone)?.fact
+      xxContactUsername = try? state.xxContact?.getFact(.username)?.value
+      xxContactEmail = try? state.xxContact?.getFact(.email)?.value
+      xxContactPhone = try? state.xxContact?.getFact(.phone)?.value
       importUsername = state.importUsername
       importEmail = state.importEmail
       importPhone = state.importPhone
diff --git a/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift b/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift
index 712855ba..9df6d745 100644
--- a/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift
+++ b/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift
@@ -200,7 +200,7 @@ public let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment>
         let contact = try env.dbManager.getDB().fetchContacts(.init(id: [contactId])).first
         if let username = contact?.username {
           let ud = try env.messenger.ud.tryGet()
-          try ud.permanentDeleteAccount(username: Fact(fact: username, type: 0))
+          try ud.permanentDeleteAccount(username: Fact(type: .username, value: username))
         }
         try env.messenger.destroy()
         try env.dbManager.removeDB()
diff --git a/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestView.swift b/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestView.swift
index 5f1cd7d5..fbfd9958 100644
--- a/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestView.swift
+++ b/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestView.swift
@@ -24,12 +24,12 @@ public struct SendRequestView: View {
     var failure: String?
 
     init(state: SendRequestState) {
-      contactUsername = try? state.contact.getFact(.username)?.fact
-      contactEmail = try? state.contact.getFact(.email)?.fact
-      contactPhone = try? state.contact.getFact(.phone)?.fact
-      myUsername = try? state.myContact?.getFact(.username)?.fact
-      myEmail = try? state.myContact?.getFact(.email)?.fact
-      myPhone = try? state.myContact?.getFact(.phone)?.fact
+      contactUsername = try? state.contact.getFact(.username)?.value
+      contactEmail = try? state.contact.getFact(.email)?.value
+      contactPhone = try? state.contact.getFact(.phone)?.value
+      myUsername = try? state.myContact?.getFact(.username)?.value
+      myEmail = try? state.myContact?.getFact(.email)?.value
+      myPhone = try? state.myContact?.getFact(.phone)?.value
       sendUsername = state.sendUsername
       sendEmail = state.sendEmail
       sendPhone = state.sendPhone
@@ -134,9 +134,9 @@ public struct SendRequestView_Previews: PreviewProvider {
             var contact = XXClient.Contact.unimplemented("contact-data".data(using: .utf8)!)
             contact.getFactsFromContact.run = { _ in
               [
-                Fact(fact: "contact-username", type: 0),
-                Fact(fact: "contact-email", type: 1),
-                Fact(fact: "contact-phone", type: 2),
+                Fact(type: .username, value: "contact-username"),
+                Fact(type: .email, value: "contact-email"),
+                Fact(type: .phone, value: "contact-phone"),
               ]
             }
             return contact
@@ -145,9 +145,9 @@ public struct SendRequestView_Previews: PreviewProvider {
             var contact = XXClient.Contact.unimplemented("my-data".data(using: .utf8)!)
             contact.getFactsFromContact.run = { _ in
               [
-                Fact(fact: "my-username", type: 0),
-                Fact(fact: "my-email", type: 1),
-                Fact(fact: "my-phone", type: 2),
+                Fact(type: .username, value: "my-username"),
+                Fact(type: .email, value: "my-email"),
+                Fact(type: .phone, value: "my-phone"),
               ]
             }
             return contact
diff --git a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchFeature.swift b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchFeature.swift
index d49b4440..2e892bb4 100644
--- a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchFeature.swift
+++ b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchFeature.swift
@@ -130,9 +130,9 @@ public let userSearchReducer = Reducer<UserSearchState, UserSearchAction, UserSe
       return UserSearchState.Result(
         id: id,
         xxContact: contact,
-        username: try? contact.getFact(.username)?.fact,
-        email: try? contact.getFact(.email)?.fact,
-        phone: try? contact.getFact(.phone)?.fact
+        username: try? contact.getFact(.username)?.value,
+        email: try? contact.getFact(.email)?.value,
+        phone: try? contact.getFact(.phone)?.value
       )
     })
     return .none
-- 
GitLab


From 119d69ffc3d250532ccde072a71263126d2343ae Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Thu, 8 Sep 2022 12:34:25 +0200
Subject: [PATCH 4/5] Update deprecations

---
 .../ContactFeatureTests/ContactFeatureTests.swift    |  6 +++---
 .../Tests/HomeFeatureTests/HomeFeatureTests.swift    |  2 +-
 .../RegisterFeatureTests/RegisterFeatureTests.swift  |  2 +-
 .../SendRequestFeatureTests.swift                    | 12 ++++++------
 4 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift
index 244cc900..247abb49 100644
--- a/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift
@@ -55,9 +55,9 @@ final class ContactFeatureTests: XCTestCase {
     var xxContact: XXClient.Contact = .unimplemented("contact-data".data(using: .utf8)!)
     xxContact.getFactsFromContact.run = { _ in
       [
-        Fact(fact: "contact-username", type: 0),
-        Fact(fact: "contact-email", type: 1),
-        Fact(fact: "contact-phone", type: 2),
+        Fact(type: .username, value: "contact-username"),
+        Fact(type: .email, value: "contact-email"),
+        Fact(type: .phone, value: "contact-phone"),
       ]
     }
 
diff --git a/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift b/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift
index cbc261ce..2d7f6daf 100644
--- a/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift
@@ -370,7 +370,7 @@ final class HomeFeatureTests: XCTestCase {
     }
 
     XCTAssertNoDifference(dbDidFetchContacts, [.init(id: ["contact-id".data(using: .utf8)!])])
-    XCTAssertNoDifference(udDidPermanentDeleteAccount, [Fact(fact: "MyUsername", type: 0)])
+    XCTAssertNoDifference(udDidPermanentDeleteAccount, [Fact(type: .username, value: "MyUsername")])
     XCTAssertNoDifference(messengerDidDestroy, 1)
     XCTAssertNoDifference(didRemoveDB, 1)
 
diff --git a/Examples/xx-messenger/Tests/RegisterFeatureTests/RegisterFeatureTests.swift b/Examples/xx-messenger/Tests/RegisterFeatureTests/RegisterFeatureTests.swift
index 12addba7..6c802874 100644
--- a/Examples/xx-messenger/Tests/RegisterFeatureTests/RegisterFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/RegisterFeatureTests/RegisterFeatureTests.swift
@@ -63,7 +63,7 @@ final class RegisterFeatureTests: XCTestCase {
     bgQueue.advance()
 
     XCTAssertNoDifference(messengerDidRegisterUsername, ["NewUser"])
-    XCTAssertNoDifference(didSetFactsOnContact, [[Fact(fact: "NewUser", type: 0)]])
+    XCTAssertNoDifference(didSetFactsOnContact, [[Fact(type: .username, value: "NewUser")]])
     XCTAssertNoDifference(dbDidSaveContact, [
       XXModels.Contact(
         id: "contact-id".data(using: .utf8)!,
diff --git a/Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestFeatureTests.swift b/Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestFeatureTests.swift
index 1dbe26be..cec3587e 100644
--- a/Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestFeatureTests.swift
@@ -63,9 +63,9 @@ final class SendRequestFeatureTests: XCTestCase {
 
     var myContact: XXClient.Contact = .unimplemented("my-contact-data".data(using: .utf8)!)
     let myFacts = [
-      Fact(fact: "my-username", type: 0),
-      Fact(fact: "my-email", type: 1),
-      Fact(fact: "my-phone", type: 2),
+      Fact(type: .username, value: "my-username"),
+      Fact(type: .email, value: "my-email"),
+      Fact(type: .phone, value: "my-phone"),
     ]
     myContact.getFactsFromContact.run = { _ in myFacts }
 
@@ -142,9 +142,9 @@ final class SendRequestFeatureTests: XCTestCase {
 
     var myContact: XXClient.Contact = .unimplemented("my-contact-data".data(using: .utf8)!)
     let myFacts = [
-      Fact(fact: "my-username", type: 0),
-      Fact(fact: "my-email", type: 1),
-      Fact(fact: "my-phone", type: 2),
+      Fact(type: .username, value: "my-username"),
+      Fact(type: .email, value: "my-email"),
+      Fact(type: .phone, value: "my-phone"),
     ]
     myContact.getFactsFromContact.run = { _ in myFacts }
 
-- 
GitLab


From d042833cb69824b9be91b3fd3e6c9081749ac888 Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Thu, 8 Sep 2022 12:39:21 +0200
Subject: [PATCH 5/5] Update UI

---
 .../Sources/ContactFeature/ContactView.swift         | 12 ++++++++----
 .../Sources/UserSearchFeature/UserSearchView.swift   | 10 +++++-----
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
index a35e2074..98691693 100644
--- a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
+++ b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
@@ -79,10 +79,14 @@ public struct ContactView: View {
             Button {
               viewStore.send(.importFactsTapped)
             } label: {
-              if viewStore.dbContact == nil {
-                Text("Save contact")
-              } else {
-                Text("Update contact")
+              HStack {
+                if viewStore.dbContact == nil {
+                  Text("Save contact")
+                } else {
+                  Text("Update contact")
+                }
+                Spacer()
+                Image(systemName: "arrow.down")
               }
             }
           } header: {
diff --git a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchView.swift b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchView.swift
index 9f76ed0a..c6944119 100644
--- a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchView.swift
+++ b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchView.swift
@@ -98,19 +98,19 @@ public struct UserSearchView: View {
                 VStack {
                   if result.hasFacts {
                     if let username = result.username {
-                      Text(username)
+                      Label(username, systemImage: "person")
                     }
                     if let email = result.email {
-                      Text(email)
+                      Label(email, systemImage: "envelope")
                     }
                     if let phone = result.phone {
-                      Text(phone)
+                      Label(phone, systemImage: "phone")
                     }
                   } else {
-                    Image(systemName: "questionmark")
-                      .frame(maxWidth: .infinity)
+                    Label("No facts", systemImage: "questionmark")
                   }
                 }
+                .tint(Color.primary)
                 Spacer()
                 Image(systemName: "chevron.forward")
               }
-- 
GitLab