From 976cf766afe60447f72706e4f721ed7a17599623 Mon Sep 17 00:00:00 2001 From: Dariusz Rybicki <dariusz@elixxir.io> Date: Wed, 7 Sep 2022 00:47:08 +0200 Subject: [PATCH] Fetch db contact and update UI --- Examples/xx-messenger/Package.swift | 2 + .../AppFeature/AppEnvironment+Live.swift | 6 +- .../UserSearchResultFeature.swift | 45 +++++++++- .../UserSearchResultView.swift | 88 +++++++++++++++++++ .../UserSearchResultFeatureTests.swift | 43 +++++++++ 5 files changed, 180 insertions(+), 4 deletions(-) diff --git a/Examples/xx-messenger/Package.swift b/Examples/xx-messenger/Package.swift index 565dccc6..8070952e 100644 --- a/Examples/xx-messenger/Package.swift +++ b/Examples/xx-messenger/Package.swift @@ -140,10 +140,12 @@ let package = Package( .target( name: "UserSearchFeature", dependencies: [ + .target(name: "AppCore"), .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), .product(name: "ComposablePresentation", package: "swift-composable-presentation"), .product(name: "XXClient", package: "elixxir-dapps-sdk-swift"), .product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"), + .product(name: "XXModels", package: "client-ios-db"), ], swiftSettings: swiftSettings ), diff --git a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift index 00415b43..d8761a65 100644 --- a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift +++ b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift @@ -52,7 +52,11 @@ extension AppEnvironment { mainQueue: mainQueue, bgQueue: bgQueue, result: { - UserSearchResultEnvironment() + UserSearchResultEnvironment( + db: dbManager.getDB, + mainQueue: mainQueue, + bgQueue: bgQueue + ) } ) } diff --git a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchResultFeature.swift b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchResultFeature.swift index f4c0f856..f13c4b10 100644 --- a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchResultFeature.swift +++ b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchResultFeature.swift @@ -1,18 +1,22 @@ +import AppCore import ComposableArchitecture import Foundation import XCTestDynamicOverlay import XXClient +import XXModels public struct UserSearchResultState: Equatable, Identifiable { public init( id: Data, - xxContact: Contact, + xxContact: XXClient.Contact, + dbContact: XXModels.Contact? = nil, username: String? = nil, email: String? = nil, phone: String? = nil ) { self.id = id self.xxContact = xxContact + self.dbContact = dbContact self.username = username self.email = email self.phone = phone @@ -20,6 +24,7 @@ public struct UserSearchResultState: Equatable, Identifiable { public var id: Data public var xxContact: XXClient.Contact + public var dbContact: XXModels.Contact? public var username: String? public var email: String? public var phone: String? @@ -27,26 +32,60 @@ public struct UserSearchResultState: Equatable, Identifiable { public enum UserSearchResultAction: Equatable { case start + case didUpdateContact(XXModels.Contact?) + case sendRequestButtonTapped } public struct UserSearchResultEnvironment { - public init() {} + public init( + db: DBManagerGetDB, + mainQueue: AnySchedulerOf<DispatchQueue>, + bgQueue: AnySchedulerOf<DispatchQueue> + ) { + self.db = db + self.mainQueue = mainQueue + self.bgQueue = bgQueue + } + + public var db: DBManagerGetDB + public var mainQueue: AnySchedulerOf<DispatchQueue> + public var bgQueue: AnySchedulerOf<DispatchQueue> } #if DEBUG extension UserSearchResultEnvironment { - public static let unimplemented = UserSearchResultEnvironment() + public static let unimplemented = UserSearchResultEnvironment( + db: .unimplemented, + mainQueue: .unimplemented, + bgQueue: .unimplemented + ) } #endif public let userSearchResultReducer = Reducer<UserSearchResultState, UserSearchResultAction, UserSearchResultEnvironment> { state, action, env in + enum DBFetchEffectID {} + switch action { case .start: let facts = (try? state.xxContact.getFacts()) ?? [] state.username = facts.first(where: { $0.type == 0 })?.fact state.email = facts.first(where: { $0.type == 1 })?.fact state.phone = facts.first(where: { $0.type == 2 })?.fact + return try! env.db().fetchContactsPublisher(.init(id: [state.id])) + .assertNoFailure() + .map(\.first) + .map(UserSearchResultAction.didUpdateContact) + .subscribe(on: env.bgQueue) + .receive(on: env.mainQueue) + .eraseToEffect() + .cancellable(id: DBFetchEffectID.self, cancelInFlight: true) + + case .didUpdateContact(let contact): + state.dbContact = contact + return .none + + case .sendRequestButtonTapped: return .none } } diff --git a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchResultView.swift b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchResultView.swift index 129081fe..4eb4ca8b 100644 --- a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchResultView.swift +++ b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchResultView.swift @@ -1,5 +1,6 @@ import ComposableArchitecture import SwiftUI +import XXModels public struct UserSearchResultView: View { public init(store: Store<UserSearchResultState, UserSearchResultAction>) { @@ -12,11 +13,13 @@ public struct UserSearchResultView: View { var username: String? var email: String? var phone: String? + var dbContactAuth: XXModels.Contact.AuthStatus? init(state: UserSearchResultState) { username = state.username email = state.email phone = state.phone + dbContactAuth = state.dbContact?.authStatus } var isEmpty: Bool { @@ -41,6 +44,91 @@ public struct UserSearchResultView: View { Text(phone) } } + switch viewStore.dbContactAuth { + case .none, .stranger: + Button { + viewStore.send(.sendRequestButtonTapped) + } label: { + HStack { + Text("Send request") + Spacer() + Image(systemName: "person.badge.plus") + } + } + + 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") + } + } } .task { viewStore.send(.start) } } diff --git a/Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchResultFeatureTests.swift b/Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchResultFeatureTests.swift index c9efe0c7..e776f803 100644 --- a/Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchResultFeatureTests.swift +++ b/Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchResultFeatureTests.swift @@ -1,6 +1,9 @@ +import Combine import ComposableArchitecture import XCTest +import XCTestDynamicOverlay import XXClient +import XXModels @testable import UserSearchFeature final class UserSearchResultFeatureTests: XCTestCase { @@ -23,10 +26,50 @@ final class UserSearchResultFeatureTests: XCTestCase { environment: .unimplemented ) + var dbDidFetchContacts: [XXModels.Contact.Query] = [] + let dbContactsPublisher = PassthroughSubject<[XXModels.Contact], Error>() + + store.environment.mainQueue = .immediate + store.environment.bgQueue = .immediate + store.environment.db.run = { + var db: Database = .failing + db.fetchContactsPublisher.run = { query in + dbDidFetchContacts.append(query) + return dbContactsPublisher.eraseToAnyPublisher() + } + return db + } + store.send(.start) { $0.username = "contact-username" $0.email = "contact-email" $0.phone = "contact-phone" } + + XCTAssertNoDifference(dbDidFetchContacts, [ + .init(id: ["contact-id".data(using: .utf8)!]) + ]) + + let dbContact = XXModels.Contact(id: "contact-id".data(using: .utf8)!) + dbContactsPublisher.send([dbContact]) + + store.receive(.didUpdateContact(dbContact)) { + $0.dbContact = dbContact + } + + dbContactsPublisher.send(completion: .finished) + } + + func testSendRequest() { + let store = TestStore( + initialState: UserSearchResultState( + id: "contact-id".data(using: .utf8)!, + xxContact: .unimplemented("contact-data".data(using: .utf8)!) + ), + reducer: userSearchResultReducer, + environment: .unimplemented + ) + + store.send(.sendRequestButtonTapped) } } -- GitLab