old home->sandbox, new home for #255

hotfix
This commit is contained in:
Lukas Korba 2022-04-14 11:25:05 +02:00
parent d6289afdd1
commit 330bc3256f
9 changed files with 309 additions and 209 deletions

View File

@ -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 = "<group>"; };
9E69A24C27FB002800A55317 /* Welcome.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Welcome.swift; sourceTree = "<group>"; };
9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorage.swift; sourceTree = "<group>"; };
9EAFEB812805793200199FC9 /* AppReducerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppReducerTests.swift; sourceTree = "<group>"; };
9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItem.swift; sourceTree = "<group>"; };
9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItemTests.swift; sourceTree = "<group>"; };
9EAFEB812805793200199FC9 /* AppReducerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppReducerTests.swift; sourceTree = "<group>"; };
9EAFEB8D2808183D00199FC9 /* SandboxView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SandboxView.swift; sourceTree = "<group>"; };
9EAFEB8E2808183D00199FC9 /* SandboxStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SandboxStore.swift; sourceTree = "<group>"; };
9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseTestPreambleView.swift; sourceTree = "<group>"; };
9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseFiles.swift; sourceTree = "<group>"; };
9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletStorageTests.swift; sourceTree = "<group>"; };
@ -664,6 +668,7 @@
6654C73B2715A3F000901167 /* Features */ = {
isa = PBXGroup;
children = (
9EAFEB8B2808174900199FC9 /* Sandbox */,
0D0781C2278750C00083ACD7 /* Welcome */,
F9971A4927680DC400A2DB75 /* App */,
F93874EC273C4DE200F0E875 /* Home */,
@ -768,6 +773,23 @@
path = AppReducer;
sourceTree = "<group>";
};
9EAFEB8B2808174900199FC9 /* Sandbox */ = {
isa = PBXGroup;
children = (
9EAFEB8E2808183D00199FC9 /* SandboxStore.swift */,
9EAFEB8C2808183D00199FC9 /* Views */,
);
path = Sandbox;
sourceTree = "<group>";
};
9EAFEB8C2808183D00199FC9 /* Views */ = {
isa = PBXGroup;
children = (
9EAFEB8D2808183D00199FC9 /* SandboxView.swift */,
);
path = Views;
sourceTree = "<group>";
};
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 */,

View File

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

View File

@ -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") {

View File

@ -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<HomeState, HomeAction, Void>
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<HomeState, HomeAction>
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<HomeState, HomeAction>
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<Bool> {
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
)
}
}

View File

@ -4,113 +4,21 @@ import ComposableArchitecture
struct HomeView: View {
let store: Store<HomeState, HomeAction>
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: ()
)

View File

@ -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<SandboxState, SandboxAction, Void>
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<SandboxState, SandboxAction>
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<SandboxState, SandboxAction>
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<Bool> {
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
)
}
}

View File

@ -0,0 +1,126 @@
import SwiftUI
import ComposableArchitecture
struct SandboxView: View {
let store: Store<SandboxState, SandboxAction>
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)
}
}
}

View File

@ -16,7 +16,6 @@ extension WelcomeState {
enum WelcomeAction: Equatable {
case debugMenuStartup
case debugMenuHome
}
typealias WelcomeReducer = Reducer<WelcomeState, WelcomeAction, Void>

View File

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