184 lines
6.6 KiB
Swift
184 lines
6.6 KiB
Swift
//
|
|
// RootDestination.swift
|
|
// Zashi
|
|
//
|
|
// Created by Lukáš Korba on 01.12.2022.
|
|
//
|
|
|
|
import Foundation
|
|
import ComposableArchitecture
|
|
import ZcashLightClientKit
|
|
import Deeplink
|
|
import DerivationTool
|
|
import Generated
|
|
|
|
import SwiftUI
|
|
|
|
/// In this file is a collection of helpers that control all state and action related operations
|
|
/// for the `Root` with a connection to the UI navigation.
|
|
extension Root {
|
|
public struct DestinationState {
|
|
public enum Destination {
|
|
case deeplinkWarning
|
|
case notEnoughFreeSpace
|
|
case onboarding
|
|
case osStatusError
|
|
//case phraseDisplay
|
|
case startup
|
|
case home
|
|
case welcome
|
|
}
|
|
|
|
public var internalDestination: Destination = .welcome
|
|
public var preNotEnoughFreeSpaceDestination: Destination?
|
|
public var previousDestination: Destination?
|
|
|
|
public var destination: Destination {
|
|
get { internalDestination }
|
|
set {
|
|
previousDestination = internalDestination
|
|
internalDestination = newValue
|
|
}
|
|
}
|
|
}
|
|
|
|
public enum DestinationAction {
|
|
case deeplink(URL)
|
|
case deeplinkHome
|
|
case deeplinkSend(Zatoshi, String, String)
|
|
case deeplinkFailed(URL, ZcashError)
|
|
case updateDestination(Root.DestinationState.Destination)
|
|
case serverSwitch
|
|
}
|
|
|
|
// swiftlint:disable:next cyclomatic_complexity
|
|
public func destinationReduce() -> Reduce<Root.State, Root.Action> {
|
|
Reduce { state, action in
|
|
switch action {
|
|
case let .destination(.updateDestination(destination)):
|
|
guard (state.destinationState.destination != .deeplinkWarning)
|
|
|| (state.destinationState.destination == .deeplinkWarning && destination == .home) else {
|
|
return .none
|
|
}
|
|
state.destinationState.destination = destination
|
|
return .none
|
|
|
|
case .destination(.deeplink(let url)):
|
|
if let _ = uriParser.checkRP(url.absoluteString) {
|
|
// The deeplink is some zip321, we ignore it and let users know in a warning screen
|
|
return .send(.destination(.updateDestination(.deeplinkWarning)))
|
|
}
|
|
return .none
|
|
|
|
case .destination(.deeplinkHome):
|
|
return .none
|
|
|
|
case .destination(.deeplinkSend):
|
|
return .none
|
|
|
|
case let .destination(.deeplinkFailed(url, error)):
|
|
state.alert = AlertState.failedToProcessDeeplink(url, error)
|
|
return .none
|
|
|
|
case .destination(.serverSwitch):
|
|
state.serverSetupViewBinding = true
|
|
return .none
|
|
|
|
case .splashRemovalRequested:
|
|
return .run { send in
|
|
try await mainQueue.sleep(for: .seconds(0.01))
|
|
await send(.splashFinished)
|
|
}
|
|
|
|
case .splashFinished:
|
|
state.splashAppeared = true
|
|
state.$lastAuthenticationTimestamp.withLock { $0 = Int(Date().timeIntervalSince1970) }
|
|
exchangeRate.refreshExchangeRateUSD()
|
|
return .none
|
|
|
|
case .flexaOnTransactionRequest(let transaction):
|
|
guard let transaction else {
|
|
return .none
|
|
}
|
|
guard let account = state.selectedWalletAccount, let zip32AccountIndex = account.zip32AccountIndex else {
|
|
return .none
|
|
}
|
|
flexaHandler.clearTransactionRequest()
|
|
return .run { send in
|
|
do {
|
|
if await !localAuthentication.authenticate() {
|
|
return
|
|
}
|
|
|
|
// get a proposal
|
|
let recipient = try Recipient(transaction.address, network: zcashSDKEnvironment.network.networkType)
|
|
let proposal = try await sdkSynchronizer.proposeTransfer(account.id, recipient, transaction.amount, nil)
|
|
|
|
// make the actual send
|
|
let storedWallet = try walletStorage.exportWallet()
|
|
let seedBytes = try mnemonic.toSeed(storedWallet.seedPhrase.value())
|
|
let network = zcashSDKEnvironment.network.networkType
|
|
let spendingKey = try derivationTool.deriveSpendingKey(seedBytes, zip32AccountIndex, network)
|
|
|
|
let result = try await sdkSynchronizer.createProposedTransactions(proposal, spendingKey)
|
|
|
|
switch result {
|
|
case .partial, .failure:
|
|
await send(.flexaTransactionFailed(L10n.Partners.Flexa.transactionFailedMessage))
|
|
case .success(let txIds), .grpcFailure(let txIds):
|
|
if let txId = txIds.first {
|
|
flexaHandler.transactionSent(transaction.commerceSessionId, txId)
|
|
}
|
|
}
|
|
} catch {
|
|
await send(.flexaTransactionFailed(error.localizedDescription))
|
|
}
|
|
}
|
|
|
|
case .flexaTransactionFailed(let message):
|
|
flexaHandler.flexaAlert(L10n.Partners.Flexa.transactionFailedTitle, message)
|
|
return .none
|
|
|
|
default:
|
|
return .none
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension Root {
|
|
func process(
|
|
url: URL,
|
|
deeplink: DeeplinkClient,
|
|
derivationTool: DerivationToolClient
|
|
) async throws -> Root.Action {
|
|
@Dependency(\.zcashSDKEnvironment) var zcashSDKEnvironment
|
|
let deeplink = try deeplink.resolveDeeplinkURL(url, zcashSDKEnvironment.network.networkType, derivationTool)
|
|
|
|
switch deeplink {
|
|
case .home:
|
|
return .destination(.deeplinkHome)
|
|
case let .send(amount, address, memo):
|
|
return .destination(.deeplinkSend(Zatoshi(Int64(amount)), address, memo))
|
|
}
|
|
}
|
|
}
|
|
|
|
extension StoreOf<Root> {
|
|
public func goToDestination(_ destination: Root.DestinationState.Destination) {
|
|
send(.destination(.updateDestination(destination)))
|
|
}
|
|
|
|
public func goToDeeplink(_ deeplink: URL) {
|
|
send(.destination(.deeplink(deeplink)))
|
|
}
|
|
}
|
|
|
|
// MARK: Placeholders
|
|
|
|
extension Root.DestinationState {
|
|
public static var initial: Self {
|
|
.init()
|
|
}
|
|
}
|