From c7e474b152deccc7fa08d3e3a0fef3071bbbe4ae Mon Sep 17 00:00:00 2001 From: Dariusz Rybicki <dariusz@elixxir.io> Date: Mon, 5 Sep 2022 10:15:43 +0200 Subject: [PATCH 1/5] Save user contact when registering --- Examples/xx-messenger/Package.swift | 2 + .../AppFeature/AppEnvironment+Live.swift | 2 + .../RegisterFeature/RegisterFeature.swift | 19 ++++++ .../RegisterFeatureTests.swift | 66 ++++++++++++++++++- 4 files changed, 87 insertions(+), 2 deletions(-) diff --git a/Examples/xx-messenger/Package.swift b/Examples/xx-messenger/Package.swift index f7e0d039..8f1e26f7 100644 --- a/Examples/xx-messenger/Package.swift +++ b/Examples/xx-messenger/Package.swift @@ -106,8 +106,10 @@ let package = Package( .target( name: "RegisterFeature", dependencies: [ + .target(name: "AppCore"), .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), .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 d3cc134a..3cbca1db 100644 --- a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift +++ b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift @@ -38,6 +38,8 @@ extension AppEnvironment { register: { RegisterEnvironment( messenger: messenger, + db: dbManager.getDB, + now: Date.init, mainQueue: mainQueue, bgQueue: bgQueue ) diff --git a/Examples/xx-messenger/Sources/RegisterFeature/RegisterFeature.swift b/Examples/xx-messenger/Sources/RegisterFeature/RegisterFeature.swift index f929d18f..a8d3280c 100644 --- a/Examples/xx-messenger/Sources/RegisterFeature/RegisterFeature.swift +++ b/Examples/xx-messenger/Sources/RegisterFeature/RegisterFeature.swift @@ -1,6 +1,9 @@ +import AppCore import ComposableArchitecture import SwiftUI +import XCTestDynamicOverlay import XXMessengerClient +import XXModels public struct RegisterState: Equatable { public enum Field: String, Hashable { @@ -33,15 +36,21 @@ public enum RegisterAction: Equatable, BindableAction { public struct RegisterEnvironment { public init( messenger: Messenger, + db: DBManagerGetDB, + now: @escaping () -> Date, mainQueue: AnySchedulerOf<DispatchQueue>, bgQueue: AnySchedulerOf<DispatchQueue> ) { self.messenger = messenger + self.db = db + self.now = now self.mainQueue = mainQueue self.bgQueue = bgQueue } public var messenger: Messenger + public var db: DBManagerGetDB + public var now: () -> Date public var mainQueue: AnySchedulerOf<DispatchQueue> public var bgQueue: AnySchedulerOf<DispatchQueue> } @@ -49,6 +58,8 @@ public struct RegisterEnvironment { extension RegisterEnvironment { public static let unimplemented = RegisterEnvironment( messenger: .unimplemented, + db: .unimplemented, + now: XCTUnimplemented("\(Self.self).now"), mainQueue: .unimplemented, bgQueue: .unimplemented ) @@ -66,7 +77,15 @@ public let registerReducer = Reducer<RegisterState, RegisterAction, RegisterEnvi state.failure = nil return .future { [username = state.username] fulfill in do { + let db = try env.db() try env.messenger.register(username: username) + let contact = env.messenger.e2e()!.getContact() + try db.saveContact(Contact( + id: try contact.getId(), + marshaled: contact.data, + username: username, + createdAt: env.now() + )) fulfill(.success(.finished)) } catch { diff --git a/Examples/xx-messenger/Tests/RegisterFeatureTests/RegisterFeatureTests.swift b/Examples/xx-messenger/Tests/RegisterFeatureTests/RegisterFeatureTests.swift index 21d4da16..a2aad5bd 100644 --- a/Examples/xx-messenger/Tests/RegisterFeatureTests/RegisterFeatureTests.swift +++ b/Examples/xx-messenger/Tests/RegisterFeatureTests/RegisterFeatureTests.swift @@ -1,8 +1,10 @@ import ComposableArchitecture import XCTest +import XXClient +import XXMessengerClient +import XXModels @testable import RegisterFeature -@MainActor final class RegisterFeatureTests: XCTestCase { func testRegister() throws { let store = TestStore( @@ -11,15 +13,35 @@ final class RegisterFeatureTests: XCTestCase { environment: .unimplemented ) + let now = Date() let mainQueue = DispatchQueue.test let bgQueue = DispatchQueue.test + var dbDidSaveContact: [XXModels.Contact] = [] var messengerDidRegisterUsername: [String] = [] + store.environment.now = { now } store.environment.mainQueue = mainQueue.eraseToAnyScheduler() store.environment.bgQueue = bgQueue.eraseToAnyScheduler() store.environment.messenger.register.run = { username in messengerDidRegisterUsername.append(username) } + store.environment.messenger.e2e.get = { + var e2e: E2E = .unimplemented + e2e.getContact.run = { + var contact = XXClient.Contact.unimplemented("contact-data".data(using: .utf8)!) + contact.getIdFromContact.run = { _ in "contact-id".data(using: .utf8)! } + return contact + } + return e2e + } + store.environment.db.run = { + var db: Database = .failing + db.saveContact.run = { contact in + dbDidSaveContact.append(contact) + return contact + } + return db + } store.send(.set(\.$username, "NewUser")) { $0.username = "NewUser" @@ -30,17 +52,56 @@ final class RegisterFeatureTests: XCTestCase { } XCTAssertNoDifference(messengerDidRegisterUsername, []) + XCTAssertNoDifference(dbDidSaveContact, []) bgQueue.advance() XCTAssertNoDifference(messengerDidRegisterUsername, ["NewUser"]) + XCTAssertNoDifference(dbDidSaveContact, [ + XXModels.Contact( + id: "contact-id".data(using: .utf8)!, + marshaled: "contact-data".data(using: .utf8)!, + username: "NewUser", + createdAt: now + ) + ]) mainQueue.advance() store.receive(.finished) } - func testRegisterFailure() throws { + func testGetDbFailure() throws { + struct Error: Swift.Error, Equatable {} + let error = Error() + + let store = TestStore( + initialState: RegisterState(), + reducer: registerReducer, + environment: .unimplemented + ) + + let mainQueue = DispatchQueue.test + let bgQueue = DispatchQueue.test + + store.environment.mainQueue = mainQueue.eraseToAnyScheduler() + store.environment.bgQueue = bgQueue.eraseToAnyScheduler() + store.environment.db.run = { throw error } + + store.send(.registerTapped) { + $0.isRegistering = true + } + + bgQueue.advance() + mainQueue.advance() + + store.receive(.failed(error.localizedDescription)) { + $0.isRegistering = false + $0.failure = error.localizedDescription + } + } + + func testMessengerRegisterFailure() throws { struct Error: Swift.Error, Equatable {} let error = Error() @@ -55,6 +116,7 @@ final class RegisterFeatureTests: XCTestCase { store.environment.mainQueue = mainQueue.eraseToAnyScheduler() store.environment.bgQueue = bgQueue.eraseToAnyScheduler() + store.environment.db.run = { .failing } store.environment.messenger.register.run = { _ in throw error } store.send(.registerTapped) { -- GitLab From 4a38e1cd5aef3edff0c76177b8ef859759d42f75 Mon Sep 17 00:00:00 2001 From: Dariusz Rybicki <dariusz@elixxir.io> Date: Mon, 5 Sep 2022 10:18:33 +0200 Subject: [PATCH 2/5] Remove username from HomeFeature --- .../Sources/HomeFeature/HomeFeature.swift | 9 ------ .../Sources/HomeFeature/HomeView.swift | 10 ------- .../HomeFeatureTests/HomeFeatureTests.swift | 28 ------------------- 3 files changed, 47 deletions(-) diff --git a/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift b/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift index 29abfb6b..5c20364d 100644 --- a/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift +++ b/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift @@ -8,16 +8,13 @@ import XXMessengerClient public struct HomeState: Equatable { public init( - username: String? = nil, failure: String? = nil, register: RegisterState? = nil ) { - self.username = username self.failure = failure self.register = register } - @BindableState public var username: String? @BindableState public var failure: String? @BindableState public var register: RegisterState? } @@ -76,12 +73,6 @@ public let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment> } try env.messenger.logIn() } - - if let contact = env.messenger.e2e()?.getContact(), - let facts = try? contact.getFacts(), - let username = facts.first(where: { $0.type == 0 })?.fact { - subscriber.send(.set(\.$username, username)) - } } catch { subscriber.send(.set(\.$failure, error.localizedDescription)) } diff --git a/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift b/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift index e40bb1db..7dd8f668 100644 --- a/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift +++ b/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift @@ -11,11 +11,9 @@ public struct HomeView: View { let store: Store<HomeState, HomeAction> struct ViewState: Equatable { - var username: String? var failure: String? init(state: HomeState) { - username = state.username failure = state.failure } } @@ -24,14 +22,6 @@ public struct HomeView: View { WithViewStore(store.scope(state: ViewState.init)) { viewStore in NavigationView { Form { - if let username = viewStore.username { - Section { - Text(username) - } header: { - Text("Username") - } - } - if let failure = viewStore.failure { Section { Text(failure) diff --git a/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift b/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift index efdd867f..ddb6b035 100644 --- a/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift +++ b/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift @@ -47,7 +47,6 @@ final class HomeFeatureTests: XCTestCase { environment: .unimplemented ) - let username = "test_username" let bgQueue = DispatchQueue.test let mainQueue = DispatchQueue.test var messengerDidStartWithTimeout: [Int] = [] @@ -62,15 +61,6 @@ final class HomeFeatureTests: XCTestCase { store.environment.messenger.isLoggedIn.run = { false } store.environment.messenger.isRegistered.run = { true } store.environment.messenger.logIn.run = { messengerDidLogIn += 1 } - store.environment.messenger.e2e.get = { - var e2e = E2E.unimplemented - e2e.getContact.run = { - var contact = Contact.unimplemented(Data()) - contact.getFactsFromContact.run = { _ in [Fact(fact: username, type: 0)] } - return contact - } - return e2e - } store.send(.start) @@ -81,10 +71,6 @@ final class HomeFeatureTests: XCTestCase { XCTAssertNoDifference(messengerDidLogIn, 1) mainQueue.advance() - - store.receive(.set(\.$username, username)) { - $0.username = username - } } func testRegisterFinished() { @@ -96,7 +82,6 @@ final class HomeFeatureTests: XCTestCase { environment: .unimplemented ) - let username = "test_username" let bgQueue = DispatchQueue.test let mainQueue = DispatchQueue.test var messengerDidStartWithTimeout: [Int] = [] @@ -109,15 +94,6 @@ final class HomeFeatureTests: XCTestCase { store.environment.messenger.isLoggedIn.run = { false } store.environment.messenger.isRegistered.run = { true } store.environment.messenger.logIn.run = { messengerDidLogIn += 1 } - store.environment.messenger.e2e.get = { - var e2e = E2E.unimplemented - e2e.getContact.run = { - var contact = Contact.unimplemented(Data()) - contact.getFactsFromContact.run = { _ in [Fact(fact: username, type: 0)] } - return contact - } - return e2e - } store.send(.register(.finished)) { $0.register = nil @@ -131,10 +107,6 @@ final class HomeFeatureTests: XCTestCase { XCTAssertNoDifference(messengerDidLogIn, 1) mainQueue.advance() - - store.receive(.set(\.$username, username)) { - $0.username = username - } } func testStartMessengerStartFailure() { -- GitLab From 747e6c6069c373b90d76df10ad642a97724510d2 Mon Sep 17 00:00:00 2001 From: Dariusz Rybicki <dariusz@elixxir.io> Date: Mon, 5 Sep 2022 11:00:24 +0200 Subject: [PATCH 3/5] Add Stored.tryGet extension --- Sources/XXMessengerClient/Utils/Stored.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Sources/XXMessengerClient/Utils/Stored.swift b/Sources/XXMessengerClient/Utils/Stored.swift index 3f5d5fdb..052d6387 100644 --- a/Sources/XXMessengerClient/Utils/Stored.swift +++ b/Sources/XXMessengerClient/Utils/Stored.swift @@ -31,6 +31,23 @@ private final class Memory<Value> { var value: Value } +extension Stored { + public struct MissingValueError: Error, Equatable { + public init(typeDescription: String) { + self.typeDescription = typeDescription + } + + public var typeDescription: String + } + + public func tryGet<T>() throws -> T where Value == Optional<T> { + guard let value = get() else { + throw MissingValueError(typeDescription: "\(Self.self)") + } + return value + } +} + extension Stored { public static func unimplemented(placeholder: Value) -> Stored<Value> { Stored<Value>( -- GitLab From a020aae2f3cc8e55ff20de85552e1d4c30cdc30c Mon Sep 17 00:00:00 2001 From: Dariusz Rybicki <dariusz@elixxir.io> Date: Mon, 5 Sep 2022 11:18:41 +0200 Subject: [PATCH 4/5] Implement messenger account deletion example --- .../AppFeature/AppEnvironment+Live.swift | 1 + .../Sources/AppFeature/AppFeature.swift | 2 +- .../Sources/HomeFeature/Alerts.swift | 22 ++++ .../Sources/HomeFeature/HomeFeature.swift | 48 +++++++- .../Sources/HomeFeature/HomeView.swift | 23 ++++ .../AppFeatureTests/AppFeatureTests.swift | 30 +++++ .../HomeFeatureTests/HomeFeatureTests.swift | 109 ++++++++++++++++++ 7 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 Examples/xx-messenger/Sources/HomeFeature/Alerts.swift diff --git a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift index 3cbca1db..19f1b7f5 100644 --- a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift +++ b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift @@ -33,6 +33,7 @@ extension AppEnvironment { home: { HomeEnvironment( messenger: messenger, + db: dbManager.getDB, mainQueue: mainQueue, bgQueue: bgQueue, register: { diff --git a/Examples/xx-messenger/Sources/AppFeature/AppFeature.swift b/Examples/xx-messenger/Sources/AppFeature/AppFeature.swift index 535d69fc..582478d4 100644 --- a/Examples/xx-messenger/Sources/AppFeature/AppFeature.swift +++ b/Examples/xx-messenger/Sources/AppFeature/AppFeature.swift @@ -68,7 +68,7 @@ extension AppEnvironment { let appReducer = Reducer<AppState, AppAction, AppEnvironment> { state, action, env in switch action { - case .start, .welcome(.finished), .restore(.finished): + case .start, .welcome(.finished), .restore(.finished), .home(.didDeleteAccount): state.screen = .loading return .run { subscriber in do { diff --git a/Examples/xx-messenger/Sources/HomeFeature/Alerts.swift b/Examples/xx-messenger/Sources/HomeFeature/Alerts.swift new file mode 100644 index 00000000..a3347be4 --- /dev/null +++ b/Examples/xx-messenger/Sources/HomeFeature/Alerts.swift @@ -0,0 +1,22 @@ +import ComposableArchitecture + +extension AlertState { + public static func confirmAccountDeletion() -> AlertState<HomeAction> { + AlertState<HomeAction>( + title: TextState("Delete Account"), + message: TextState("This will permanently delete your account and can't be undone."), + buttons: [ + .destructive(TextState("Delete"), action: .send(.deleteAccountConfirmed)), + .cancel(TextState("Cancel")) + ] + ) + } + + public static func accountDeletionFailed(_ error: Error) -> AlertState<HomeAction> { + AlertState<HomeAction>( + title: TextState("Error"), + message: TextState(error.localizedDescription), + buttons: [] + ) + } +} diff --git a/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift b/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift index 5c20364d..279a5628 100644 --- a/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift +++ b/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift @@ -1,3 +1,4 @@ +import AppCore import Combine import ComposableArchitecture import ComposablePresentation @@ -9,18 +10,27 @@ import XXMessengerClient public struct HomeState: Equatable { public init( failure: String? = nil, - register: RegisterState? = nil + register: RegisterState? = nil, + alert: AlertState<HomeAction>? = nil, + isDeletingAccount: Bool = false ) { self.failure = failure self.register = register + self.alert = alert + self.isDeletingAccount = isDeletingAccount } @BindableState public var failure: String? @BindableState public var register: RegisterState? + @BindableState public var alert: AlertState<HomeAction>? + @BindableState public var isDeletingAccount: Bool } public enum HomeAction: Equatable, BindableAction { case start + case deleteAccountButtonTapped + case deleteAccountConfirmed + case didDeleteAccount case binding(BindingAction<HomeState>) case register(RegisterAction) } @@ -28,17 +38,20 @@ public enum HomeAction: Equatable, BindableAction { public struct HomeEnvironment { public init( messenger: Messenger, + db: DBManagerGetDB, mainQueue: AnySchedulerOf<DispatchQueue>, bgQueue: AnySchedulerOf<DispatchQueue>, register: @escaping () -> RegisterEnvironment ) { self.messenger = messenger + self.db = db self.mainQueue = mainQueue self.bgQueue = bgQueue self.register = register } public var messenger: Messenger + public var db: DBManagerGetDB public var mainQueue: AnySchedulerOf<DispatchQueue> public var bgQueue: AnySchedulerOf<DispatchQueue> public var register: () -> RegisterEnvironment @@ -47,6 +60,7 @@ public struct HomeEnvironment { extension HomeEnvironment { public static let unimplemented = HomeEnvironment( messenger: .unimplemented, + db: .unimplemented, mainQueue: .unimplemented, bgQueue: .unimplemented, register: { .unimplemented } @@ -83,6 +97,38 @@ public let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment> .receive(on: env.mainQueue) .eraseToEffect() + case .deleteAccountButtonTapped: + state.alert = .confirmAccountDeletion() + return .none + + case .deleteAccountConfirmed: + state.isDeletingAccount = true + return .run { subscriber in + do { + let contactId = try env.messenger.e2e.tryGet().getContact().getId() + let contact = try env.db().fetchContacts(.init(id: [contactId])).first + if let username = contact?.username { + let ud = try env.messenger.ud.tryGet() + try ud.permanentDeleteAccount(username: Fact(fact: username, type: 0)) + } + try env.messenger.destroy() + try env.db().drop() + subscriber.send(.didDeleteAccount) + } catch { + subscriber.send(.set(\.$isDeletingAccount, false)) + subscriber.send(.set(\.$alert, .accountDeletionFailed(error))) + } + subscriber.send(completion: .finished) + return AnyCancellable {} + } + .subscribe(on: env.bgQueue) + .receive(on: env.mainQueue) + .eraseToEffect() + + case .didDeleteAccount: + state.isDeletingAccount = false + return .none + case .register(.finished): state.register = nil return Effect(value: .start) diff --git a/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift b/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift index 7dd8f668..f73f6b8a 100644 --- a/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift +++ b/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift @@ -12,9 +12,11 @@ public struct HomeView: View { struct ViewState: Equatable { var failure: String? + var isDeletingAccount: Bool init(state: HomeState) { failure = state.failure + isDeletingAccount = state.isDeletingAccount } } @@ -34,8 +36,29 @@ public struct HomeView: View { Text("Error") } } + + Section { + Button(role: .destructive) { + viewStore.send(.deleteAccountButtonTapped) + } label: { + HStack { + Text("Delete Account") + Spacer() + if viewStore.isDeletingAccount { + ProgressView() + } + } + } + .disabled(viewStore.isDeletingAccount) + } header: { + Text("Account") + } } .navigationTitle("Home") + .alert( + store.scope(state: \.alert), + dismiss: HomeAction.set(\.$alert, nil) + ) } .navigationViewStyle(.stack) .task { viewStore.send(.start) } diff --git a/Examples/xx-messenger/Tests/AppFeatureTests/AppFeatureTests.swift b/Examples/xx-messenger/Tests/AppFeatureTests/AppFeatureTests.swift index 5f055492..5ea96813 100644 --- a/Examples/xx-messenger/Tests/AppFeatureTests/AppFeatureTests.swift +++ b/Examples/xx-messenger/Tests/AppFeatureTests/AppFeatureTests.swift @@ -141,6 +141,36 @@ final class AppFeatureTests: XCTestCase { } } + func testHomeDidDeleteAccount() { + let store = TestStore( + initialState: AppState( + screen: .home(HomeState()) + ), + reducer: appReducer, + environment: .unimplemented + ) + + let mainQueue = DispatchQueue.test + let bgQueue = DispatchQueue.test + + store.environment.mainQueue = mainQueue.eraseToAnyScheduler() + store.environment.bgQueue = bgQueue.eraseToAnyScheduler() + store.environment.dbManager.hasDB.run = { true } + store.environment.messenger.isLoaded.run = { false } + store.environment.messenger.isCreated.run = { false } + + store.send(.home(.didDeleteAccount)) { + $0.screen = .loading + } + + bgQueue.advance() + mainQueue.advance() + + store.receive(.set(\.$screen, .welcome(WelcomeState()))) { + $0.screen = .welcome(WelcomeState()) + } + } + func testWelcomeRestoreTapped() { let store = TestStore( initialState: AppState( diff --git a/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift b/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift index ddb6b035..4fbad5b8 100644 --- a/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift +++ b/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift @@ -3,6 +3,7 @@ import RegisterFeature import XCTest import XXClient import XXMessengerClient +import XXModels @testable import HomeFeature final class HomeFeatureTests: XCTestCase { @@ -201,4 +202,112 @@ final class HomeFeatureTests: XCTestCase { $0.failure = error.localizedDescription } } + + func testAccountDeletion() { + let store = TestStore( + initialState: HomeState(), + reducer: homeReducer, + environment: .unimplemented + ) + + var dbDidFetchContacts: [XXModels.Contact.Query] = [] + var udDidPermanentDeleteAccount: [Fact] = [] + var messengerDidDestroy = 0 + var dbDidDrop = 0 + + store.environment.bgQueue = .immediate + store.environment.mainQueue = .immediate + store.environment.messenger.e2e.get = { + var e2e: E2E = .unimplemented + e2e.getContact.run = { + var contact = Contact.unimplemented("contact-data".data(using: .utf8)!) + contact.getIdFromContact.run = { _ in "contact-id".data(using: .utf8)! } + return contact + } + return e2e + } + store.environment.db.run = { + var db: Database = .failing + db.fetchContacts.run = { query in + dbDidFetchContacts.append(query) + return [ + XXModels.Contact( + id: "contact-id".data(using: .utf8)!, + marshaled: "contact-data".data(using: .utf8)!, + username: "MyUsername" + ) + ] + } + db.drop.run = { + dbDidDrop += 1 + } + return db + } + store.environment.messenger.ud.get = { + var ud: UserDiscovery = .unimplemented + ud.permanentDeleteAccount.run = { usernameFact in + udDidPermanentDeleteAccount.append(usernameFact) + } + return ud + } + store.environment.messenger.destroy.run = { + messengerDidDestroy += 1 + } + + store.send(.deleteAccountButtonTapped) { + $0.alert = .confirmAccountDeletion() + } + + store.send(.set(\.$alert, nil)) { + $0.alert = nil + } + + store.send(.deleteAccountConfirmed) { + $0.isDeletingAccount = true + } + + XCTAssertNoDifference(dbDidFetchContacts, [.init(id: ["contact-id".data(using: .utf8)!])]) + XCTAssertNoDifference(udDidPermanentDeleteAccount, [Fact(fact: "MyUsername", type: 0)]) + XCTAssertNoDifference(messengerDidDestroy, 1) + XCTAssertNoDifference(dbDidDrop, 1) + + store.receive(.didDeleteAccount) { + $0.isDeletingAccount = false + } + } + + func testAccountDeletionFailure() { + let store = TestStore( + initialState: HomeState(), + reducer: homeReducer, + environment: .unimplemented + ) + + struct Failure: Error {} + let error = Failure() + + store.environment.bgQueue = .immediate + store.environment.mainQueue = .immediate + store.environment.messenger.e2e.get = { + var e2e: E2E = .unimplemented + e2e.getContact.run = { + var contact = Contact.unimplemented("contact-data".data(using: .utf8)!) + contact.getIdFromContact.run = { _ in throw error } + return contact + } + return e2e + } + + store.send(.deleteAccountConfirmed) { + $0.isDeletingAccount = true + } + + store.receive(.set(\.$isDeletingAccount, false)) { + $0.isDeletingAccount = false + } + + store.receive(.set(\.$alert, .accountDeletionFailed(error))) { + $0.alert = .accountDeletionFailed(error) + } + } } -- GitLab From a5c9f161fb92c062a99d11895ba885e74a8405bc Mon Sep 17 00:00:00 2001 From: Dariusz Rybicki <dariusz@elixxir.io> Date: Mon, 5 Sep 2022 11:22:30 +0200 Subject: [PATCH 5/5] Update buttons appearance --- .../Sources/RegisterFeature/RegisterView.swift | 8 +++----- .../Sources/WelcomeFeature/WelcomeView.swift | 9 +++------ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Examples/xx-messenger/Sources/RegisterFeature/RegisterView.swift b/Examples/xx-messenger/Sources/RegisterFeature/RegisterView.swift index 27bc0962..195b655b 100644 --- a/Examples/xx-messenger/Sources/RegisterFeature/RegisterView.swift +++ b/Examples/xx-messenger/Sources/RegisterFeature/RegisterView.swift @@ -46,14 +46,12 @@ public struct RegisterView: View { viewStore.send(.registerTapped) } label: { HStack { + Text("Register") + Spacer() if viewStore.isRegistering { - ProgressView().padding(.trailing) - Text("Registering...") - } else { - Text("Register") + ProgressView() } } - .frame(maxWidth: .infinity) } } diff --git a/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeView.swift b/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeView.swift index d3686c5a..1205c67e 100644 --- a/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeView.swift +++ b/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeView.swift @@ -29,21 +29,18 @@ public struct WelcomeView: View { viewStore.send(.newAccountTapped) } label: { HStack { + Text("New Account") + Spacer() if viewStore.isCreatingAccount { - ProgressView().padding(.trailing) - Text("Creating Account...") - } else { - Text("New Account") + ProgressView() } } - .frame(maxWidth: .infinity) } Button { viewStore.send(.restoreTapped) } label: { Text("Restore from Backup") - .frame(maxWidth: .infinity) } } } -- GitLab