From 330bc3256fce012eea1d20005d6ebef57f8d7bae Mon Sep 17 00:00:00 2001 From: Lukas Korba Date: Thu, 14 Apr 2022 11:25:05 +0200 Subject: [PATCH] old home->sandbox, new home for #255 hotfix --- secant.xcodeproj/project.pbxproj | 36 ++++- secant/Features/App/App.swift | 31 +++-- secant/Features/App/Views/AppView.swift | 18 ++- secant/Features/Home/HomeStore.swift | 91 +------------ secant/Features/Home/Views/HomeView.swift | 96 +------------ secant/Features/Sandbox/SandboxStore.swift | 115 ++++++++++++++++ .../Features/Sandbox/Views/SandboxView.swift | 126 ++++++++++++++++++ secant/Features/Welcome/Welcome.swift | 1 - secant/Features/Welcome/WelcomeView.swift | 4 +- 9 files changed, 309 insertions(+), 209 deletions(-) create mode 100644 secant/Features/Sandbox/SandboxStore.swift create mode 100644 secant/Features/Sandbox/Views/SandboxView.swift diff --git a/secant.xcodeproj/project.pbxproj b/secant.xcodeproj/project.pbxproj index 54f23e6..51cd0bb 100644 --- a/secant.xcodeproj/project.pbxproj +++ b/secant.xcodeproj/project.pbxproj @@ -101,9 +101,13 @@ 9E4DC6E227C4C6B700E657F4 /* SecantButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */; }; 9E69A24D27FB002800A55317 /* Welcome.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E69A24C27FB002800A55317 /* Welcome.swift */; }; 9E80B47227E4B34B008FF493 /* UserPreferencesStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */; }; + 9EAFEB822805793200199FC9 /* AppReducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB812805793200199FC9 /* AppReducerTests.swift */; }; 9EAFEB84280597B700199FC9 /* WrappedSecItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */; }; 9EAFEB862805A23100199FC9 /* WrappedSecItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */; }; - 9EAFEB822805793200199FC9 /* AppReducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB812805793200199FC9 /* AppReducerTests.swift */; }; + 9EAFEB8F2808183D00199FC9 /* SandboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB8D2808183D00199FC9 /* SandboxView.swift */; }; + 9EAFEB902808183D00199FC9 /* SandboxStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB8E2808183D00199FC9 /* SandboxStore.swift */; }; + 9EAFEB9128081E9400199FC9 /* HomeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874ED273C4DE200F0E875 /* HomeStore.swift */; }; + 9EAFEB9228081E9400199FC9 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874EF273C4DE200F0E875 /* HomeView.swift */; }; 9EBEF87A27CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */; }; 9ECAE56827FC713C0089A0EF /* DatabaseFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */; }; 9EF8135C27ECC25E0075AF48 /* WalletStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */; }; @@ -114,8 +118,6 @@ 9EF8139C27F47AED0075AF48 /* InitializationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8139B27F47AED0075AF48 /* InitializationState.swift */; }; F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9322DBF273B555C00C105B5 /* NavigationLinks.swift */; }; F93673D62742CB840099C6AF /* Previews.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93673D52742CB840099C6AF /* Previews.swift */; }; - F93874F0273C4DE200F0E875 /* HomeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874ED273C4DE200F0E875 /* HomeStore.swift */; }; - F93874F1273C4DE200F0E875 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874EF273C4DE200F0E875 /* HomeView.swift */; }; F96B41E7273B501F0021B49A /* TransactionHistoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E3273B501F0021B49A /* TransactionHistoryStore.swift */; }; F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E5273B501F0021B49A /* TransactionDetailView.swift */; }; F96B41E9273B501F0021B49A /* TransactionHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E6273B501F0021B49A /* TransactionHistoryView.swift */; }; @@ -257,9 +259,11 @@ 9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecantButtonStyles.swift; sourceTree = ""; }; 9E69A24C27FB002800A55317 /* Welcome.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Welcome.swift; sourceTree = ""; }; 9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorage.swift; sourceTree = ""; }; + 9EAFEB812805793200199FC9 /* AppReducerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppReducerTests.swift; sourceTree = ""; }; 9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItem.swift; sourceTree = ""; }; 9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItemTests.swift; sourceTree = ""; }; - 9EAFEB812805793200199FC9 /* AppReducerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppReducerTests.swift; sourceTree = ""; }; + 9EAFEB8D2808183D00199FC9 /* SandboxView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SandboxView.swift; sourceTree = ""; }; + 9EAFEB8E2808183D00199FC9 /* SandboxStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SandboxStore.swift; sourceTree = ""; }; 9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseTestPreambleView.swift; sourceTree = ""; }; 9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseFiles.swift; sourceTree = ""; }; 9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletStorageTests.swift; sourceTree = ""; }; @@ -664,6 +668,7 @@ 6654C73B2715A3F000901167 /* Features */ = { isa = PBXGroup; children = ( + 9EAFEB8B2808174900199FC9 /* Sandbox */, 0D0781C2278750C00083ACD7 /* Welcome */, F9971A4927680DC400A2DB75 /* App */, F93874EC273C4DE200F0E875 /* Home */, @@ -768,6 +773,23 @@ path = AppReducer; sourceTree = ""; }; + 9EAFEB8B2808174900199FC9 /* Sandbox */ = { + isa = PBXGroup; + children = ( + 9EAFEB8E2808183D00199FC9 /* SandboxStore.swift */, + 9EAFEB8C2808183D00199FC9 /* Views */, + ); + path = Sandbox; + sourceTree = ""; + }; + 9EAFEB8C2808183D00199FC9 /* Views */ = { + isa = PBXGroup; + children = ( + 9EAFEB8D2808183D00199FC9 /* SandboxView.swift */, + ); + path = Views; + sourceTree = ""; + }; 9EBEF87827CE365D00B4F343 /* Preamble */ = { isa = PBXGroup; children = ( @@ -1149,6 +1171,7 @@ files = ( 2EB660E02747EAB900A06A07 /* OnboardingScreen.swift in Sources */, 660558F8270C862F009D6954 /* XCAssets+Generated.swift in Sources */, + 9EAFEB902808183D00199FC9 /* SandboxStore.swift in Sources */, 0D35CC46277A36E00074316A /* ScrollableWhenScaled.swift in Sources */, F96B41E9273B501F0021B49A /* TransactionHistoryView.swift in Sources */, 2EDA07A027EDE18C00D6F09B /* TextFieldInput.swift in Sources */, @@ -1176,7 +1199,6 @@ 0D6D628B276A528E002FB4CC /* DropDelegate.swift in Sources */, 9E2DF99D27CF704D00649636 /* ImportSeedEditor.swift in Sources */, F9971A5327680DD000A2DB75 /* Profile.swift in Sources */, - F93874F0273C4DE200F0E875 /* HomeStore.swift in Sources */, 669FDAEB272C23C2007B9422 /* CircularFrameBadge.swift in Sources */, 2E8719CD27FB0D3B0082C926 /* TransactionCurrencySelector.swift in Sources */, F9971A6C27680E1000A2DB75 /* WalletInfoView.swift in Sources */, @@ -1188,8 +1210,9 @@ 665C963F272C26E600BC04FB /* CircularFrameBackground.swift in Sources */, 0DB8AA81271DC7520035BC9D /* DesignGuide.swift in Sources */, F9971A4D27680DC400A2DB75 /* App.swift in Sources */, + 9EAFEB9228081E9400199FC9 /* HomeView.swift in Sources */, F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */, - F93874F1273C4DE200F0E875 /* HomeView.swift in Sources */, + 9EAFEB8F2808183D00199FC9 /* SandboxView.swift in Sources */, 0D7CE63427349B5D0020E050 /* View+WhenDraggable.swift in Sources */, 0D3D04082728B3440032ABC1 /* RecoveryPhraseDisplayView.swift in Sources */, F9971A5F27680DF600A2DB75 /* ScanView.swift in Sources */, @@ -1203,6 +1226,7 @@ 0D354A0926D5A9D000315F45 /* Services.swift in Sources */, 660558F7270C862F009D6954 /* Fonts+Generated.swift in Sources */, F96B41E7273B501F0021B49A /* TransactionHistoryStore.swift in Sources */, + 9EAFEB9128081E9400199FC9 /* HomeStore.swift in Sources */, F9971A5A27680DDE00A2DB75 /* RequestView.swift in Sources */, 0DACFA8127208D940039EEA5 /* UInt+SuperscriptText.swift in Sources */, 0DF2DC51272344E400FA31E2 /* EmptyChip.swift in Sources */, diff --git a/secant/Features/App/App.swift b/secant/Features/App/App.swift index f14557e..f7203e5 100644 --- a/secant/Features/App/App.swift +++ b/secant/Features/App/App.swift @@ -6,19 +6,21 @@ struct AppState: Equatable { case welcome case startup case onboarding + case sandbox case home case phraseValidation case phraseDisplay } + var appInitializationState: InitializationState = .uninitialized var homeState: HomeState var onboardingState: OnboardingState var phraseValidationState: RecoveryPhraseValidationState var phraseDisplayState: RecoveryPhraseDisplayState - var welcomeState: WelcomeState var route: Route = .welcome + var sandboxState: SandboxState var storedWallet: StoredWallet? - var appInitializationState: InitializationState = .uninitialized + var welcomeState: WelcomeState } enum AppAction: Equatable { @@ -32,6 +34,7 @@ enum AppAction: Equatable { case phraseDisplay(RecoveryPhraseDisplayAction) case phraseValidation(RecoveryPhraseValidationAction) case respondToWalletInitializationState(InitializationState) + case sandbox(SandboxAction) case updateRoute(AppState.Route) case welcome(WelcomeAction) } @@ -72,11 +75,12 @@ extension AppReducer { static let `default` = AppReducer.combine( [ appReducer, - routeReducer, homeReducer, onboardingReducer, phraseValidationReducer, - phraseDisplayReducer + phraseDisplayReducer, + routeReducer, + sandboxReducer ] ) .debug() @@ -136,7 +140,7 @@ extension AppReducer { .cancellable(id: ListenerId(), cancelInFlight: true) } - var landingRoute: AppState.Route = .startup + var landingRoute: AppState.Route = .home if !storedWallet.hasUserPassedPhraseBackupTest { let phraseWords: [String] @@ -195,12 +199,6 @@ extension AppReducer { // TODO: - when DatabaseFiles dependency is merged, nukeFiles as well, issue #220 (https://github.com/zcash/secant-ios-wallet/issues/220) return .none - case .welcome(.debugMenuHome): - return .concatenate( - Effect.cancel(id: ListenerId()), - Effect(value: .updateRoute(.home)) - ) - case .welcome(.debugMenuStartup): return .concatenate( Effect.cancel(id: ListenerId()), @@ -208,7 +206,7 @@ extension AppReducer { ) case .onboarding(.importWallet(.successfullyRecovered)): - return Effect(value: .updateRoute(.home)) + return Effect(value: .updateRoute(.sandbox)) /// Default is meaningful here because there's `routeReducer` handling routes and this reducer is handling only actions. We don't here plenty of unused cases. default: @@ -221,7 +219,7 @@ extension AppReducer { case let .updateRoute(route): state.route = route - case .home(.reset): + case .sandbox(.reset): state.route = .startup case .onboarding(.createNewWallet): @@ -274,6 +272,12 @@ extension AppReducer { environment: { _ in BackupPhraseEnvironment.demo } ) + private static let sandboxReducer: AppReducer = SandboxReducer.default.pullback( + state: \AppState.sandboxState, + action: /AppAction.sandbox, + environment: { _ in } + ) + private static let welcomeReducer: AppReducer = WelcomeReducer.default.pullback( state: \AppState.welcomeState, action: /AppAction.welcome, @@ -356,6 +360,7 @@ extension AppState { phraseDisplayState: RecoveryPhraseDisplayState( phrase: .placeholder ), + sandboxState: .placeholder, welcomeState: .placeholder ) } diff --git a/secant/Features/App/Views/AppView.swift b/secant/Features/App/Views/AppView.swift index e6d2a25..d949ee7 100644 --- a/secant/Features/App/Views/AppView.swift +++ b/secant/Features/App/Views/AppView.swift @@ -9,11 +9,19 @@ struct AppView: View { WithViewStore(store) { viewStore in switch viewStore.route { case .home: + HomeView( + store: store.scope( + state: \.homeState, + action: AppAction.home + ) + ) + + case .sandbox: NavigationView { - HomeView( + SandboxView( store: store.scope( - state: \.homeState, - action: AppAction.home + state: \.sandboxState, + action: AppAction.sandbox ) ) } @@ -75,8 +83,8 @@ private struct StartupView: View { var body: some View { List { Section(header: Text("Navigation Stack Routes")) { - Button("Go To Home") { - sendAction(.updateRoute(.home)) + Button("Go To Sandbox (navigation proof)") { + sendAction(.updateRoute(.sandbox)) } Button("Go To Onboarding") { diff --git a/secant/Features/Home/HomeStore.swift b/secant/Features/Home/HomeStore.swift index cc234bc..bae3f27 100644 --- a/secant/Features/Home/HomeStore.swift +++ b/secant/Features/Home/HomeStore.swift @@ -2,24 +2,10 @@ import ComposableArchitecture import SwiftUI struct HomeState: Equatable { - enum Route: Equatable, CaseIterable { - case history - case send - case recoveryPhraseDisplay - case profile - case scan - case request - } - var transactionHistoryState: TransactionHistoryState - var profileState: ProfileState - var route: Route? + var balance: Double } enum HomeAction: Equatable { - case updateRoute(HomeState.Route?) - case transactionHistory(TransactionHistoryAction) - case profile(ProfileAction) - case reset } // MARK: - HomeReducer @@ -27,30 +13,8 @@ enum HomeAction: Equatable { typealias HomeReducer = Reducer extension HomeReducer { - static let `default` = HomeReducer { state, action, _ in - switch action { - case let .updateRoute(route): - state.route = route - return .none - case let .transactionHistory(transactionHistoryAction): - return TransactionHistoryReducer - .default - .run(&state.transactionHistoryState, transactionHistoryAction, ()) - .map(HomeAction.transactionHistory) - case let .profile(profileAction): - return ProfileReducer - .default - .pullback( - state: \.profileState, - action: /HomeAction.profile, - environment: { _ in - return ProfileEnvironment() - } - ) - .run(&state, action, ()) - case .reset: - return .none - } + static let `default` = HomeReducer { _, _, _ in + return .none } } @@ -58,58 +22,11 @@ extension HomeReducer { typealias HomeStore = Store -extension HomeStore { - func historyStore() -> TransactionHistoryStore { - self.scope( - state: \.transactionHistoryState, - action: HomeAction.transactionHistory - ) - } - - func profileStore() -> ProfileStore { - self.scope( - state: \.profileState, - action: HomeAction.profile - ) - } -} - -// MARK: - HomeViewStore - -typealias HomeViewStore = ViewStore - -extension HomeViewStore { - func toggleSelectedTransaction() { - let isAlreadySelected = (self.selectedTranactionID != nil) - let transcation = self.transactionHistoryState.transactions[5] - let newRoute = isAlreadySelected ? nil : TransactionHistoryState.Route.showTransaction(transcation) - send(.transactionHistory(.setRoute(newRoute))) - } - - var selectedTranactionID: Int? { - self.transactionHistoryState - .route - .flatMap(/TransactionHistoryState.Route.showTransaction) - .map(\.id) - } - - func bindingForRoute(_ route: HomeState.Route) -> Binding { - self.binding( - get: { $0.route == route }, - send: { isActive in - return .updateRoute(isActive ? route : nil) - } - ) - } -} - // MARK: PlaceHolders extension HomeState { static var placeholder: Self { .init( - transactionHistoryState: .placeHolder, - profileState: .placeholder, - route: nil + balance: 1.2 ) } } diff --git a/secant/Features/Home/Views/HomeView.swift b/secant/Features/Home/Views/HomeView.swift index 1f12cdd..280c050 100644 --- a/secant/Features/Home/Views/HomeView.swift +++ b/secant/Features/Home/Views/HomeView.swift @@ -4,113 +4,21 @@ import ComposableArchitecture struct HomeView: View { let store: Store - var navigationRouteValues: [RouteValue] = HomeState.Route.allCases - .enumerated() - .filter { $0.1 != .history } - .map { RouteValue(id: $0.0, route: $0.1) } - - var modalRoutes: [RouteValue] = HomeState.Route.allCases - .enumerated() - .filter { $0.1 == .history } - .map { RouteValue(id: $0.0, route: $0.1) } - - @ViewBuilder func view(for route: HomeState.Route) -> some View { - switch route { - case .history: - TransactionHistoryView(store: store.historyStore()) - case .send: - SendView( - store: .init( - initialState: .placeholder, - reducer: SendReducer.default( - whenDone: { HomeViewStore(store).send(.updateRoute(nil)) } - ) - .debug(), - environment: () - ) - ) - case .recoveryPhraseDisplay: - RecoveryPhraseDisplayView(store: .demo) - case .scan: - ScanView() - case .profile: - ProfileView(store: store.profileStore()) - case .request: - RequestView() - } - } - var body: some View { WithViewStore(store) { viewStore in VStack { - List { - Section(header: Text("Navigation Stack Routes")) { - ForEach(navigationRouteValues) { routeValue in - Text("\(String(describing: routeValue.route))") - .navigationLink( - isActive: viewStore.bindingForRoute(routeValue.route), - destination: { - view(for: routeValue.route) - } - ) - } - } - - Section(header: Text("Modal Routes")) { - ForEach(modalRoutes) { routeValue in - Button( - action: { viewStore.send(.updateRoute(routeValue.route)) }, - label: { Text("\(String(describing: routeValue.route))") } - ) - } - } - - Section(header: Text("Other Actions")) { - Button( - action: { viewStore.toggleSelectedTransaction() }, - label: { Text("Toggle Selected Transaction") } - ) - - Button( - action: { viewStore.send(.reset) }, - label: { Text("Reset (to startup)") } - ) - } - } + Text("balance \(viewStore.balance)") } - .fullScreenCover( - isPresented: viewStore.bindingForRoute(.history), - content: { - NavigationView { - TransactionHistoryView(store: store.historyStore()) - .toolbar { - ToolbarItem { - Button("Done") { viewStore.send(.updateRoute(nil)) } - } - } - } - } - ) - .navigationBarTitle("Home") } } } -struct RouteValue: Identifiable { - let id: Int - let route: HomeState.Route -} - // MARK: - Previews extension HomeStore { static var placeholder: HomeStore { HomeStore( - initialState: HomeState( - transactionHistoryState: .placeHolder, - profileState: .placeholder, - route: nil - ), + initialState: .placeholder, reducer: .default.debug(), environment: () ) diff --git a/secant/Features/Sandbox/SandboxStore.swift b/secant/Features/Sandbox/SandboxStore.swift new file mode 100644 index 0000000..2a404f3 --- /dev/null +++ b/secant/Features/Sandbox/SandboxStore.swift @@ -0,0 +1,115 @@ +import ComposableArchitecture +import SwiftUI + +struct SandboxState: Equatable { + enum Route: Equatable, CaseIterable { + case history + case send + case recoveryPhraseDisplay + case profile + case scan + case request + } + var transactionHistoryState: TransactionHistoryState + var profileState: ProfileState + var route: Route? +} + +enum SandboxAction: Equatable { + case updateRoute(SandboxState.Route?) + case transactionHistory(TransactionHistoryAction) + case profile(ProfileAction) + case reset +} + +// MARK: - SandboxReducer + +typealias SandboxReducer = Reducer + +extension SandboxReducer { + static let `default` = SandboxReducer { state, action, _ in + switch action { + case let .updateRoute(route): + state.route = route + return .none + case let .transactionHistory(transactionHistoryAction): + return TransactionHistoryReducer + .default + .run(&state.transactionHistoryState, transactionHistoryAction, ()) + .map(SandboxAction.transactionHistory) + case let .profile(profileAction): + return ProfileReducer + .default + .pullback( + state: \.profileState, + action: /SandboxAction.profile, + environment: { _ in + return ProfileEnvironment() + } + ) + .run(&state, action, ()) + case .reset: + return .none + } + } +} + +// MARK: - SandboxStore + +typealias SandboxStore = Store + +extension SandboxStore { + func historyStore() -> TransactionHistoryStore { + self.scope( + state: \.transactionHistoryState, + action: SandboxAction.transactionHistory + ) + } + + func profileStore() -> ProfileStore { + self.scope( + state: \.profileState, + action: SandboxAction.profile + ) + } +} + +// MARK: - SandboxViewStore + +typealias SandboxViewStore = ViewStore + +extension SandboxViewStore { + func toggleSelectedTransaction() { + let isAlreadySelected = (self.selectedTranactionID != nil) + let transcation = self.transactionHistoryState.transactions[5] + let newRoute = isAlreadySelected ? nil : TransactionHistoryState.Route.showTransaction(transcation) + send(.transactionHistory(.setRoute(newRoute))) + } + + var selectedTranactionID: Int? { + self.transactionHistoryState + .route + .flatMap(/TransactionHistoryState.Route.showTransaction) + .map(\.id) + } + + func bindingForRoute(_ route: SandboxState.Route) -> Binding { + self.binding( + get: { $0.route == route }, + send: { isActive in + return .updateRoute(isActive ? route : nil) + } + ) + } +} + +// MARK: PlaceHolders +extension SandboxState { + static var placeholder: Self { + .init( + transactionHistoryState: .placeHolder, + profileState: .placeholder, + route: nil + ) + } +} diff --git a/secant/Features/Sandbox/Views/SandboxView.swift b/secant/Features/Sandbox/Views/SandboxView.swift new file mode 100644 index 0000000..c7fc1c1 --- /dev/null +++ b/secant/Features/Sandbox/Views/SandboxView.swift @@ -0,0 +1,126 @@ +import SwiftUI +import ComposableArchitecture + +struct SandboxView: View { + let store: Store + + var navigationRouteValues: [SandboxRouteValue] = SandboxState.Route.allCases + .enumerated() + .filter { $0.1 != .history } + .map { SandboxRouteValue(id: $0.0, route: $0.1) } + + var modalRoutes: [SandboxRouteValue] = SandboxState.Route.allCases + .enumerated() + .filter { $0.1 == .history } + .map { SandboxRouteValue(id: $0.0, route: $0.1) } + + @ViewBuilder func view(for route: SandboxState.Route) -> some View { + switch route { + case .history: + TransactionHistoryView(store: store.historyStore()) + case .send: + SendView( + store: .init( + initialState: .placeholder, + reducer: SendReducer.default( + whenDone: { SandboxViewStore(store).send(.updateRoute(nil)) } + ) + .debug(), + environment: () + ) + ) + case .recoveryPhraseDisplay: + RecoveryPhraseDisplayView(store: .demo) + case .scan: + ScanView() + case .profile: + ProfileView(store: store.profileStore()) + case .request: + RequestView() + } + } + + var body: some View { + WithViewStore(store) { viewStore in + VStack { + List { + Section(header: Text("Navigation Stack Routes")) { + ForEach(navigationRouteValues) { routeValue in + Text("\(String(describing: routeValue.route))") + .navigationLink( + isActive: viewStore.bindingForRoute(routeValue.route), + destination: { + view(for: routeValue.route) + } + ) + } + } + + Section(header: Text("Modal Routes")) { + ForEach(modalRoutes) { routeValue in + Button( + action: { viewStore.send(.updateRoute(routeValue.route)) }, + label: { Text("\(String(describing: routeValue.route))") } + ) + } + } + + Section(header: Text("Other Actions")) { + Button( + action: { viewStore.toggleSelectedTransaction() }, + label: { Text("Toggle Selected Transaction") } + ) + + Button( + action: { viewStore.send(.reset) }, + label: { Text("Reset (to startup)") } + ) + } + } + } + .fullScreenCover( + isPresented: viewStore.bindingForRoute(.history), + content: { + NavigationView { + TransactionHistoryView(store: store.historyStore()) + .toolbar { + ToolbarItem { + Button("Done") { viewStore.send(.updateRoute(nil)) } + } + } + } + } + ) + .navigationBarTitle("Sandbox") + } + } +} + +struct SandboxRouteValue: Identifiable { + let id: Int + let route: SandboxState.Route +} + +// MARK: - Previews + +extension SandboxStore { + static var placeholder: SandboxStore { + SandboxStore( + initialState: SandboxState( + transactionHistoryState: .placeHolder, + profileState: .placeholder, + route: nil + ), + reducer: .default.debug(), + environment: () + ) + } +} + +struct SandboxView_Previews: PreviewProvider { + static var previews: some View { + NavigationView { + SandboxView(store: .placeholder) + } + } +} diff --git a/secant/Features/Welcome/Welcome.swift b/secant/Features/Welcome/Welcome.swift index 4d1a477..8107fd3 100644 --- a/secant/Features/Welcome/Welcome.swift +++ b/secant/Features/Welcome/Welcome.swift @@ -16,7 +16,6 @@ extension WelcomeState { enum WelcomeAction: Equatable { case debugMenuStartup - case debugMenuHome } typealias WelcomeReducer = Reducer diff --git a/secant/Features/Welcome/WelcomeView.swift b/secant/Features/Welcome/WelcomeView.swift index 3cd2568..ab1c4c4 100644 --- a/secant/Features/Welcome/WelcomeView.swift +++ b/secant/Features/Welcome/WelcomeView.swift @@ -41,9 +41,7 @@ struct WelcomeView: View { .onEnded { value in guard case .second(true, let drag?) = value else { return } - if drag.translation.height < 0 { - ViewStore(store).send(.debugMenuHome) - } else { + if drag.translation.height > 0 { ViewStore(store).send(.debugMenuStartup) } }