Skip to content
Snippets Groups Projects
Commit 976cf766 authored by Dariusz Rybicki's avatar Dariusz Rybicki
Browse files

Fetch db contact and update UI

parent a35d5b95
No related branches found
No related tags found
2 merge requests!102Release 1.0.0,!68Messenger example - send auth request
...@@ -140,10 +140,12 @@ let package = Package( ...@@ -140,10 +140,12 @@ let package = Package(
.target( .target(
name: "UserSearchFeature", name: "UserSearchFeature",
dependencies: [ dependencies: [
.target(name: "AppCore"),
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"), .product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ComposablePresentation", package: "swift-composable-presentation"), .product(name: "ComposablePresentation", package: "swift-composable-presentation"),
.product(name: "XXClient", package: "elixxir-dapps-sdk-swift"), .product(name: "XXClient", package: "elixxir-dapps-sdk-swift"),
.product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"), .product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"),
.product(name: "XXModels", package: "client-ios-db"),
], ],
swiftSettings: swiftSettings swiftSettings: swiftSettings
), ),
......
...@@ -52,7 +52,11 @@ extension AppEnvironment { ...@@ -52,7 +52,11 @@ extension AppEnvironment {
mainQueue: mainQueue, mainQueue: mainQueue,
bgQueue: bgQueue, bgQueue: bgQueue,
result: { result: {
UserSearchResultEnvironment() UserSearchResultEnvironment(
db: dbManager.getDB,
mainQueue: mainQueue,
bgQueue: bgQueue
)
} }
) )
} }
......
import AppCore
import ComposableArchitecture import ComposableArchitecture
import Foundation import Foundation
import XCTestDynamicOverlay import XCTestDynamicOverlay
import XXClient import XXClient
import XXModels
public struct UserSearchResultState: Equatable, Identifiable { public struct UserSearchResultState: Equatable, Identifiable {
public init( public init(
id: Data, id: Data,
xxContact: Contact, xxContact: XXClient.Contact,
dbContact: XXModels.Contact? = nil,
username: String? = nil, username: String? = nil,
email: String? = nil, email: String? = nil,
phone: String? = nil phone: String? = nil
) { ) {
self.id = id self.id = id
self.xxContact = xxContact self.xxContact = xxContact
self.dbContact = dbContact
self.username = username self.username = username
self.email = email self.email = email
self.phone = phone self.phone = phone
...@@ -20,6 +24,7 @@ public struct UserSearchResultState: Equatable, Identifiable { ...@@ -20,6 +24,7 @@ public struct UserSearchResultState: Equatable, Identifiable {
public var id: Data public var id: Data
public var xxContact: XXClient.Contact public var xxContact: XXClient.Contact
public var dbContact: XXModels.Contact?
public var username: String? public var username: String?
public var email: String? public var email: String?
public var phone: String? public var phone: String?
...@@ -27,26 +32,60 @@ public struct UserSearchResultState: Equatable, Identifiable { ...@@ -27,26 +32,60 @@ public struct UserSearchResultState: Equatable, Identifiable {
public enum UserSearchResultAction: Equatable { public enum UserSearchResultAction: Equatable {
case start case start
case didUpdateContact(XXModels.Contact?)
case sendRequestButtonTapped
} }
public struct UserSearchResultEnvironment { 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 #if DEBUG
extension UserSearchResultEnvironment { extension UserSearchResultEnvironment {
public static let unimplemented = UserSearchResultEnvironment() public static let unimplemented = UserSearchResultEnvironment(
db: .unimplemented,
mainQueue: .unimplemented,
bgQueue: .unimplemented
)
} }
#endif #endif
public let userSearchResultReducer = Reducer<UserSearchResultState, UserSearchResultAction, UserSearchResultEnvironment> public let userSearchResultReducer = Reducer<UserSearchResultState, UserSearchResultAction, UserSearchResultEnvironment>
{ state, action, env in { state, action, env in
enum DBFetchEffectID {}
switch action { switch action {
case .start: case .start:
let facts = (try? state.xxContact.getFacts()) ?? [] let facts = (try? state.xxContact.getFacts()) ?? []
state.username = facts.first(where: { $0.type == 0 })?.fact state.username = facts.first(where: { $0.type == 0 })?.fact
state.email = facts.first(where: { $0.type == 1 })?.fact state.email = facts.first(where: { $0.type == 1 })?.fact
state.phone = facts.first(where: { $0.type == 2 })?.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 return .none
} }
} }
import ComposableArchitecture import ComposableArchitecture
import SwiftUI import SwiftUI
import XXModels
public struct UserSearchResultView: View { public struct UserSearchResultView: View {
public init(store: Store<UserSearchResultState, UserSearchResultAction>) { public init(store: Store<UserSearchResultState, UserSearchResultAction>) {
...@@ -12,11 +13,13 @@ public struct UserSearchResultView: View { ...@@ -12,11 +13,13 @@ public struct UserSearchResultView: View {
var username: String? var username: String?
var email: String? var email: String?
var phone: String? var phone: String?
var dbContactAuth: XXModels.Contact.AuthStatus?
init(state: UserSearchResultState) { init(state: UserSearchResultState) {
username = state.username username = state.username
email = state.email email = state.email
phone = state.phone phone = state.phone
dbContactAuth = state.dbContact?.authStatus
} }
var isEmpty: Bool { var isEmpty: Bool {
...@@ -41,6 +44,91 @@ public struct UserSearchResultView: View { ...@@ -41,6 +44,91 @@ public struct UserSearchResultView: View {
Text(phone) 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) } .task { viewStore.send(.start) }
} }
......
import Combine
import ComposableArchitecture import ComposableArchitecture
import XCTest import XCTest
import XCTestDynamicOverlay
import XXClient import XXClient
import XXModels
@testable import UserSearchFeature @testable import UserSearchFeature
final class UserSearchResultFeatureTests: XCTestCase { final class UserSearchResultFeatureTests: XCTestCase {
...@@ -23,10 +26,50 @@ final class UserSearchResultFeatureTests: XCTestCase { ...@@ -23,10 +26,50 @@ final class UserSearchResultFeatureTests: XCTestCase {
environment: .unimplemented 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) { store.send(.start) {
$0.username = "contact-username" $0.username = "contact-username"
$0.email = "contact-email" $0.email = "contact-email"
$0.phone = "contact-phone" $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)
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment