diff --git a/Examples/xx-messenger/Package.swift b/Examples/xx-messenger/Package.swift index bbd8afa7cb6f4e32e903459986562397fed96c65..7a0357bdb8d200d2e7147fce3c90f15fc48aab35 100644 --- a/Examples/xx-messenger/Package.swift +++ b/Examples/xx-messenger/Package.swift @@ -164,7 +164,11 @@ let package = Package( .target( name: "SendRequestFeature", dependencies: [ + .target(name: "AppCore"), .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), + .product(name: "XXClient", package: "elixxir-dapps-sdk-swift"), + .product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"), + .product(name: "XXModels", package: "client-ios-db"), ], swiftSettings: swiftSettings ), diff --git a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift index a060ad4863d5eb979fe6ad26a9d1cb843d345a99..49ea8087585a70a1cbd61827b08b15de43682d1b 100644 --- a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift +++ b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift @@ -4,6 +4,7 @@ import Foundation import HomeFeature import RegisterFeature import RestoreFeature +import SendRequestFeature import UserSearchFeature import WelcomeFeature import XXMessengerClient diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift index 99b7b2bc00f3ef50e901268eaf3c7dd3370719ba..0bf0e29b51fa3a2e027b4dcdb6597d80a0b84076 100644 --- a/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift +++ b/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift @@ -104,7 +104,11 @@ public let contactReducer = Reducer<ContactState, ContactAction, ContactEnvironm .eraseToEffect() case .sendRequestTapped: - state.sendRequest = SendRequestState() + if let xxContact = state.xxContact { + state.sendRequest = SendRequestState(contact: xxContact) + } else if let marshaled = state.dbContact?.marshaled { + state.sendRequest = SendRequestState(contact: .live(marshaled)) + } return .none case .sendRequestDismissed: diff --git a/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestFeature.swift b/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestFeature.swift index a6683c294111334d1d207d1f64cef0fb413ad6e3..cefd80d3a9494835f5d6abaedec30f4d5953c189 100644 --- a/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestFeature.swift +++ b/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestFeature.swift @@ -1,12 +1,40 @@ import ComposableArchitecture import XCTestDynamicOverlay +import XXClient +import XXModels public struct SendRequestState: Equatable { - public init() {} + public init( + contact: XXClient.Contact, + myContact: XXClient.Contact? = nil, + sendUsername: Bool = true, + sendEmail: Bool = true, + sendPhone: Bool = true, + isSending: Bool = false, + failure: String? = nil + ) { + self.contact = contact + self.myContact = myContact + self.sendUsername = sendUsername + self.sendEmail = sendEmail + self.sendPhone = sendPhone + self.isSending = isSending + self.failure = failure + } + + public var contact: XXClient.Contact + public var myContact: XXClient.Contact? + @BindableState public var sendUsername: Bool + @BindableState public var sendEmail: Bool + @BindableState public var sendPhone: Bool + public var isSending: Bool + public var failure: String? } -public enum SendRequestAction: Equatable { +public enum SendRequestAction: Equatable, BindableAction { case start + case sendTapped + case binding(BindingAction<SendRequestState>) } public struct SendRequestEnvironment { @@ -24,5 +52,11 @@ public let sendRequestReducer = Reducer<SendRequestState, SendRequestAction, Sen switch action { case .start: return .none + + case .sendTapped: + return .none + + case .binding(_): + return .none } } diff --git a/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestView.swift b/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestView.swift index 4004246662915a47849468152b40dc1b25f1ca0d..f1bd8ff5ec39d173c6d8c0ab91a6877903a7ef48 100644 --- a/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestView.swift +++ b/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestView.swift @@ -1,5 +1,7 @@ +import AppCore import ComposableArchitecture import SwiftUI +import XXClient public struct SendRequestView: View { public init(store: Store<SendRequestState, SendRequestAction>) { @@ -9,13 +11,101 @@ public struct SendRequestView: View { let store: Store<SendRequestState, SendRequestAction> struct ViewState: Equatable { - init(state: SendRequestState) {} + var contact: XXClient.Contact + var myContact: XXClient.Contact? + var sendUsername: Bool + var sendEmail: Bool + var sendPhone: Bool + var isSending: Bool + var failure: String? + + init(state: SendRequestState) { + contact = state.contact + myContact = state.myContact + sendUsername = state.sendUsername + sendEmail = state.sendEmail + sendPhone = state.sendPhone + isSending = state.isSending + failure = state.failure + } } public var body: some View { WithViewStore(store.scope(state: ViewState.init)) { viewStore in Form { + Section { + HStack { + Label(viewStore.myContact?.username ?? "", systemImage: "person") + Spacer() + Toggle( + isOn: viewStore.binding( + get: \.sendUsername, + send: { SendRequestAction.set(\.$sendUsername, $0) } + ), + label: EmptyView.init + ) + } + + HStack { + Label(viewStore.myContact?.email ?? "", systemImage: "envelope") + Spacer() + Toggle( + isOn: viewStore.binding( + get: \.sendEmail, + send: { SendRequestAction.set(\.$sendEmail, $0) } + ), + label: EmptyView.init + ) + } + + HStack { + Label(viewStore.myContact?.phone ?? "", systemImage: "phone") + Spacer() + Toggle( + isOn: viewStore.binding( + get: \.sendPhone, + send: { SendRequestAction.set(\.$sendPhone, $0) } + ), + label: EmptyView.init + ) + } + } header: { + Text("My facts") + } + .disabled(viewStore.isSending) + Section { + Label(viewStore.contact.username ?? "", systemImage: "person") + Label(viewStore.contact.email ?? "", systemImage: "envelope") + Label(viewStore.contact.phone ?? "", systemImage: "phone") + } header: { + Text("Contact") + } + + Section { + Button { + viewStore.send(.sendTapped) + } label: { + HStack { + Text("Send request") + Spacer() + if viewStore.isSending { + ProgressView() + } else { + Image(systemName: "paperplane") + } + } + } + } + .disabled(viewStore.isSending) + + if let failure = viewStore.failure { + Section { + Text(failure) + } header: { + Text("Error") + } + } } .navigationTitle("Send Request") .task { viewStore.send(.start) } @@ -26,11 +116,38 @@ public struct SendRequestView: View { #if DEBUG public struct SendRequestView_Previews: PreviewProvider { public static var previews: some View { - SendRequestView(store: Store( - initialState: SendRequestState(), - reducer: .empty, - environment: () - )) + NavigationView { + SendRequestView(store: Store( + initialState: SendRequestState( + contact: { + var contact = XXClient.Contact.unimplemented("contact-data".data(using: .utf8)!) + contact.getFactsFromContact.run = { _ in + [ + Fact(fact: "contact-username", type: 0), + Fact(fact: "contact-email", type: 1), + Fact(fact: "contact-phone", type: 2), + ] + } + return contact + }(), + myContact: { + var contact = XXClient.Contact.unimplemented("my-data".data(using: .utf8)!) + contact.getFactsFromContact.run = { _ in + [ + Fact(fact: "my-username", type: 0), + Fact(fact: "my-email", type: 1), + Fact(fact: "my-phone", type: 2), + ] + } + return contact + }(), + isSending: true, + failure: "Something went wrong" + ), + reducer: .empty, + environment: () + )) + } } } #endif diff --git a/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift index 9f3238709229e25edb0c8d27512a66d778bc6234..e629b3f0b81f8d39a0f0633d57383853881762fc 100644 --- a/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift +++ b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift @@ -95,18 +95,52 @@ final class ContactFeatureTests: XCTestCase { XCTAssertNoDifference(dbDidSaveContact, [expectedSavedContact]) } - func testSendRequest() { + func testSendRequestWithDBContact() { + var dbContact = XXModels.Contact(id: "contact-id".data(using: .utf8)!) + dbContact.marshaled = "contact-data".data(using: .utf8)! + let store = TestStore( initialState: ContactState( - id: "contact-id".data(using: .utf8)! + id: dbContact.id, + dbContact: dbContact ), reducer: contactReducer, environment: .unimplemented ) store.send(.sendRequestTapped) { - $0.sendRequest = SendRequestState() + $0.sendRequest = SendRequestState(contact: .live(dbContact.marshaled!)) } + } + + func testSendRequestWithXXContact() { + let xxContact = XXClient.Contact.unimplemented("contact-id".data(using: .utf8)!) + + let store = TestStore( + initialState: ContactState( + id: "contact-id".data(using: .utf8)!, + xxContact: xxContact + ), + reducer: contactReducer, + environment: .unimplemented + ) + + store.send(.sendRequestTapped) { + $0.sendRequest = SendRequestState(contact: xxContact) + } + } + + func testSendRequestDismissed() { + let store = TestStore( + initialState: ContactState( + id: "contact-id".data(using: .utf8)!, + sendRequest: SendRequestState( + contact: .unimplemented("contact-id".data(using: .utf8)!) + ) + ), + reducer: contactReducer, + environment: .unimplemented + ) store.send(.sendRequestDismissed) { $0.sendRequest = nil diff --git a/Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestFeatureTests.swift b/Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestFeatureTests.swift index c64665d2408838577cdcf8f5ce5696e7f653940e..a67a241c4944a7ec3c116e463924444a80fe3855 100644 --- a/Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestFeatureTests.swift +++ b/Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestFeatureTests.swift @@ -5,7 +5,9 @@ import XCTest final class SendRequestFeatureTests: XCTestCase { func testStart() { let store = TestStore( - initialState: SendRequestState(), + initialState: SendRequestState( + contact: .unimplemented("contact-data".data(using: .utf8)!) + ), reducer: sendRequestReducer, environment: .unimplemented )