From aad544cd6ca3591117c33306ca6f4b1525bcf8d9 Mon Sep 17 00:00:00 2001 From: Dariusz Rybicki <dariusz@elixxir.io> Date: Wed, 7 Sep 2022 12:04:13 +0200 Subject: [PATCH] Fetch db contact in ContactFeature --- .../ContactFeature/ContactFeature.swift | 14 +++ .../Sources/ContactFeature/ContactView.swift | 115 +++++++++++++++++- .../ContactFeatureTests.swift | 30 +++++ 3 files changed, 158 insertions(+), 1 deletion(-) diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift index 80fbcc81..6624bb2c 100644 --- a/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift +++ b/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift @@ -24,6 +24,7 @@ public struct ContactState: Equatable { public enum ContactAction: Equatable { case start + case dbContactFetched(XXModels.Contact?) } public struct ContactEnvironment { @@ -58,8 +59,21 @@ extension ContactEnvironment { public let contactReducer = Reducer<ContactState, ContactAction, ContactEnvironment> { state, action, env in + enum DBFetchEffectID {} + switch action { case .start: + return try! env.db().fetchContactsPublisher(.init(id: [state.id])) + .assertNoFailure() + .map(\.first) + .map(ContactAction.dbContactFetched) + .subscribe(on: env.bgQueue) + .receive(on: env.mainQueue) + .eraseToEffect() + .cancellable(id: DBFetchEffectID.self, cancelInFlight: true) + + case .dbContactFetched(let contact): + state.dbContact = contact return .none } } diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift index a050747e..0d985c62 100644 --- a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift +++ b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift @@ -1,5 +1,8 @@ +import AppCore import ComposableArchitecture import SwiftUI +import XXClient +import XXModels public struct ContactView: View { public init(store: Store<ContactState, ContactAction>) { @@ -9,13 +12,123 @@ public struct ContactView: View { let store: Store<ContactState, ContactAction> struct ViewState: Equatable { - init(state: ContactState) {} + var dbContact: XXModels.Contact? + var xxContact: XXClient.Contact? + + init(state: ContactState) { + dbContact = state.dbContact + xxContact = state.xxContact + } } public var body: some View { WithViewStore(store.scope(state: ViewState.init)) { viewStore in Form { + Section { + if let dbContact = viewStore.dbContact { + Label(dbContact.username ?? "", systemImage: "person") + Label(dbContact.email ?? "", systemImage: "envelope") + Label(dbContact.phone ?? "", systemImage: "phone") + } else { + Text("Contact not saved locally") + } + } header: { + Text("Local data") + } + + Section { + Label(viewStore.xxContact?.username ?? "", systemImage: "person") + Label(viewStore.xxContact?.email ?? "", systemImage: "envelope") + Label(viewStore.xxContact?.phone ?? "", systemImage: "phone") + } header: { + Text("Facts") + } + + Section { + switch viewStore.dbContact?.authStatus { + case .none, .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") + } + } + } header: { + Text("Auth status") + } } .navigationTitle("Contact") } diff --git a/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift index 4711a132..5145acce 100644 --- a/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift +++ b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift @@ -1,5 +1,8 @@ +import Combine import ComposableArchitecture +import CustomDump import XCTest +import XXModels @testable import ContactFeature final class ContactFeatureTests: XCTestCase { @@ -12,6 +15,33 @@ final class ContactFeatureTests: 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) + + XCTAssertNoDifference(dbDidFetchContacts, [ + .init(id: ["contact-id".data(using: .utf8)!]) + ]) + + let dbContact = XXModels.Contact(id: "contact-id".data(using: .utf8)!) + dbContactsPublisher.send([dbContact]) + + store.receive(.dbContactFetched(dbContact)) { + $0.dbContact = dbContact + } + + dbContactsPublisher.send(completion: .finished) } } -- GitLab