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