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
No related branches found
No related tags found
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.
Finish editing this message first!
Please register or to comment