diff --git a/secant.xcodeproj/project.pbxproj b/secant.xcodeproj/project.pbxproj index c6b9a3b..88ce944 100644 --- a/secant.xcodeproj/project.pbxproj +++ b/secant.xcodeproj/project.pbxproj @@ -68,6 +68,11 @@ 2EDA07A027EDE18C00D6F09B /* TCATextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EDA079F27EDE18C00D6F09B /* TCATextField.swift */; }; 2EDA07A227EDE1AE00D6F09B /* TextFieldFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EDA07A127EDE1AE00D6F09B /* TextFieldFooter.swift */; }; 2EDA07A427EDE2A900D6F09B /* DebugFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EDA07A327EDE2A900D6F09B /* DebugFrame.swift */; }; + 346715A528E2027D0035F7C4 /* CheckCircleStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346715A428E2027D0035F7C4 /* CheckCircleStore.swift */; }; + 346715A828E20FE40035F7C4 /* TransactionConfirmationSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346715A728E20FE40035F7C4 /* TransactionConfirmationSnapshotTests.swift */; }; + 346D41E428DF0B8600963F36 /* CheckCircle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346D41E328DF0B8600963F36 /* CheckCircle.swift */; }; + 34E0AF0F28DEE4C70034CF37 /* HoldToSendButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E0AF0E28DEE4C70034CF37 /* HoldToSendButton.swift */; }; + 34E0AF1128DEE5220034CF37 /* Wedge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E0AF1028DEE5220034CF37 /* Wedge.swift */; }; 660558E9270C7A54009D6954 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 660558E8270C7A54009D6954 /* Colors.xcassets */; }; 660558F7270C862F009D6954 /* Fonts+Generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660558F5270C862F009D6954 /* Fonts+Generated.swift */; }; 660558F8270C862F009D6954 /* XCAssets+Generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660558F6270C862F009D6954 /* XCAssets+Generated.swift */; }; @@ -302,6 +307,11 @@ 2EDA079F27EDE18C00D6F09B /* TCATextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TCATextField.swift; sourceTree = ""; }; 2EDA07A127EDE1AE00D6F09B /* TextFieldFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldFooter.swift; sourceTree = ""; }; 2EDA07A327EDE2A900D6F09B /* DebugFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugFrame.swift; sourceTree = ""; }; + 346715A428E2027D0035F7C4 /* CheckCircleStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckCircleStore.swift; sourceTree = ""; }; + 346715A728E20FE40035F7C4 /* TransactionConfirmationSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionConfirmationSnapshotTests.swift; sourceTree = ""; }; + 346D41E328DF0B8600963F36 /* CheckCircle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckCircle.swift; sourceTree = ""; }; + 34E0AF0E28DEE4C70034CF37 /* HoldToSendButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HoldToSendButton.swift; sourceTree = ""; }; + 34E0AF1028DEE5220034CF37 /* Wedge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wedge.swift; sourceTree = ""; }; 660558E8270C7A54009D6954 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; 660558F5270C862F009D6954 /* Fonts+Generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Fonts+Generated.swift"; sourceTree = ""; }; 660558F6270C862F009D6954 /* XCAssets+Generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XCAssets+Generated.swift"; sourceTree = ""; }; @@ -488,6 +498,7 @@ 0D0781C5278776B90083ACD7 /* Shapes */ = { isa = PBXGroup; children = ( + 34E0AF1028DEE5220034CF37 /* Wedge.swift */, 0D0781C7278776D20083ACD7 /* ZcashSymbol.swift */, ); path = Shapes; @@ -707,6 +718,23 @@ path = Components; sourceTree = ""; }; + 346715A628E20FB30035F7C4 /* SendSnapshotTests */ = { + isa = PBXGroup; + children = ( + 346715A728E20FE40035F7C4 /* TransactionConfirmationSnapshotTests.swift */, + ); + path = SendSnapshotTests; + sourceTree = ""; + }; + 346D41E228DF0B0900963F36 /* CheckCircle */ = { + isa = PBXGroup; + children = ( + 346D41E328DF0B8600963F36 /* CheckCircle.swift */, + 346715A428E2027D0035F7C4 /* CheckCircleStore.swift */, + ); + path = CheckCircle; + sourceTree = ""; + }; 663FAB9A271D873300E495F8 /* Buttons */ = { isa = PBXGroup; children = ( @@ -866,17 +894,18 @@ 9E391162284E3ECF0073DD9A /* SnapshotTests */ = { isa = PBXGroup; children = ( + 9E92AF0728530EBF007367AD /* View+UIImage.swift */, 9E94C62128AA7ECD008256E9 /* BalanceBreakdownSnapshotTests */, - 9E7225EF2889537E00DF7F17 /* SettingsSnapshotTests */, - 9E7CB6252874267B00A02233 /* ProfileSnapshotTests */, - 9E7CB6102869881300A02233 /* WalletEventsSnapshotTests */, 9E9ECC8B28589E150099D5A2 /* HomeSnapshotTests */, 9E9ECC9328589E150099D5A2 /* ImportWalletSnapshotTests */, 9E9ECC9528589E150099D5A2 /* OnboardingSnapshotTests */, + 9E7CB6252874267B00A02233 /* ProfileSnapshotTests */, 9E9ECC8F28589E150099D5A2 /* RecoveryPhraseDisplaySnapshotTests */, 9E9ECC9128589E150099D5A2 /* RecoveryPhraseValidationFlowSnapshotTests */, + 346715A628E20FB30035F7C4 /* SendSnapshotTests */, + 9E7225EF2889537E00DF7F17 /* SettingsSnapshotTests */, + 9E7CB6102869881300A02233 /* WalletEventsSnapshotTests */, 9E9ECC8D28589E150099D5A2 /* WelcomeSnapshotTests */, - 9E92AF0728530EBF007367AD /* View+UIImage.swift */, ); path = SnapshotTests; sourceTree = ""; @@ -1073,6 +1102,7 @@ 9E7FE0BE282D1DFE00C374E8 /* UI Components */ = { isa = PBXGroup; children = ( + 346D41E228DF0B0900963F36 /* CheckCircle */, 9E7CB6132869E8A700A02233 /* CircularProgress */, 0DF2DC5227235E1F00FA31E2 /* Extensions */, 0DB8AA80271DC7520035BC9D /* DesignGuide.swift */, @@ -1355,8 +1385,9 @@ children = ( F9C165BB2740403600592F76 /* CreateTransactionView.swift */, F9C165B92740403600592F76 /* TransactionConfirmationView.swift */, - F9C165BD2740403600592F76 /* TransactionSentView.swift */, 9E5BF640281FD7B600BA3F17 /* TransactionFailedView.swift */, + F9C165BD2740403600592F76 /* TransactionSentView.swift */, + 34E0AF0E28DEE4C70034CF37 /* HoldToSendButton.swift */, ); path = Views; sourceTree = ""; @@ -1588,6 +1619,7 @@ 2EB7758727FC67FD00269373 /* TransactionAmountTextFieldStore.swift in Sources */, 669FDAE9272C23B3007B9422 /* CircularFrame.swift in Sources */, 9EF8136027F043CC0075AF48 /* AppDelegate.swift in Sources */, + 34E0AF1128DEE5220034CF37 /* Wedge.swift in Sources */, F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */, 9E02B56A27FED43E005B809B /* WrappedFileManager.swift in Sources */, 663FABA2271D876C00E495F8 /* SecondaryButton.swift in Sources */, @@ -1617,6 +1649,7 @@ 9E2DF99D27CF704D00649636 /* ImportSeedEditor.swift in Sources */, F9971A5327680DD000A2DB75 /* ProfileStore.swift in Sources */, 9E39114D2848EEB90073DD9A /* WalletStorage.swift in Sources */, + 346D41E428DF0B8600963F36 /* CheckCircle.swift in Sources */, 669FDAEB272C23C2007B9422 /* CircularFrameBadge.swift in Sources */, 9E39113B2848D5180073DD9A /* WrappedNumberFormatter.swift in Sources */, 2E8719CD27FB0D3B0082C926 /* CurrencySelectionView.swift in Sources */, @@ -1671,6 +1704,7 @@ 663FAB9C271D874D00E495F8 /* ActiveButton.swift in Sources */, 9E2F1C842809B606004E65FE /* DebugMenu.swift in Sources */, 9E02B5C3280458D2005B809B /* WrappedDerivationTool.swift in Sources */, + 34E0AF0F28DEE4C70034CF37 /* HoldToSendButton.swift in Sources */, F9C165C02740403600592F76 /* TransactionConfirmationView.swift in Sources */, 0DF2DC5427235E3E00FA31E2 /* View+InnerShadow.swift in Sources */, 9E39113F2848EC360073DD9A /* WrappedRecoveryPhraseRandomizer.swift in Sources */, @@ -1689,6 +1723,7 @@ 66A0807B271993C500118B79 /* OnboardingProgressIndicator.swift in Sources */, 0D7DF08C271DCC0E00530046 /* ScreenBackground.swift in Sources */, 9E01F8242833C0D8000EFC57 /* WrappedURIParser.swift in Sources */, + 346715A528E2027D0035F7C4 /* CheckCircleStore.swift in Sources */, F9C165C22740403600592F76 /* CreateTransactionView.swift in Sources */, F9C165B4274031F600592F76 /* Bindings.swift in Sources */, 2E35F99A27B3E99C00EB79CD /* TextFieldTitleAccessoryButtonStyle.swift in Sources */, @@ -1747,6 +1782,7 @@ 9E391132284644580073DD9A /* AppInitializationTests.swift in Sources */, 9E9ECC9928589E150099D5A2 /* RecoveryPhraseDisplaySnapshotTests.swift in Sources */, 9E391124283E4CAC0073DD9A /* ImportWalletTests.swift in Sources */, + 346715A828E20FE40035F7C4 /* TransactionConfirmationSnapshotTests.swift in Sources */, 9E5BF63F2819542C00BA3F17 /* WalletEventsTests.swift in Sources */, 0D4E7A1B26B364180058B01E /* secantTests.swift in Sources */, 0DFE93E6272CB6F7000FCCA5 /* RecoveryPhraseValidationTests.swift in Sources */, @@ -2082,7 +2118,7 @@ repositoryURL = "https://github.com/zcash/ZcashLightClientKit/"; requirement = { kind = exactVersion; - version = "0.16.6-beta"; + version = "0.16.10-beta"; }; }; 6654C7382715A38000901167 /* XCRemoteSwiftPackageReference "swift-composable-architecture" */ = { diff --git a/secant.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/secant.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index f8a897f..a2d4847 100644 --- a/secant.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/secant.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -203,8 +203,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/zcash/ZcashLightClientKit", "state" : { - "revision" : "a37c140441d78fa6ad9c89e2fbb539f1ddb7e5a1", - "version" : "0.16.6-beta" + "revision" : "9e41fb43757fd7b0eb8e817320537230774258cf", + "version" : "0.16.10-beta" } } ], diff --git a/secant/Features/SendFlow/SendFlowStore.swift b/secant/Features/SendFlow/SendFlowStore.swift index dae3b8e..82444ab 100644 --- a/secant/Features/SendFlow/SendFlowStore.swift +++ b/secant/Features/SendFlow/SendFlowStore.swift @@ -23,6 +23,7 @@ struct SendFlowState: Equatable { case done } + var addMemoState: Bool var isSendingTransaction = false var memoState: MultiLineTextFieldState var route: Route? @@ -74,6 +75,7 @@ struct SendFlowState: Equatable { // MARK: - Action enum SendFlowAction: Equatable { + case addMemo(CheckCircleAction) case memo(MultiLineTextFieldAction) case onAppear case onDisappear @@ -107,12 +109,16 @@ extension SendFlowReducer { sendReducer, transactionAddressInputReducer, transactionAmountInputReducer, - memoReducer + memoReducer, + addMemoReducer ] ) private static let sendReducer = SendFlowReducer { state, action, environment in switch action { + case .addMemo: + return .none + case .updateRoute(.failure): state.route = .failure state.isSendingTransaction = false @@ -146,7 +152,7 @@ extension SendFlowReducer { with: spendingKey, zatoshi: state.amount, to: state.address, - memo: state.memoState.text, + memo: state.addMemoState ? state.memoState.text : nil, from: 0 ) .receive(on: environment.scheduler) @@ -196,6 +202,12 @@ extension SendFlowReducer { } } + private static let addMemoReducer: SendFlowReducer = CheckCircleReducer.default.pullback( + state: \SendFlowState.addMemoState, + action: /SendFlowAction.addMemo, + environment: { _ in Void() } + ) + private static let transactionAddressInputReducer: SendFlowReducer = TransactionAddressTextFieldReducer.default.pullback( state: \SendFlowState.transactionAddressInputState, action: /SendFlowAction.transactionAddressInput, @@ -237,6 +249,13 @@ extension SendFlowReducer { // MARK: - Store extension SendFlowStore { + func addMemoStore() -> CheckCircleStore { + self.scope( + state: \.addMemoState, + action: SendFlowAction.addMemo + ) + } + func memoStore() -> MultiLineTextFieldStore { self.scope( state: \.memoState, @@ -289,6 +308,7 @@ extension SendFlowViewStore { extension SendFlowState { static var placeholder: Self { .init( + addMemoState: true, memoState: .placeholder, route: nil, transactionAddressInputState: .placeholder, @@ -298,6 +318,7 @@ extension SendFlowState { static var emptyPlaceholder: Self { .init( + addMemoState: true, memoState: .placeholder, route: nil, transactionAddressInputState: .placeholder, @@ -310,12 +331,7 @@ extension SendFlowState { extension SendFlowStore { static var placeholder: SendFlowStore { return SendFlowStore( - initialState: .init( - memoState: .placeholder, - route: nil, - transactionAddressInputState: .placeholder, - transactionAmountInputState: .placeholder - ), + initialState: .emptyPlaceholder, reducer: .default, environment: SendFlowEnvironment( derivationTool: .live(), diff --git a/secant/Features/SendFlow/SendFlowView.swift b/secant/Features/SendFlow/SendFlowView.swift index b939135..54ebc21 100644 --- a/secant/Features/SendFlow/SendFlowView.swift +++ b/secant/Features/SendFlow/SendFlowView.swift @@ -19,7 +19,7 @@ struct SendFlowView: View { .navigationLinkEmpty( isActive: viewStore.bindingForConfirmation, destination: { - TransactionConfirmation(viewStore: viewStore) + TransactionConfirmation(store: store) } ) } @@ -34,6 +34,7 @@ struct SendFLowView_Previews: PreviewProvider { SendFlowView( store: .init( initialState: .init( + addMemoState: true, memoState: .placeholder, route: nil, transactionAddressInputState: .placeholder, diff --git a/secant/Features/SendFlow/Views/HoldToSendButton.swift b/secant/Features/SendFlow/Views/HoldToSendButton.swift new file mode 100644 index 0000000..2916078 --- /dev/null +++ b/secant/Features/SendFlow/Views/HoldToSendButton.swift @@ -0,0 +1,114 @@ +// +// ZcashSendButton.swift +// wallet +// +// Created by Francisco Gindre on 1/9/20. +// Copyright © 2020 Francisco Gindre. All rights reserved. +// + +import SwiftUI +import ComposableArchitecture + +struct HoldToSendButton: View { + var minimumDuration: TimeInterval = 5 + let innerCircleScale: CGFloat = 0.8 + var completionStrokeWidth: CGFloat = 16.0 + @State var isPressing = false + @State var startAngle: CGFloat = -90 + @State var endAngle: CGFloat = -90 + + var longPressSucceded: () -> Void + + var body: some View { + ZStack(alignment: .center) { + GeometryReader { geometry in + Circle() + .size(geometry.size) + .fill(Color.black) + .shadow(color: Asset.Colors.Shadow.holdToSendButtonShadow.color, radius: 2, x: 0, y: 2) + + Circle() + .size(geometry.size) + .fill(Asset.Colors.Shadow.holdToSendButtonShadow.color) + .scaleEffect(self.innerCircleScale) + .opacity(0.35) + + Wedge( + startAngle: self.startAngle, + endAngle: self.endAngle, + clockwise: false + ) + .stroke(Asset.Colors.ProgressIndicator.holdToSendButton.color, lineWidth: self.completionStrokeWidth) + .frame( + width: geometry.size.width - self.completionStrokeWidth, + height: geometry.size.height - self.completionStrokeWidth + ) + .offset(x: self.completionStrokeWidth / 2, y: self.completionStrokeWidth / 2) + + Text("Press and hold\nto send ZEC") + .foregroundColor(.white) + .multilineTextAlignment(.center) + .frame( + minWidth: geometry.size.width, + idealWidth: geometry.size.width, + maxWidth: geometry.size.width, + minHeight: geometry.size.height, + idealHeight: geometry.size.height, + maxHeight: geometry.size.height, + alignment: .center + ) + } + } + .frame( + width: 167, + height: 167, + alignment: .center + ) + .onLongPressGesture( + minimumDuration: minimumDuration, + maximumDistance: 167, + pressing: { isPressing in + if !self.isPressing && isPressing { + self.isPressing = isPressing + withAnimation(.linear(duration: self.minimumDuration)) { + self.startAnimation() + } + } else if self.isPressing && !isPressing { + self.isPressing = isPressing + withAnimation(.easeOut(duration: 0.3)) { + self.cancelAnimation() + } + } + }, + perform: { + self.endAnimation() + self.isPressing = false + longPressSucceded() + } + ) + } + + func startAnimation() { + self.startAngle = -90 + self.endAngle = 270 + } + + func endAnimation() { + self.startAngle = -90 + self.endAngle = 270 + } + + func cancelAnimation() { + self.startAngle = -90 + self.endAngle = -90 + } +} + +struct HoldToSendButton_Previews: PreviewProvider { + static var previews: some View { + ZStack { + HoldToSendButton(longPressSucceded: { }) + .applyDarkScreenBackground() + } + } +} diff --git a/secant/Features/SendFlow/Views/TransactionConfirmationView.swift b/secant/Features/SendFlow/Views/TransactionConfirmationView.swift index 7bf0acc..2a1640d 100644 --- a/secant/Features/SendFlow/Views/TransactionConfirmationView.swift +++ b/secant/Features/SendFlow/Views/TransactionConfirmationView.swift @@ -2,37 +2,45 @@ import SwiftUI import ComposableArchitecture struct TransactionConfirmation: View { - let viewStore: SendFlowViewStore + let store: SendFlowStore var body: some View { - VStack { - Text("Send \(viewStore.amount.decimalString()) ZEC") - .padding() + WithViewStore(store) { viewStore in + VStack { + Text("Send \(viewStore.amount.decimalString()) ZEC to") + .padding() + .foregroundColor(Asset.Colors.Text.forDarkBackground.color) - Text("To \(viewStore.address)") - .padding() + Text("\(viewStore.address)?") + .truncationMode(.middle) + .lineLimit(1) + .padding() + .foregroundColor(Asset.Colors.Text.forDarkBackground.color) - Spacer() + HStack { + CheckCircle(viewStore: ViewStore(store.addMemoStore())) + Text("Includes memo") + .foregroundColor(Asset.Colors.Text.forDarkBackground.color) + } - Button( - action: { viewStore.send(.sendConfirmationPressed) }, - label: { Text("Confirm") } + Spacer() + + HoldToSendButton { + viewStore.send(.sendConfirmationPressed) + } + + Spacer() + } + .applyDarkScreenBackground() + .navigationLinkEmpty( + isActive: viewStore.bindingForSuccess, + destination: { TransactionSent(viewStore: viewStore) } + ) + .navigationLinkEmpty( + isActive: viewStore.bindingForFailure, + destination: { TransactionFailed(viewStore: viewStore) } ) - .activeButtonStyle - .frame(height: 50) - .padding() - - Spacer() } - .applyScreenBackground() - .navigationLinkEmpty( - isActive: viewStore.bindingForSuccess, - destination: { TransactionSent(viewStore: viewStore) } - ) - .navigationLinkEmpty( - isActive: viewStore.bindingForFailure, - destination: { TransactionFailed(viewStore: viewStore) } - ) } } @@ -44,11 +52,8 @@ struct Confirmation_Previews: PreviewProvider { StateContainer( initialState: (false) ) { _ in - TransactionConfirmation( - viewStore: ViewStore(.placeholder) - ) + TransactionConfirmation(store: .placeholder) } } - .preferredColorScheme(.dark) } } diff --git a/secant/Resources/Assets.xcassets/Icons/checkmark.imageset/Contents.json b/secant/Resources/Assets.xcassets/Icons/checkmark.imageset/Contents.json new file mode 100644 index 0000000..190415a --- /dev/null +++ b/secant/Resources/Assets.xcassets/Icons/checkmark.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "checkmark.pdf", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Resources/Assets.xcassets/Icons/checkmark.imageset/checkmark.pdf b/secant/Resources/Assets.xcassets/Icons/checkmark.imageset/checkmark.pdf new file mode 100644 index 0000000..e3b89e8 Binary files /dev/null and b/secant/Resources/Assets.xcassets/Icons/checkmark.imageset/checkmark.pdf differ diff --git a/secant/Resources/Colors.xcassets/CheckCircle/Contents.json b/secant/Resources/Colors.xcassets/CheckCircle/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/secant/Resources/Colors.xcassets/CheckCircle/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Resources/Colors.xcassets/CheckCircle/externalRing.colorset/Contents.json b/secant/Resources/Colors.xcassets/CheckCircle/externalRing.colorset/Contents.json new file mode 100644 index 0000000..c311a36 --- /dev/null +++ b/secant/Resources/Colors.xcassets/CheckCircle/externalRing.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x65", + "green" : "0x65", + "red" : "0x65" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x65", + "green" : "0x65", + "red" : "0x65" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Resources/Colors.xcassets/CheckCircle/internalRing.colorset/Contents.json b/secant/Resources/Colors.xcassets/CheckCircle/internalRing.colorset/Contents.json new file mode 100644 index 0000000..aacc15d --- /dev/null +++ b/secant/Resources/Colors.xcassets/CheckCircle/internalRing.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x00", + "green" : "0x94", + "red" : "0xFF" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x00", + "green" : "0x94", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Resources/Colors.xcassets/ProgressIndicator/holdToSendButton.colorset/Contents.json b/secant/Resources/Colors.xcassets/ProgressIndicator/holdToSendButton.colorset/Contents.json new file mode 100644 index 0000000..aacc15d --- /dev/null +++ b/secant/Resources/Colors.xcassets/ProgressIndicator/holdToSendButton.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x00", + "green" : "0x94", + "red" : "0xFF" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x00", + "green" : "0x94", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Resources/Colors.xcassets/ScreenBackground/gradientDarkEnd.colorset/Contents.json b/secant/Resources/Colors.xcassets/ScreenBackground/gradientDarkEnd.colorset/Contents.json new file mode 100644 index 0000000..b33e02b --- /dev/null +++ b/secant/Resources/Colors.xcassets/ScreenBackground/gradientDarkEnd.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x1E", + "green" : "0x1B", + "red" : "0x19" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x1E", + "green" : "0x1B", + "red" : "0x19" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Resources/Colors.xcassets/ScreenBackground/gradientDarkStart.colorset/Contents.json b/secant/Resources/Colors.xcassets/ScreenBackground/gradientDarkStart.colorset/Contents.json new file mode 100644 index 0000000..ca40f3f --- /dev/null +++ b/secant/Resources/Colors.xcassets/ScreenBackground/gradientDarkStart.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x3D", + "green" : "0x38", + "red" : "0x33" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x3D", + "green" : "0x38", + "red" : "0x33" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Resources/Colors.xcassets/Shadow/drawerShadow.colorset/Contents.json b/secant/Resources/Colors.xcassets/Shadow/drawerShadow.colorset/Contents.json index dc83d93..920bc64 100644 --- a/secant/Resources/Colors.xcassets/Shadow/drawerShadow.colorset/Contents.json +++ b/secant/Resources/Colors.xcassets/Shadow/drawerShadow.colorset/Contents.json @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0x45", - "green" : "0x1F", - "red" : "0x13" + "blue" : "69", + "green" : "31", + "red" : "19" } }, "idiom" : "universal" diff --git a/secant/Resources/Colors.xcassets/Shadow/emptyChipInnerShadow.colorset/Contents.json b/secant/Resources/Colors.xcassets/Shadow/emptyChipInnerShadow.colorset/Contents.json index 8a1da1f..91a1c31 100644 --- a/secant/Resources/Colors.xcassets/Shadow/emptyChipInnerShadow.colorset/Contents.json +++ b/secant/Resources/Colors.xcassets/Shadow/emptyChipInnerShadow.colorset/Contents.json @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0x00", - "green" : "0x00", - "red" : "0x00" + "blue" : "0", + "green" : "0", + "red" : "0" } }, "idiom" : "universal" diff --git a/secant/Resources/Colors.xcassets/Shadow/holdToSendButtonShadow.colorset/Contents.json b/secant/Resources/Colors.xcassets/Shadow/holdToSendButtonShadow.colorset/Contents.json new file mode 100644 index 0000000..af6cd3f --- /dev/null +++ b/secant/Resources/Colors.xcassets/Shadow/holdToSendButtonShadow.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x97", + "green" : "0x97", + "red" : "0x97" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x97", + "green" : "0x97", + "red" : "0x97" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Resources/Colors.xcassets/Shadow/numberedTextShadow.colorset/Contents.json b/secant/Resources/Colors.xcassets/Shadow/numberedTextShadow.colorset/Contents.json index 36d2d1e..561aaf0 100644 --- a/secant/Resources/Colors.xcassets/Shadow/numberedTextShadow.colorset/Contents.json +++ b/secant/Resources/Colors.xcassets/Shadow/numberedTextShadow.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space" : "srgb", "components" : { "alpha" : "0.200", - "blue" : "0x36", - "green" : "0x2C", - "red" : "0x27" + "blue" : "54", + "green" : "44", + "red" : "39" } }, "idiom" : "universal" @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "0.250", - "blue" : "0x00", - "green" : "0x00", - "red" : "0x00" + "blue" : "0", + "green" : "0", + "red" : "0" } }, "idiom" : "universal" diff --git a/secant/Resources/Colors.xcassets/Text/forDarkBackground.colorset/Contents.json b/secant/Resources/Colors.xcassets/Text/forDarkBackground.colorset/Contents.json new file mode 100644 index 0000000..2536dc2 --- /dev/null +++ b/secant/Resources/Colors.xcassets/Text/forDarkBackground.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFF" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Resources/Colors.xcassets/TextField/Underline/Gray.colorset/Contents.json b/secant/Resources/Colors.xcassets/TextField/Underline/Gray.colorset/Contents.json index 9751b83..cef3abc 100644 --- a/secant/Resources/Colors.xcassets/TextField/Underline/Gray.colorset/Contents.json +++ b/secant/Resources/Colors.xcassets/TextField/Underline/Gray.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space" : "srgb", "components" : { "alpha" : "0.827", - "blue" : "0.623", - "green" : "0.589", - "red" : "0.560" + "blue" : "0x9E", + "green" : "0x96", + "red" : "0x8E" } }, "idiom" : "universal" @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.290", - "green" : "0.275", - "red" : "0.259" + "blue" : "0x49", + "green" : "0x46", + "red" : "0x42" } }, "idiom" : "universal" diff --git a/secant/Resources/Generated/XCAssets+Generated.swift b/secant/Resources/Generated/XCAssets+Generated.swift index 9ab0816..cf1bab3 100644 --- a/secant/Resources/Generated/XCAssets+Generated.swift +++ b/secant/Resources/Generated/XCAssets+Generated.swift @@ -35,6 +35,7 @@ internal enum Asset { } internal enum Icons { internal static let bank = ImageAsset(name: "bank") + internal static let checkmark = ImageAsset(name: "checkmark") internal static let list = ImageAsset(name: "list") internal static let profile = ImageAsset(name: "profile") internal static let qrCode = ImageAsset(name: "qr-code") @@ -67,6 +68,10 @@ internal enum Asset { internal static let secondaryButton = ColorAsset(name: "SecondaryButton") internal static let secondaryButtonPressed = ColorAsset(name: "SecondaryButtonPressed") } + internal enum CheckCircle { + internal static let externalRing = ColorAsset(name: "externalRing") + internal static let internalRing = ColorAsset(name: "internalRing") + } internal enum Cursor { internal static let bar = ColorAsset(name: "Bar") } @@ -86,11 +91,14 @@ internal enum Asset { internal static let gradientLeft = ColorAsset(name: "GradientLeft") internal static let gradientRight = ColorAsset(name: "GradientRight") internal static let negativeSpace = ColorAsset(name: "NegativeSpace") + internal static let holdToSendButton = ColorAsset(name: "holdToSendButton") } internal enum QRScan { internal static let frame = ColorAsset(name: "frame") } internal enum ScreenBackground { + internal static let gradientDarkEnd = ColorAsset(name: "gradientDarkEnd") + internal static let gradientDarkStart = ColorAsset(name: "gradientDarkStart") internal static let gradientEnd = ColorAsset(name: "gradientEnd") internal static let gradientStart = ColorAsset(name: "gradientStart") internal static let greenGradientEnd = ColorAsset(name: "greenGradientEnd") @@ -104,6 +112,7 @@ internal enum Asset { internal enum Shadow { internal static let drawerShadow = ColorAsset(name: "drawerShadow") internal static let emptyChipInnerShadow = ColorAsset(name: "emptyChipInnerShadow") + internal static let holdToSendButtonShadow = ColorAsset(name: "holdToSendButtonShadow") internal static let numberedTextShadow = ColorAsset(name: "numberedTextShadow") } internal enum Text { @@ -124,6 +133,7 @@ internal enum Asset { internal static let balanceText = ColorAsset(name: "balanceText") internal static let captionText = ColorAsset(name: "captionText") internal static let captionTextShadow = ColorAsset(name: "captionTextShadow") + internal static let forDarkBackground = ColorAsset(name: "forDarkBackground") internal static let highlightedSuperscriptText = ColorAsset(name: "highlightedSuperscriptText") internal static let moreInfoText = ColorAsset(name: "moreInfoText") } diff --git a/secant/UI Components/Backgrounds/ScreenBackground.swift b/secant/UI Components/Backgrounds/ScreenBackground.swift index 25b1abc..b3c7445 100644 --- a/secant/UI Components/Backgrounds/ScreenBackground.swift +++ b/secant/UI Components/Backgrounds/ScreenBackground.swift @@ -32,6 +32,7 @@ extension ScreenBackground { ] ) } + struct ScreenBackgroundModifier: ViewModifier { var colors: [Color] var darkGradientEndPointY = 1.0 @@ -97,6 +98,17 @@ extension View { ) ) } + + func applyDarkScreenBackground() -> some View { + self.modifier( + ScreenBackgroundModifier( + colors: [ + Asset.Colors.ScreenBackground.gradientDarkStart.color, + Asset.Colors.ScreenBackground.gradientDarkEnd.color + ] + ) + ) + } } struct ScreenBackground_Previews: PreviewProvider { diff --git a/secant/UI Components/CheckCircle/CheckCircle.swift b/secant/UI Components/CheckCircle/CheckCircle.swift new file mode 100644 index 0000000..b032d07 --- /dev/null +++ b/secant/UI Components/CheckCircle/CheckCircle.swift @@ -0,0 +1,91 @@ +// +// ZcashCheckCircle.swift +// wallet +// +// Created by Francisco Gindre on 1/7/20. +// Copyright © 2020 Francisco Gindre. All rights reserved. +// + +import ComposableArchitecture +import SwiftUI + +struct CheckCircle: View { + let viewStore: CheckCircleViewStore + + var externalRingColor: Color = Asset.Colors.CheckCircle.externalRing.color + var internalRingColor: Color = Asset.Colors.CheckCircle.internalRing.color + + var backgroundColor: Color = .clear + + func backgroundShape(size: CGSize) -> some View { + Path { path in + path.addArc( + center: CGPoint( + x: size.width / 2, + y: size.height / 2 + ), + radius: size.width / 2, + startAngle: Angle(degrees: 0), + endAngle: Angle(degrees: 360), + clockwise: true + ) + } + .fill(self.backgroundColor) + } + + var body: some View { + GeometryReader { geometry in + ZStack(alignment: .center) { + self.backgroundShape(size: geometry.size) + self.ring(size: geometry.size, color: self.externalRingColor, lineWidth: 2) + self.ring( + size: CGSize(width: geometry.size.width, height: geometry.size.height), + color: self.internalRingColor, + lineWidth: 4 + ) + .scaleEffect(0.9, anchor: UnitPoint(x: 0.5, y: 0.5)) + .opacity(self.viewStore.state ? 1 : 0) + + Image("checkmark") + .opacity(self.viewStore.state ? 1 : 0) + } + .gesture( + TapGesture() + .onEnded { _ in self.viewStore.send(.updateIsChecked) } + ) + } + .frame(width: 30, height: 30, alignment: .center) + } + + func ringPath(size: CGSize) -> Path { + Path { path in + path.addArc( + center: CGPoint( + x: size.width / 2, + y: size.height / 2 + ), + radius: size.width / 2, + startAngle: Angle(degrees: 0), + endAngle: Angle(degrees: 360), + clockwise: true + ) + } + } + + func ring(size: CGSize, color: Color, lineWidth: CGFloat) -> some View { + ringPath(size: size) + .stroke(color, lineWidth: lineWidth) + } +} + +struct ZcashCheckCircle_Previews: PreviewProvider { + static var previews: some View { + VStack { + Spacer() + CheckCircle(viewStore: ViewStore(CheckCircleStore.mock(isChecked: true))) + CheckCircle(viewStore: ViewStore(CheckCircleStore.mock(isChecked: false))) + Spacer() + } + .applyDarkScreenBackground() + } +} diff --git a/secant/UI Components/CheckCircle/CheckCircleStore.swift b/secant/UI Components/CheckCircle/CheckCircleStore.swift new file mode 100644 index 0000000..c25d573 --- /dev/null +++ b/secant/UI Components/CheckCircle/CheckCircleStore.swift @@ -0,0 +1,54 @@ +// +// CheckCircleStore.swift +// secant-testnet +// +// Created by Michal Fousek on 26.09.2022. +// + +import Foundation +import ComposableArchitecture +import SwiftUI + +typealias CheckCircleReducer = Reducer +typealias CheckCircleStore = Store +typealias CheckCircleViewStore = ViewStore + +// MARK: - Action + +enum CheckCircleAction: Equatable { + case updateIsChecked +} + +// MARK: - Reducer + +extension CheckCircleReducer { + static let `default` = CheckCircleReducer { state, action, _ in + switch action { + case .updateIsChecked: + state.toggle() + return .none + } + } +} + +// MARK: - Store + +extension CheckCircleStore { + static func mock(isChecked: Bool) -> CheckCircleStore { + return CheckCircleStore( + initialState: isChecked, + reducer: .default, + environment: Void() + ) + } +} + +// MARK: - ViewStore + +extension CheckCircleViewStore { + static let placeholder = CheckCircleStore( + initialState: true, + reducer: .default, + environment: Void() + ) +} diff --git a/secant/UI Components/Shapes/Wedge.swift b/secant/UI Components/Shapes/Wedge.swift new file mode 100644 index 0000000..ae2a809 --- /dev/null +++ b/secant/UI Components/Shapes/Wedge.swift @@ -0,0 +1,39 @@ +// +// Wedge.swift +// secant-testnet +// +// Created by Michal Fousek on 24.09.2022. +// + +import SwiftUI + +struct Wedge: Shape { + var startAngle: CGFloat + var endAngle: CGFloat + var clockwise = true + + var animatableData: AnimatablePair { + get { AnimatablePair(startAngle, endAngle) } + set { + startAngle = newValue.first + endAngle = newValue.second + } + } + + func path(in rect: CGRect) -> Path { + let callback: (inout Path) -> Void = { path in + path.addArc( + center: CGPoint( + x: rect.midX, + y: rect.midY + ), + radius: rect.width / 2 , + startAngle: Angle(degrees: Double(startAngle)), + endAngle: Angle(degrees: Double(endAngle)), + clockwise: clockwise + ) + } + + return Path(callback) + } +} diff --git a/secant/UI Components/TextFields/MultiLineTextField/MultiLineTextFieldStore.swift b/secant/UI Components/TextFields/MultiLineTextField/MultiLineTextFieldStore.swift index e7c26b8..c8f9c77 100644 --- a/secant/UI Components/TextFields/MultiLineTextField/MultiLineTextFieldStore.swift +++ b/secant/UI Components/TextFields/MultiLineTextField/MultiLineTextFieldStore.swift @@ -86,5 +86,9 @@ extension MultiLineTextFieldStore { // MARK: - Placeholders extension MultiLineTextFieldState { - static let placeholder = MultiLineTextFieldState() + static let placeholder: MultiLineTextFieldState = { + var state = MultiLineTextFieldState() + state.text = "test" + return state + }() } diff --git a/secant/Wrappers/WrappedSDKSynchronizer.swift b/secant/Wrappers/WrappedSDKSynchronizer.swift index 8898e80..8331853 100644 --- a/secant/Wrappers/WrappedSDKSynchronizer.swift +++ b/secant/Wrappers/WrappedSDKSynchronizer.swift @@ -405,7 +405,7 @@ class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer { ) -> Effect, Never> { let transactionState = TransactionState( expirationHeight: 40, - memo: "test", + memo: memo, minedHeight: 50, shielded: true, zAddress: "tteafadlamnelkqe", diff --git a/secantTests/SendTests/SendTests.swift b/secantTests/SendTests/SendTests.swift index 246af87..318cbff 100644 --- a/secantTests/SendTests/SendTests.swift +++ b/secantTests/SendTests/SendTests.swift @@ -84,6 +84,67 @@ class SendTests: XCTestCase { state.route = .success } } + + func testSendSucceededWithoutMemo() throws { + // the test needs to pass the exportWallet() so we simulate some in the keychain + try storage.importWallet(bip39: "one two three", birthday: nil) + + // setup the store and environment to be fully mocked + let testScheduler = DispatchQueue.test + + let testEnvironment = SendFlowEnvironment( + derivationTool: .live(), + mnemonic: .mock, + numberFormatter: .live(), + SDKSynchronizer: MockWrappedSDKSynchronizer(), + scheduler: testScheduler.eraseToAnyScheduler(), + walletStorage: .live(walletStorage: storage), + zcashSDKEnvironment: .testnet + ) + + var state = SendFlowState.placeholder + state.addMemoState = false + + let store = TestStore( + initialState: state, + reducer: SendFlowReducer.default, + environment: testEnvironment + ) + + // simulate the sending confirmation button to be pressed + store.send(.sendConfirmationPressed) { state in + // once sending is confirmed, the attemts to try to send again by pressing the button + // needs to be eliminated, indicated by the flag `isSendingTransaction`, need to be true + state.isSendingTransaction = true + } + + testScheduler.advance(by: 0.01) + + let transactionState = TransactionState( + expirationHeight: 40, + memo: nil, + minedHeight: 50, + shielded: true, + zAddress: "tteafadlamnelkqe", + fee: Zatoshi(10), + id: "id", + status: .paid(success: true), + timestamp: 1234567, + zecAmount: Zatoshi(10) + ) + + // check the success transaction to be received back + store.receive(.sendTransactionResult(Result.success(transactionState))) { state in + // from this moment on the sending next transaction is allowed again + // the 'isSendingTransaction' needs to be false again + state.isSendingTransaction = false + } + + // all went well, the success screen is triggered + store.receive(.updateRoute(.success)) { state in + state.route = .success + } + } func testSendFailed() throws { // the test needs to pass the exportWallet() so we simulate some in the keychain @@ -235,6 +296,7 @@ class SendTests: XCTestCase { func testFundsSufficiency() throws { let sendState = SendFlowState( + addMemoState: true, memoState: .placeholder, transactionAddressInputState: .placeholder, transactionAmountInputState: @@ -313,6 +375,7 @@ class SendTests: XCTestCase { let store = TestStore( initialState: .init( + addMemoState: true, memoState: .placeholder, route: nil, transactionAddressInputState: .placeholder, @@ -346,6 +409,7 @@ class SendTests: XCTestCase { func testValidForm() throws { let sendState = SendFlowState( + addMemoState: true, memoState: .placeholder, transactionAddressInputState: .placeholder, transactionAmountInputState: @@ -394,6 +458,7 @@ class SendTests: XCTestCase { func testInvalidForm_InsufficientFunds() throws { let sendState = SendFlowState( + addMemoState: true, memoState: .placeholder, transactionAddressInputState: .placeholder, transactionAmountInputState: @@ -441,6 +506,7 @@ class SendTests: XCTestCase { func testInvalidForm_AddressFormat() throws { let sendState = SendFlowState( + addMemoState: true, memoState: .placeholder, transactionAddressInputState: .placeholder, transactionAmountInputState: @@ -488,6 +554,7 @@ class SendTests: XCTestCase { func testInvalidForm_AmountFormat() throws { let sendState = SendFlowState( + addMemoState: true, memoState: .placeholder, transactionAddressInputState: .placeholder, transactionAmountInputState: @@ -535,6 +602,7 @@ class SendTests: XCTestCase { func testInvalidForm_ExceededMemoCharLimit() throws { let sendState = SendFlowState( + addMemoState: true, memoState: MultiLineTextFieldState(charLimit: 3), shieldedBalance: WalletBalance(verified: Zatoshi(1), total: Zatoshi(1)), transactionAddressInputState: @@ -588,6 +656,7 @@ class SendTests: XCTestCase { func testMemoCharLimitSet() throws { let sendState = SendFlowState( + addMemoState: true, memoState: .placeholder, transactionAddressInputState: .placeholder, transactionAmountInputState: diff --git a/secantTests/SnapshotTests/SendSnapshotTests/TransactionConfirmationSnapshotTests.swift b/secantTests/SnapshotTests/SendSnapshotTests/TransactionConfirmationSnapshotTests.swift new file mode 100644 index 0000000..275bf69 --- /dev/null +++ b/secantTests/SnapshotTests/SendSnapshotTests/TransactionConfirmationSnapshotTests.swift @@ -0,0 +1,88 @@ +// +// TransactionConfirmationSnapshotTests.swift +// secantTests +// +// Created by Michal Fousek on 26.09.2022. +// + +import XCTest +@testable import secant_testnet +import ComposableArchitecture +import SwiftUI +import ZcashLightClientKit + +class TransactionConfirmationSnapshotTests: XCTestCase { + func testTransactionConfirmationSnapshot_addMemo() throws { + let testEnvironment = SendFlowEnvironment( + derivationTool: .live(derivationTool: DerivationTool(networkType: .testnet)), + mnemonic: .mock, + numberFormatter: .live(), + SDKSynchronizer: MockWrappedSDKSynchronizer(), + scheduler: DispatchQueue.main.eraseToAnyScheduler(), + walletStorage: .live(), + zcashSDKEnvironment: .testnet + ) + + var state = SendFlowState.placeholder + state.addMemoState = true + state.transactionAddressInputState = TransactionAddressTextFieldState( + textFieldState: TCATextFieldState( + validationType: nil, + text: "ztestmockeddestinationaddress" + ) + ) + state.transactionAmountInputState = TransactionAmountTextFieldState( + currencySelectionState: CurrencySelectionState(), + textFieldState: TCATextFieldState( + validationType: nil, + text: "2.91" + ) + ) + + let store = Store( + initialState: state, + reducer: SendFlowReducer.default, + environment: testEnvironment + ) + + ViewStore(store).send(.onAppear) + addAttachments(TransactionConfirmation(store: store)) + } + + func testTransactionConfirmationSnapshot_dontAddMemo() throws { + let testEnvironment = SendFlowEnvironment( + derivationTool: .live(derivationTool: DerivationTool(networkType: .testnet)), + mnemonic: .mock, + numberFormatter: .live(), + SDKSynchronizer: MockWrappedSDKSynchronizer(), + scheduler: DispatchQueue.main.eraseToAnyScheduler(), + walletStorage: .live(), + zcashSDKEnvironment: .testnet + ) + + var state = SendFlowState.placeholder + state.addMemoState = true + state.transactionAddressInputState = TransactionAddressTextFieldState( + textFieldState: TCATextFieldState( + validationType: nil, + text: "ztestmockeddestinationaddress" + ) + ) + state.transactionAmountInputState = TransactionAmountTextFieldState( + currencySelectionState: CurrencySelectionState(), + textFieldState: TCATextFieldState( + validationType: nil, + text: "2.91" + ) + ) + + let store = Store( + initialState: state, + reducer: SendFlowReducer.default, + environment: testEnvironment + ) + + ViewStore(store).send(.onAppear) + addAttachments(TransactionConfirmation(store: store)) + } +}