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

Present contact from search results

parent b5bbc0ca
Branches
Tags
2 merge requests!102Release 1.0.0,!68Messenger example - send auth request
......@@ -69,6 +69,7 @@ let package = Package(
name: "AppFeature",
dependencies: [
.target(name: "AppCore"),
.target(name: "ContactFeature"),
.target(name: "HomeFeature"),
.target(name: "RegisterFeature"),
.target(name: "RestoreFeature"),
......@@ -158,6 +159,7 @@ let package = Package(
.target(
name: "UserSearchFeature",
dependencies: [
.target(name: "ContactFeature"),
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ComposablePresentation", package: "swift-composable-presentation"),
.product(name: "XXClient", package: "elixxir-dapps-sdk-swift"),
......
......@@ -52,11 +52,10 @@ extension AppEnvironment {
mainQueue: mainQueue,
bgQueue: bgQueue,
result: {
UserSearchResultEnvironment(
db: dbManager.getDB,
mainQueue: mainQueue,
bgQueue: bgQueue
)
UserSearchResultEnvironment()
},
contact: {
ContactEnvironment()
}
)
}
......
import ComposableArchitecture
import ComposablePresentation
import ContactFeature
import Foundation
import XCTestDynamicOverlay
import XXClient
......@@ -17,13 +18,15 @@ public struct UserSearchState: Equatable {
query: MessengerSearchUsers.Query = .init(),
isSearching: Bool = false,
failure: String? = nil,
results: IdentifiedArrayOf<UserSearchResultState> = []
results: IdentifiedArrayOf<UserSearchResultState> = [],
contact: ContactState? = nil
) {
self.focusedField = focusedField
self.query = query
self.isSearching = isSearching
self.failure = failure
self.results = results
self.contact = contact
}
@BindableState public var focusedField: Field?
......@@ -31,14 +34,17 @@ public struct UserSearchState: Equatable {
public var isSearching: Bool
public var failure: String?
public var results: IdentifiedArrayOf<UserSearchResultState>
public var contact: ContactState?
}
public enum UserSearchAction: Equatable, BindableAction {
case searchTapped
case didFail(String)
case didSucceed([Contact])
case didDismissContact
case binding(BindingAction<UserSearchState>)
case result(id: UserSearchResultState.ID, action: UserSearchResultAction)
case contact(ContactAction)
}
public struct UserSearchEnvironment {
......@@ -46,18 +52,21 @@ public struct UserSearchEnvironment {
messenger: Messenger,
mainQueue: AnySchedulerOf<DispatchQueue>,
bgQueue: AnySchedulerOf<DispatchQueue>,
result: @escaping () -> UserSearchResultEnvironment
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
}
#if DEBUG
......@@ -66,7 +75,8 @@ extension UserSearchEnvironment {
messenger: .unimplemented,
mainQueue: .unimplemented,
bgQueue: .unimplemented,
result: { .unimplemented }
result: { .unimplemented },
contact: { .unimplemented }
)
}
#endif
......@@ -105,7 +115,15 @@ public let userSearchReducer = Reducer<UserSearchState, UserSearchAction, UserSe
state.results = []
return .none
case .binding(_), .result(_, _):
case .didDismissContact:
state.contact = nil
return .none
case .result(let id, action: .tapped):
state.contact = ContactState()
return .none
case .binding(_), .result(_, _), .contact(_):
return .none
}
}
......@@ -116,3 +134,10 @@ public let userSearchReducer = Reducer<UserSearchState, UserSearchAction, UserSe
action: /UserSearchAction.result(id:action:),
environment: { $0.result() }
)
.presenting(
contactReducer,
state: .keyPath(\.contact),
id: .notNil(), // TODO: use Contact.ID
action: /UserSearchAction.contact,
environment: { $0.contact() }
)
import ComposableArchitecture
import ComposablePresentation
import ContactFeature
import SwiftUI
import XXMessengerClient
......@@ -96,6 +98,14 @@ public struct UserSearchView: View {
.onChange(of: viewStore.focusedField) { focusedField = $0 }
.onChange(of: focusedField) { viewStore.send(.set(\.$focusedField, $0)) }
.navigationTitle("User Search")
.background(NavigationLinkWithStore(
store.scope(
state: \.contact,
action: UserSearchAction.contact
),
onDeactivate: { viewStore.send(.didDismissContact) },
destination: ContactView.init(store:)
))
}
}
}
......
import ComposableArchitecture
import ContactFeature
import XCTest
import XXClient
import XXMessengerClient
......@@ -87,4 +88,37 @@ final class UserSearchFeatureTests: XCTestCase {
$0.results = []
}
}
func testResultTapped() {
let store = TestStore(
initialState: UserSearchState(
results: [
.init(
id: "contact-id".data(using: .utf8)!,
xxContact: .unimplemented("contact-data".data(using: .utf8)!)
)
]
),
reducer: userSearchReducer,
environment: .unimplemented
)
store.send(.result(id: "contact-id".data(using: .utf8)!, action: .tapped)) {
$0.contact = ContactState()
}
}
func testDismissingContact() {
let store = TestStore(
initialState: UserSearchState(
contact: ContactState()
),
reducer: userSearchReducer,
environment: .unimplemented
)
store.send(.didDismissContact) {
$0.contact = nil
}
}
}
import Combine
import ComposableArchitecture
import XCTest
import XCTestDynamicOverlay
import XXClient
import XXModels
@testable import UserSearchFeature
final class UserSearchResultFeatureTests: XCTestCase {
......@@ -26,41 +24,14 @@ 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() {
func testTapped() {
let store = TestStore(
initialState: UserSearchResultState(
id: "contact-id".data(using: .utf8)!,
......@@ -70,6 +41,6 @@ final class UserSearchResultFeatureTests: XCTestCase {
environment: .unimplemented
)
store.send(.sendRequestButtonTapped)
store.send(.tapped)
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment