diff --git a/Examples/xx-messenger/Package.swift b/Examples/xx-messenger/Package.swift index 1cab487ef6b858745e56cce7eda49a3e6c9d4a89..4dad2c2bf992cff93e37f2f6f7dcfd24356602c9 100644 --- a/Examples/xx-messenger/Package.swift +++ b/Examples/xx-messenger/Package.swift @@ -90,6 +90,7 @@ let package = Package( .target(name: "CheckContactAuthFeature"), .target(name: "ConfirmRequestFeature"), .target(name: "ContactFeature"), + .target(name: "ContactLookupFeature"), .target(name: "ContactsFeature"), .target(name: "HomeFeature"), .target(name: "MyContactFeature"), @@ -192,6 +193,7 @@ let package = Package( .target(name: "ChatFeature"), .target(name: "CheckContactAuthFeature"), .target(name: "ConfirmRequestFeature"), + .target(name: "ContactLookupFeature"), .target(name: "SendRequestFeature"), .target(name: "VerifyContactFeature"), .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), diff --git a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift index 3cf2cb0b065eda90aeff16cf1ce76f14297fd012..34d9f80675fd106a479ea9707c193ab810344222 100644 --- a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift +++ b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift @@ -37,6 +37,9 @@ extension AppEnvironment { db: dbManager.getDB, mainQueue: mainQueue, bgQueue: bgQueue, + lookup: { + ContactLookupEnvironment() + }, sendRequest: { SendRequestEnvironment( messenger: messenger, diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift index 993796e4bf4f286a29fdb7319fb39332074c68da..16c6a9a6cba3d1bf335070d44aab052af1b2b913 100644 --- a/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift +++ b/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift @@ -4,6 +4,7 @@ import CheckContactAuthFeature import ComposableArchitecture import ComposablePresentation import ConfirmRequestFeature +import ContactLookupFeature import Foundation import SendRequestFeature import VerifyContactFeature @@ -20,6 +21,7 @@ public struct ContactState: Equatable { importUsername: Bool = true, importEmail: Bool = true, importPhone: Bool = true, + lookup: ContactLookupState? = nil, sendRequest: SendRequestState? = nil, verifyContact: VerifyContactState? = nil, confirmRequest: ConfirmRequestState? = nil, @@ -32,6 +34,7 @@ public struct ContactState: Equatable { self.importUsername = importUsername self.importEmail = importEmail self.importPhone = importPhone + self.lookup = lookup self.sendRequest = sendRequest self.verifyContact = verifyContact self.confirmRequest = confirmRequest @@ -45,6 +48,7 @@ public struct ContactState: Equatable { @BindableState public var importUsername: Bool @BindableState public var importEmail: Bool @BindableState public var importPhone: Bool + public var lookup: ContactLookupState? public var sendRequest: SendRequestState? public var verifyContact: VerifyContactState? public var confirmRequest: ConfirmRequestState? @@ -56,6 +60,9 @@ public enum ContactAction: Equatable, BindableAction { case start case dbContactFetched(XXModels.Contact?) case importFactsTapped + case lookupTapped + case lookupDismissed + case lookup(ContactLookupAction) case sendRequestTapped case sendRequestDismissed case sendRequest(SendRequestAction) @@ -80,6 +87,7 @@ public struct ContactEnvironment { db: DBManagerGetDB, mainQueue: AnySchedulerOf<DispatchQueue>, bgQueue: AnySchedulerOf<DispatchQueue>, + lookup: @escaping () -> ContactLookupEnvironment, sendRequest: @escaping () -> SendRequestEnvironment, verifyContact: @escaping () -> VerifyContactEnvironment, confirmRequest: @escaping () -> ConfirmRequestEnvironment, @@ -90,6 +98,7 @@ public struct ContactEnvironment { self.db = db self.mainQueue = mainQueue self.bgQueue = bgQueue + self.lookup = lookup self.sendRequest = sendRequest self.verifyContact = verifyContact self.confirmRequest = confirmRequest @@ -101,6 +110,7 @@ public struct ContactEnvironment { public var db: DBManagerGetDB public var mainQueue: AnySchedulerOf<DispatchQueue> public var bgQueue: AnySchedulerOf<DispatchQueue> + public var lookup: () -> ContactLookupEnvironment public var sendRequest: () -> SendRequestEnvironment public var verifyContact: () -> VerifyContactEnvironment public var confirmRequest: () -> ConfirmRequestEnvironment @@ -115,6 +125,7 @@ extension ContactEnvironment { db: .unimplemented, mainQueue: .unimplemented, bgQueue: .unimplemented, + lookup: { .unimplemented }, sendRequest: { .unimplemented }, verifyContact: { .unimplemented }, confirmRequest: { .unimplemented }, @@ -163,6 +174,14 @@ public let contactReducer = Reducer<ContactState, ContactAction, ContactEnvironm .receive(on: env.mainQueue) .eraseToEffect() + case .lookupTapped: + state.lookup = ContactLookupState(id: state.id) + return .none + + case .lookupDismissed: + state.lookup = nil + return .none + case .sendRequestTapped: if let xxContact = state.xxContact { state.sendRequest = SendRequestState(contact: xxContact) @@ -223,11 +242,20 @@ public let contactReducer = Reducer<ContactState, ContactAction, ContactEnvironm state.chat = nil return .none - case .binding(_), .sendRequest(_), .verifyContact(_), .confirmRequest(_), .checkAuth(_), .chat(_): + case .binding(_), .lookup(_), .sendRequest(_), + .verifyContact(_), .confirmRequest(_), + .checkAuth(_), .chat(_): return .none } } .binding() +.presenting( + contactLookupReducer, + state: .keyPath(\.lookup), + id: .notNil(), + action: /ContactAction.lookup, + environment: { $0.lookup() } +) .presenting( sendRequestReducer, state: .keyPath(\.sendRequest), diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift index 2afc08f5bf4d74355c6f7558180f938237aaee94..5bfe475b44aca55fffbbcb787135788422dc1a87 100644 --- a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift +++ b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift @@ -4,6 +4,7 @@ import CheckContactAuthFeature import ComposableArchitecture import ComposablePresentation import ConfirmRequestFeature +import ContactLookupFeature import SendRequestFeature import SwiftUI import VerifyContactFeature @@ -26,6 +27,7 @@ public struct ContactView: View { var importUsername: Bool var importEmail: Bool var importPhone: Bool + var canLookup: Bool var canSendRequest: Bool var canVerifyContact: Bool var canConfirmRequest: Bool @@ -40,6 +42,7 @@ public struct ContactView: View { importUsername = state.importUsername importEmail = state.importEmail importPhone = state.importPhone + canLookup = state.dbContact?.id != nil canSendRequest = state.xxContact != nil || state.dbContact?.marshaled != nil canVerifyContact = state.dbContact?.marshaled != nil canConfirmRequest = state.dbContact?.marshaled != nil @@ -121,6 +124,17 @@ public struct ContactView: View { Section { ContactAuthStatusView(dbContact.authStatus) + Button { + viewStore.send(.lookupTapped) + } label: { + HStack { + Text("Lookup") + Spacer() + Image(systemName: "chevron.forward") + } + } + .disabled(!viewStore.canLookup) + Button { viewStore.send(.sendRequestTapped) } label: { @@ -186,6 +200,15 @@ public struct ContactView: View { } .navigationTitle("Contact") .task { viewStore.send(.start) } + .background(NavigationLinkWithStore( + store.scope( + state: \.lookup, + action: ContactAction.lookup + ), + mapState: replayNonNil(), + onDeactivate: { viewStore.send(.lookupDismissed) }, + destination: ContactLookupView.init(store:) + )) .background(NavigationLinkWithStore( store.scope( state: \.sendRequest, diff --git a/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift index feeccc98094da76d1f5ebd21122a4b3214afbcb7..a8f9f144eb3e2ff6efa99ca844039b7b9d74f619 100644 --- a/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift +++ b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift @@ -3,6 +3,7 @@ import CheckContactAuthFeature import Combine import ComposableArchitecture import ConfirmRequestFeature +import ContactLookupFeature import CustomDump import SendRequestFeature import VerifyContactFeature @@ -99,6 +100,37 @@ final class ContactFeatureTests: XCTestCase { XCTAssertNoDifference(dbDidSaveContact, [expectedSavedContact]) } + func testLookupTapped() { + let contactId = "contact-id".data(using: .utf8)! + let store = TestStore( + initialState: ContactState( + id: contactId + ), + reducer: contactReducer, + environment: .unimplemented + ) + + store.send(.lookupTapped) { + $0.lookup = ContactLookupState(id: contactId) + } + } + + func testLookupDismissed() { + let contactId = "contact-id".data(using: .utf8)! + let store = TestStore( + initialState: ContactState( + id: contactId, + lookup: ContactLookupState(id: contactId) + ), + reducer: contactReducer, + environment: .unimplemented + ) + + store.send(.lookupDismissed) { + $0.lookup = nil + } + } + func testSendRequestWithDBContact() { var dbContact = XXModels.Contact(id: "contact-id".data(using: .utf8)!) dbContact.marshaled = "contact-data".data(using: .utf8)!