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

Embed UserSearchResultFeature in UserSearchFeature

parent 2e50e9c8
No related branches found
No related tags found
2 merge requests!102Release 1.0.0,!68Messenger example - send auth request
This commit is part of merge request !68. Comments created here will be created in the context of that merge request.
...@@ -141,6 +141,7 @@ let package = Package( ...@@ -141,6 +141,7 @@ let package = Package(
name: "UserSearchFeature", name: "UserSearchFeature",
dependencies: [ dependencies: [
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"), .product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.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"),
], ],
......
...@@ -50,7 +50,10 @@ extension AppEnvironment { ...@@ -50,7 +50,10 @@ extension AppEnvironment {
UserSearchEnvironment( UserSearchEnvironment(
messenger: messenger, messenger: messenger,
mainQueue: mainQueue, mainQueue: mainQueue,
bgQueue: bgQueue bgQueue: bgQueue,
result: {
UserSearchResultEnvironment()
}
) )
} }
) )
......
import ComposableArchitecture import ComposableArchitecture
import ComposablePresentation
import Foundation import Foundation
import XCTestDynamicOverlay import XCTestDynamicOverlay
import XXClient import XXClient
...@@ -11,34 +12,12 @@ public struct UserSearchState: Equatable { ...@@ -11,34 +12,12 @@ public struct UserSearchState: Equatable {
case phone case phone
} }
public struct Result: Equatable, Identifiable {
public init(
id: Data,
contact: Contact,
username: String? = nil,
email: String? = nil,
phone: String? = nil
) {
self.id = id
self.contact = contact
self.username = username
self.email = email
self.phone = phone
}
public var id: Data
public var contact: XXClient.Contact
public var username: String?
public var email: String?
public var phone: String?
}
public init( public init(
focusedField: Field? = nil, focusedField: Field? = nil,
query: MessengerSearchUsers.Query = .init(), query: MessengerSearchUsers.Query = .init(),
isSearching: Bool = false, isSearching: Bool = false,
failure: String? = nil, failure: String? = nil,
results: IdentifiedArrayOf<Result> = [] results: IdentifiedArrayOf<UserSearchResultState> = []
) { ) {
self.focusedField = focusedField self.focusedField = focusedField
self.query = query self.query = query
...@@ -51,7 +30,7 @@ public struct UserSearchState: Equatable { ...@@ -51,7 +30,7 @@ public struct UserSearchState: Equatable {
@BindableState public var query: MessengerSearchUsers.Query @BindableState public var query: MessengerSearchUsers.Query
public var isSearching: Bool public var isSearching: Bool
public var failure: String? public var failure: String?
public var results: IdentifiedArrayOf<Result> public var results: IdentifiedArrayOf<UserSearchResultState>
} }
public enum UserSearchAction: Equatable, BindableAction { public enum UserSearchAction: Equatable, BindableAction {
...@@ -59,22 +38,26 @@ public enum UserSearchAction: Equatable, BindableAction { ...@@ -59,22 +38,26 @@ public enum UserSearchAction: Equatable, BindableAction {
case didFail(String) case didFail(String)
case didSucceed([Contact]) case didSucceed([Contact])
case binding(BindingAction<UserSearchState>) case binding(BindingAction<UserSearchState>)
case result(id: UserSearchResultState.ID, action: UserSearchResultAction)
} }
public struct UserSearchEnvironment { public struct UserSearchEnvironment {
public init( public init(
messenger: Messenger, messenger: Messenger,
mainQueue: AnySchedulerOf<DispatchQueue>, mainQueue: AnySchedulerOf<DispatchQueue>,
bgQueue: AnySchedulerOf<DispatchQueue> bgQueue: AnySchedulerOf<DispatchQueue>,
result: @escaping () -> UserSearchResultEnvironment
) { ) {
self.messenger = messenger self.messenger = messenger
self.mainQueue = mainQueue self.mainQueue = mainQueue
self.bgQueue = bgQueue self.bgQueue = bgQueue
self.result = result
} }
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
} }
#if DEBUG #if DEBUG
...@@ -82,7 +65,8 @@ extension UserSearchEnvironment { ...@@ -82,7 +65,8 @@ extension UserSearchEnvironment {
public static let unimplemented = UserSearchEnvironment( public static let unimplemented = UserSearchEnvironment(
messenger: .unimplemented, messenger: .unimplemented,
mainQueue: .unimplemented, mainQueue: .unimplemented,
bgQueue: .unimplemented bgQueue: .unimplemented,
result: { .unimplemented }
) )
} }
#endif #endif
...@@ -111,14 +95,7 @@ public let userSearchReducer = Reducer<UserSearchState, UserSearchAction, UserSe ...@@ -111,14 +95,7 @@ public let userSearchReducer = Reducer<UserSearchState, UserSearchAction, UserSe
state.failure = nil state.failure = nil
state.results = IdentifiedArray(uniqueElements: contacts.compactMap { contact in state.results = IdentifiedArray(uniqueElements: contacts.compactMap { contact in
guard let id = try? contact.getId() else { return nil } guard let id = try? contact.getId() else { return nil }
let facts = (try? contact.getFacts()) ?? [] return UserSearchResultState(id: id, contact: contact)
return UserSearchState.Result(
id: id,
contact: contact,
username: facts.first(where: { $0.type == 0 })?.fact,
email: facts.first(where: { $0.type == 1 })?.fact,
phone: facts.first(where: { $0.type == 2 })?.fact
)
}) })
return .none return .none
...@@ -128,8 +105,14 @@ public let userSearchReducer = Reducer<UserSearchState, UserSearchAction, UserSe ...@@ -128,8 +105,14 @@ public let userSearchReducer = Reducer<UserSearchState, UserSearchAction, UserSe
state.results = [] state.results = []
return .none return .none
case .binding(_): case .binding(_), .result(_, _):
return .none return .none
} }
} }
.binding() .binding()
.presenting(
forEach: userSearchResultReducer,
state: \.results,
action: /UserSearchAction.result(id:action:),
environment: { $0.result() }
)
...@@ -15,14 +15,12 @@ public struct UserSearchView: View { ...@@ -15,14 +15,12 @@ public struct UserSearchView: View {
var query: MessengerSearchUsers.Query var query: MessengerSearchUsers.Query
var isSearching: Bool var isSearching: Bool
var failure: String? var failure: String?
var results: IdentifiedArrayOf<UserSearchState.Result>
init(state: UserSearchState) { init(state: UserSearchState) {
focusedField = state.focusedField focusedField = state.focusedField
query = state.query query = state.query
isSearching = state.isSearching isSearching = state.isSearching
failure = state.failure failure = state.failure
results = state.results
} }
} }
...@@ -87,23 +85,13 @@ public struct UserSearchView: View { ...@@ -87,23 +85,13 @@ public struct UserSearchView: View {
} }
} }
ForEach(viewStore.results) { result in ForEachStore(
Section { store.scope(
if let username = result.username { state: \.results,
Text(username) action: UserSearchAction.result(id:action:)
} ),
if let email = result.email { content: UserSearchResultView.init(store:)
Text(email) )
}
if let phone = result.phone {
Text(phone)
}
if result.username == nil, result.email == nil, result.phone == nil {
Image(systemName: "questionmark")
.frame(maxWidth: .infinity)
}
}
}
} }
.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)) }
......
...@@ -19,23 +19,12 @@ final class UserSearchFeatureTests: XCTestCase { ...@@ -19,23 +19,12 @@ final class UserSearchFeatureTests: XCTestCase {
var contact1 = Contact.unimplemented("contact-1".data(using: .utf8)!) var contact1 = Contact.unimplemented("contact-1".data(using: .utf8)!)
contact1.getIdFromContact.run = { _ in "contact-1-id".data(using: .utf8)! } contact1.getIdFromContact.run = { _ in "contact-1-id".data(using: .utf8)! }
contact1.getFactsFromContact.run = { _ in
[Fact(fact: "contact-1-username", type: 0),
Fact(fact: "contact-1-email", type: 1),
Fact(fact: "contact-1-phone", type: 2)]
}
var contact2 = Contact.unimplemented("contact-1".data(using: .utf8)!) var contact2 = Contact.unimplemented("contact-1".data(using: .utf8)!)
contact2.getIdFromContact.run = { _ in "contact-2-id".data(using: .utf8)! } contact2.getIdFromContact.run = { _ in "contact-2-id".data(using: .utf8)! }
contact2.getFactsFromContact.run = { _ in
[Fact(fact: "contact-2-username", type: 0),
Fact(fact: "contact-2-email", type: 1),
Fact(fact: "contact-2-phone", type: 2)]
}
var contact3 = Contact.unimplemented("contact-3".data(using: .utf8)!) var contact3 = Contact.unimplemented("contact-3".data(using: .utf8)!)
contact3.getIdFromContact.run = { _ in throw GetIdFromContactError() } contact3.getIdFromContact.run = { _ in throw GetIdFromContactError() }
var contact4 = Contact.unimplemented("contact-4".data(using: .utf8)!) var contact4 = Contact.unimplemented("contact-4".data(using: .utf8)!)
contact4.getIdFromContact.run = { _ in "contact-4-id".data(using: .utf8)! } contact4.getIdFromContact.run = { _ in "contact-4-id".data(using: .utf8)! }
contact4.getFactsFromContact.run = { _ in throw GetFactsFromContactError() }
let contacts = [contact1, contact2, contact3, contact4] let contacts = [contact1, contact2, contact3, contact4]
store.environment.bgQueue = .immediate store.environment.bgQueue = .immediate
...@@ -64,27 +53,9 @@ final class UserSearchFeatureTests: XCTestCase { ...@@ -64,27 +53,9 @@ final class UserSearchFeatureTests: XCTestCase {
$0.isSearching = false $0.isSearching = false
$0.failure = nil $0.failure = nil
$0.results = [ $0.results = [
.init( .init(id: "contact-1-id".data(using: .utf8)!, contact: contact1),
id: "contact-1-id".data(using: .utf8)!, .init(id: "contact-2-id".data(using: .utf8)!, contact: contact2),
contact: contact1, .init(id: "contact-4-id".data(using: .utf8)!, contact: contact4)
username: "contact-1-username",
email: "contact-1-email",
phone: "contact-1-phone"
),
.init(
id: "contact-2-id".data(using: .utf8)!,
contact: contact2,
username: "contact-2-username",
email: "contact-2-email",
phone: "contact-2-phone"
),
.init(
id: "contact-4-id".data(using: .utf8)!,
contact: contact4,
username: nil,
email: nil,
phone: nil
)
] ]
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment