Issue #276 - [Scaffold] Drawer for the Home Screen

Drawer + transactions history

clean up
This commit is contained in:
Lukas Korba 2022-04-20 16:45:24 +02:00 committed by GitHub
parent a15596b3aa
commit 1a16282899
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 228 additions and 26 deletions

View File

@ -96,6 +96,7 @@
9E2DF99E27CF704D00649636 /* ImportWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99B27CF704D00649636 /* ImportWalletView.swift */; };
9E2F1C8228095AFE004E65FE /* Int64+Zcash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2F1C8128095AFE004E65FE /* Int64+Zcash.swift */; };
9E2F1C842809B606004E65FE /* DebugMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2F1C832809B606004E65FE /* DebugMenu.swift */; };
9E2F1C8F280EDE09004E65FE /* Drawer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2F1C8E280EDE09004E65FE /* Drawer.swift */; };
9E37A2B827C8F59F00AE57B3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9E37A2B727C8F59F00AE57B3 /* Localizable.strings */; };
9E4DC6E027C409A100E657F4 /* NeumorphicDesignModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6DF27C409A100E657F4 /* NeumorphicDesignModifier.swift */; };
9E4DC6E227C4C6B700E657F4 /* SecantButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */; };
@ -257,6 +258,7 @@
9E2DF99B27CF704D00649636 /* ImportWalletView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportWalletView.swift; sourceTree = "<group>"; };
9E2F1C8128095AFE004E65FE /* Int64+Zcash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int64+Zcash.swift"; sourceTree = "<group>"; };
9E2F1C832809B606004E65FE /* DebugMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugMenu.swift; sourceTree = "<group>"; };
9E2F1C8E280EDE09004E65FE /* Drawer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Drawer.swift; sourceTree = "<group>"; };
9E37A2B727C8F59F00AE57B3 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = "<group>"; };
9E4DC6DF27C409A100E657F4 /* NeumorphicDesignModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NeumorphicDesignModifier.swift; sourceTree = "<group>"; };
9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecantButtonStyles.swift; sourceTree = "<group>"; };
@ -515,6 +517,7 @@
0DA13C9126C15E1900E3B610 /* UIComponents */ = {
isa = PBXGroup;
children = (
9E2F1C8D280EDDEF004E65FE /* Drawer */,
2E35F99027B28E6800EB79CD /* TextFields */,
0D0781C5278776B90083ACD7 /* Shapes */,
0D8A43C2272AEEA7005A6414 /* FontStyles */,
@ -765,6 +768,14 @@
path = Views;
sourceTree = "<group>";
};
9E2F1C8D280EDDEF004E65FE /* Drawer */ = {
isa = PBXGroup;
children = (
9E2F1C8E280EDE09004E65FE /* Drawer.swift */,
);
path = Drawer;
sourceTree = "<group>";
};
9EAFEB802805791400199FC9 /* AppReducer */ = {
isa = PBXGroup;
children = (
@ -1208,6 +1219,7 @@
0D5D16F526E24CCF00AD33D1 /* AppError.swift in Sources */,
0D18581B272728D60046B928 /* PhraseChip.swift in Sources */,
0DF482BA2787ADA800EB37D6 /* ConditionalModifier.swift in Sources */,
9E2F1C8F280EDE09004E65FE /* Drawer.swift in Sources */,
665C963F272C26E600BC04FB /* CircularFrameBackground.swift in Sources */,
9EAFEB882806E5AE00199FC9 /* CombineSynchronizer.swift in Sources */,
0DB8AA81271DC7520035BC9D /* DesignGuide.swift in Sources */,

View File

@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.271",
"green" : "0.125",
"red" : "0.075"
"blue" : "0x45",
"green" : "0x1F",
"red" : "0x13"
}
},
"idiom" : "universal"

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.100",
"blue" : "0.000",
"green" : "0.000",
"red" : "0.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x45",
"green" : "0x1F",
"red" : "0x13"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -9,13 +9,16 @@ struct AppView: View {
WithViewStore(store) { viewStore in
switch viewStore.route {
case .home:
HomeView(
store: store.scope(
state: \.homeState,
action: AppAction.home
NavigationView {
HomeView(
store: store.scope(
state: \.homeState,
action: AppAction.home
)
)
)
}
.navigationViewStyle(StackNavigationViewStyle())
case .sandbox:
NavigationView {
SandboxView(

View File

@ -2,15 +2,20 @@ import ComposableArchitecture
import SwiftUI
struct HomeState: Equatable {
var totalBalance: Double
var verifiedBalance: Double
var arePublishersPrepared = false
var drawerOverlay: DrawerOverlay
var totalBalance: Double
var transactionHistoryState: TransactionHistoryState
var verifiedBalance: Double
}
enum HomeAction: Equatable {
case debugMenuStartup
case preparePublishers
case transactionHistory(TransactionHistoryAction)
case updateBalance(Balance)
case updateDrawer(DrawerOverlay)
}
struct HomeEnvironment {
@ -43,6 +48,16 @@ extension HomeReducer {
case .debugMenuStartup:
return .none
case .updateDrawer(let drawerOverlay):
state.drawerOverlay = drawerOverlay
return .none
case .transactionHistory(let historyAction):
return TransactionHistoryReducer
.default
.run(&state.transactionHistoryState, historyAction, ())
.map(HomeAction.transactionHistory)
}
}
}
@ -51,11 +66,36 @@ extension HomeReducer {
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 bindingForDrawer() -> Binding<DrawerOverlay> {
self.binding(
get: { $0.drawerOverlay },
send: { .updateDrawer($0) }
)
}
}
// MARK: PlaceHolders
extension HomeState {
static var placeholder: Self {
.init(
drawerOverlay: .partial,
totalBalance: 0.0,
transactionHistoryState: .placeHolder,
verifiedBalance: 0.0
)
}

View File

@ -6,12 +6,29 @@ struct HomeView: View {
var body: some View {
WithViewStore(store) { viewStore in
VStack(alignment: .center, spacing: 30.0) {
Text("totalBalance \(viewStore.totalBalance)")
Text("verifiedBalance \(viewStore.verifiedBalance)")
.accessDebugMenuWithHiddenGesture {
viewStore.send(.debugMenuStartup)
GeometryReader { proxy in
ZStack {
VStack {
Text("totalBalance \(viewStore.totalBalance)")
Text("verifiedBalance \(viewStore.verifiedBalance)")
.accessDebugMenuWithHiddenGesture {
viewStore.send(.debugMenuStartup)
}
Spacer()
}
Drawer(overlay: viewStore.bindingForDrawer(), maxHeight: proxy.size.height) {
VStack {
TransactionHistoryView(store: store.historyStore())
.padding(.top, 10)
Spacer()
}
.applyScreenBackground()
}
}
.applyScreenBackground()
}
.onAppear(perform: { viewStore.send(.preparePublishers) })
}

View File

@ -6,18 +6,15 @@ struct TransactionHistoryView: View {
var body: some View {
WithViewStore(store) { viewStore in
List {
ForEach(viewStore.transactions) { transaction in
WithStateBinding(binding: viewStore.bindingForSelectingTransaction(transaction)) {
Text("Show Transaction \(transaction.id)")
.navigationLink(
isActive: $0,
destination: { TransactionDetailView(transaction: transaction) }
)
}
ForEach(viewStore.transactions) { transaction in
WithStateBinding(binding: viewStore.bindingForSelectingTransaction(transaction)) {
Text("Show Transaction \(transaction.id)")
.navigationLink(
isActive: $0,
destination: { TransactionDetailView(transaction: transaction) }
)
}
}
.navigationTitle(Text("Transactions"))
}
}
}

View File

@ -92,6 +92,7 @@ internal enum Asset {
internal static let redGradientStart = ColorAsset(name: "redGradientStart")
}
internal enum Shadow {
internal static let drawerShadow = ColorAsset(name: "drawerShadow")
internal static let emptyChipInnerShadow = ColorAsset(name: "emptyChipInnerShadow")
internal static let numberedTextShadow = ColorAsset(name: "numberedTextShadow")
}

View File

@ -0,0 +1,94 @@
//
// Drawer.swift
// secant-testnet
//
// Created by Lukáš Korba on 19.04.2022.
//
import SwiftUI
enum DrawerOverlay {
case full
case partial
case bottom
}
struct Drawer<Content: View>: View {
@GestureState private var translation: CGFloat = 0
@Binding var overlay: DrawerOverlay
let maxHeight: CGFloat
let content: Content
private var offset: CGFloat {
switch overlay {
case .full: return 70.0
case .partial: return maxHeight * 0.75
case .bottom: return maxHeight
}
}
init(overlay: Binding<DrawerOverlay>, maxHeight: CGFloat, @ViewBuilder content: () -> Content) {
self._overlay = overlay
self.maxHeight = maxHeight
self.content = content()
}
var body: some View {
GeometryReader { proxy in
ZStack {
VStack {
RoundedRectangle(cornerRadius: 16.0)
.fill(Color.secondary)
.opacity(0.2)
.frame(
width: 50,
height: 6
)
.padding(.top, 10)
content
}
}
.frame(width: proxy.size.width, height: maxHeight, alignment: .top)
.applyScreenBackground()
.background(Color(.secondarySystemBackground))
.cornerRadius(16.0)
.frame(height: proxy.size.height, alignment: .bottom)
.offset(y: max(offset + translation, 0))
.animation(.interactiveSpring())
.gesture(
DragGesture().updating($translation) { value, state, _ in
state = value.translation.height
}
.onEnded { value in
let snapDistanceFull = maxHeight * 0.55
let snapDistancePartial = maxHeight * 0.8
if value.location.y <= snapDistanceFull {
overlay = .full
} else if value.location.y > snapDistanceFull && value.location.y <= snapDistancePartial {
overlay = .partial
} else {
overlay = .bottom
}
}
)
}
.shadow(color: Asset.Colors.Shadow.drawerShadow.color, radius: 15.0, x: 0.0, y: -4.0)
}
}
struct Drawer_Previews: PreviewProvider {
static var previews: some View {
@State var overlay: DrawerOverlay = .partial
return Drawer(overlay: $overlay, maxHeight: 800.0) {
VStack {
Text("Transaction History")
Spacer()
}
}
}
}