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

Present Contact from Contacts

parent 300a762a
No related branches found
No related tags found
2 merge requests!102Release 1.0.0,!75Messenger example - contacts list
...@@ -117,6 +117,7 @@ let package = Package( ...@@ -117,6 +117,7 @@ let package = Package(
name: "ContactsFeature", name: "ContactsFeature",
dependencies: [ dependencies: [
.target(name: "AppCore"), .target(name: "AppCore"),
.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: "XXModels", package: "client-ios-db"), .product(name: "XXModels", package: "client-ios-db"),
......
...@@ -19,6 +19,21 @@ extension AppEnvironment { ...@@ -19,6 +19,21 @@ extension AppEnvironment {
let mainQueue = DispatchQueue.main.eraseToAnyScheduler() let mainQueue = DispatchQueue.main.eraseToAnyScheduler()
let bgQueue = DispatchQueue.global(qos: .background).eraseToAnyScheduler() let bgQueue = DispatchQueue.global(qos: .background).eraseToAnyScheduler()
let contactEnvironment = ContactEnvironment(
messenger: messenger,
db: dbManager.getDB,
mainQueue: mainQueue,
bgQueue: bgQueue,
sendRequest: {
SendRequestEnvironment(
messenger: messenger,
db: dbManager.getDB,
mainQueue: mainQueue,
bgQueue: bgQueue
)
}
)
return AppEnvironment( return AppEnvironment(
dbManager: dbManager, dbManager: dbManager,
messenger: messenger, messenger: messenger,
...@@ -53,7 +68,8 @@ extension AppEnvironment { ...@@ -53,7 +68,8 @@ extension AppEnvironment {
ContactsEnvironment( ContactsEnvironment(
db: dbManager.getDB, db: dbManager.getDB,
mainQueue: mainQueue, mainQueue: mainQueue,
bgQueue: bgQueue bgQueue: bgQueue,
contact: { contactEnvironment }
) )
}, },
userSearch: { userSearch: {
...@@ -61,22 +77,7 @@ extension AppEnvironment { ...@@ -61,22 +77,7 @@ extension AppEnvironment {
messenger: messenger, messenger: messenger,
mainQueue: mainQueue, mainQueue: mainQueue,
bgQueue: bgQueue, bgQueue: bgQueue,
contact: { contact: { contactEnvironment }
ContactEnvironment(
messenger: messenger,
db: dbManager.getDB,
mainQueue: mainQueue,
bgQueue: bgQueue,
sendRequest: {
SendRequestEnvironment(
messenger: messenger,
db: dbManager.getDB,
mainQueue: mainQueue,
bgQueue: bgQueue
)
}
)
}
) )
} }
) )
......
import AppCore import AppCore
import ComposableArchitecture import ComposableArchitecture
import ComposablePresentation
import ContactFeature
import Foundation import Foundation
import XCTestDynamicOverlay import XCTestDynamicOverlay
import XXModels import XXModels
public struct ContactsState: Equatable { public struct ContactsState: Equatable {
public init( public init(
contacts: IdentifiedArrayOf<Contact> = [] contacts: IdentifiedArrayOf<Contact> = [],
contact: ContactState? = nil
) { ) {
self.contacts = contacts self.contacts = contacts
self.contact = contact
} }
public var contacts: IdentifiedArrayOf<XXModels.Contact> public var contacts: IdentifiedArrayOf<XXModels.Contact>
public var contact: ContactState?
} }
public enum ContactsAction: Equatable { public enum ContactsAction: Equatable {
case start case start
case didFetchContacts([XXModels.Contact]) case didFetchContacts([XXModels.Contact])
case contactSelected(XXModels.Contact)
case contactDismissed
case contact(ContactAction)
} }
public struct ContactsEnvironment { public struct ContactsEnvironment {
public init( public init(
db: DBManagerGetDB, db: DBManagerGetDB,
mainQueue: AnySchedulerOf<DispatchQueue>, mainQueue: AnySchedulerOf<DispatchQueue>,
bgQueue: AnySchedulerOf<DispatchQueue> bgQueue: AnySchedulerOf<DispatchQueue>,
contact: @escaping () -> ContactEnvironment
) { ) {
self.db = db self.db = db
self.mainQueue = mainQueue self.mainQueue = mainQueue
self.bgQueue = bgQueue self.bgQueue = bgQueue
self.contact = contact
} }
public var db: DBManagerGetDB public var db: DBManagerGetDB
public var mainQueue: AnySchedulerOf<DispatchQueue> public var mainQueue: AnySchedulerOf<DispatchQueue>
public var bgQueue: AnySchedulerOf<DispatchQueue> public var bgQueue: AnySchedulerOf<DispatchQueue>
public var contact: () -> ContactEnvironment
} }
#if DEBUG #if DEBUG
...@@ -40,7 +51,8 @@ extension ContactsEnvironment { ...@@ -40,7 +51,8 @@ extension ContactsEnvironment {
public static let unimplemented = ContactsEnvironment( public static let unimplemented = ContactsEnvironment(
db: .unimplemented, db: .unimplemented,
mainQueue: .unimplemented, mainQueue: .unimplemented,
bgQueue: .unimplemented bgQueue: .unimplemented,
contact: { .unimplemented }
) )
} }
#endif #endif
...@@ -61,5 +73,23 @@ public let contactsReducer = Reducer<ContactsState, ContactsAction, ContactsEnvi ...@@ -61,5 +73,23 @@ public let contactsReducer = Reducer<ContactsState, ContactsAction, ContactsEnvi
case .didFetchContacts(let contacts): case .didFetchContacts(let contacts):
state.contacts = IdentifiedArray(uniqueElements: contacts) state.contacts = IdentifiedArray(uniqueElements: contacts)
return .none return .none
case .contactSelected(let contact):
state.contact = ContactState(id: contact.id, dbContact: contact)
return .none
case .contactDismissed:
state.contact = nil
return .none
case .contact(_):
return .none
} }
} }
.presenting(
contactReducer,
state: .keyPath(\.contact),
id: .keyPath(\.?.id),
action: /ContactsAction.contact,
environment: { $0.contact() }
)
import AppCore import AppCore
import ComposableArchitecture import ComposableArchitecture
import ComposablePresentation
import ContactFeature
import SwiftUI import SwiftUI
import XXModels import XXModels
...@@ -24,7 +26,7 @@ public struct ContactsView: View { ...@@ -24,7 +26,7 @@ public struct ContactsView: View {
ForEach(viewStore.contacts) { contact in ForEach(viewStore.contacts) { contact in
Section { Section {
Button { Button {
// TODO: viewStore.send(.contactSelected(contact))
} label: { } label: {
HStack { HStack {
VStack(alignment: .leading, spacing: 8) { VStack(alignment: .leading, spacing: 8) {
...@@ -44,6 +46,14 @@ public struct ContactsView: View { ...@@ -44,6 +46,14 @@ public struct ContactsView: View {
} }
.navigationTitle("Contacts") .navigationTitle("Contacts")
.task { viewStore.send(.start) } .task { viewStore.send(.start) }
.background(NavigationLinkWithStore(
store.scope(
state: \.contact,
action: ContactsAction.contact
),
onDeactivate: { viewStore.send(.contactDismissed) },
destination: ContactView.init(store:)
))
} }
} }
} }
......
import Combine import Combine
import ComposableArchitecture import ComposableArchitecture
import ContactFeature
import CustomDump import CustomDump
import XCTest import XCTest
import XXModels import XXModels
...@@ -44,4 +45,35 @@ final class ContactsFeatureTests: XCTestCase { ...@@ -44,4 +45,35 @@ final class ContactsFeatureTests: XCTestCase {
contactsPublisher.send(completion: .finished) contactsPublisher.send(completion: .finished)
} }
func testSelectContact() {
let store = TestStore(
initialState: ContactsState(),
reducer: contactsReducer,
environment: .unimplemented
)
let contact = XXModels.Contact(id: "id".data(using: .utf8)!)
store.send(.contactSelected(contact)) {
$0.contact = ContactState(id: contact.id, dbContact: contact)
}
}
func testDismissContact() {
let store = TestStore(
initialState: ContactsState(
contact: ContactState(
id: "id".data(using: .utf8)!,
dbContact: Contact(id: "id".data(using: .utf8)!)
)
),
reducer: contactsReducer,
environment: .unimplemented
)
store.send(.contactDismissed) {
$0.contact = nil
}
}
} }
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