303 lines
13 KiB
Swift
303 lines
13 KiB
Swift
//
|
|
// RootCoordinator.swift
|
|
// modules
|
|
//
|
|
// Created by Lukáš Korba on 07.03.2025.
|
|
//
|
|
|
|
import ComposableArchitecture
|
|
import ZcashLightClientKit
|
|
|
|
extension Root {
|
|
public func coordinatorReduce() -> Reduce<Root.State, Root.Action> {
|
|
Reduce { state, action in
|
|
switch action {
|
|
|
|
// MARK: - Accounts
|
|
|
|
case .home(.walletAccountTapped(let walletAccount)):
|
|
guard state.selectedWalletAccount != walletAccount else {
|
|
return .none
|
|
}
|
|
state.$selectedWalletAccount.withLock { $0 = walletAccount }
|
|
state.homeState.transactionListState.isInvalidated = true
|
|
return .merge(
|
|
.send(.home(.smartBanner(.walletAccountChanged))),
|
|
.send(.home(.walletBalances(.updateBalances))),
|
|
.send(.loadContacts),
|
|
.send(.resolveMetadataEncryptionKeys),
|
|
.send(.loadUserMetadata),
|
|
.send(.fetchTransactionsForTheSelectedAccount)
|
|
)
|
|
|
|
// MARK: - Add Keystone HW Wallet Coord Flow
|
|
|
|
case .addKeystoneHWWalletCoordFlow(.path(.element(id: _, action: .accountHWWalletSelection(.forgetThisDeviceTapped)))):
|
|
state.path = nil
|
|
return .none
|
|
|
|
case .addKeystoneHWWalletCoordFlow(.path(.element(id: _, action: .accountHWWalletSelection(.accountImportSucceeded)))):
|
|
state.path = nil
|
|
return .merge(
|
|
.send(.loadContacts),
|
|
.send(.resolveMetadataEncryptionKeys),
|
|
.send(.loadUserMetadata),
|
|
.send(.fetchTransactionsForTheSelectedAccount)
|
|
)
|
|
|
|
// MARK: - Add Keystone HW Wallet from Settings
|
|
|
|
case .settings(.path(.element(id: _, action: .accountHWWalletSelection(.accountImportSucceeded)))):
|
|
state.path = nil
|
|
return .merge(
|
|
.send(.loadContacts),
|
|
.send(.resolveMetadataEncryptionKeys),
|
|
.send(.loadUserMetadata),
|
|
.send(.fetchTransactionsForTheSelectedAccount)
|
|
)
|
|
|
|
// MARK: - Flexa
|
|
|
|
case .flexaOpenRequest:
|
|
flexaHandler.open()
|
|
return .publisher {
|
|
flexaHandler.onTransactionRequest()
|
|
.map(Root.Action.flexaOnTransactionRequest)
|
|
.receive(on: mainQueue)
|
|
}
|
|
.cancellable(id: CancelFlexaId, cancelInFlight: true)
|
|
|
|
// MARK: - Currency Conversion Setup
|
|
|
|
case .currencyConversionSetup(.skipTapped), .currencyConversionSetup(.enableTapped):
|
|
state.path = nil
|
|
state.homeState.isRateEducationEnabled = false
|
|
return .send(.home(.smartBanner(.closeAndCleanupBanner)))
|
|
|
|
// MARK: - Home
|
|
|
|
case .home(.settingsTapped):
|
|
state.settingsState = .initial
|
|
state.path = .settings
|
|
return .none
|
|
|
|
case .home(.receiveTapped):
|
|
state.receiveState = .initial
|
|
state.path = .receive
|
|
return .none
|
|
|
|
case .home(.sendTapped):
|
|
state.sendCoordFlowState = .initial
|
|
state.path = .sendCoordFlow
|
|
exchangeRate.refreshExchangeRateUSD()
|
|
return .none
|
|
|
|
case .home(.scanTapped):
|
|
state.scanCoordFlowState = .initial
|
|
state.path = .scanCoordFlow
|
|
return .none
|
|
|
|
case .home(.getSomeZecTapped):
|
|
state.requestZecCoordFlowState = .initial
|
|
state.path = .requestZecCoordFlow
|
|
return .none
|
|
|
|
case .home(.flexaTapped):
|
|
return .send(.flexaOpenRequest)
|
|
|
|
case .home(.addKeystoneHWWalletTapped):
|
|
state.addKeystoneHWWalletCoordFlowState = .initial
|
|
state.path = .addKeystoneHWWalletCoordFlow
|
|
return .none
|
|
|
|
case .home(.transactionList(.transactionTapped(let txId))):
|
|
state.transactionsCoordFlowState = .initial
|
|
state.transactionsCoordFlowState.transactionToOpen = txId
|
|
if let index = state.transactions.index(id: txId) {
|
|
state.transactionsCoordFlowState.transactionDetailsState.transaction = state.transactions[index]
|
|
}
|
|
state.path = .transactionsCoordFlow
|
|
return .none
|
|
|
|
case .home(.seeAllTransactionsTapped):
|
|
state.transactionsCoordFlowState = .initial
|
|
state.path = .transactionsCoordFlow
|
|
return .none
|
|
|
|
case .home(.currencyConversionSetupTapped):
|
|
state.currencyConversionSetupState = .initial
|
|
state.path = .currencyConversionSetup
|
|
return .none
|
|
|
|
case .home(.smartBanner(.walletBackupTapped)):
|
|
state.walletBackupCoordFlowState = .initial
|
|
state.path = .walletBackup
|
|
return .none
|
|
|
|
// MARK: - Integrations
|
|
|
|
case .settings(.path(.element(id: _, action: .integrations(.flexaTapped)))):
|
|
return .send(.flexaOpenRequest)
|
|
|
|
// MARK: - Keystone
|
|
|
|
case .sendCoordFlow(.path(.element(id: _, action: .confirmWithKeystone(.rejectTapped)))),
|
|
.signWithKeystoneCoordFlow(.sendConfirmation(.rejectTapped)):
|
|
state.path = nil
|
|
return .none
|
|
|
|
case .signWithKeystoneRequested:
|
|
//state.path = .signWithKeystoneCoordFlow
|
|
state.signWithKeystoneCoordFlowBinding = true
|
|
return .send(.signWithKeystoneCoordFlow(.sendConfirmation(.resolvePCZT)))
|
|
|
|
// MARK: - Request Zec
|
|
|
|
case .requestZecCoordFlow(.path(.element(id: _, action: .requestZecSummary(.cancelRequestTapped)))):
|
|
state.path = nil
|
|
return .none
|
|
|
|
// MARK: - Reset Zashi
|
|
|
|
case .settings(.path(.element(id: _, action: .resetZashi(.deleteTapped)))):
|
|
return .send(.initialization(.resetZashiRequest))
|
|
|
|
// MARK: - Restore Wallet Coord Flow from Onboarding
|
|
|
|
case .onboarding(.restoreWalletCoordFlow(.path(.element(id: _, action: .restoreInfo(.gotItTapped))))):
|
|
var leavesScreenOpen = false
|
|
for element in state.onboardingState.restoreWalletCoordFlowState.path {
|
|
if case .restoreInfo(let restoreInfoState) = element {
|
|
leavesScreenOpen = restoreInfoState.isAcknowledged
|
|
}
|
|
}
|
|
userDefaults.setValue(leavesScreenOpen, Constants.udLeavesScreenOpen)
|
|
state.isRestoringWallet = true
|
|
userDefaults.setValue(true, Constants.udIsRestoringWallet)
|
|
state.$walletStatus.withLock { $0 = .restoring }
|
|
return .concatenate(
|
|
.send(.initialization(.initializeSDK(.restoreWallet))),
|
|
.send(.initialization(.checkBackupPhraseValidation)),
|
|
.send(.batteryStateChanged(nil))
|
|
)
|
|
|
|
// MARK: - Scan Coord Flow
|
|
|
|
case .scanCoordFlow(.scan(.cancelTapped)):
|
|
state.path = nil
|
|
return .none
|
|
|
|
case .scanCoordFlow(.path(.element(id: _, action: .sendForm(.dismissRequired)))):
|
|
state.path = nil
|
|
return .none
|
|
|
|
case .scanCoordFlow(.path(.element(id: _, action: .transactionDetails(.closeDetailTapped)))):
|
|
state.path = nil
|
|
return .none
|
|
|
|
case .scanCoordFlow(.path(.element(id: _, action: .sendResultSuccess(.closeTapped)))),
|
|
.scanCoordFlow(.path(.element(id: _, action: .sendResultResubmission(.closeTapped)))),
|
|
.scanCoordFlow(.path(.element(id: _, action: .sendResultPartial(.dismiss)))):
|
|
state.path = nil
|
|
return .none
|
|
|
|
// MARK: - Self
|
|
|
|
case .sendAgainRequested(let transactionState):
|
|
state.sendCoordFlowState = .initial
|
|
state.path = .sendCoordFlow
|
|
state.sendCoordFlowState.sendFormState.memoState.text = state.transactionMemos[transactionState.id]?.first ?? ""
|
|
return .merge(
|
|
.send(.sendCoordFlow(.sendForm(.zecAmountUpdated(transactionState.amountWithoutFee.decimalString().redacted)))),
|
|
.send(.sendCoordFlow(.sendForm(.addressUpdated(transactionState.address.redacted))))
|
|
)
|
|
|
|
case .deeplinkWarning(.rescanInZashi):
|
|
state = .initial
|
|
state.splashAppeared = true
|
|
return .merge(
|
|
.send(.destination(.updateDestination(.home))),
|
|
.send(.home(.scanTapped))
|
|
)
|
|
|
|
// MARK: - Send Coord Flow
|
|
|
|
case .sendCoordFlow(.path(.element(id: _, action: .sendResultSuccess(.closeTapped)))),
|
|
.sendCoordFlow(.path(.element(id: _, action: .sendResultResubmission(.closeTapped)))),
|
|
.sendCoordFlow(.path(.element(id: _, action: .sendResultPartial(.dismiss)))):
|
|
state.path = nil
|
|
return .none
|
|
|
|
case .sendCoordFlow(.path(.element(id: _, action: .transactionDetails(.closeDetailTapped)))):
|
|
state.path = nil
|
|
return .none
|
|
|
|
case .sendCoordFlow(.dismissRequired):
|
|
state.path = nil
|
|
return .none
|
|
|
|
// MARK: - Sign with Keystone Coord Flow
|
|
|
|
case .signWithKeystoneCoordFlow(.path(.element(id: _, action: .sendResultSuccess(.closeTapped)))),
|
|
.signWithKeystoneCoordFlow(.path(.element(id: _, action: .sendResultResubmission(.closeTapped)))):
|
|
state.signWithKeystoneCoordFlowBinding = false
|
|
return .none
|
|
|
|
case .signWithKeystoneCoordFlow(.path(.element(id: _, action: .transactionDetails(.closeDetailTapped)))):
|
|
state.signWithKeystoneCoordFlowBinding = false
|
|
return .none
|
|
|
|
// MARK: - Transactions Coord Flow
|
|
|
|
case .transactionsCoordFlow(.transactionDetails(.closeDetailTapped)):
|
|
state.path = nil
|
|
return .none
|
|
|
|
case .transactionsCoordFlow(.transactionsManager(.dismissRequired)):
|
|
state.path = nil
|
|
return .none
|
|
|
|
case .transactionsCoordFlow(.transactionDetails(.sendAgainTapped)):
|
|
state.path = nil
|
|
let transactionState = state.transactionsCoordFlowState.transactionDetailsState.transaction
|
|
return .run { send in
|
|
try? await mainQueue.sleep(for: .seconds(0.8))
|
|
await send(.sendAgainRequested(transactionState))
|
|
}
|
|
|
|
case .transactionsCoordFlow(.path(.element(id: _, action: .transactionDetails(.sendAgainTapped)))):
|
|
for element in state.transactionsCoordFlowState.path {
|
|
if case .transactionDetails(let transactionDetailsState) = element {
|
|
state.path = nil
|
|
return .run { send in
|
|
try? await mainQueue.sleep(for: .seconds(0.8))
|
|
await send(.sendAgainRequested(transactionDetailsState.transaction))
|
|
}
|
|
}
|
|
}
|
|
return .none
|
|
|
|
// MARK: - Wallet Backup Coord Flow
|
|
|
|
case .walletBackupCoordFlow(.path(.element(id: _, action: .phrase(.remindMeLaterTapped)))):
|
|
state.path = nil
|
|
return .send(.home(.smartBanner(.remindMeLaterTapped(.priority6))))
|
|
|
|
case .walletBackupCoordFlow(.path(.element(id: _, action: .phrase(.seedSavedTapped)))):
|
|
state.path = nil
|
|
do {
|
|
try walletStorage.markUserPassedPhraseBackupTest(true)
|
|
} catch {
|
|
state.alert = AlertState.cantStoreThatUserPassedPhraseBackupTest(error.toZcashError())
|
|
}
|
|
return .merge(
|
|
.send(.home(.smartBanner(.closeAndCleanupBanner))),
|
|
.send(.home(.smartBanner(.closeSheetTapped)))
|
|
)
|
|
|
|
default: return .none
|
|
}
|
|
}
|
|
}
|
|
}
|