diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Adapters/BitcoinBaseAdapter.swift b/UnstoppableWallet/UnstoppableWallet/Core/Adapters/BitcoinBaseAdapter.swift index b0b8e099c6..e761311841 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Adapters/BitcoinBaseAdapter.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Adapters/BitcoinBaseAdapter.swift @@ -294,7 +294,7 @@ extension BitcoinBaseAdapter { } } - func validate(address: String, pluginData: [UInt8: IBitcoinPluginData]) throws { + func validate(address: String, pluginData: [UInt8: IPluginData]) throws { try abstractKit.validate(address: address, pluginData: pluginData) } diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Protocols.swift b/UnstoppableWallet/UnstoppableWallet/Core/Protocols.swift index df0b8003de..f924fa382c 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Protocols.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Protocols.swift @@ -71,7 +71,7 @@ protocol ISendBitcoinAdapter { func availableBalance(params: SendParameters) -> Decimal func maximumSendAmount(pluginData: [UInt8: IBitcoinPluginData]) -> Decimal? func minimumSendAmount(params: SendParameters) -> Decimal - func validate(address: String, pluginData: [UInt8: IBitcoinPluginData]) throws + func validate(address: String, pluginData: [UInt8: IPluginData]) throws func unspentOutputs(filters: UtxoFilters) -> [UnspentOutputInfo] func sendInfo(params: SendParameters) throws -> SendInfo func sendSingle(params: SendParameters, logger: HsToolKit.Logger) -> Single diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/BitcoinPreSendHandler.swift b/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/BitcoinPreSendHandler.swift index 8dbcf23a35..c6077bea72 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/BitcoinPreSendHandler.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/BitcoinPreSendHandler.swift @@ -10,13 +10,30 @@ import SwiftUI class BitcoinPreSendHandler { let token: Token - var sortMode: TransactionDataSortMode - var rbfEnabled: Bool - var lockTimeInterval: HodlerPlugin.LockTimeInterval? - var customUtxos: [UnspentOutputInfo]? { didSet { - syncBalance() + balanceSubject.send(availableBalanceDecimal) + settingsModifiedSubject.send(settingsModified) + } + } + + var sortMode: TransactionDataSortMode { + didSet { + blockchainManager.save(transactionSortMode: sortMode, blockchainType: token.blockchainType) + settingsModifiedSubject.send(settingsModified) + } + } + + var rbfEnabled: Bool { + didSet { + blockchainManager.save(rbfEnabled: rbfEnabled, blockchainType: token.blockchainType) + settingsModifiedSubject.send(settingsModified) + } + } + + var lockTimeInterval: HodlerPlugin.LockTimeInterval? { + didSet { + settingsModifiedSubject.send(settingsModified) } } @@ -26,10 +43,20 @@ class BitcoinPreSendHandler { return utxos.map(\.value).reduce(0, +) } + var availableBalanceDecimal: Decimal { + let coinRate = pow(10, token.decimals) + return Decimal(availableBalance) / coinRate + } + private let adapter: BitcoinBaseAdapter + private let blockchainManager: BtcBlockchainManager private let disposeBag = DisposeBag() + + private let defaultSortMode: TransactionDataSortMode + private let defaultRbfEnabled: Bool private let stateSubject = PassthroughSubject() private let balanceSubject = PassthroughSubject() + private let settingsModifiedSubject = PassthroughSubject() private var pluginData: [UInt8: IPluginData] { guard let lockTimeInterval else { @@ -44,10 +71,13 @@ class BitcoinPreSendHandler { self.adapter = adapter let blockchainType = token.blockchainType - let blockchainManager = App.shared.btcBlockchainManager + blockchainManager = App.shared.btcBlockchainManager - sortMode = blockchainManager.transactionSortMode(blockchainType: blockchainType) - rbfEnabled = blockchainManager.transactionRbfEnabled(blockchainType: blockchainType) + defaultSortMode = blockchainManager.transactionSortMode(blockchainType: blockchainType) + sortMode = defaultSortMode + + defaultRbfEnabled = blockchainManager.transactionRbfEnabled(blockchainType: blockchainType) + rbfEnabled = defaultRbfEnabled adapter.balanceStateUpdatedObservable .observeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated)) @@ -68,13 +98,19 @@ class BitcoinPreSendHandler { private func syncBalance() { allUtxos = adapter.unspentOutputs(filters: .init()) - - let coinRate = pow(10, token.decimals) - let availableBalanceDecimal = Decimal(availableBalance) / coinRate balanceSubject.send(availableBalanceDecimal) } } +extension BitcoinPreSendHandler { + func reset() { + sortMode = defaultSortMode + rbfEnabled = defaultRbfEnabled + lockTimeInterval = nil + customUtxos = nil + } +} + extension BitcoinPreSendHandler: IPreSendHandler { var hasSettings: Bool { true @@ -89,13 +125,21 @@ extension BitcoinPreSendHandler: IPreSendHandler { } var balance: Decimal { - adapter.balanceData.available + availableBalanceDecimal } var balancePublisher: AnyPublisher { balanceSubject.eraseToAnyPublisher() } + var settingsModified: Bool { + sortMode != defaultSortMode || rbfEnabled != defaultRbfEnabled || customUtxos != nil || lockTimeInterval != nil + } + + var settingsModifiedPublisher: AnyPublisher { + settingsModifiedSubject.eraseToAnyPublisher() + } + func hasMemo(address _: String?) -> Bool { true } @@ -109,6 +153,12 @@ extension BitcoinPreSendHandler: IPreSendHandler { } func sendData(amount: Decimal, address: String, memo: String?) -> SendDataResult { + do { + try adapter.validate(address: address, pluginData: pluginData) + } catch { + return .invalid(cautions: [CautionNew(text: error.localizedDescription, type: .error)]) + } + let params = SendParameters( address: address, value: adapter.convertToSatoshi(value: amount), diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/BitcoinSendSettingsView.swift b/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/BitcoinSendSettingsView.swift index d89ed166b5..2b389c27ce 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/BitcoinSendSettingsView.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/BitcoinSendSettingsView.swift @@ -171,11 +171,9 @@ struct BitcoinSendSettingsView: View { ToolbarItem(placement: .confirmationAction) { Button("button.done".localized) { - viewModel.applySettings() onChangeSettings() presentationMode.wrappedValue.dismiss() } - .disabled(!viewModel.doneEnabled) } } } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/BitcoinSendSettingsViewModel.swift b/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/BitcoinSendSettingsViewModel.swift index f4cb38ffb8..fdcbbab8f3 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/BitcoinSendSettingsViewModel.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/BitcoinSendSettingsViewModel.swift @@ -7,11 +7,29 @@ class BitcoinSendSettingsViewModel: ObservableObject { let handler: BitcoinPreSendHandler - @Published var sortMode: TransactionDataSortMode - @Published var rbfEnabled: Bool - @Published var lockTimeInterval: HodlerPlugin.LockTimeInterval? - @Published var resetEnabled = false - @Published var doneEnabled = true + @Published var sortMode: TransactionDataSortMode { + didSet { + handler.sortMode = sortMode + } + } + + @Published var rbfEnabled: Bool { + didSet { + handler.rbfEnabled = rbfEnabled + } + } + + @Published var lockTimeInterval: HodlerPlugin.LockTimeInterval? { + didSet { + handler.lockTimeInterval = lockTimeInterval + } + } + + @Published var resetEnabled: Bool { + didSet { + print("resetEnabled: \(resetEnabled)") + } + } @Published var utxos: String = "" @@ -22,6 +40,13 @@ class BitcoinSendSettingsViewModel: ObservableObject { rbfEnabled = handler.rbfEnabled lockTimeInterval = handler.lockTimeInterval + resetEnabled = handler.settingsModified + + handler.settingsModifiedPublisher + .receive(on: DispatchQueue.main) + .sink { [weak self] in self?.resetEnabled = $0 } + .store(in: &cancellables) + handler.balancePublisher .receive(on: DispatchQueue.main) .sink { [weak self] _ in self?.syncUtxos() } @@ -39,11 +64,7 @@ class BitcoinSendSettingsViewModel: ObservableObject { } extension BitcoinSendSettingsViewModel { - func reset() {} - - func applySettings() { - handler.sortMode = sortMode - handler.rbfEnabled = rbfEnabled - handler.lockTimeInterval = lockTimeInterval + func reset() { + handler.reset() } } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/IPreSendHandler.swift b/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/IPreSendHandler.swift index 0de0ab1e3c..d93f0e66ce 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/IPreSendHandler.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/IPreSendHandler.swift @@ -8,7 +8,8 @@ protocol IPreSendHandler { var statePublisher: AnyPublisher { get } var balance: Decimal { get } var balancePublisher: AnyPublisher { get } - func validate(address: String) -> Caution? + var settingsModified: Bool { get } + var settingsModifiedPublisher: AnyPublisher { get } func hasMemo(address: String?) -> Bool func settingsView(onChangeSettings: @escaping () -> Void) -> AnyView func sendData(amount: Decimal, address: String, memo: String?) -> SendDataResult @@ -19,10 +20,6 @@ extension IPreSendHandler { false } - func validate(address _: String) -> Caution? { - nil - } - func hasMemo(address _: String?) -> Bool { false } @@ -30,6 +27,14 @@ extension IPreSendHandler { func settingsView(onChangeSettings _: @escaping () -> Void) -> AnyView { AnyView(EmptyView()) } + + var settingsModified: Bool { + false + } + + var settingsModifiedPublisher: AnyPublisher { + Empty().eraseToAnyPublisher() + } } enum SendDataResult { diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/PreSendViewModel.swift b/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/PreSendViewModel.swift index 4a6936d1ce..6bbf600702 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/PreSendViewModel.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/PreSendViewModel.swift @@ -159,6 +159,11 @@ class PreSendViewModel: ObservableObject { .receive(on: DispatchQueue.main) .sink { [weak self] in self?.availableBalance = $0 } .store(in: &cancellables) + + handler.settingsModifiedPublisher + .receive(on: DispatchQueue.main) + .sink { [weak self] _ in self?.syncSendData() } + .store(in: &cancellables) } syncFiatAmount() @@ -200,14 +205,8 @@ class PreSendViewModel: ObservableObject { addressCautionState = .none case let .valid(success): let address = success.address.raw - - if let handler, let caution = handler.validate(address: address) { - addressState = .invalid - addressCautionState = .caution(caution) - } else { - addressState = .valid(address: address) - addressCautionState = .none - } + addressState = .valid(address: address) + addressCautionState = .none } } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/ZcashPreSendHandler.swift b/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/ZcashPreSendHandler.swift index 69b8b4e05f..790c268e66 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/ZcashPreSendHandler.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/ZcashPreSendHandler.swift @@ -50,15 +50,6 @@ extension ZcashPreSendHandler: IPreSendHandler { balanceSubject.eraseToAnyPublisher() } - func validate(address: String) -> Caution? { - do { - _ = try adapter.validate(address: address, checkSendToSelf: true) - return nil - } catch { - return Caution(text: error.smartDescription, type: .error) - } - } - func hasMemo(address: String?) -> Bool { guard let address, let addressType = try? adapter.validate(address: address, checkSendToSelf: true) else { return false @@ -68,6 +59,12 @@ extension ZcashPreSendHandler: IPreSendHandler { } func sendData(amount: Decimal, address: String, memo: String?) -> SendDataResult { + do { + _ = try adapter.validate(address: address, checkSendToSelf: true) + } catch { + return .invalid(cautions: [CautionNew(text: error.smartDescription, type: .error)]) + } + guard let recipient = adapter.recipient(from: address) else { return .invalid(cautions: []) }