Send flows WIP, mostly done

This commit is contained in:
Lukas Korba 2025-03-11 16:18:44 +01:00
parent 994ca3329a
commit fcfca57eed
30 changed files with 760 additions and 528 deletions

View File

@ -65,7 +65,7 @@ let package = Package(
.library(name: "SecurityWarning", targets: ["SecurityWarning"]),
.library(name: "SendConfirmation", targets: ["SendConfirmation"]),
.library(name: "SendFeedback", targets: ["SendFeedback"]),
.library(name: "SendFlow", targets: ["SendFlow"]),
.library(name: "SendForm", targets: ["SendForm"]),
.library(name: "ServerSetup", targets: ["ServerSetup"]),
.library(name: "Settings", targets: ["Settings"]),
.library(name: "SupportDataGenerator", targets: ["SupportDataGenerator"]),
@ -616,6 +616,7 @@ let package = Package(
"AddKeystoneHWWallet",
"AddressBook",
"AddressDetails",
"AudioServices",
"AutolockHandler",
"CurrencyConversionSetup",
"DeleteWallet",
@ -637,6 +638,7 @@ let package = Package(
"NumberFormatter",
"OnboardingFlow",
"OSStatusError",
"PartialProposalError",
"Pasteboard",
"PrivateDataConsent",
"ReadTransactionsStorage",
@ -645,10 +647,13 @@ let package = Package(
"RequestZec",
"SDKSynchronizer",
"Scan",
"SendConfirmation",
"SendFeedback",
"SendFlow",
"SendForm",
"ServerSetup",
"Settings",
"TransactionDetails",
"TransactionsManager",
"UIComponents",
"URIParser",
"UserDefaults",
@ -764,7 +769,7 @@ let package = Package(
path: "Sources/Features/SendFeedback"
),
.target(
name: "SendFlow",
name: "SendForm",
dependencies: [
"AddressBookClient",
"AudioServices",
@ -782,7 +787,7 @@ let package = Package(
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Features/SendFlow"
path: "Sources/Features/SendForm"
),
.target(
name: "ServerSetup",

View File

@ -108,17 +108,17 @@ public struct AddressBook {
Reduce { state, action in
switch action {
case .onAppear:
state.originalName = ""
state.originalAddress = ""
// state.originalName = ""
// state.originalAddress = ""
state.isValidForm = false
state.isValidZcashAddress = false
// state.isValidZcashAddress = false
state.deleteIdToConfirm = nil
state.name = ""
state.address = ""
// state.name = ""
// state.address = ""
state.nameAlreadyExists = false
state.addressAlreadyExists = false
state.isAddressFocused = false
state.isNameFocused = false
// state.isAddressFocused = false
// state.isNameFocused = false
// state.scanState.checkers = [.zcashAddressScanChecker, .requestZecScanChecker]
if let editId = state.editId {
return .concatenate(
@ -172,7 +172,7 @@ public struct AddressBook {
//// return .send(.updateDestination(.add))
// return .none
// case .scan(.cancelPressed):
// case .scan(.cancelTapped):
// state.scanViewBinding = false
// return .none

View File

@ -76,12 +76,12 @@ public struct BalancesView: View {
)
.padding(.top, walletStatus == .restoring ? 0 : 40)
.padding(.bottom, 25)
.navigationLinkEmpty(
isActive: store.bindingFor(.partialProposalError),
destination: {
PartialProposalErrorView(store: store.partialProposalErrorStore())
}
)
// .navigationLinkEmpty(
// isActive: store.bindingFor(.partialProposalError),
// destination: {
// PartialProposalErrorView(store: store.partialProposalErrorStore())
// }
// )
}
}
.navigationBarTitleDisplayMode(.inline)

View File

@ -78,7 +78,7 @@ public struct ImportWallet {
case binding(BindingAction<ImportWallet.State>)
case importPrivateOrViewingKey
case initializeSDK
case nextPressed
case nextTapped
case onAppear
case restoreInfo(RestoreInfo.Action)
case restoreInfoRequested(Bool)
@ -142,7 +142,7 @@ public struct ImportWallet {
case .alert:
return .none
case .nextPressed:
case .nextTapped:
return .none
case .restoreInfo:

View File

@ -105,7 +105,7 @@ public struct ImportWalletView: View {
}
ZashiButton(L10n.General.next) {
store.send(.nextPressed)
store.send(.nextTapped)
}
.disabled(!store.isValidForm)
.padding(.top, 50)

View File

@ -42,7 +42,7 @@ public struct RecoveryPhraseDisplay {
public enum Action: Equatable {
case alert(PresentationAction<Action>)
case finishedPressed
case finishedTapped
case onAppear
case recoveryPhraseTapped
case tooltipTapped
@ -82,7 +82,7 @@ public struct RecoveryPhraseDisplay {
state.alert = nil
return .none
case .finishedPressed:
case .finishedTapped:
return .none
case .tooltipTapped:

View File

@ -149,7 +149,7 @@ public struct RecoveryPhraseDisplayView: View {
if !store.showBackButton {
ZashiButton(L10n.RecoveryPhraseDisplay.Button.wroteItDown) {
store.send(.finishedPressed)
store.send(.finishedTapped)
}
.padding(.bottom, 20)
} else {

View File

@ -15,15 +15,19 @@ import AddressDetails
import CurrencyConversionSetup
import DeleteWallet
import ExportTransactionHistory
import PartialProposalError
import PrivateDataConsent
import Receive
import RecoveryPhraseDisplay
import RequestZec
import Scan
import SendConfirmation
import SendFeedback
import SendFlow
import SendForm
import ServerSetup
import Settings
import TransactionDetails
import TransactionsManager
import WhatsNew
import ZecKeyboard
@ -45,7 +49,7 @@ extension Root {
case .path(.element(id: _, action: .accountHWWalletSelection(.forgetThisDeviceTapped))):
var isIntegrationsFlow = false
for (id, element) in zip(state.path.ids, state.path) {
if case .integrations = element {
if element.is(\.integrations) {
isIntegrationsFlow = true
state.path.pop(to: id)
break
@ -68,10 +72,24 @@ extension Root {
case .path(.element(id: _, action: .addressBook(.editId(let address)))):
var isAddressBookFromSettings = false
for element in state.path {
if case .settings = element {
for (id, element) in zip(state.path.ids, state.path) {
if element.is(\.settings) {
isAddressBookFromSettings = true
break
}
if element.is(\.sendForm) {
state.path[id: id, case: \.sendForm]?.address = address.redacted
state.path[id: id, case: \.sendForm]?.isValidAddress = true
state.path[id: id, case: \.sendForm]?.isValidTransparentAddress = derivationTool.isTransparentAddress(
address,
zcashSDKEnvironment.network.networkType
)
state.path[id: id, case: \.sendForm]?.isValidTexAddress = derivationTool.isTexAddress(
address,
zcashSDKEnvironment.network.networkType
)
audioServices.systemSoundVibrate()
let _ = state.path.popLast()
return .none
}
}
if isAddressBookFromSettings {
@ -80,9 +98,9 @@ extension Root {
addressBookContactState.isNameFocused = true
state.path.append(.addressBookContact(addressBookContactState))
} else {
var sendFlowState = SendFlow.State.initial
sendFlowState.address = address.redacted
state.path.append(.sendFlow(sendFlowState))
var sendFormState = SendForm.State.initial
sendFormState.address = address.redacted
state.path.append(.sendForm(sendFormState))
}
return .none
@ -141,7 +159,7 @@ extension Root {
return .none
case .home(.sendTapped):
state.path.append(.addressBook(AddressBook.State.initial))
state.path.append(.sendForm(SendForm.State.initial))
return .none
case .home(.flexaTapped):
@ -151,6 +169,13 @@ extension Root {
state.path.append(.addKeystoneHWWallet(AddKeystoneHWWallet.State.initial))
return .none
case .home(.seeAllTransactionsTapped):
state.path.append(.transactionsManager(TransactionsManager.State.initial))
return .none
case .home(.transactionList(.transactionTapped(let txId))):
return .send(.transactionDetailsOpen(txId))
// MARK: - Integrations
case .path(.element(id: _, action: .integrations(.flexaTapped))):
@ -207,7 +232,7 @@ extension Root {
case .path(.element(id: _, action: .scan(.foundZA(let account)))):
for element in state.path {
if case .addKeystoneHWWallet = element {
if element.is(\.addKeystoneHWWallet) {
var addKeystoneHWWalletState = AddKeystoneHWWallet.State.initial
addKeystoneHWWalletState.zcashAccounts = account
state.path.append(.accountHWWalletSelection(addKeystoneHWWalletState))
@ -216,11 +241,116 @@ extension Root {
}
return .none
// MARK: - Send Flow
// MARK: - Send Confirmation
case .path(.element(id: _, action: .sendConfirmation(.cancelTapped))):
let _ = state.path.popLast()
return .none
case .path(.element(id: _, action: .sendConfirmation(.sendTapped))):
for element in state.path {
if case .sendConfirmation(let sendConfirmationState) = element {
state.path.append(.sending(sendConfirmationState))
break
}
}
return .none
case .path(.element(id: _, action: .sendFlow(.dismissRequired))):
case .path(.element(id: _, action: .sendConfirmation(.updateResult(let result)))):
for element in state.path {
if case .sendConfirmation(let sendConfirmationState) = element {
switch result {
case .failure:
state.path.append(.sendResultFailure(sendConfirmationState))
break
case .partial:
var partialProposalErrorState = PartialProposalError.State.initial
partialProposalErrorState.statuses = sendConfirmationState.partialFailureStatuses
partialProposalErrorState.txIds = sendConfirmationState.partialFailureTxIds
state.path.append(.sendResultPartial(partialProposalErrorState))
break
case .resubmission:
state.path.append(.sendResultResubmission(sendConfirmationState))
break
case .success:
state.path.append(.sendResultSuccess(sendConfirmationState))
default: break
}
break
}
}
return .none
case .path(.element(id: _, action: .sendResultSuccess(.closeTapped))):
state.path.removeAll()
return .none
case .path(.element(id: _, action: .sendResultSuccess(.backFromFailureTapped))):
for (id, element) in zip(state.path.ids, state.path) {
if element.is(\.sendForm) {
state.path.pop(to: id)
break
}
}
return .none
case .path(.element(id: _, action: .sendResultSuccess(.viewTransactionTapped))):
var transactionDetailsState = TransactionDetails.State.initial
for element in state.path {
if case .sendConfirmation(let sendConfirmationState) = element {
if let txid = sendConfirmationState.txIdToExpand {
if let index = state.transactions.index(id: txid) {
transactionDetailsState.transaction = state.transactions[index]
transactionDetailsState.isCloseButtonRequired = true
state.path.append(.transactionDetails(transactionDetailsState))
break
}
}
}
}
return .none
// MARK: - Send Form
case .path(.element(id: _, action: .sendForm(.dismissRequired))):
var popToRoot = true
for element in state.path {
if element.is(\.transactionDetails) {
popToRoot = false
break
}
}
if popToRoot {
state.path.removeAll()
} else {
let _ = state.path.popLast()
}
return .none
case .path(.element(id: _, action: .sendForm(.addressBookTapped))):
var addressBookState = AddressBook.State.initial
addressBookState.isInSelectMode = true
state.path.append(.addressBook(addressBookState))
return .none
case .path(.element(id: _, action: .sendForm(.confirmationRequired(let confirmationType)))):
var sendConfirmationState = SendConfirmation.State.initial
for element in state.path.reversed() {
if case .sendForm(let sendState) = element {
sendConfirmationState.amount = sendState.amount
sendConfirmationState.address = sendState.address.data
sendConfirmationState.isShielding = false
sendConfirmationState.proposal = sendState.proposal
sendConfirmationState.feeRequired = sendState.feeRequired
sendConfirmationState.message = sendState.message
sendConfirmationState.currencyAmount = sendState.currencyConversion?.convert(sendState.amount).redacted ?? .empty
break
}
}
if confirmationType == .send {
state.path.append(.sendConfirmation(sendConfirmationState))
}
return .none
// MARK: - Settings
@ -250,14 +380,59 @@ extension Root {
state.path.append(.sendUsFeedback(SendFeedback.State.initial))
return .none
// MARK: - Transaction Details
case .transactionDetailsOpen(let txId):
var transactionDetailsState = TransactionDetails.State.initial
if let index = state.transactions.index(id: txId) {
transactionDetailsState.transaction = state.transactions[index]
}
state.path.append(.transactionDetails(transactionDetailsState))
return .none
case .path(.element(id: _, action: .transactionDetails(.saveAddressTapped))):
for element in state.path {
if case .transactionDetails(let transactionDetailsState) = element {
var addressBookState = AddressBook.State.initial
addressBookState.address = transactionDetailsState.transaction.address
addressBookState.isNameFocused = true
addressBookState.isValidZcashAddress = true
state.path.append(.addressBookContact(addressBookState))
break
}
}
return .none
case .path(.element(id: _, action: .transactionDetails(.sendAgainTapped))):
for element in state.path {
if case .transactionDetails(let transactionDetailsState) = element {
var sendFormState = SendForm.State.initial
sendFormState.address = transactionDetailsState.transaction.address.redacted
sendFormState.isValidAddress = true
sendFormState.zecAmountText = transactionDetailsState.transaction.amountWithoutFee.decimalString().redacted
sendFormState.memoState.text = state.transactionMemos[transactionDetailsState.transaction.id]?.first ?? ""
state.path.append(.sendForm(sendFormState))
break
}
}
return .none
case .path(.element(id: _, action: .transactionDetails(.closeDetailTapped))):
state.path.removeAll()
return .none
// MARK: - Transactions Manager
case .path(.element(id: _, action: .transactionsManager(.transactionTapped(let txId)))):
return .send(.transactionDetailsOpen(txId))
// MARK: - Zec Keyboard
case .path(.element(id: _, action: .zecKeyboard(.nextTapped))):
for (_, element) in zip(state.path.ids, state.path) {
switch element {
case .zecKeyboard(let zecKeyboardState):
for element in state.path {
if case .zecKeyboard(let zecKeyboardState) = element {
state.requestZecState.requestedZec = zecKeyboardState.amount
default: break
break
}
}
state.path.append(.requestZec(state.requestZecState))
@ -272,3 +447,11 @@ extension Root {
}
}
//for (_, element) in zip(state.path.ids, state.path) {
// switch element {
// case .zecKeyboard(let zecKeyboardState):
// state.requestZecState.requestedZec = zecKeyboardState.amount
// default: break
// }
//}

View File

@ -613,7 +613,7 @@ extension Root {
state.alert = AlertState.wipeFailed(Int32.max)
return .cancel(id: SynchronizerCancelId)
case .phraseDisplay(.finishedPressed), .onboarding(.securityWarning(.recoveryPhraseDisplay(.finishedPressed))):
case .phraseDisplay(.finishedTapped), .onboarding(.securityWarning(.recoveryPhraseDisplay(.finishedTapped))):
do {
try walletStorage.markUserPassedPhraseBackupTest(true)
state.destinationState.destination = .home
@ -643,7 +643,7 @@ extension Root {
await send(.onboarding(.importExistingWallet))
}
case .onboarding(.importWallet(.nextPressed)):
case .onboarding(.importWallet(.nextTapped)):
if state.appInitializationState == .keysMissing {
let seedPhrase = state.onboardingState.importWalletState.importedSeedPhrase
return .run { send in

View File

@ -29,6 +29,7 @@ import URIParser
import OSStatusError
import AddressBookClient
import UserMetadataProvider
import AudioServices
// Screens
import About
@ -39,15 +40,19 @@ import CurrencyConversionSetup
import DeleteWallet
import ExportTransactionHistory
import Home
import PartialProposalError
import PrivateDataConsent
import Receive
import RecoveryPhraseDisplay
import RequestZec
import Scan
import SendConfirmation
import SendFeedback
import SendFlow
import SendForm
import ServerSetup
import Settings
import TransactionDetails
import TransactionsManager
import WhatsNew
import ZecKeyboard
@ -72,19 +77,28 @@ public struct Root {
case exportPrivateData(PrivateDataConsent)
case exportTransactionHistory(ExportTransactionHistory)
case integrations(Integrations)
//case preSendingFailure(SendConfirmation)
case receive(Receive)
case recoveryPhrase(RecoveryPhraseDisplay)
case requestZec(RequestZec)
case requestZecSummary(RequestZec)
case resetZashi(DeleteWallet)
case scan(Scan)
case sendFlow(SendFlow)
case sendConfirmation(SendConfirmation)
case sendForm(SendForm)
case sending(SendConfirmation)
case sendResultFailure(SendConfirmation)
case sendResultPartial(PartialProposalError)
case sendResultResubmission(SendConfirmation)
case sendResultSuccess(SendConfirmation)
case sendUsFeedback(SendFeedback)
case settings(Settings)
case transactionDetails(TransactionDetails)
case transactionsManager(TransactionsManager)
case whatsNew(WhatsNew)
case zecKeyboard(ZecKeyboard)
}
let CancelId = UUID()
let CancelStateId = UUID()
let CancelBatteryStateId = UUID()
@ -139,7 +153,7 @@ public struct Root {
// coordinator states
public var requestZecState = RequestZec.State.initial
public init(
// addressBookState: AddressBook.State = .initial,
appInitializationState: InitializationState = .uninitialized,
@ -215,6 +229,7 @@ public struct Root {
case serverSetup(ServerSetup.Action)
case serverSetupBindingUpdated(Bool)
case synchronizerStateChanged(RedactableSynchronizerState)
case transactionDetailsOpen(String)
case updateStateAfterConfigUpdate(WalletConfig)
case walletConfigLoaded(WalletConfig)
case welcome(Welcome.Action)
@ -237,6 +252,7 @@ public struct Root {
}
@Dependency(\.addressBook) var addressBook
@Dependency(\.audioServices) var audioServices
@Dependency(\.autolockHandler) var autolockHandler
@Dependency(\.databaseFiles) var databaseFiles
@Dependency(\.deeplink) var deeplink

View File

@ -20,15 +20,19 @@ import CurrencyConversionSetup
import DeleteWallet
import ExportTransactionHistory
import Home
import PartialProposalError
import PrivateDataConsent
import Receive
import RecoveryPhraseDisplay
import RequestZec
import Scan
import SendConfirmation
import SendFeedback
import SendFlow
import SendForm
import ServerSetup
import Settings
import TransactionDetails
import TransactionsManager
import WhatsNew
import ZecKeyboard
@ -170,14 +174,32 @@ private extension RootView {
RequestZecSummaryView(store: store, tokenName: tokenName)
case let .resetZashi(store):
DeleteWalletView(store: store)
// case let .preSendingFailure(store):
// PreSendingFailureView(store: store, tokenName: tokenName)
case let .scan(store):
ScanView(store: store)
case let .sendConfirmation(store):
SendConfirmationView(store: store, tokenName: tokenName)
case let .sending(store):
SendingView(store: store, tokenName: tokenName)
case let .sendUsFeedback(store):
SendFeedbackView(store: store)
case let .sendFlow(store):
SendFlowView(store: store, tokenName: tokenName)
case let .sendForm(store):
SendFormView(store: store, tokenName: tokenName)
case let .sendResultFailure(store):
FailureView(store: store, tokenName: tokenName)
case let .sendResultPartial(store):
PartialProposalErrorView(store: store)
case let .sendResultResubmission(store):
ResubmissionView(store: store, tokenName: tokenName)
case let .sendResultSuccess(store):
SuccessView(store: store, tokenName: tokenName)
case let .settings(store):
SettingsView(store: store)
case let .transactionDetails(store):
TransactionDetailsView(store: store, tokenName: tokenName)
case let .transactionsManager(store):
TransactionsManagerView(store: store, tokenName: tokenName)
case let .whatsNew(store):
WhatsNewView(store: store)
case let .zecKeyboard(store):

View File

@ -75,7 +75,7 @@ public struct Scan {
@Dependency(\.zcashSDKEnvironment) var zcashSDKEnvironment
public enum Action: Equatable {
case cancelPressed
case cancelTapped
case clearInfo
case libraryImage(UIImage?)
case onAppear
@ -87,7 +87,7 @@ public struct Scan {
case animatedQRProgress(Int, Int?, Int?)
case scanFailed(ScanImageResult)
case scan(RedactableString)
case torchPressed
case torchTapped
}
public init() { }
@ -135,7 +135,7 @@ public struct Scan {
state.progress = nil
return .none
case .cancelPressed:
case .cancelTapped:
return .none
case .clearInfo:
@ -206,7 +206,7 @@ public struct Scan {
}
return .send(.scanFailed(.noQRCodeFound))
case .torchPressed:
case .torchTapped:
do {
try captureDevice.torch(!state.isTorchOn)
state.isTorchOn.toggle()

View File

@ -94,7 +94,7 @@ public struct ScanView: View {
}
} else {
primaryButton(L10n.General.cancel) {
store.send(.cancelPressed)
store.send(.cancelTapped)
}
}
}
@ -145,7 +145,7 @@ public struct ScanView: View {
return WithPerceptionTracking {
Button {
store.send(.torchPressed)
store.send(.torchTapped)
} label: {
if store.isTorchOn {
Asset.Assets.Icons.flashOff.image

View File

@ -109,7 +109,7 @@ public struct SecurityWarning {
case .newWalletCreated:
return .none
case .recoveryPhraseDisplay(.finishedPressed):
case .recoveryPhraseDisplay(.finishedTapped):
state.recoveryPhraseDisplayViewBinding = false
return .none

View File

@ -58,7 +58,7 @@ public struct FailureView: View {
Spacer()
ZashiButton(L10n.General.close) {
store.send(.backFromFailurePressed)
store.send(.backFromFailureTapped)
}
.padding(.bottom, 8)
@ -84,21 +84,21 @@ public struct FailureView: View {
shareMessageView()
}
.navigationLinkEmpty(
isActive: store.bindingForStackTransactions(.details),
destination: {
TransactionDetailsView(
store: store.transactionDetailsStore(),
tokenName: tokenName
)
.navigationLinkEmpty(
isActive: store.bindingForStackTransactions(.addressBook),
destination: {
AddressBookContactView(store: store.addressBookStore())
}
)
}
)
// .navigationLinkEmpty(
// isActive: store.bindingForStackTransactions(.details),
// destination: {
// TransactionDetailsView(
// store: store.transactionDetailsStore(),
// tokenName: tokenName
// )
// .navigationLinkEmpty(
// isActive: store.bindingForStackTransactions(.addressBook),
// destination: {
// AddressBookContactView(store: store.addressBookStore())
// }
// )
// }
// )
}
.navigationBarBackButtonHidden()
.padding(.vertical, 1)

View File

@ -45,7 +45,7 @@ public struct PreSendingFailureView: View {
Spacer()
ZashiButton(L10n.General.close) {
store.send(.backFromPCZTFailurePressed)
store.send(.backFromPCZTFailureTapped)
}
.padding(.bottom, 8)

View File

@ -203,17 +203,17 @@ public struct RequestPaymentConfirmationView: View {
.padding(.bottom, 20)
}
.padding(.vertical, 1)
.navigationLinkEmpty(
isActive: $store.partialProposalErrorViewBinding,
destination: {
PartialProposalErrorView(
store: store.scope(
state: \.partialProposalErrorState,
action: \.partialProposalError
)
)
}
)
// .navigationLinkEmpty(
// isActive: $store.partialProposalErrorViewBinding,
// destination: {
// PartialProposalErrorView(
// store: store.scope(
// state: \.partialProposalErrorState,
// action: \.partialProposalError
// )
// )
// }
// )
.alert($store.scope(state: \.alert, action: \.alert))
Spacer()
@ -241,7 +241,7 @@ public struct RequestPaymentConfirmationView: View {
.disabled(store.isSending)
} else {
ZashiButton(L10n.General.send) {
store.send(.sendPressed)
store.send(.sendTapped)
}
.screenHorizontalPadding()
.padding(.top, 40)
@ -249,7 +249,7 @@ public struct RequestPaymentConfirmationView: View {
}
ZashiButton(L10n.Send.goBack, type: .tertiary) {
store.send(.goBackPressedFromRequestZec)
store.send(.goBackTappedFromRequestZec)
}
.screenHorizontalPadding()
.disabled(store.isSending)
@ -258,18 +258,18 @@ public struct RequestPaymentConfirmationView: View {
}
.onAppear { store.send(.onAppear) }
.screenTitle(L10n.Send.RequestPayment.title.uppercased())
.navigationLinkEmpty(
isActive: store.bindingFor(.sending),
destination: {
SendingView(store: store, tokenName: tokenName)
}
)
.navigationLinkEmpty(
isActive: store.bindingForStack(.signWithKeystone),
destination: {
SignWithKeystoneView(store: store, tokenName: tokenName)
}
)
// .navigationLinkEmpty(
// isActive: store.bindingFor(.sending),
// destination: {
// SendingView(store: store, tokenName: tokenName)
// }
// )
// .navigationLinkEmpty(
// isActive: store.bindingForStack(.signWithKeystone),
// destination: {
// SignWithKeystoneView(store: store, tokenName: tokenName)
// }
// )
}
.navigationBarBackButtonHidden()
.padding(.vertical, 1)

View File

@ -62,21 +62,21 @@ public struct ResubmissionView: View {
}
.padding(.bottom, 24)
}
.navigationLinkEmpty(
isActive: store.bindingForStackTransactions(.details),
destination: {
TransactionDetailsView(
store: store.transactionDetailsStore(),
tokenName: tokenName
)
.navigationLinkEmpty(
isActive: store.bindingForStackTransactions(.addressBook),
destination: {
AddressBookContactView(store: store.addressBookStore())
}
)
}
)
// .navigationLinkEmpty(
// isActive: store.bindingForStackTransactions(.details),
// destination: {
// TransactionDetailsView(
// store: store.transactionDetailsStore(),
// tokenName: tokenName
// )
// .navigationLinkEmpty(
// isActive: store.bindingForStackTransactions(.addressBook),
// destination: {
// AddressBookContactView(store: store.addressBookStore())
// }
// )
// }
// )
}
.navigationBarBackButtonHidden()
.padding(.vertical, 1)

View File

@ -161,23 +161,23 @@ public struct SendConfirmationView: View {
}
}
.padding(.vertical, 1)
.navigationLinkEmpty(
isActive: $store.partialProposalErrorViewBinding,
destination: {
PartialProposalErrorView(
store: store.scope(
state: \.partialProposalErrorState,
action: \.partialProposalError
)
)
}
)
// .navigationLinkEmpty(
// isActive: $store.partialProposalErrorViewBinding,
// destination: {
// PartialProposalErrorView(
// store: store.scope(
// state: \.partialProposalErrorState,
// action: \.partialProposalError
// )
// )
// }
// )
.alert($store.scope(state: \.alert, action: \.alert))
Spacer()
ZashiButton(L10n.Send.goBack, type: .ghost) {
store.send(.goBackPressed)
store.send(.cancelTapped)
}
.screenHorizontalPadding()
.disabled(store.isSending)
@ -207,7 +207,7 @@ public struct SendConfirmationView: View {
.disabled(store.isSending)
} else {
ZashiButton(L10n.General.send) {
store.send(.sendPressed)
store.send(.sendTapped)
}
.screenHorizontalPadding()
.padding(.bottom, 24)
@ -220,18 +220,18 @@ public struct SendConfirmationView: View {
? L10n.Send.review
: L10n.Send.confirmationTitle
)
.navigationLinkEmpty(
isActive: store.bindingFor(.sending),
destination: {
SendingView(store: store, tokenName: tokenName)
}
)
.navigationLinkEmpty(
isActive: store.bindingForStack(.signWithKeystone),
destination: {
SignWithKeystoneView(store: store, tokenName: tokenName)
}
)
// .navigationLinkEmpty(
// isActive: store.bindingFor(.sending),
// destination: {
// SendingView(store: store, tokenName: tokenName)
// }
// )
// .navigationLinkEmpty(
// isActive: store.bindingForStack(.signWithKeystone),
// destination: {
// SignWithKeystoneView(store: store, tokenName: tokenName)
// }
// )
}
// .zashiBack()
.navigationBarTitleDisplayMode(.inline)
@ -252,88 +252,88 @@ public struct SendConfirmationView: View {
// MARK: - ViewStore
extension StoreOf<SendConfirmation> {
func bindingForStack(_ destination: SendConfirmation.State.StackDestination) -> Binding<Bool> {
Binding<Bool>(
get: {
if let currentStackValue = self.stackDestination?.rawValue {
return currentStackValue >= destination.rawValue
} else {
if destination.rawValue == 0 {
return false
} else if destination.rawValue <= self.stackDestinationBindingsAlive {
return true
} else {
return false
}
}
},
set: { _ in
if let currentStackValue = self.stackDestination?.rawValue, currentStackValue == destination.rawValue {
let popIndex = destination.rawValue - 1
if popIndex >= 0 {
let popDestination = SendConfirmation.State.StackDestination(rawValue: popIndex)
self.send(.updateStackDestination(popDestination))
} else {
self.send(.updateStackDestination(nil))
}
}
}
)
}
func bindingForStackTransactions(_ destination: SendConfirmation.State.StackDestinationTransactions) -> Binding<Bool> {
Binding<Bool>(
get: {
if let currentStackValue = self.stackDestinationTransactions?.rawValue {
return currentStackValue >= destination.rawValue
} else {
if destination.rawValue == 0 {
return false
} else if destination.rawValue <= self.stackDestinationTransactionsBindingsAlive {
return true
} else {
return false
}
}
},
set: { _ in
if let currentStackValue = self.stackDestinationTransactions?.rawValue, currentStackValue == destination.rawValue {
let popIndex = destination.rawValue - 1
if popIndex >= 0 {
let popDestination = SendConfirmation.State.StackDestinationTransactions(rawValue: popIndex)
self.send(.updateStackDestinationTransactions(popDestination))
} else {
self.send(.updateStackDestinationTransactions(nil))
}
}
}
)
}
}
//extension StoreOf<SendConfirmation> {
// func bindingForStack(_ destination: SendConfirmation.State.StackDestination) -> Binding<Bool> {
// Binding<Bool>(
// get: {
// if let currentStackValue = self.stackDestination?.rawValue {
// return currentStackValue >= destination.rawValue
// } else {
// if destination.rawValue == 0 {
// return false
// } else if destination.rawValue <= self.stackDestinationBindingsAlive {
// return true
// } else {
// return false
// }
// }
// },
// set: { _ in
// if let currentStackValue = self.stackDestination?.rawValue, currentStackValue == destination.rawValue {
// let popIndex = destination.rawValue - 1
// if popIndex >= 0 {
// let popDestination = SendConfirmation.State.StackDestination(rawValue: popIndex)
// self.send(.updateStackDestination(popDestination))
// } else {
// self.send(.updateStackDestination(nil))
// }
// }
// }
// )
// }
//
// func bindingForStackTransactions(_ destination: SendConfirmation.State.StackDestinationTransactions) -> Binding<Bool> {
// Binding<Bool>(
// get: {
// if let currentStackValue = self.stackDestinationTransactions?.rawValue {
// return currentStackValue >= destination.rawValue
// } else {
// if destination.rawValue == 0 {
// return false
// } else if destination.rawValue <= self.stackDestinationTransactionsBindingsAlive {
// return true
// } else {
// return false
// }
// }
// },
// set: { _ in
// if let currentStackValue = self.stackDestinationTransactions?.rawValue, currentStackValue == destination.rawValue {
// let popIndex = destination.rawValue - 1
// if popIndex >= 0 {
// let popDestination = SendConfirmation.State.StackDestinationTransactions(rawValue: popIndex)
// self.send(.updateStackDestinationTransactions(popDestination))
// } else {
// self.send(.updateStackDestinationTransactions(nil))
// }
// }
// }
// )
// }
//}
extension StoreOf<SendConfirmation> {
func scanStore() -> StoreOf<Scan> {
self.scope(
state: \.scanState,
action: \.scan
)
}
func addressBookStore() -> StoreOf<AddressBook> {
self.scope(
state: \.addressBookState,
action: \.addressBook
)
}
func transactionDetailsStore() -> StoreOf<TransactionDetails> {
self.scope(
state: \.transactionDetailsState,
action: \.transactionDetails
)
}
}
//extension StoreOf<SendConfirmation> {
//// func scanStore() -> StoreOf<Scan> {
//// self.scope(
//// state: \.scanState,
//// action: \.scan
//// )
//// }
//
// func addressBookStore() -> StoreOf<AddressBook> {
// self.scope(
// state: \.addressBookState,
// action: \.addressBook
// )
// }
//
// func transactionDetailsStore() -> StoreOf<TransactionDetails> {
// self.scope(
// state: \.transactionDetailsState,
// action: \.transactionDetails
// )
// }
//}
// MARK: - Store
@ -353,25 +353,25 @@ extension SendConfirmation.State {
amount: .zero,
feeRequired: .zero,
message: "",
partialProposalErrorState: .initial,
// partialProposalErrorState: .initial,
proposal: nil
)
}
// MARK: - ViewStore
extension StoreOf<SendConfirmation> {
func bindingFor(_ destination: SendConfirmation.State.Destination) -> Binding<Bool> {
Binding<Bool>(
get: { self.destination == destination },
set: { self.send(.updateDestination($0 ? destination : nil)) }
)
}
func bindingForResult(_ result: SendConfirmation.State.Result) -> Binding<Bool> {
Binding<Bool>(
get: { self.result == result },
set: { self.send(.updateResult($0 ? result : nil)) }
)
}
}
//extension StoreOf<SendConfirmation> {
//// func bindingFor(_ destination: SendConfirmation.State.Destination) -> Binding<Bool> {
// Binding<Bool>(
// get: { self.destination == destination },
// set: { self.send(.updateDestination($0 ? destination : nil)) }
// )
// }
//
// func bindingForResult(_ result: SendConfirmation.State.Result) -> Binding<Bool> {
// Binding<Bool>(
// get: { self.result == result },
// set: { self.send(.updateResult($0 ? result : nil)) }
// )
// }
//}

View File

@ -60,35 +60,35 @@ public struct SendingView: View {
}
}
.onAppear { store.send(.sendingScreenOnAppear) }
.navigationLinkEmpty(
isActive: store.bindingForResult(.success),
destination: {
SuccessView(store: store, tokenName: tokenName)
}
)
.navigationLinkEmpty(
isActive: store.bindingForResult(.failure),
destination: {
FailureView(store: store, tokenName: tokenName)
}
)
.navigationLinkEmpty(
isActive: store.bindingForResult(.resubmission),
destination: {
ResubmissionView(store: store, tokenName: tokenName)
}
)
.navigationLinkEmpty(
isActive: store.bindingForResult(.partial),
destination: {
PartialProposalErrorView(
store: store.scope(
state: \.partialProposalErrorState,
action: \.partialProposalError
)
)
}
)
// .navigationLinkEmpty(
// isActive: store.bindingForResult(.success),
// destination: {
// SuccessView(store: store, tokenName: tokenName)
// }
// )
// .navigationLinkEmpty(
// isActive: store.bindingForResult(.failure),
// destination: {
// FailureView(store: store, tokenName: tokenName)
// }
// )
// .navigationLinkEmpty(
// isActive: store.bindingForResult(.resubmission),
// destination: {
// ResubmissionView(store: store, tokenName: tokenName)
// }
// )
// .navigationLinkEmpty(
// isActive: store.bindingForResult(.partial),
// destination: {
// PartialProposalErrorView(
// store: store.scope(
// state: \.partialProposalErrorState,
// action: \.partialProposalError
// )
// )
// }
// )
}
.navigationBarBackButtonHidden()
.screenHorizontalPadding()

View File

@ -177,32 +177,32 @@ public struct SignWithKeystoneView: View {
}
.frame(maxWidth: .infinity)
.padding(.top, 20)
.navigationLinkEmpty(
isActive: store.bindingForStack(.scan),
destination: {
ScanView(
store: store.scanStore()
)
.navigationLinkEmpty(
isActive: store.bindingForStack(.sending),
destination: {
SendingView(store: store, tokenName: tokenName)
}
)
.navigationLinkEmpty(
isActive: $store.scanFailedDuringScanBinding,
destination: {
PreSendingFailureView(store: store, tokenName: tokenName)
}
)
}
)
.navigationLinkEmpty(
isActive: $store.scanFailedPreScanBinding,
destination: {
PreSendingFailureView(store: store, tokenName: tokenName)
}
)
// .navigationLinkEmpty(
// isActive: store.bindingForStack(.scan),
// destination: {
// ScanView(
// store: store.scanStore()
// )
// .navigationLinkEmpty(
// isActive: store.bindingForStack(.sending),
// destination: {
// SendingView(store: store, tokenName: tokenName)
// }
// )
// .navigationLinkEmpty(
// isActive: $store.scanFailedDuringScanBinding,
// destination: {
// PreSendingFailureView(store: store, tokenName: tokenName)
// }
// )
// }
// )
// .navigationLinkEmpty(
// isActive: $store.scanFailedPreScanBinding,
// destination: {
// PreSendingFailureView(store: store, tokenName: tokenName)
// }
// )
}
.screenHorizontalPadding()
.applyScreenBackground()

View File

@ -68,21 +68,21 @@ public struct SuccessView: View {
}
.padding(.bottom, 24)
}
.navigationLinkEmpty(
isActive: store.bindingForStackTransactions(.details),
destination: {
TransactionDetailsView(
store: store.transactionDetailsStore(),
tokenName: tokenName
)
.navigationLinkEmpty(
isActive: store.bindingForStackTransactions(.addressBook),
destination: {
AddressBookContactView(store: store.addressBookStore())
}
)
}
)
// .navigationLinkEmpty(
// isActive: store.bindingForStackTransactions(.details),
// destination: {
// TransactionDetailsView(
// store: store.transactionDetailsStore(),
// tokenName: tokenName
// )
// .navigationLinkEmpty(
// isActive: store.bindingForStackTransactions(.addressBook),
// destination: {
// AddressBookContactView(store: store.addressBookStore())
// }
// )
// }
// )
}
.navigationBarBackButtonHidden()
.padding(.vertical, 1)

View File

@ -1,5 +1,5 @@
//
// SendFlowStore.swift
// SendFormStore.swift
// secant-testnet
//
// Created by Lukáš Korba on 04/25/2022.
@ -23,7 +23,7 @@ import UserPreferencesStorage
import AddressBookClient
@Reducer
public struct SendFlow {
public struct SendForm {
public enum Confirmation {
case requestPayment
case send
@ -31,10 +31,10 @@ public struct SendFlow {
@ObservableState
public struct State {
public enum Destination {
case partialProposalError
case scanQR
}
// public enum Destination {
// case partialProposalError
// case scanQR
// }
public var cancelId = UUID()
@ -44,7 +44,7 @@ public struct SendFlow {
@Presents public var alert: AlertState<Action>?
@Shared(.inMemory(.exchangeRate)) public var currencyConversion: CurrencyConversion? = nil
public var currencyText: RedactableString = .empty
public var destination: Destination?
// public var destination: Destination?
public var isAddressBookHintVisible = false
public var isCurrencyConversionEnabled = false
public var isNotAddressInAddressBook = false
@ -55,7 +55,7 @@ public struct SendFlow {
public var memoState: MessageEditor.State
public var proposal: Proposal?
@Shared(.inMemory(.selectedWalletAccount)) public var selectedWalletAccount: WalletAccount? = nil
public var scanState: Scan.State
// public var scanState: Scan.State
public var shieldedBalance: Zatoshi
public var walletBalancesState: WalletBalances.State
public var requestsAddressFocus = false
@ -179,16 +179,16 @@ public struct SendFlow {
public init(
addMemoState: Bool,
destination: Destination? = nil,
// destination: Destination? = nil,
memoState: MessageEditor.State,
scanState: Scan.State,
// scanState: Scan.State,
shieldedBalance: Zatoshi = .zero,
walletBalancesState: WalletBalances.State
) {
self.addMemoState = addMemoState
self.destination = destination
// self.destination = destination
self.memoState = memoState
self.scanState = scanState
// self.scanState = scanState
self.shieldedBalance = shieldedBalance
self.walletBalancesState = walletBalancesState
}
@ -211,11 +211,11 @@ public struct SendFlow {
case proposal(Proposal)
case requestsAddressFocusResolved
case resetForm
case reviewPressed
case scan(Scan.Action)
case reviewTapped
// case scan(Scan.Action)
case sendFailed(ZcashError, Confirmation)
case syncAmounts(Bool)
case updateDestination(SendFlow.State.Destination?)
// case updateDestination(SendForm.State.Destination?)
case validateAddress
case walletBalances(WalletBalances.Action)
case zecAmountUpdated(RedactableString)
@ -236,9 +236,9 @@ public struct SendFlow {
MessageEditor()
}
Scope(state: \.scanState, action: \.scan) {
Scan()
}
// Scope(state: \.scanState, action: \.scan) {
// Scan()
// }
Scope(state: \.walletBalancesState, action: \.walletBalances) {
WalletBalances()
@ -247,7 +247,7 @@ public struct SendFlow {
Reduce { state, action in
switch action {
case .onAppear:
state.scanState.checkers = [.zcashAddressScanChecker, .requestZecScanChecker]
// state.scanState.checkers = [.zcashAddressScanChecker, .requestZecScanChecker]
state.memoState.charLimit = zcashSDKEnvironment.memoCharLimit
guard let _ = state.zashiWalletAccount else {
return .send(.exchangeRateSetupChanged)
@ -290,12 +290,12 @@ public struct SendFlow {
state.proposal = proposal
return .none
case let .updateDestination(destination):
if destination == .scanQR {
state.isPaymentRequestInProgress = false
}
state.destination = destination
return .none
// case let .updateDestination(destination):
// if destination == .scanQR {
// state.isPaymentRequestInProgress = false
// }
// state.destination = destination
// return .none
case .walletBalances(.exchangeRateEvent(let result)):
switch result {
@ -310,7 +310,7 @@ public struct SendFlow {
}
return .none
case .reviewPressed:
case .reviewTapped:
return .send(.getProposal(.send))
case .getProposal(let confirmationType):
@ -341,12 +341,13 @@ public struct SendFlow {
case let .sendFailed(error, confirmationType):
if confirmationType == .requestPayment {
return .send(.updateDestination(nil))
// return .send(.updateDestination(nil))
} else {
state.alert = AlertState.sendFailure(error)
return .none
}
return .none
case .confirmationRequired:
return .none
@ -387,50 +388,51 @@ public struct SendFlow {
case .memo:
return .none
case .scan(.foundRP(let requestPayment)):
guard !state.isPaymentRequestInProgress else {
return .none
}
state.isPaymentRequestInProgress = true
if case .legacy(let address) = requestPayment {
audioServices.systemSoundVibrate()
return .send(.scan(.found(address.value.redacted)))
} else if case .request(let paymentRequest) = requestPayment {
if let payment = paymentRequest.payments.first {
if let memoBytes = payment.memo, let memo = try? Memo(bytes: [UInt8](memoBytes.memoData)) {
state.memoState.text = memo.toString() ?? ""
}
let numberLocale = numberFormatter.convertUSToLocale(payment.amount.toString()) ?? ""
state.address = payment.recipientAddress.value.redacted
state.zecAmountText = numberLocale.redacted
audioServices.systemSoundVibrate()
return .send(.getProposal(.requestPayment))
}
}
return .none
case .scan(.found(let address)):
state.address = address
// The is valid Zcash address check is already covered in the scan feature
// so we can be sure it's valid and thus `true` value here.
state.isValidAddress = true
state.isValidTransparentAddress = derivationTool.isTransparentAddress(
address.data,
zcashSDKEnvironment.network.networkType
)
state.isValidTexAddress = derivationTool.isTexAddress(
address.data,
zcashSDKEnvironment.network.networkType
)
audioServices.systemSoundVibrate()
return .send(.updateDestination(nil))
case .scan(.cancelPressed):
state.destination = nil
return .none
case .scan:
return .none
// case .scan(.foundRP(let requestPayment)):
// guard !state.isPaymentRequestInProgress else {
// return .none
// }
// state.isPaymentRequestInProgress = true
// if case .legacy(let address) = requestPayment {
// audioServices.systemSoundVibrate()
// return .send(.scan(.found(address.value.redacted)))
// } else if case .request(let paymentRequest) = requestPayment {
// if let payment = paymentRequest.payments.first {
// if let memoBytes = payment.memo, let memo = try? Memo(bytes: [UInt8](memoBytes.memoData)) {
// state.memoState.text = memo.toString() ?? ""
// }
// let numberLocale = numberFormatter.convertUSToLocale(payment.amount.toString()) ?? ""
// state.address = payment.recipientAddress.value.redacted
// state.zecAmountText = numberLocale.redacted
// audioServices.systemSoundVibrate()
// return .send(.getProposal(.requestPayment))
// }
// }
// return .none
//
// case .scan(.found(let address)):
// state.address = address
// // The is valid Zcash address check is already covered in the scan feature
// // so we can be sure it's valid and thus `true` value here.
// state.isValidAddress = true
// state.isValidTransparentAddress = derivationTool.isTransparentAddress(
// address.data,
// zcashSDKEnvironment.network.networkType
// )
// state.isValidTexAddress = derivationTool.isTexAddress(
// address.data,
// zcashSDKEnvironment.network.networkType
// )
// audioServices.systemSoundVibrate()
//// return .send(.updateDestination(nil))
// return .none
//
// case .scan(.cancelTapped):
// state.destination = nil
// return .none
//
// case .scan:
// return .none
case .walletBalances(.balanceUpdated):
state.shieldedBalance = state.walletBalancesState.shieldedBalance
@ -499,7 +501,7 @@ public struct SendFlow {
// MARK: Alerts
extension AlertState where Action == SendFlow.Action {
extension AlertState where Action == SendForm.Action {
public static func sendFailure(_ error: ZcashError) -> AlertState {
AlertState {
TextState(L10n.Send.Alert.Failure.title)

View File

@ -1,5 +1,5 @@
//
// SendFlowView.swift
// SendFormView.swift
// secant-testnet
//
// Created by Lukáš Korba on 04/25/2022.
@ -8,12 +8,12 @@
import SwiftUI
import ComposableArchitecture
import Generated
import Scan
//import Scan
import UIComponents
import BalanceFormatter
import WalletBalances
public struct SendFlowView: View {
public struct SendFormView: View {
@Environment(\.colorScheme) private var colorScheme
private enum InputID: Hashable {
@ -23,7 +23,7 @@ public struct SendFlowView: View {
@State private var keyboardVisible: Bool = false
@Perception.Bindable var store: StoreOf<SendFlow>
@Perception.Bindable var store: StoreOf<SendForm>
let tokenName: String
@FocusState private var isAddressFocused
@ -31,7 +31,7 @@ public struct SendFlowView: View {
@FocusState private var isCurrencyFocused
@FocusState private var isMemoFocused
public init(store: StoreOf<SendFlow>, tokenName: String) {
public init(store: StoreOf<SendForm>, tokenName: String) {
self.store = store
self.tokenName = tokenName
}
@ -77,7 +77,7 @@ public struct SendFlowView: View {
}
fieldButton(icon: Asset.Assets.Icons.qr.image) {
store.send(.updateDestination(.scanQR))
// store.send(.updateDestination(.scanQR))
}
}
}
@ -171,7 +171,7 @@ public struct SendFlowView: View {
}
ZashiButton(L10n.Send.review) {
store.send(.reviewPressed)
store.send(.reviewTapped)
}
.disabled(!store.isValidForm)
.padding(.top, 40)
@ -203,12 +203,12 @@ public struct SendFlowView: View {
}
}
.applyScreenBackground()
.navigationLinkEmpty(
isActive: store.bindingFor(.scanQR),
destination: {
ScanView(store: store.scanStore())
}
)
// .navigationLinkEmpty(
// isActive: store.bindingFor(.scanQR),
// destination: {
// ScanView(store: store.scanStore())
// }
// )
}
}
}
@ -316,17 +316,17 @@ public struct SendFlowView: View {
#Preview {
NavigationView {
SendFlowView(
SendFormView(
store: .init(
initialState: .init(
addMemoState: true,
destination: nil,
// destination: nil,
memoState: .initial,
scanState: .initial,
// scanState: .initial,
walletBalancesState: .initial
)
) {
SendFlow()
SendForm()
},
tokenName: "ZEC"
)
@ -336,31 +336,31 @@ public struct SendFlowView: View {
// MARK: - Store
extension StoreOf<SendFlow> {
extension StoreOf<SendForm> {
func memoStore() -> StoreOf<MessageEditor> {
self.scope(
state: \.memoState,
action: \.memo
)
}
func scanStore() -> StoreOf<Scan> {
self.scope(
state: \.scanState,
action: \.scan
)
}
//
// func scanStore() -> StoreOf<Scan> {
// self.scope(
// state: \.scanState,
// action: \.scan
// )
// }
}
// MARK: - ViewStore
extension StoreOf<SendFlow> {
func bindingFor(_ destination: SendFlow.State.Destination) -> Binding<Bool> {
Binding<Bool>(
get: { self.destination == destination },
set: { self.send(.updateDestination($0 ? destination : nil)) }
)
}
extension StoreOf<SendForm> {
// func bindingFor(_ destination: SendForm.State.Destination) -> Binding<Bool> {
// Binding<Bool>(
// get: { self.destination == destination },
// set: { self.send(.updateDestination($0 ? destination : nil)) }
// )
// }
var bindingForAddress: Binding<String> {
Binding(
@ -386,25 +386,25 @@ extension StoreOf<SendFlow> {
// MARK: Placeholders
extension SendFlow.State {
extension SendForm.State {
public static var initial: Self {
.init(
addMemoState: true,
destination: nil,
// destination: nil,
memoState: .initial,
scanState: .initial,
// scanState: .initial,
walletBalancesState: .initial
)
}
}
// #if DEBUG // FIX: Issue #306 - Release build is broken
extension StoreOf<SendFlow> {
public static var placeholder: StoreOf<SendFlow> {
StoreOf<SendFlow>(
extension StoreOf<SendForm> {
public static var placeholder: StoreOf<SendForm> {
StoreOf<SendForm>(
initialState: .initial
) {
SendFlow()
SendForm()
}
}
}

View File

@ -15,7 +15,7 @@ import Generated
import Receive
import BalanceBreakdown
import Home
import SendFlow
import SendForm
import Settings
import ZcashLightClientKit
import SendConfirmation
@ -41,7 +41,7 @@ public struct Tabs {
public enum Path {
case receive(Receive)
case scan(Scan)
case sendFlow(SendFlow)
case sendFlow(SendForm)
case sendConfirmation(SendConfirmation)
}
@ -119,7 +119,7 @@ public struct Tabs {
@Shared(.inMemory(.selectedWalletAccount)) public var selectedWalletAccount: WalletAccount? = nil
public var selectTextRequest = false
public var sendConfirmationState: SendConfirmation.State
public var sendState: SendFlow.State
public var sendState: SendForm.State
public var settingsState: Settings.State
public var stackDestinationAddKeystoneHWWallet: StackDestinationAddKeystoneHWWallet?
public var stackDestinationAddKeystoneHWWalletBindingsAlive = 0
@ -172,7 +172,7 @@ public struct Tabs {
requestZecState: RequestZec.State,
selectedTab: Tab = .account,
sendConfirmationState: SendConfirmation.State,
sendState: SendFlow.State,
sendState: SendForm.State,
settingsState: Settings.State,
zecKeyboardState: ZecKeyboard.State
) {
@ -214,7 +214,7 @@ public struct Tabs {
case requestZec(RequestZec.Action)
case scan(Scan.Action)
case selectedTabChanged(State.Tab)
case send(SendFlow.Action)
case send(SendForm.Action)
case sendConfirmation(SendConfirmation.Action)
case settings(Settings.Action)
case transactionDetails(TransactionDetails.Action)
@ -258,7 +258,7 @@ public struct Tabs {
}
// Scope(state: \.sendState, action: \.send) {
// SendFlow()
// SendForm()
// }
Scope(state: \.sendConfirmationState, action: \.sendConfirmation) {
@ -408,19 +408,19 @@ public struct Tabs {
// state.selectedTab = .receive
return .none
case .home(.sendTapped):
// var test = SendFlow.State(
// addMemoState: false,
// memoState: .initial,
// scanState: .initial,
// walletBalancesState: .initial
// )
// test.address = "aaa".redacted
// state.path.append(.sendFlow(test))
//state.sendState.address = "aaa2".redacted
state.path.append(.sendFlow(state.sendState))
// state.selectedTab = .send
return .none
// case .home(.sendTapped):
//// var test = SendForm.State(
//// addMemoState: false,
//// memoState: .initial,
//// scanState: .initial,
//// walletBalancesState: .initial
//// )
//// test.address = "aaa".redacted
//// state.path.append(.sendFlow(test))
// //state.sendState.address = "aaa2".redacted
// state.path.append(.sendFlow(state.sendState))
//// state.selectedTab = .send
// return .none
// case .path(.element(id: _, action: .sendFlow(.addressUpdated))):
// print("__LD \(state.sendState.address)")
@ -511,7 +511,7 @@ public struct Tabs {
case .settings(.advancedSettings(.currencyConversionSetup(.saveChangesTapped))):
return .send(.send(.exchangeRateSetupChanged))
case .path(.element(id: _, action: .scan(.cancelPressed))):
case .path(.element(id: _, action: .scan(.cancelTapped))):
state.path.removeAll()
return .none
@ -521,38 +521,38 @@ public struct Tabs {
return .none
//case .send(.confirmationRequired(let type)):
case .path(.element(id: _, action: .sendFlow(.confirmationRequired(let type)))):
// if case .sendFlow = state.path {
//
// case .path(.element(id: _, action: .sendFlow(.confirmationRequired(let type)))):
//// if case .sendFlow = state.path {
////
//// }
//
// for (_, element) in zip(state.path.ids, state.path) {
// switch element {
// case .sendFlow(let sendState):
// state.sendConfirmationState.amount = sendState.amount
// state.sendConfirmationState.address = sendState.address.data
// state.sendConfirmationState.isShielding = false
// state.sendConfirmationState.proposal = sendState.proposal
// state.sendConfirmationState.feeRequired = sendState.feeRequired
// state.sendConfirmationState.message = sendState.message
// state.sendConfirmationState.currencyAmount = sendState.currencyConversion?.convert(sendState.amount).redacted ?? .empty
// default: break
// }
// }
//// state.sendConfirmationState.amount = state.sendState.amount
//// state.sendConfirmationState.address = state.sendState.address.data
//// state.sendConfirmationState.isShielding = false
//// state.sendConfirmationState.proposal = state.sendState.proposal
//// state.sendConfirmationState.feeRequired = state.sendState.feeRequired
//// state.sendConfirmationState.message = state.sendState.message
//// state.sendConfirmationState.currencyAmount = state.sendState.currencyConversion?.convert(state.sendState.amount).redacted ?? .empty
// if type == .send {
// //return .send(.updateDestination(.sendConfirmation))
// state.path.append(.sendConfirmation(state.sendConfirmationState))
// return .none
// } else {
// return .send(.updateStackDestinationRequestPayment(.requestPaymentConfirmation))
// }
for (_, element) in zip(state.path.ids, state.path) {
switch element {
case .sendFlow(let sendState):
state.sendConfirmationState.amount = sendState.amount
state.sendConfirmationState.address = sendState.address.data
state.sendConfirmationState.isShielding = false
state.sendConfirmationState.proposal = sendState.proposal
state.sendConfirmationState.feeRequired = sendState.feeRequired
state.sendConfirmationState.message = sendState.message
state.sendConfirmationState.currencyAmount = sendState.currencyConversion?.convert(sendState.amount).redacted ?? .empty
default: break
}
}
// state.sendConfirmationState.amount = state.sendState.amount
// state.sendConfirmationState.address = state.sendState.address.data
// state.sendConfirmationState.isShielding = false
// state.sendConfirmationState.proposal = state.sendState.proposal
// state.sendConfirmationState.feeRequired = state.sendState.feeRequired
// state.sendConfirmationState.message = state.sendState.message
// state.sendConfirmationState.currencyAmount = state.sendState.currencyConversion?.convert(state.sendState.amount).redacted ?? .empty
if type == .send {
//return .send(.updateDestination(.sendConfirmation))
state.path.append(.sendConfirmation(state.sendConfirmationState))
return .none
} else {
return .send(.updateStackDestinationRequestPayment(.requestPaymentConfirmation))
}
case .send:
return .none
@ -584,17 +584,17 @@ public struct Tabs {
.send(.send(.resetForm))
)
case .sendConfirmation(.backFromFailurePressed):
state.sendConfirmationState.stackDestinationBindingsAlive = 0
return .concatenate(
.send(.updateDestination(nil)),
.send(.updateStackDestinationRequestPayment(nil)),
.send(.sendConfirmation(.updateDestination(nil))),
.send(.sendConfirmation(.updateResult(nil))),
.send(.sendConfirmation(.updateStackDestination(nil)))
)
// case .sendConfirmation(.backFromFailureTapped):
// state.sendConfirmationState.stackDestinationBindingsAlive = 0
// return .concatenate(
// .send(.updateDestination(nil)),
// .send(.updateStackDestinationRequestPayment(nil)),
// .send(.sendConfirmation(.updateDestination(nil))),
// .send(.sendConfirmation(.updateResult(nil))),
// .send(.sendConfirmation(.updateStackDestination(nil)))
// )
case .sendConfirmation(.backFromPCZTFailurePressed):
case .sendConfirmation(.backFromPCZTFailureTapped):
state.sendConfirmationState.stackDestinationBindingsAlive = 0
state.destination = nil
state.sendConfirmationState.scanFailedPreScanBinding = false
@ -626,7 +626,7 @@ public struct Tabs {
}
case .sendConfirmation(.goBackPressedFromRequestZec):
case .sendConfirmation(.goBackTappedFromRequestZec):
state.sendState.address = .empty
state.sendState.memoState.text = ""
state.sendState.zecAmountText = .empty
@ -657,7 +657,7 @@ public struct Tabs {
state.isRateTooltipEnabled = false
return .none
case .scan(.cancelPressed):
case .scan(.cancelTapped):
return .send(.updateStackDestinationAddKeystoneHWWallet(.addKeystoneHWWallet))
case .scan(.foundZA(let zcashAccounts)):
@ -732,10 +732,10 @@ public struct Tabs {
// MARK: - Send Confirmation
// case .sendConfirmation(.goBackPressed):
case .path(.element(id: _, action: .sendConfirmation(.goBackPressed))):
state.path.popLast()
return .none
// case .sendConfirmation(.goBackTapped):
// case .path(.element(id: _, action: .sendConfirmation(.goBackTapped))):
// state.path.popLast()
// return .none
//return .send(.updateDestination(nil))
// MARK: - Transaction History

View File

@ -14,7 +14,7 @@ import Generated
import Receive
import BalanceBreakdown
import Home
import SendFlow
import SendForm
import Settings
import UIComponents
import SendConfirmation
@ -74,7 +74,7 @@ public struct TabsView: View {
// } destination: { store in
// switch store.case {
// case let .sendFlow(store):
// SendFlowView(store: store, tokenName: tokenName)
// SendFormView(store: store, tokenName: tokenName)
// case let .receive(store):
// ReceiveView(store: store, networkType: networkType)
// case let .scan(store):
@ -686,7 +686,7 @@ public struct TabsView: View {
// .navigationLinkEmpty(
// isActive: store.bindingTabFor(.send),
// destination: {
// SendFlowView(
// SendFormView(
// store: self.store.scope(
// state: \.sendState,
// action: \.send

View File

@ -207,6 +207,10 @@ public struct TransactionState: Equatable, Identifiable {
: zecAmount.decimalString()
}
public var amountWithoutFee: Zatoshi {
Zatoshi(zecAmount.amount - (fee?.amount ?? 0))
}
public init(
errorMessage: String? = nil,
expiryHeight: BlockHeight? = nil,

View File

@ -46,7 +46,7 @@ class ScanTests: XCTestCase {
store.dependencies.captureDevice = .noOp
await store.send(.torchPressed) { state in
await store.send(.torchTapped) { state in
state.isTorchOn = true
}
@ -64,7 +64,7 @@ class ScanTests: XCTestCase {
store.dependencies.captureDevice = .noOp
await store.send(.torchPressed) { state in
await store.send(.torchTapped) { state in
state.isTorchOn = false
}

View File

@ -13,7 +13,7 @@ import NumberFormatter
import Models
import WalletStorage
import SendConfirmation
import SendFlow
import SendForm
import UIComponents
import WalletBalances
@testable import secant_testnet
@ -61,7 +61,7 @@ class SendTests: XCTestCase {
// store.dependencies.zcashSDKEnvironment = .testValue
//
// // simulate the sending confirmation button to be pressed
// await store.send(.sendPressed) { state in
// await store.send(.sendTapped) { state in
// // once sending is confirmed, the attempts to try to send again by pressing the button
// // needs to be eliminated, indicated by the flag `isSending`, need to be true
// state.isSending = true
@ -100,7 +100,7 @@ class SendTests: XCTestCase {
// store.dependencies.zcashSDKEnvironment = .testValue
//
// // simulate the sending confirmation button to be pressed
// await store.send(.sendPressed) { state in
// await store.send(.sendTapped) { state in
// // once sending is confirmed, the attempts to try to send again by pressing the button
// // needs to be eliminated, indicated by the flag `isSending`, need to be true
// state.isSending = true
@ -140,7 +140,7 @@ class SendTests: XCTestCase {
// store.dependencies.walletStorage.exportWallet = { throw walletStorageError }
//
// // simulate the sending confirmation button to be pressed
// await store.send(.sendPressed) { state in
// await store.send(.sendTapped) { state in
// state.isSending = true
// }
//
@ -156,7 +156,7 @@ class SendTests: XCTestCase {
// let store = TestStore(
// initialState: .initial
// ) {
// SendFlow()
// SendForm()
// }
//
// store.dependencies.derivationTool = .noOp
@ -181,7 +181,7 @@ class SendTests: XCTestCase {
// let store = TestStore(
// initialState: .initial
// ) {
// SendFlow()
// SendForm()
// }
//
// store.dependencies.derivationTool = .noOp
@ -203,13 +203,13 @@ class SendTests: XCTestCase {
// }
//
// func testInvalidAmountFormatEmptyInput() async throws {
// var state = SendFlow.State.initial
// var state = SendForm.State.initial
// state.transactionAmountInputState = .amount
//
// let store = TestStore(
// initialState: state
// ) {
// SendFlow()
// SendForm()
// }
//
// store.dependencies.numberFormatter = .noOp
@ -225,7 +225,7 @@ class SendTests: XCTestCase {
// let store = TestStore(
// initialState: .initial
// ) {
// SendFlow()
// SendForm()
// }
//
// store.dependencies.derivationTool = .noOp
@ -245,7 +245,7 @@ class SendTests: XCTestCase {
// }
//
// func testFundsSufficiency_SufficientAmount() async throws {
// let sendState = SendFlow.State(
// let sendState = SendForm.State(
// addMemoState: true,
// memoState: .initial,
// scanState: .initial,
@ -258,7 +258,7 @@ class SendTests: XCTestCase {
// let store = TestStore(
// initialState: sendState
// ) {
// SendFlow()
// SendForm()
// }
//
// store.dependencies.numberFormatter = .live(numberFormatter: usNumberFormatter)
@ -280,7 +280,7 @@ class SendTests: XCTestCase {
// }
//
// func testFundsSufficiency_InsufficientAmount() async throws {
// let sendState = SendFlow.State(
// let sendState = SendForm.State(
// addMemoState: true,
// memoState: .initial,
// scanState: .initial,
@ -292,7 +292,7 @@ class SendTests: XCTestCase {
// let store = TestStore(
// initialState: sendState
// ) {
// SendFlow()
// SendForm()
// }
//
// store.dependencies.numberFormatter = .live(numberFormatter: usNumberFormatter)
@ -336,7 +336,7 @@ class SendTests: XCTestCase {
// }
//
// func testValidForm() async throws {
// let sendState = SendFlow.State(
// let sendState = SendForm.State(
// addMemoState: true,
// memoState: .initial,
// scanState: .initial,
@ -349,7 +349,7 @@ class SendTests: XCTestCase {
// let store = TestStore(
// initialState: sendState
// ) {
// SendFlow()
// SendForm()
// }
//
// store.dependencies.numberFormatter = .live(numberFormatter: usNumberFormatter)
@ -387,7 +387,7 @@ class SendTests: XCTestCase {
// }
//
// func testValidForm_NoFees() async throws {
// let sendState = SendFlow.State(
// let sendState = SendForm.State(
// addMemoState: true,
// memoState: .initial,
// scanState: .initial,
@ -409,7 +409,7 @@ class SendTests: XCTestCase {
// let store = TestStore(
// initialState: sendState
// ) {
// SendFlow()
// SendForm()
// }
//
// store.dependencies.derivationTool = .noOp
@ -432,7 +432,7 @@ class SendTests: XCTestCase {
// }
//
// func testInvalidForm_InsufficientFunds() async throws {
// let sendState = SendFlow.State(
// let sendState = SendForm.State(
// addMemoState: true,
// memoState: .initial,
// scanState: .initial,
@ -454,7 +454,7 @@ class SendTests: XCTestCase {
// let store = TestStore(
// initialState: sendState
// ) {
// SendFlow()
// SendForm()
// }
//
// store.dependencies.derivationTool = .noOp
@ -478,7 +478,7 @@ class SendTests: XCTestCase {
// }
//
// func testInvalidForm_AddressFormat() async throws {
// let sendState = SendFlow.State(
// let sendState = SendForm.State(
// addMemoState: true,
// memoState: .initial,
// scanState: .initial,
@ -499,7 +499,7 @@ class SendTests: XCTestCase {
// let store = TestStore(
// initialState: sendState
// ) {
// SendFlow()
// SendForm()
// }
//
// store.dependencies.derivationTool = .noOp
@ -521,7 +521,7 @@ class SendTests: XCTestCase {
// }
//
// func testInvalidForm_ExceededMemoCharLimit() async throws {
// let sendState = SendFlow.State(
// let sendState = SendForm.State(
// addMemoState: true,
// memoState: MessageEditor.State(charLimit: 3),
// scanState: .initial,
@ -551,7 +551,7 @@ class SendTests: XCTestCase {
// let store = TestStore(
// initialState: sendState
// ) {
// SendFlow()
// SendForm()
// }
//
// let value = "test".redacted
@ -568,7 +568,7 @@ class SendTests: XCTestCase {
// }
//
// func testMemoCharLimitSet() async throws {
// let sendState = SendFlow.State(
// let sendState = SendForm.State(
// addMemoState: true,
// memoState: .initial,
// scanState: .initial,
@ -589,7 +589,7 @@ class SendTests: XCTestCase {
// let store = TestStore(
// initialState: sendState
// ) {
// SendFlow()
// SendForm()
// }
//
// store.dependencies.mainQueue = .immediate
@ -601,7 +601,7 @@ class SendTests: XCTestCase {
// }
//
// func testScannedAddress() async throws {
// let sendState = SendFlow.State(
// let sendState = SendForm.State(
// addMemoState: true,
// memoState: .initial,
// scanState: .initial,
@ -613,7 +613,7 @@ class SendTests: XCTestCase {
// let store = TestStore(
// initialState: sendState
// ) {
// SendFlow()
// SendForm()
// }
//
// store.dependencies.audioServices = AudioServicesClient(systemSoundVibrate: { })
@ -631,8 +631,8 @@ class SendTests: XCTestCase {
// await store.receive(.updateDestination(nil))
// }
//
// func testReviewPressed() async throws {
// var sendState = SendFlow.State(
// func testReviewTapped() async throws {
// var sendState = SendForm.State(
// addMemoState: true,
// memoState: .initial,
// scanState: .initial,
@ -645,14 +645,14 @@ class SendTests: XCTestCase {
// let store = TestStore(
// initialState: sendState
// ) {
// SendFlow()
// SendForm()
// }
//
// store.dependencies.sdkSynchronizer = .noOp
// let proposal = Proposal.testOnlyFakeProposal(totalFee: 10_000)
// store.dependencies.sdkSynchronizer.proposeTransfer = { _, _, _, _ in proposal }
//
// await store.send(.reviewPressed)
// await store.send(.reviewTapped)
//
// await store.receive(.proposal(proposal)) { state in
// state.proposal = proposal
@ -666,7 +666,7 @@ class SendTests: XCTestCase {
// func testMemoToMessage() throws {
// let testMessage = "test message".redacted
//
// let sendState = SendFlow.State(
// let sendState = SendForm.State(
// addMemoState: true,
// memoState: MessageEditor.State(charLimit: 512, text: testMessage),
// scanState: .initial,
@ -678,7 +678,7 @@ class SendTests: XCTestCase {
// let store = TestStore(
// initialState: sendState
// ) {
// SendFlow()
// SendForm()
// }
//
// XCTAssertEqual(store.state.message, testMessage.data)
@ -690,7 +690,7 @@ class SendTests: XCTestCase {
//
// let feeFormat = "(Typical Fee < 0.001)"
//
// let sendState = SendFlow.State(
// let sendState = SendForm.State(
// addMemoState: true,
// memoState: .initial,
// scanState: .initial,
@ -702,7 +702,7 @@ class SendTests: XCTestCase {
// let store = TestStore(
// initialState: sendState
// ) {
// SendFlow()
// SendForm()
// }
//
// XCTAssertEqual(store.state.feeFormat, feeFormat)

View File

@ -9,7 +9,7 @@ import XCTest
import ComposableArchitecture
import SwiftUI
import ZcashLightClientKit
import SendFlow
import SendForm
import UIComponents
import Utils
import SendConfirmation
@ -17,13 +17,13 @@ import SendConfirmation
class SendSnapshotTests: XCTestCase {
func testTransactionSendSnapshot() throws {
var state = SendFlow.State.initial
var state = SendForm.State.initial
state.addMemoState = true
let store = Store(
initialState: state
) {
SendFlow()
SendForm()
.dependency(\.derivationTool, .live())
.dependency(\.mainQueue, DispatchQueue.main.eraseToAnyScheduler())
.dependency(\.numberFormatter, .live())
@ -33,7 +33,7 @@ class SendSnapshotTests: XCTestCase {
.dependency(\.exchangeRate, .noOp)
}
addAttachments(SendFlowView(store: store, tokenName: "ZEC"))
addAttachments(SendFormView(store: store, tokenName: "ZEC"))
}
func testTransactionConfirmationScreen_withMemo() throws {