From 24c21d49650eae08b9d3ebe61cc97572449a5044 Mon Sep 17 00:00:00 2001 From: Daniel Haight Date: Sun, 21 Nov 2021 14:28:08 +0000 Subject: [PATCH 1/4] Update `Binding` Helpers - Rename `Binding.map` to `Binding.compactMap` - Add `Binding.map` with an updated signature These should now match the naming conventions of similar methods on types. --- secant/Util/Bindings.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/secant/Util/Bindings.swift b/secant/Util/Bindings.swift index a72b88b..847ae24 100644 --- a/secant/Util/Bindings.swift +++ b/secant/Util/Bindings.swift @@ -74,7 +74,14 @@ extension Binding { ) } - func map(extract: @escaping (Value) -> T, embed: @escaping (T) -> Value?) -> Binding { + func map(extract: @escaping (Value) -> T, embed: @escaping (T) -> Value) -> Binding { + Binding( + get: { extract(wrappedValue) }, + set: { wrappedValue = embed($0) } + ) + } + + func compactMap(extract: @escaping (Value) -> T, embed: @escaping (T) -> Value?) -> Binding { Binding( get: { extract(wrappedValue) }, set: { From 85395bb9f022378b7e3bbc2627dd570c27c2768f Mon Sep 17 00:00:00 2001 From: Daniel Haight Date: Sun, 21 Nov 2021 14:28:10 +0000 Subject: [PATCH 2/4] Add `Send` "Placeholder" Feature This adds screens to represent a basic "Send" Feature. The main purpose of this is two-fold. 1. To act as a placeholder for the actual journet 2. To demonstrate a slightly different navigation paradigm, i.e, a `NavigationView` with >1 depth. --- secant.xcodeproj/project.pbxproj | 32 +++++++ secant/Features/Home/HomeStore.swift | 9 ++ secant/Features/Home/Views/HomeView.swift | 22 +++++ secant/Features/Send/SendStore.swift | 57 +++++++++++++ secant/Features/Send/Views/ApproveView.swift | 63 ++++++++++++++ secant/Features/Send/Views/CreateView.swift | 89 ++++++++++++++++++++ secant/Features/Send/Views/SentView.swift | 37 ++++++++ secant/SecantApp.swift | 1 + 8 files changed, 310 insertions(+) create mode 100644 secant/Features/Send/SendStore.swift create mode 100644 secant/Features/Send/Views/ApproveView.swift create mode 100644 secant/Features/Send/Views/CreateView.swift create mode 100644 secant/Features/Send/Views/SentView.swift diff --git a/secant.xcodeproj/project.pbxproj b/secant.xcodeproj/project.pbxproj index 494fe1b..12bf7b5 100644 --- a/secant.xcodeproj/project.pbxproj +++ b/secant.xcodeproj/project.pbxproj @@ -98,6 +98,10 @@ F96B41E9273B501F0021B49A /* TransactionHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E6273B501F0021B49A /* TransactionHistoryView.swift */; }; F96B41EB273B50520021B49A /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41EA273B50520021B49A /* Strings.swift */; }; F9C165B4274031F600592F76 /* Bindings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165B3274031F600592F76 /* Bindings.swift */; }; + F9C165BF2740403600592F76 /* SendStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165B72740403600592F76 /* SendStore.swift */; }; + F9C165C02740403600592F76 /* ApproveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165B92740403600592F76 /* ApproveView.swift */; }; + F9C165C22740403600592F76 /* CreateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165BB2740403600592F76 /* CreateView.swift */; }; + F9C165C42740403600592F76 /* SentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165BD2740403600592F76 /* SentView.swift */; }; F9EEB8162742C2210032EEB8 /* WithStateBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9EEB8152742C2210032EEB8 /* WithStateBinding.swift */; }; /* End PBXBuildFile section */ @@ -215,6 +219,10 @@ F96B41E6273B501F0021B49A /* TransactionHistoryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionHistoryView.swift; sourceTree = ""; }; F96B41EA273B50520021B49A /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = ""; }; F9C165B3274031F600592F76 /* Bindings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bindings.swift; sourceTree = ""; }; + F9C165B72740403600592F76 /* SendStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendStore.swift; sourceTree = ""; }; + F9C165B92740403600592F76 /* ApproveView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApproveView.swift; sourceTree = ""; }; + F9C165BB2740403600592F76 /* CreateView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateView.swift; sourceTree = ""; }; + F9C165BD2740403600592F76 /* SentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SentView.swift; sourceTree = ""; }; F9EEB8152742C2210032EEB8 /* WithStateBinding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithStateBinding.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -607,6 +615,7 @@ isa = PBXGroup; children = ( F93874EC273C4DE200F0E875 /* Home */, + F9C165B62740403600592F76 /* Send */, F96B41E2273B501F0021B49A /* TransactionHistory */, 6654C73C2715A3FA00901167 /* Onboarding */, ); @@ -699,6 +708,25 @@ path = Views; sourceTree = ""; }; + F9C165B62740403600592F76 /* Send */ = { + isa = PBXGroup; + children = ( + F9C165B72740403600592F76 /* SendStore.swift */, + F9C165B82740403600592F76 /* Views */, + ); + path = Send; + sourceTree = ""; + }; + F9C165B82740403600592F76 /* Views */ = { + isa = PBXGroup; + children = ( + F9C165BB2740403600592F76 /* CreateView.swift */, + F9C165B92740403600592F76 /* ApproveView.swift */, + F9C165BD2740403600592F76 /* SentView.swift */, + ); + path = Views; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -939,12 +967,14 @@ 0DA13C9726C186FF00E3B610 /* RestoreWalletScreen.swift in Sources */, 0DACFA8127208D940039EEA5 /* UInt+SuperscriptText.swift in Sources */, 0DF2DC51272344E400FA31E2 /* EmptyChip.swift in Sources */, + F9C165BF2740403600592F76 /* SendStore.swift in Sources */, 0D1922EA26BDD96A00052649 /* ViewModel.swift in Sources */, 0D4E7A0926B364170058B01E /* SecantApp.swift in Sources */, 66DC733F271D88CC0053CBB6 /* StandardButtonStyle.swift in Sources */, 0D864A0926E154FD00A61879 /* InitFailedScreen.swift in Sources */, 663FABA0271D876200E495F8 /* PrimaryButton.swift in Sources */, 663FAB9C271D874D00E495F8 /* ActiveButton.swift in Sources */, + F9C165C02740403600592F76 /* ApproveView.swift in Sources */, 0DF2DC5427235E3E00FA31E2 /* View+InnerShadow.swift in Sources */, 0D32281A26C5864B00262533 /* ProfileScreenViewModel.swift in Sources */, 0D185819272723FF0046B928 /* BlueChip.swift in Sources */, @@ -954,6 +984,7 @@ 66A0807B271993C500118B79 /* OnboardingProgressIndicator.swift in Sources */, 663FAB9E271D875700E495F8 /* CreateButton.swift in Sources */, 0D7DF08C271DCC0E00530046 /* ScreenBackground.swift in Sources */, + F9C165C22740403600592F76 /* CreateView.swift in Sources */, 0DA13C8F26C15D1D00E3B610 /* WelcomeScreen.swift in Sources */, F9C165B4274031F600592F76 /* Bindings.swift in Sources */, 0D32282826C586E000262533 /* RequestZcashScreen.swift in Sources */, @@ -965,6 +996,7 @@ 0D32281926C5864B00262533 /* ProfileScreen.swift in Sources */, 6654C7412715A47300901167 /* Onboarding.swift in Sources */, 0D32282426C586A800262533 /* HistoryScreenViewModel.swift in Sources */, + F9C165C42740403600592F76 /* SentView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/secant/Features/Home/HomeStore.swift b/secant/Features/Home/HomeStore.swift index b961a9c..66b8388 100644 --- a/secant/Features/Home/HomeStore.swift +++ b/secant/Features/Home/HomeStore.swift @@ -89,4 +89,13 @@ extension HomeViewStore { } ) } + + var showSendBinding: Binding { + self.binding( + get: { $0.route == .send }, + send: { isActive in + return .updateRoute(isActive ? .send : nil) + } + ) + } } diff --git a/secant/Features/Home/Views/HomeView.swift b/secant/Features/Home/Views/HomeView.swift index a173c1e..278459f 100644 --- a/secant/Features/Home/Views/HomeView.swift +++ b/secant/Features/Home/Views/HomeView.swift @@ -21,6 +21,13 @@ struct HomeView: View { .primaryButtonStyle .frame(height: 50) + Button( + action: { viewStore.send(.updateRoute(.send)) }, + label: { Text("Go to Send") } + ) + .primaryButtonStyle + .frame(height: 50) + Spacer() HStack { @@ -36,6 +43,21 @@ struct HomeView: View { } .padding(.horizontal, 30) .navigationBarTitle("Home", displayMode: .inline) + .navigationLinkEmpty( + isActive: viewStore.showSendBinding, + destination: { + Create( + store: .init( + initialState: .init( + transaction: .demo, + route: nil + ), + reducer: .default.debug(), + environment: () + ) + ) + } + ) .fullScreenCover( isPresented: viewStore.showHistoryBinding, content: { diff --git a/secant/Features/Send/SendStore.swift b/secant/Features/Send/SendStore.swift new file mode 100644 index 0000000..3f310a3 --- /dev/null +++ b/secant/Features/Send/SendStore.swift @@ -0,0 +1,57 @@ +import SwiftUI +import ComposableArchitecture + +struct SendState: Equatable { + var transaction: Transaction + var route: Create.Route? +} + +enum SendAction: Equatable { + case updateTransaction(Transaction) + case updateRoute(Create.Route?) +} + +// Mark: - SendReducer + +typealias SendReducer = Reducer + +extension SendReducer { + private struct SyncStatusUpdatesID: Hashable {} + + static let `default` = Reducer { state, action, _ in + switch action { + case let .updateTransaction(transaction): + state.transaction = transaction + return .none + case let .updateRoute(route): + state.route = route + return .none + } + } +} + +// Mark: - SendStore + +typealias SendStore = Store + +// Mark: - SendViewStore + +typealias SendViewStore = ViewStore + +extension SendViewStore { + + var bindingForTransaction: Binding { + self.binding( + get: \.transaction, + send: SendAction.updateTransaction + ) + } + + var routeBinding: Binding { + self.binding( + get: \.route, + send: SendAction.updateRoute + ) + } +} + diff --git a/secant/Features/Send/Views/ApproveView.swift b/secant/Features/Send/Views/ApproveView.swift new file mode 100644 index 0000000..cca4e81 --- /dev/null +++ b/secant/Features/Send/Views/ApproveView.swift @@ -0,0 +1,63 @@ +import SwiftUI +import ComposableArchitecture + +struct Approve: View { + enum Route: Equatable { + case showSent(route: Sent.Route?) + } + + let transaction: Transaction + @Binding var route: Route? + + var body: some View { + VStack { + Button( + action: { route = .showSent(route: nil) }, + label: { Text("Go to sent") } + ) + .primaryButtonStyle + .frame(height: 50) + .padding() + + Text("\(String(dumping: transaction))") + Text("\(String(dumping: route))") + + Spacer() + } + .navigationTitle(Text("2. Approve")) + .navigationLinkEmpty( + isActive: $route.map( + extract: { + if case .showSent = $0 { + return true + } else { + return false + } + }, + embed: { $0 ? .showSent(route: (/Route.showSent).extract(from: route)) : nil } + ), + destination: { + Sent( + transaction: transaction, + route: $route.map( + extract: /Route.showSent, + embed: Route.showSent + ) + ) + } + ) + } +} + +struct Approve_Previews: PreviewProvider { + static var previews: some View { + NavigationView { + StateContainer(initialState: (Transaction.demo, Approve.Route?.none)) { + Approve( + transaction: $0.0.wrappedValue, + route: $0.1 + ) + } + } + } +} diff --git a/secant/Features/Send/Views/CreateView.swift b/secant/Features/Send/Views/CreateView.swift new file mode 100644 index 0000000..66c9223 --- /dev/null +++ b/secant/Features/Send/Views/CreateView.swift @@ -0,0 +1,89 @@ +import SwiftUI +import ComposableArchitecture + +struct Create: View { + enum Route: Equatable { + case showApprove(route: Approve.Route?) + } + + let store: Store + + var body: some View { + WithViewStore(store) { viewStore in + VStack { + Button( + action: { viewStore.send(.updateRoute(.showApprove(route: nil))) }, + label: { Text("Go To Approve") } + ) + .primaryButtonStyle + .frame(height: 50) + .padding() + + TextField( + "Amount", + text: viewStore + .bindingForTransaction + .amount + .compactMap( + extract: String.init, + embed: UInt.init + ) + ) + .padding() + + TextField( + "Address", + text: viewStore.bindingForTransaction.toAddress + ) + + Text("\(String(dumping: viewStore.transaction))") + Text("\(String(dumping: viewStore.route))") + + Spacer() + } + .padding() + .navigationTitle(Text("1. Create")) + .navigationLinkEmpty( + isActive: viewStore.routeBinding.map( + extract: { $0.map(/Route.showApprove) != nil }, + embed: { $0 ? .showApprove(route: (/Route.showApprove).extract(from: viewStore.route)) : nil } + ), + destination: { + Approve( + transaction: viewStore.transaction, + route: viewStore.routeBinding.map( + extract: /Route.showApprove, + embed: Route.showApprove + ) + ) + } + ) + } + } +} + +// MARK: - Previews + +struct Create_Previews: PreviewProvider { + static var previews: some View { + NavigationView { + Create(store: .demo) + .navigationBarTitleDisplayMode(.inline) + } + } +} + +#if DEBUG +extension SendStore { + static var demo: SendStore { + return SendStore( + initialState: .init( + transaction: .demo, + route: nil + ), + reducer: .default, + environment: () + ) + } +} +#endif diff --git a/secant/Features/Send/Views/SentView.swift b/secant/Features/Send/Views/SentView.swift new file mode 100644 index 0000000..0d02c46 --- /dev/null +++ b/secant/Features/Send/Views/SentView.swift @@ -0,0 +1,37 @@ +import SwiftUI +import ComposableArchitecture + +struct Sent: View { + enum Route { + case done + } + @State var transaction: Transaction + @Binding var route: Route? + + var body: some View { + VStack { + Button( + action: { + route = .done + }, + label: { Text("Done") } + ) + .primaryButtonStyle + .frame(height: 50) + .padding() + + + Text("\(String(dumping: transaction))") + Text("\(String(dumping: route))") + + Spacer() + } + .navigationTitle(Text("3. Sent")) + } +} + +struct Done_Previews: PreviewProvider { + static var previews: some View { + Sent(transaction: .demo, route: .constant(nil)) + } +} diff --git a/secant/SecantApp.swift b/secant/SecantApp.swift index dda3f07..2b44a08 100644 --- a/secant/SecantApp.swift +++ b/secant/SecantApp.swift @@ -15,6 +15,7 @@ struct SecantApp: App { NavigationView { HomeView(store: homeStore) } + .navigationViewStyle(StackNavigationViewStyle()) } } } From a8fc8d79f79e2bf7fa3b084111dafe4c7905dcdc Mon Sep 17 00:00:00 2001 From: Daniel Haight Date: Sun, 21 Nov 2021 14:28:11 +0000 Subject: [PATCH 3/4] Create a reducer to go back `Home` when finished sending This is done as a seperate commit to demonstrate composing routing logic where a 'child' feature can send messages to a 'parent'. --- secant/Features/Home/Views/HomeView.swift | 5 ++++- secant/Features/Send/SendStore.swift | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/secant/Features/Home/Views/HomeView.swift b/secant/Features/Home/Views/HomeView.swift index 278459f..9ea68de 100644 --- a/secant/Features/Home/Views/HomeView.swift +++ b/secant/Features/Home/Views/HomeView.swift @@ -52,7 +52,10 @@ struct HomeView: View { transaction: .demo, route: nil ), - reducer: .default.debug(), + reducer: SendReducer.default( + whenDone: { viewStore.send(.updateRoute(nil)) } + ) + .debug(), environment: () ) ) diff --git a/secant/Features/Send/SendStore.swift b/secant/Features/Send/SendStore.swift index 3f310a3..8483ab9 100644 --- a/secant/Features/Send/SendStore.swift +++ b/secant/Features/Send/SendStore.swift @@ -28,6 +28,17 @@ extension SendReducer { return .none } } + + static func `default`(whenDone: @escaping () -> Void) -> SendReducer { + SendReducer { state, action, _ in + switch action { + case let .updateRoute(route) where route == .showApprove(route: .showSent(route: .done)): + return Effect.fireAndForget(whenDone) + default: + return Self.default.run(&state, action, ()) + } + } + } } // Mark: - SendStore From 780dd0fafae5bebdc40c821c7753212ffb0192de Mon Sep 17 00:00:00 2001 From: Daniel Haight Date: Sun, 21 Nov 2021 14:28:12 +0000 Subject: [PATCH 4/4] [Experiment] Move `Send` routing into a parent `SendView` This is to demonstrate having something like a 'Coordinator' that handles the routing logic of the feature. It is done as a seperate commit to compare against the original implementation that has the routing logic encoded explicitely in each view. --- secant.xcodeproj/project.pbxproj | 4 + secant/Features/Home/Views/HomeView.swift | 2 +- secant/Features/Send/SendStore.swift | 29 +++++- secant/Features/Send/Views/ApproveView.swift | 35 ++------ secant/Features/Send/Views/CreateView.swift | 92 ++++++++------------ secant/Features/Send/Views/SendView.swift | 58 ++++++++++++ secant/Features/Send/Views/SentView.swift | 13 ++- 7 files changed, 136 insertions(+), 97 deletions(-) create mode 100644 secant/Features/Send/Views/SendView.swift diff --git a/secant.xcodeproj/project.pbxproj b/secant.xcodeproj/project.pbxproj index 12bf7b5..922264f 100644 --- a/secant.xcodeproj/project.pbxproj +++ b/secant.xcodeproj/project.pbxproj @@ -103,6 +103,7 @@ F9C165C22740403600592F76 /* CreateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165BB2740403600592F76 /* CreateView.swift */; }; F9C165C42740403600592F76 /* SentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165BD2740403600592F76 /* SentView.swift */; }; F9EEB8162742C2210032EEB8 /* WithStateBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9EEB8152742C2210032EEB8 /* WithStateBinding.swift */; }; + F9C165CB2741AB5D00592F76 /* SendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165CA2741AB5D00592F76 /* SendView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -224,6 +225,7 @@ F9C165BB2740403600592F76 /* CreateView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateView.swift; sourceTree = ""; }; F9C165BD2740403600592F76 /* SentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SentView.swift; sourceTree = ""; }; F9EEB8152742C2210032EEB8 /* WithStateBinding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithStateBinding.swift; sourceTree = ""; }; + F9C165CA2741AB5D00592F76 /* SendView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -720,6 +722,7 @@ F9C165B82740403600592F76 /* Views */ = { isa = PBXGroup; children = ( + F9C165CA2741AB5D00592F76 /* SendView.swift */, F9C165BB2740403600592F76 /* CreateView.swift */, F9C165B92740403600592F76 /* ApproveView.swift */, F9C165BD2740403600592F76 /* SentView.swift */, @@ -994,6 +997,7 @@ 0DA13CA226C1955600E3B610 /* HomeScreenViewModel.swift in Sources */, 0D32282926C586E000262533 /* RequestZcashScreenViewModel.swift in Sources */, 0D32281926C5864B00262533 /* ProfileScreen.swift in Sources */, + F9C165CB2741AB5D00592F76 /* SendView.swift in Sources */, 6654C7412715A47300901167 /* Onboarding.swift in Sources */, 0D32282426C586A800262533 /* HistoryScreenViewModel.swift in Sources */, F9C165C42740403600592F76 /* SentView.swift in Sources */, diff --git a/secant/Features/Home/Views/HomeView.swift b/secant/Features/Home/Views/HomeView.swift index 9ea68de..4fb68b2 100644 --- a/secant/Features/Home/Views/HomeView.swift +++ b/secant/Features/Home/Views/HomeView.swift @@ -46,7 +46,7 @@ struct HomeView: View { .navigationLinkEmpty( isActive: viewStore.showSendBinding, destination: { - Create( + SendView( store: .init( initialState: .init( transaction: .demo, diff --git a/secant/Features/Send/SendStore.swift b/secant/Features/Send/SendStore.swift index 8483ab9..c6e7b47 100644 --- a/secant/Features/Send/SendStore.swift +++ b/secant/Features/Send/SendStore.swift @@ -3,12 +3,12 @@ import ComposableArchitecture struct SendState: Equatable { var transaction: Transaction - var route: Create.Route? + var route: SendView.Route? } enum SendAction: Equatable { case updateTransaction(Transaction) - case updateRoute(Create.Route?) + case updateRoute(SendView.Route?) } // Mark: - SendReducer @@ -32,7 +32,7 @@ extension SendReducer { static func `default`(whenDone: @escaping () -> Void) -> SendReducer { SendReducer { state, action, _ in switch action { - case let .updateRoute(route) where route == .showApprove(route: .showSent(route: .done)): + case let .updateRoute(route) where route == .done: return Effect.fireAndForget(whenDone) default: return Self.default.run(&state, action, ()) @@ -58,11 +58,32 @@ extension SendViewStore { ) } - var routeBinding: Binding { + var routeBinding: Binding { self.binding( get: \.route, send: SendAction.updateRoute ) } + + var bindingForApprove: Binding { + self.routeBinding.map( + extract: { $0 == .showApprove || self.bindingForSent.wrappedValue }, + embed: { $0 ? SendView.Route.showApprove : nil } + ) + } + + var bindingForSent: Binding { + self.routeBinding.map( + extract: { $0 == .showSent || self.bindingForDone.wrappedValue }, + embed: { $0 ? SendView.Route.showSent : SendView.Route.showApprove } + ) + } + + var bindingForDone: Binding { + self.routeBinding.map( + extract: { $0 == .done }, + embed: { $0 ? SendView.Route.done : SendView.Route.showSent } + ) + } } diff --git a/secant/Features/Send/Views/ApproveView.swift b/secant/Features/Send/Views/ApproveView.swift index cca4e81..852df4e 100644 --- a/secant/Features/Send/Views/ApproveView.swift +++ b/secant/Features/Send/Views/ApproveView.swift @@ -2,17 +2,13 @@ import SwiftUI import ComposableArchitecture struct Approve: View { - enum Route: Equatable { - case showSent(route: Sent.Route?) - } - let transaction: Transaction - @Binding var route: Route? + @Binding var isComplete: Bool var body: some View { VStack { Button( - action: { route = .showSent(route: nil) }, + action: { isComplete = true }, label: { Text("Go to sent") } ) .primaryButtonStyle @@ -20,42 +16,21 @@ struct Approve: View { .padding() Text("\(String(dumping: transaction))") - Text("\(String(dumping: route))") + Text("\(String(dumping: isComplete))") Spacer() } .navigationTitle(Text("2. Approve")) - .navigationLinkEmpty( - isActive: $route.map( - extract: { - if case .showSent = $0 { - return true - } else { - return false - } - }, - embed: { $0 ? .showSent(route: (/Route.showSent).extract(from: route)) : nil } - ), - destination: { - Sent( - transaction: transaction, - route: $route.map( - extract: /Route.showSent, - embed: Route.showSent - ) - ) - } - ) } } struct Approve_Previews: PreviewProvider { static var previews: some View { NavigationView { - StateContainer(initialState: (Transaction.demo, Approve.Route?.none)) { + StateContainer(initialState: (Transaction.demo, false)) { Approve( transaction: $0.0.wrappedValue, - route: $0.1 + isComplete: $0.1 ) } } diff --git a/secant/Features/Send/Views/CreateView.swift b/secant/Features/Send/Views/CreateView.swift index 66c9223..9e0c704 100644 --- a/secant/Features/Send/Views/CreateView.swift +++ b/secant/Features/Send/Views/CreateView.swift @@ -2,63 +2,42 @@ import SwiftUI import ComposableArchitecture struct Create: View { - enum Route: Equatable { - case showApprove(route: Approve.Route?) - } - - let store: Store + @Binding var transaction: Transaction + @Binding var isComplete: Bool var body: some View { - WithViewStore(store) { viewStore in - VStack { - Button( - action: { viewStore.send(.updateRoute(.showApprove(route: nil))) }, - label: { Text("Go To Approve") } - ) - .primaryButtonStyle - .frame(height: 50) - .padding() - - TextField( - "Amount", - text: viewStore - .bindingForTransaction - .amount - .compactMap( - extract: String.init, - embed: UInt.init - ) - ) - .padding() - - TextField( - "Address", - text: viewStore.bindingForTransaction.toAddress - ) - - Text("\(String(dumping: viewStore.transaction))") - Text("\(String(dumping: viewStore.route))") - - Spacer() - } - .padding() - .navigationTitle(Text("1. Create")) - .navigationLinkEmpty( - isActive: viewStore.routeBinding.map( - extract: { $0.map(/Route.showApprove) != nil }, - embed: { $0 ? .showApprove(route: (/Route.showApprove).extract(from: viewStore.route)) : nil } - ), - destination: { - Approve( - transaction: viewStore.transaction, - route: viewStore.routeBinding.map( - extract: /Route.showApprove, - embed: Route.showApprove - ) - ) - } + VStack { + Button( + action: { isComplete = true }, + label: { Text("Go To Approve") } ) + .primaryButtonStyle + .frame(height: 50) + .padding() + + TextField( + "Amount", + text: $transaction + .amount + .compactMap( + extract: String.init, + embed: UInt.init + ) + ) + .padding() + + TextField( + "Address", + text: $transaction.toAddress + ) + + Text("\(String(dumping: transaction))") + Text("\(String(dumping: isComplete))") + + Spacer() } + .padding() + .navigationTitle(Text("1. Create")) } } @@ -67,7 +46,12 @@ struct Create: View { struct Create_Previews: PreviewProvider { static var previews: some View { NavigationView { - Create(store: .demo) + StateContainer(initialState: (Transaction.demo, false)) { + Create( + transaction: $0.0, + isComplete: $0.1 + ) + } .navigationBarTitleDisplayMode(.inline) } } diff --git a/secant/Features/Send/Views/SendView.swift b/secant/Features/Send/Views/SendView.swift new file mode 100644 index 0000000..72c6a35 --- /dev/null +++ b/secant/Features/Send/Views/SendView.swift @@ -0,0 +1,58 @@ +import SwiftUI +import ComposableArchitecture + +struct SendView: View { + enum Route: Equatable { + case showApprove + case showSent + case done + } + + let store: Store + + var body: some View { + WithViewStore(store) { viewStore in + Create( + transaction: viewStore.bindingForTransaction, + isComplete: viewStore.bindingForApprove + ) + .navigationLinkEmpty( + isActive: viewStore.bindingForApprove, + destination: { + Approve( + transaction: viewStore.transaction, + isComplete: viewStore.bindingForSent + ) + .navigationLinkEmpty( + isActive: viewStore.bindingForSent, + destination: { + Sent( + transaction: viewStore.transaction, + isComplete: viewStore.bindingForDone + ) + } + ) + } + ) + } + } +} + +struct SendView_Previews: PreviewProvider { + static var previews: some View { + NavigationView { + SendView( + store: .init( + initialState: .init( + transaction: .demo, + route: nil + ), + reducer: .default, + environment: () + ) + ) + .navigationBarTitleDisplayMode(.inline) + .navigationViewStyle(StackNavigationViewStyle()) + } + } +} diff --git a/secant/Features/Send/Views/SentView.swift b/secant/Features/Send/Views/SentView.swift index 0d02c46..81aa417 100644 --- a/secant/Features/Send/Views/SentView.swift +++ b/secant/Features/Send/Views/SentView.swift @@ -2,17 +2,14 @@ import SwiftUI import ComposableArchitecture struct Sent: View { - enum Route { - case done - } - @State var transaction: Transaction - @Binding var route: Route? + var transaction: Transaction + @Binding var isComplete: Bool var body: some View { VStack { Button( action: { - route = .done + isComplete = true }, label: { Text("Done") } ) @@ -22,7 +19,7 @@ struct Sent: View { Text("\(String(dumping: transaction))") - Text("\(String(dumping: route))") + Text("\(String(dumping: isComplete))") Spacer() } @@ -32,6 +29,6 @@ struct Sent: View { struct Done_Previews: PreviewProvider { static var previews: some View { - Sent(transaction: .demo, route: .constant(nil)) + Sent(transaction: .demo, isComplete: .constant(false)) } }