diff --git a/Examples/xx-messenger/Package.swift b/Examples/xx-messenger/Package.swift index 25e6d0830aa54934bece33d503fae5b31816b4d8..db3097ee99057d8c4c52ec1fdf6b9cd1daf430e7 100644 --- a/Examples/xx-messenger/Package.swift +++ b/Examples/xx-messenger/Package.swift @@ -117,6 +117,7 @@ let package = Package( name: "ContactsFeature", dependencies: [ .target(name: "AppCore"), + .target(name: "ContactFeature"), .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), .product(name: "ComposablePresentation", package: "swift-composable-presentation"), .product(name: "XXModels", package: "client-ios-db"), diff --git a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift index 8ac8875116ffbe811331520ac53b88b6f5470a96..f57003f0335827259ab1c96d66eead155b013f08 100644 --- a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift +++ b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift @@ -19,6 +19,21 @@ extension AppEnvironment { let mainQueue = DispatchQueue.main.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( dbManager: dbManager, messenger: messenger, @@ -53,7 +68,8 @@ extension AppEnvironment { ContactsEnvironment( db: dbManager.getDB, mainQueue: mainQueue, - bgQueue: bgQueue + bgQueue: bgQueue, + contact: { contactEnvironment } ) }, userSearch: { @@ -61,22 +77,7 @@ extension AppEnvironment { messenger: messenger, mainQueue: mainQueue, bgQueue: bgQueue, - contact: { - ContactEnvironment( - messenger: messenger, - db: dbManager.getDB, - mainQueue: mainQueue, - bgQueue: bgQueue, - sendRequest: { - SendRequestEnvironment( - messenger: messenger, - db: dbManager.getDB, - mainQueue: mainQueue, - bgQueue: bgQueue - ) - } - ) - } + contact: { contactEnvironment } ) } ) diff --git a/Examples/xx-messenger/Sources/ContactsFeature/ContactsFeature.swift b/Examples/xx-messenger/Sources/ContactsFeature/ContactsFeature.swift index c578059407050024bdc0d2fd06859c91639189e5..7969093471a590d9543b5fed7c1d90a6e5975b6a 100644 --- a/Examples/xx-messenger/Sources/ContactsFeature/ContactsFeature.swift +++ b/Examples/xx-messenger/Sources/ContactsFeature/ContactsFeature.swift @@ -1,38 +1,49 @@ import AppCore import ComposableArchitecture +import ComposablePresentation +import ContactFeature import Foundation import XCTestDynamicOverlay import XXModels public struct ContactsState: Equatable { public init( - contacts: IdentifiedArrayOf<Contact> = [] + contacts: IdentifiedArrayOf<Contact> = [], + contact: ContactState? = nil ) { self.contacts = contacts + self.contact = contact } public var contacts: IdentifiedArrayOf<XXModels.Contact> + public var contact: ContactState? } public enum ContactsAction: Equatable { case start case didFetchContacts([XXModels.Contact]) + case contactSelected(XXModels.Contact) + case contactDismissed + case contact(ContactAction) } public struct ContactsEnvironment { public init( db: DBManagerGetDB, mainQueue: AnySchedulerOf<DispatchQueue>, - bgQueue: AnySchedulerOf<DispatchQueue> + bgQueue: AnySchedulerOf<DispatchQueue>, + contact: @escaping () -> ContactEnvironment ) { self.db = db self.mainQueue = mainQueue self.bgQueue = bgQueue + self.contact = contact } public var db: DBManagerGetDB public var mainQueue: AnySchedulerOf<DispatchQueue> public var bgQueue: AnySchedulerOf<DispatchQueue> + public var contact: () -> ContactEnvironment } #if DEBUG @@ -40,7 +51,8 @@ extension ContactsEnvironment { public static let unimplemented = ContactsEnvironment( db: .unimplemented, mainQueue: .unimplemented, - bgQueue: .unimplemented + bgQueue: .unimplemented, + contact: { .unimplemented } ) } #endif @@ -61,5 +73,23 @@ public let contactsReducer = Reducer<ContactsState, ContactsAction, ContactsEnvi case .didFetchContacts(let contacts): state.contacts = IdentifiedArray(uniqueElements: contacts) 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() } +) diff --git a/Examples/xx-messenger/Sources/ContactsFeature/ContactsView.swift b/Examples/xx-messenger/Sources/ContactsFeature/ContactsView.swift index ad5d1a375b3315b83e4ae3510ccac2acfa6614ae..7b02efd1786f87905d93799aa6f58af350fca27d 100644 --- a/Examples/xx-messenger/Sources/ContactsFeature/ContactsView.swift +++ b/Examples/xx-messenger/Sources/ContactsFeature/ContactsView.swift @@ -1,5 +1,7 @@ import AppCore import ComposableArchitecture +import ComposablePresentation +import ContactFeature import SwiftUI import XXModels @@ -24,7 +26,7 @@ public struct ContactsView: View { ForEach(viewStore.contacts) { contact in Section { Button { - // TODO: + viewStore.send(.contactSelected(contact)) } label: { HStack { VStack(alignment: .leading, spacing: 8) { @@ -44,6 +46,14 @@ public struct ContactsView: View { } .navigationTitle("Contacts") .task { viewStore.send(.start) } + .background(NavigationLinkWithStore( + store.scope( + state: \.contact, + action: ContactsAction.contact + ), + onDeactivate: { viewStore.send(.contactDismissed) }, + destination: ContactView.init(store:) + )) } } } diff --git a/Examples/xx-messenger/Tests/ContactsFeatureTests/ContactsFeatureTests.swift b/Examples/xx-messenger/Tests/ContactsFeatureTests/ContactsFeatureTests.swift index 04533b34aaf32a062c01e7d93249fae0f2db662d..6c68f406deb4e350c129d5a1a33920c2ef0280a0 100644 --- a/Examples/xx-messenger/Tests/ContactsFeatureTests/ContactsFeatureTests.swift +++ b/Examples/xx-messenger/Tests/ContactsFeatureTests/ContactsFeatureTests.swift @@ -1,5 +1,6 @@ import Combine import ComposableArchitecture +import ContactFeature import CustomDump import XCTest import XXModels @@ -44,4 +45,35 @@ final class ContactsFeatureTests: XCTestCase { 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 + } + } }