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:
Daniel Haight 2021-11-10 20:09:17 +00:00
parent 9724d22235
commit e458a491c4
4 changed files with 189 additions and 3 deletions

View File

@ -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 */,

View File

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

View File

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

View File

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