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()) } } }