From bab53181aad860c4bb493156353b4937fce040da Mon Sep 17 00:00:00 2001 From: Lukas Korba Date: Wed, 20 Apr 2022 18:41:32 +0200 Subject: [PATCH] Issue #276 - [Scaffold] Drawer for the Home Screen (#275) Drawer + transactions history clean up Profile button on home screen QR, Send and Request buttons cleanup conflicts resolved + cleanup --- secant.xcodeproj/project.pbxproj | 24 ++-- secant/Features/Home/HomeStore.swift | 90 +++++++++++-- secant/Features/Home/Views/HomeView.swift | 127 +++++++++++++++++- .../{Profile.swift => ProfileStore.swift} | 0 .../{Request.swift => RequestStore.swift} | 15 ++- .../Features/Request/Views/RequestView.swift | 9 +- .../Features/Sandbox/Views/SandboxView.swift | 4 +- .../Scan/{Scan.swift => ScanStore.swift} | 15 ++- secant/Features/Scan/Views/ScanView.swift | 9 +- secant/Localizable.strings | 4 + 10 files changed, 266 insertions(+), 31 deletions(-) rename secant/Features/Profile/{Profile.swift => ProfileStore.swift} (100%) rename secant/Features/Request/{Request.swift => RequestStore.swift} (69%) rename secant/Features/Scan/{Scan.swift => ScanStore.swift} (67%) diff --git a/secant.xcodeproj/project.pbxproj b/secant.xcodeproj/project.pbxproj index ff1a131..9d2fd7e 100644 --- a/secant.xcodeproj/project.pbxproj +++ b/secant.xcodeproj/project.pbxproj @@ -127,12 +127,12 @@ F96B41EB273B50520021B49A /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41EA273B50520021B49A /* Strings.swift */; }; F9971A4D27680DC400A2DB75 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A4A27680DC400A2DB75 /* App.swift */; }; F9971A4E27680DC400A2DB75 /* AppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A4C27680DC400A2DB75 /* AppView.swift */; }; - F9971A5327680DD000A2DB75 /* Profile.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A5027680DD000A2DB75 /* Profile.swift */; }; + F9971A5327680DD000A2DB75 /* ProfileStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A5027680DD000A2DB75 /* ProfileStore.swift */; }; F9971A5427680DD000A2DB75 /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A5227680DD000A2DB75 /* ProfileView.swift */; }; - F9971A5927680DDE00A2DB75 /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A5627680DDE00A2DB75 /* Request.swift */; }; + F9971A5927680DDE00A2DB75 /* RequestStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A5627680DDE00A2DB75 /* RequestStore.swift */; }; F9971A5A27680DDE00A2DB75 /* RequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A5827680DDE00A2DB75 /* RequestView.swift */; }; F9971A5F27680DF600A2DB75 /* ScanView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A5D27680DF600A2DB75 /* ScanView.swift */; }; - F9971A6027680DF600A2DB75 /* Scan.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A5E27680DF600A2DB75 /* Scan.swift */; }; + F9971A6027680DF600A2DB75 /* ScanStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A5E27680DF600A2DB75 /* ScanStore.swift */; }; F9971A6527680DFE00A2DB75 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A6227680DFE00A2DB75 /* Settings.swift */; }; F9971A6627680DFE00A2DB75 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A6427680DFE00A2DB75 /* SettingsView.swift */; }; F9971A6B27680E1000A2DB75 /* WalletInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A6827680E1000A2DB75 /* WalletInfo.swift */; }; @@ -288,12 +288,12 @@ F96B41EA273B50520021B49A /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = ""; }; F9971A4A27680DC400A2DB75 /* App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; F9971A4C27680DC400A2DB75 /* AppView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppView.swift; sourceTree = ""; }; - F9971A5027680DD000A2DB75 /* Profile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Profile.swift; sourceTree = ""; }; + F9971A5027680DD000A2DB75 /* ProfileStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProfileStore.swift; sourceTree = ""; }; F9971A5227680DD000A2DB75 /* ProfileView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = ""; }; - F9971A5627680DDE00A2DB75 /* Request.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = ""; }; + F9971A5627680DDE00A2DB75 /* RequestStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestStore.swift; sourceTree = ""; }; F9971A5827680DDE00A2DB75 /* RequestView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestView.swift; sourceTree = ""; }; F9971A5D27680DF600A2DB75 /* ScanView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScanView.swift; sourceTree = ""; }; - F9971A5E27680DF600A2DB75 /* Scan.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Scan.swift; sourceTree = ""; }; + F9971A5E27680DF600A2DB75 /* ScanStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScanStore.swift; sourceTree = ""; }; F9971A6227680DFE00A2DB75 /* Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; F9971A6427680DFE00A2DB75 /* SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; F9971A6827680E1000A2DB75 /* WalletInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletInfo.swift; sourceTree = ""; }; @@ -876,7 +876,7 @@ F9971A4F27680DD000A2DB75 /* Profile */ = { isa = PBXGroup; children = ( - F9971A5027680DD000A2DB75 /* Profile.swift */, + F9971A5027680DD000A2DB75 /* ProfileStore.swift */, F9971A5127680DD000A2DB75 /* Views */, ); path = Profile; @@ -893,7 +893,7 @@ F9971A5527680DDE00A2DB75 /* Request */ = { isa = PBXGroup; children = ( - F9971A5627680DDE00A2DB75 /* Request.swift */, + F9971A5627680DDE00A2DB75 /* RequestStore.swift */, F9971A5727680DDE00A2DB75 /* Views */, ); path = Request; @@ -910,8 +910,8 @@ F9971A5B27680DF600A2DB75 /* Scan */ = { isa = PBXGroup; children = ( + F9971A5E27680DF600A2DB75 /* ScanStore.swift */, F9971A5C27680DF600A2DB75 /* Views */, - F9971A5E27680DF600A2DB75 /* Scan.swift */, ); path = Scan; sourceTree = ""; @@ -1210,7 +1210,7 @@ 0DDB6A5127737D4A0012A410 /* ValidationFailedView.swift in Sources */, 0D6D628B276A528E002FB4CC /* DropDelegate.swift in Sources */, 9E2DF99D27CF704D00649636 /* ImportSeedEditor.swift in Sources */, - F9971A5327680DD000A2DB75 /* Profile.swift in Sources */, + F9971A5327680DD000A2DB75 /* ProfileStore.swift in Sources */, 669FDAEB272C23C2007B9422 /* CircularFrameBadge.swift in Sources */, 2E8719CD27FB0D3B0082C926 /* TransactionCurrencySelector.swift in Sources */, F9971A6C27680E1000A2DB75 /* WalletInfoView.swift in Sources */, @@ -1271,7 +1271,7 @@ F96B41EB273B50520021B49A /* Strings.swift in Sources */, 2EDA07A227EDE1AE00D6F09B /* TextFieldFooter.swift in Sources */, F9971A5427680DD000A2DB75 /* ProfileView.swift in Sources */, - F9971A6027680DF600A2DB75 /* Scan.swift in Sources */, + F9971A6027680DF600A2DB75 /* ScanStore.swift in Sources */, 9EF8139127F191BF0075AF48 /* WalletStorageInteractor.swift in Sources */, 0DFE93E1272C9ECB000FCCA5 /* RecoveryPhraseBackupValidationView.swift in Sources */, 9E69A24D27FB002800A55317 /* Welcome.swift in Sources */, @@ -1283,7 +1283,7 @@ 2E8719CB27FB09990082C926 /* TransactionTextField.swift in Sources */, 6654C7412715A47300901167 /* Onboarding.swift in Sources */, F9C165C42740403600592F76 /* SentView.swift in Sources */, - F9971A5927680DDE00A2DB75 /* Request.swift in Sources */, + F9971A5927680DDE00A2DB75 /* RequestStore.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/secant/Features/Home/HomeStore.swift b/secant/Features/Home/HomeStore.swift index 92d0cc8..84f4d4f 100644 --- a/secant/Features/Home/HomeStore.swift +++ b/secant/Features/Home/HomeStore.swift @@ -2,9 +2,21 @@ import ComposableArchitecture import SwiftUI struct HomeState: Equatable { + enum Route: Equatable { + case profile + case request + case send + case scan + } + var arePublishersPrepared = false + var route: Route? var drawerOverlay: DrawerOverlay + var profileState: ProfileState + var requestState: RequestState + var sendState: SendState + var scanState: ScanState var totalBalance: Double var transactionHistoryState: TransactionHistoryState var verifiedBalance: Double @@ -13,9 +25,14 @@ struct HomeState: Equatable { enum HomeAction: Equatable { case debugMenuStartup case preparePublishers + case profile(ProfileAction) + case request(RequestAction) + case send(SendAction) + case scan(ScanAction) case transactionHistory(TransactionHistoryAction) case updateBalance(Balance) case updateDrawer(DrawerOverlay) + case updateRoute(HomeState.Route?) } struct HomeEnvironment { @@ -58,10 +75,48 @@ extension HomeReducer { .default .run(&state.transactionHistoryState, historyAction, ()) .map(HomeAction.transactionHistory) + + case .updateRoute(let route): + state.route = route + return .none + + case .profile(let action): + return .none + + case .request(let action): + return .none + + case .send(let action): + return .none + + case .scan(let action): + return .none } } } +// MARK: - HomeViewStore + +typealias HomeViewStore = ViewStore + +extension HomeViewStore { + func bindingForRoute(_ route: HomeState.Route) -> Binding { + self.binding( + get: { $0.route == route }, + send: { isActive in + return .updateRoute(isActive ? route : nil) + } + ) + } + + func bindingForDrawer() -> Binding { + self.binding( + get: { $0.drawerOverlay }, + send: { .updateDrawer($0) } + ) + } +} + // MARK: - HomeStore typealias HomeStore = Store @@ -73,17 +128,32 @@ extension HomeStore { action: HomeAction.transactionHistory ) } -} + + func profileStore() -> ProfileStore { + self.scope( + state: \.profileState, + action: HomeAction.profile + ) + } -// MARK: - HomeViewStore + func requestStore() -> RequestStore { + self.scope( + state: \.requestState, + action: HomeAction.request + ) + } -typealias HomeViewStore = ViewStore + func sendStore() -> SendStore { + self.scope( + state: \.sendState, + action: HomeAction.send + ) + } -extension HomeViewStore { - func bindingForDrawer() -> Binding { - self.binding( - get: { $0.drawerOverlay }, - send: { .updateDrawer($0) } + func scanStore() -> ScanStore { + self.scope( + state: \.scanState, + action: HomeAction.scan ) } } @@ -94,6 +164,10 @@ extension HomeState { static var placeholder: Self { .init( drawerOverlay: .partial, + profileState: .placeholder, + requestState: .placeholder, + sendState: .placeholder, + scanState: .placeholder, totalBalance: 0.0, transactionHistoryState: .placeHolder, verifiedBalance: 0.0 diff --git a/secant/Features/Home/Views/HomeView.swift b/secant/Features/Home/Views/HomeView.swift index 18ebbc4..b0c6000 100644 --- a/secant/Features/Home/Views/HomeView.swift +++ b/secant/Features/Home/Views/HomeView.swift @@ -8,12 +8,21 @@ struct HomeView: View { WithViewStore(store) { viewStore in GeometryReader { proxy in ZStack { + scanButton(viewStore) + + profileButton(viewStore) + + sendButton(viewStore) + + requestButton(viewStore) + .padding(.top, 140) + VStack { - Text("totalBalance \(viewStore.totalBalance)") - Text("verifiedBalance \(viewStore.verifiedBalance)") + Text("balance: \(viewStore.totalBalance)") .accessDebugMenuWithHiddenGesture { viewStore.send(.debugMenuStartup) } + .padding(.top, 180) Spacer() } @@ -29,8 +38,115 @@ struct HomeView: View { } } .applyScreenBackground() + .navigationBarHidden(true) + .applyScreenBackground() + .onAppear(perform: { viewStore.send(.preparePublishers) }) } - .onAppear(perform: { viewStore.send(.preparePublishers) }) + } + } +} + +// MARK: - Buttons + +extension HomeView { + func profileButton(_ viewStore: HomeViewStore) -> some View { + VStack { + HStack { + Spacer() + + Image(Asset.Assets.Icons.profile.name) + .resizable() + .frame(width: 60, height: 60) + .padding(.trailing, 15) + .navigationLink( + isActive: viewStore.bindingForRoute(.profile), + destination: { + ProfileView(store: store.profileStore()) + } + ) + } + + Spacer() + } + } + + func requestButton(_ viewStore: HomeViewStore) -> some View { + VStack { + Spacer() + + Text("home.request") + .shadow(color: Asset.Colors.Buttons.buttonsTitleShadow.color, radius: 2, x: 0, y: 2) + .frame( + minWidth: 0, + maxWidth: .infinity, + minHeight: 0, + maxHeight: .infinity + ) + .foregroundColor(Asset.Colors.Text.secondaryButtonText.color) + .background(Asset.Colors.Buttons.secondaryButton.color) + .cornerRadius(12) + .frame(height: 60) + .padding(.horizontal, 50) + .neumorphicButton() + .navigationLink( + isActive: viewStore.bindingForRoute(.request), + destination: { + RequestView(store: store.requestStore()) + } + ) + + Spacer() + } + } + + func sendButton(_ viewStore: HomeViewStore) -> some View { + VStack { + Spacer() + + Text("Send") + .shadow(color: Asset.Colors.Buttons.buttonsTitleShadow.color, radius: 2, x: 0, y: 2) + .frame( + minWidth: 0, + maxWidth: .infinity, + minHeight: 0, + maxHeight: .infinity + ) + .foregroundColor(Asset.Colors.Text.activeButtonText.color) + .background(Asset.Colors.Buttons.activeButton.color) + .cornerRadius(12) + .frame(height: 60) + .padding(.horizontal, 50) + .neumorphicButton() + .navigationLink( + isActive: viewStore.bindingForRoute(.send), + destination: { + SendView(store: store.sendStore()) + } + ) + + Spacer() + } + } + + func scanButton(_ viewStore: HomeViewStore) -> some View { + VStack { + HStack { + Image(Asset.Assets.Icons.qrCode.name) + .resizable() + .frame(width: 40, height: 40) + .padding(.top, 7) + .padding(.leading, 22) + .navigationLink( + isActive: viewStore.bindingForRoute(.scan), + destination: { + ScanView(store: store.scanStore()) + } + ) + + Spacer() + } + + Spacer() } } } @@ -54,5 +170,10 @@ struct HomeView_Previews: PreviewProvider { NavigationView { HomeView(store: .placeholder) } + + NavigationView { + HomeView(store: .placeholder) + .preferredColorScheme(.dark) + } } } diff --git a/secant/Features/Profile/Profile.swift b/secant/Features/Profile/ProfileStore.swift similarity index 100% rename from secant/Features/Profile/Profile.swift rename to secant/Features/Profile/ProfileStore.swift diff --git a/secant/Features/Request/Request.swift b/secant/Features/Request/RequestStore.swift similarity index 69% rename from secant/Features/Request/Request.swift rename to secant/Features/Request/RequestStore.swift index 89beb84..44bbf0c 100644 --- a/secant/Features/Request/Request.swift +++ b/secant/Features/Request/RequestStore.swift @@ -7,7 +7,7 @@ enum RequestAction: Equatable { case noOp } -struct RequestEnvironment: Equatable { +struct RequestEnvironment { } // MARK: - RequestReducer @@ -28,6 +28,11 @@ extension RequestReducer { typealias RequestStore = Store extension RequestStore { + static let placeholder = RequestStore( + initialState: .placeholder, + reducer: .default, + environment: RequestEnvironment() + ) } // MARK: - RequestViewStore @@ -36,3 +41,11 @@ typealias RequestViewStore = ViewStore extension RequestViewStore { } + +// MARK: PlaceHolders + +extension RequestState { + static var placeholder: Self { + .init() + } +} diff --git a/secant/Features/Request/Views/RequestView.swift b/secant/Features/Request/Views/RequestView.swift index cb89f28..81c8bad 100644 --- a/secant/Features/Request/Views/RequestView.swift +++ b/secant/Features/Request/Views/RequestView.swift @@ -1,13 +1,18 @@ import SwiftUI +import ComposableArchitecture struct RequestView: View { + let store: RequestStore + var body: some View { - Text("\(String(describing: Self.self)) PlaceHolder") + WithViewStore(store) { _ in + Text("\(String(describing: Self.self)) PlaceHolder") + } } } struct RequestView_Previews: PreviewProvider { static var previews: some View { - RequestView() + RequestView(store: .placeholder) } } diff --git a/secant/Features/Sandbox/Views/SandboxView.swift b/secant/Features/Sandbox/Views/SandboxView.swift index c7fc1c1..98819bc 100644 --- a/secant/Features/Sandbox/Views/SandboxView.swift +++ b/secant/Features/Sandbox/Views/SandboxView.swift @@ -32,11 +32,11 @@ struct SandboxView: View { case .recoveryPhraseDisplay: RecoveryPhraseDisplayView(store: .demo) case .scan: - ScanView() + ScanView(store: .placeholder) case .profile: ProfileView(store: store.profileStore()) case .request: - RequestView() + RequestView(store: .placeholder) } } diff --git a/secant/Features/Scan/Scan.swift b/secant/Features/Scan/ScanStore.swift similarity index 67% rename from secant/Features/Scan/Scan.swift rename to secant/Features/Scan/ScanStore.swift index 3f745e4..596af0e 100644 --- a/secant/Features/Scan/Scan.swift +++ b/secant/Features/Scan/ScanStore.swift @@ -7,7 +7,7 @@ enum ScanAction: Equatable { case noOp } -struct ScanEnvironment: Equatable { +struct ScanEnvironment { } // MARK: - ScanReducer @@ -28,6 +28,11 @@ extension ScanReducer { typealias ScanStore = Store extension ScanStore { + static let placeholder = ScanStore( + initialState: .placeholder, + reducer: .default, + environment: ScanEnvironment() + ) } // MARK: - ScanViewStore @@ -36,3 +41,11 @@ typealias ScanViewStore = ViewStore extension ScanViewStore { } + +// MARK: PlaceHolders + +extension ScanState { + static var placeholder: Self { + .init() + } +} diff --git a/secant/Features/Scan/Views/ScanView.swift b/secant/Features/Scan/Views/ScanView.swift index 5974a1b..c1dbe01 100644 --- a/secant/Features/Scan/Views/ScanView.swift +++ b/secant/Features/Scan/Views/ScanView.swift @@ -1,13 +1,18 @@ import SwiftUI +import ComposableArchitecture struct ScanView: View { + let store: ScanStore + var body: some View { - Text("\(String(describing: Self.self)) PlaceHolder") + WithViewStore(store) { _ in + Text("\(String(describing: Self.self)) PlaceHolder") + } } } struct ScanView_Previews: PreviewProvider { static var previews: some View { - ScanView() + ScanView(store: .placeholder) } } diff --git a/secant/Localizable.strings b/secant/Localizable.strings index 7974866..f8404cc 100644 --- a/secant/Localizable.strings +++ b/secant/Localizable.strings @@ -55,7 +55,11 @@ "importWallet.button.importPhrase" = "Import Recovery Phrase"; "importWallet.button.importPrivateKey" = "Import a private or viewing key"; +// MARK: - Home Screen +"home.request" = "Request ZEC"; + // MARK: - Common & Shared "Back" = "Back"; "Skip" = "Skip"; "Next" = "Next"; +"Send" = "Send";