Add `Home` TCA feature
- Add the `State/Action/Reducer` combination to represent a basic TCA feature - There is a `route` property on the state which represents our navigation state (we will use this to drive more navigation state in the next commit) - The state has a property which is a `TransactionHistoryState` we create our new reducer by leaning mostly on the existing one. - Temporarily we set up a text representation of our state - Temporarily Use `HomeView` as the app's entry point, to be replaced with an `AppView`
This commit is contained in:
parent
9724d22235
commit
e458a491c4
|
@ -90,6 +90,8 @@
|
|||
66D50668271D9B6100E51F0D /* NavigationButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */; };
|
||||
66DC733F271D88CC0053CBB6 /* StandardButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66DC733E271D88CC0053CBB6 /* StandardButtonStyle.swift */; };
|
||||
F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9322DBF273B555C00C105B5 /* NavigationLinks.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 */; };
|
||||
|
@ -203,6 +205,8 @@
|
|||
66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationButtonStyle.swift; sourceTree = "<group>"; };
|
||||
66DC733E271D88CC0053CBB6 /* StandardButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardButtonStyle.swift; sourceTree = "<group>"; };
|
||||
F9322DBF273B555C00C105B5 /* NavigationLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationLinks.swift; sourceTree = "<group>"; };
|
||||
F93874ED273C4DE200F0E875 /* HomeStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeStore.swift; sourceTree = "<group>"; };
|
||||
F93874EF273C4DE200F0E875 /* HomeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
|
||||
F96B41E3273B501F0021B49A /* TransactionHistoryStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionHistoryStore.swift; sourceTree = "<group>"; };
|
||||
F96B41E5273B501F0021B49A /* TransactionDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionDetailView.swift; sourceTree = "<group>"; };
|
||||
F96B41E6273B501F0021B49A /* TransactionHistoryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionHistoryView.swift; sourceTree = "<group>"; };
|
||||
|
@ -596,6 +600,7 @@
|
|||
6654C73B2715A3F000901167 /* Features */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F93874EC273C4DE200F0E875 /* Home */,
|
||||
F96B41E2273B501F0021B49A /* TransactionHistory */,
|
||||
6654C73C2715A3FA00901167 /* Onboarding */,
|
||||
);
|
||||
|
@ -653,6 +658,23 @@
|
|||
path = CircularFrame;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F93874EC273C4DE200F0E875 /* Home */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F93874ED273C4DE200F0E875 /* HomeStore.swift */,
|
||||
F93874EE273C4DE200F0E875 /* Views */,
|
||||
);
|
||||
path = Home;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F93874EE273C4DE200F0E875 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F93874EF273C4DE200F0E875 /* HomeView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F96B41E2273B501F0021B49A /* TransactionHistory */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -884,6 +906,7 @@
|
|||
0D535FE2271F9476009A9E3E /* EnumeratedChip.swift in Sources */,
|
||||
6654C73E2715A41300901167 /* OnboardingStore.swift in Sources */,
|
||||
0D32281E26C5867D00262533 /* ScanQrScreen.swift in Sources */,
|
||||
F93874F0273C4DE200F0E875 /* HomeStore.swift in Sources */,
|
||||
669FDAEB272C23C2007B9422 /* CircularFrameBadge.swift in Sources */,
|
||||
0D864A0E26E1583000A61879 /* LoadingScreen.swift in Sources */,
|
||||
0DA13C9C26C1942100E3B610 /* BackupWalletScreen.swift in Sources */,
|
||||
|
@ -894,6 +917,7 @@
|
|||
665C963F272C26E600BC04FB /* CircularFrameBackground.swift in Sources */,
|
||||
0DB8AA81271DC7520035BC9D /* DesignGuide.swift in Sources */,
|
||||
F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */,
|
||||
F93874F1273C4DE200F0E875 /* HomeView.swift in Sources */,
|
||||
0D32282326C586A800262533 /* HistoryScreen.swift in Sources */,
|
||||
0D864A0A26E154FD00A61879 /* InitFailedScreenViewModel.swift in Sources */,
|
||||
0DA13CA526C1963000E3B610 /* Balance.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
import ComposableArchitecture
|
||||
import SwiftUI
|
||||
|
||||
struct HomeState: Equatable {
|
||||
enum Route: Equatable {
|
||||
case history
|
||||
case send
|
||||
}
|
||||
var transactionHistoryState: TransactionHistoryState
|
||||
var route: Route?
|
||||
}
|
||||
|
||||
enum HomeAction: Equatable {
|
||||
case updateRoute(HomeState.Route?)
|
||||
case transactionHistory(TransactionHistoryAction)
|
||||
}
|
||||
|
||||
// MARK: - HomeReducer
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - HomeStore
|
||||
|
||||
typealias HomeStore = Store<HomeState, HomeAction>
|
||||
|
||||
extension HomeStore {
|
||||
func historyStore() -> TransactionHistoryStore {
|
||||
self.scope(
|
||||
state: \.transactionHistoryState,
|
||||
action: HomeAction.transactionHistory
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - HomeViewStore
|
||||
|
||||
typealias HomeViewStore = ViewStore<HomeState, HomeAction>
|
||||
|
||||
extension HomeViewStore {
|
||||
func historyToggleString() -> String {
|
||||
let hideShowString = isHistoryActive ? "HIDE" : "SHOW"
|
||||
let selectedString = selectedTranactionID.map { "selected id: \($0)" } ?? "NONE selected"
|
||||
let parts = [hideShowString, "History", selectedString]
|
||||
return parts.joined(separator: " ")
|
||||
}
|
||||
|
||||
func toggleShowingHistory() {
|
||||
send(.updateRoute(isHistoryActive ? nil : .history))
|
||||
}
|
||||
|
||||
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 isHistoryActive: Bool {
|
||||
self.route == .history
|
||||
}
|
||||
|
||||
var selectedTranactionID: Int? {
|
||||
self.transactionHistoryState
|
||||
.route
|
||||
.flatMap(/TransactionHistoryState.Route.showTransaction)
|
||||
.map(\.id)
|
||||
}
|
||||
|
||||
var showHistoryBinding: Binding<Bool> {
|
||||
self.binding(
|
||||
get: { $0.route == .history },
|
||||
send: { isActive in
|
||||
return .updateRoute(isActive ? .history : nil)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
|
||||
struct HomeView: View {
|
||||
let store: Store<HomeState, HomeAction>
|
||||
|
||||
var body: some View {
|
||||
WithViewStore(store) { viewStore in
|
||||
VStack {
|
||||
Button(
|
||||
action: { viewStore.toggleShowingHistory() },
|
||||
label: { Text(viewStore.historyToggleString()) }
|
||||
)
|
||||
.primaryButtonStyle
|
||||
.frame(height: 50)
|
||||
|
||||
Button(
|
||||
action: { viewStore.toggleSelectedTransaction() },
|
||||
label: { Text("Toggle Selected Transaction") }
|
||||
)
|
||||
.primaryButtonStyle
|
||||
.frame(height: 50)
|
||||
|
||||
Spacer()
|
||||
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
Text("Route: \(String(dumping: viewStore.route))")
|
||||
Text("SelectedTransaction: \(String(dumping: viewStore.transactionHistoryState.route.map(/TransactionHistoryState.Route.showTransaction)))")
|
||||
}
|
||||
.multilineTextAlignment(.leading)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.horizontal, 30)
|
||||
.navigationBarTitle("Home", displayMode: .inline)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
#if DEBUG
|
||||
extension HomeStore {
|
||||
static var demo: HomeStore {
|
||||
HomeStore(
|
||||
initialState: HomeState(
|
||||
transactionHistoryState: .init(
|
||||
transactions: .demo,
|
||||
route: nil
|
||||
),
|
||||
route: nil
|
||||
),
|
||||
reducer: .default.debug(),
|
||||
environment: ()
|
||||
)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct HomeView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationView {
|
||||
HomeView(store: .demo)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,11 +9,12 @@ import SwiftUI
|
|||
|
||||
@main
|
||||
struct SecantApp: App {
|
||||
@StateObject var appRouter = AppRouter(services: MockServices())
|
||||
|
||||
var homeStore: HomeStore = .demo
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
appRouter.rootView()
|
||||
NavigationView {
|
||||
HomeView(store: homeStore)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue