From 56f47deb501c6ed1a23bf6cbfaaba58e78a3dc80 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Sun, 22 Sep 2024 18:18:02 +0700 Subject: [PATCH] feat: mixed balance and errors on the send screen --- DashSyncCurrentCommit | 2 +- .../Models/CoinJoin/CoinJoinService.swift | 33 ++- .../CoinJoinLevelsViewController.swift | 23 +- .../Credits/BuyCreditsViewController.swift | 9 +- .../Views/Cells/CoinJoinProgressView.swift | 22 +- .../Sources/UI/Home/Views/HomeView.swift | 3 - .../ProvideAmountViewController.swift | 211 ++++-------------- .../Amount/Model/BaseAmountModel.swift | 18 +- .../Amount/Model/Send/SendAmountModel.swift | 20 +- .../UI/Payments/Amount/Views/AmountView.swift | 5 +- .../UI/SwiftUI Components/SendIntro.swift | 19 +- .../ActionButtonViewController.swift | 10 +- 12 files changed, 143 insertions(+), 232 deletions(-) diff --git a/DashSyncCurrentCommit b/DashSyncCurrentCommit index 8de791cf2..0f3f5633b 100644 --- a/DashSyncCurrentCommit +++ b/DashSyncCurrentCommit @@ -1 +1 @@ -315879b4d157a026fc760d3742f6a55a8883fde5 +49b001b3a003a443fd3fc6d2cd1a470e2cb7638a diff --git a/DashWallet/Sources/Models/CoinJoin/CoinJoinService.swift b/DashWallet/Sources/Models/CoinJoin/CoinJoinService.swift index 54a6e1e4c..9f3a60044 100644 --- a/DashWallet/Sources/Models/CoinJoin/CoinJoinService.swift +++ b/DashWallet/Sources/Models/CoinJoin/CoinJoinService.swift @@ -75,7 +75,7 @@ class CoinJoinService: NSObject { private var networkStatus: NetworkStatus = .online private var _savedMode: Int? = nil - var savedMode: Int { + private var savedMode: Int { get { _savedMode ?? UserDefaults.standard.integer(forKey: kCoinJoinMode) } set(value) { _savedMode = value @@ -161,11 +161,20 @@ class CoinJoinService: NSObject { } private func updateProgress() { - guard let coinJoinManager = self.coinJoinManager else { return } - self.progress = coinJoinManager.getMixingProgress() - let coinJoinBalance = coinJoinManager.getBalance() - self.totalBalance = coinJoinBalance.myTrusted - self.coinJoinBalance = coinJoinBalance.anonymized + DispatchQueue.global(qos: .background).async { [weak self] in + guard let self = self, let coinJoinManager = self.coinJoinManager else { return } + + let progress = coinJoinManager.getMixingProgress() + let coinJoinBalance = coinJoinManager.getBalance() + let totalBalance = coinJoinBalance.myTrusted + let anonymizedBalance = coinJoinBalance.anonymized + + DispatchQueue.main.async { + self.progress = progress + self.totalBalance = totalBalance + self.coinJoinBalance = anonymizedBalance + } + } } private func createCoinJoinManager() -> DSCoinJoinManager? { @@ -174,12 +183,6 @@ class CoinJoinService: NSObject { return self.coinJoinManager } - private func synchronized(_ lock: NSLock, closure: () -> Void) { - lock.lock() - defer { lock.unlock() } - closure() - } - private func updateBalance(balance: UInt64) { guard let coinJoinManager = self.coinJoinManager else { return } @@ -315,5 +318,11 @@ extension CoinJoinService: DSCoinJoinManagerDelegate { DSLogger.log("[SW] CoinJoin: Active sessions: \(activeSessions)") } + + private func synchronized(_ lock: NSLock, closure: () -> Void) { + lock.lock() + defer { lock.unlock() } + closure() + } } diff --git a/DashWallet/Sources/UI/DashPay/CoinJoin/CoinJoinLevelsViewController.swift b/DashWallet/Sources/UI/DashPay/CoinJoin/CoinJoinLevelsViewController.swift index a095163d0..37140d0a1 100644 --- a/DashWallet/Sources/UI/DashPay/CoinJoin/CoinJoinLevelsViewController.swift +++ b/DashWallet/Sources/UI/DashPay/CoinJoin/CoinJoinLevelsViewController.swift @@ -88,27 +88,23 @@ extension CoinJoinLevelsViewController { @objc private func selectIntermediate() { - if viewModel.selectedMode == .intermediate { - return - } - - if viewModel.mixingState == .mixing { - confirmFor(.intermediate) - } else { - viewModel.selectedMode = .intermediate - } + selectMode(.intermediate) } @objc private func selectAdvanced() { - if viewModel.selectedMode == .advanced { + selectMode(.advanced) + } + + private func selectMode(_ mode: CoinJoinMode) { + if viewModel.selectedMode == mode { return } - if viewModel.mixingState == .mixing { - confirmFor(.advanced) + if viewModel.selectedMode == .none { + viewModel.selectedMode = mode } else { - viewModel.selectedMode = .advanced + confirmFor(mode) } } @@ -176,6 +172,7 @@ extension CoinJoinLevelsViewController { let alert = UIAlertController(title: "", message: NSLocalizedString("Are you sure you want to change the privacy level?", comment: "CoinJoin"), preferredStyle: .alert) alert.addAction(UIAlertAction(title: title, style: .default, handler: { [weak self] _ in self?.viewModel.selectedMode = mode + self?.viewModel.startMixing() })) let cancelAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel) alert.addAction(cancelAction) diff --git a/DashWallet/Sources/UI/DashPay/Credits/BuyCreditsViewController.swift b/DashWallet/Sources/UI/DashPay/Credits/BuyCreditsViewController.swift index 6c4916ce2..bc968e471 100644 --- a/DashWallet/Sources/UI/DashPay/Credits/BuyCreditsViewController.swift +++ b/DashWallet/Sources/UI/DashPay/Credits/BuyCreditsViewController.swift @@ -87,8 +87,9 @@ final class BuyCreditsViewController: SendAmountViewController, ObservableObject let intro = SendIntro( title: NSLocalizedString("Buy credits", comment: "Credits"), - dashBalance: Int64(model.walletBalance), - balanceLabel: NSLocalizedString("Dash balance", comment: "") + ":" + dashBalance: model.walletBalance, + balanceLabel: NSLocalizedString("Dash balance", comment: "") + ":", + avatarView: { } ) let swiftUIController = UIHostingController(rootView: intro) swiftUIController.view.backgroundColor = UIColor.dw_secondaryBackground() @@ -131,7 +132,9 @@ final class BuyCreditsViewController: SendAmountViewController, ObservableObject rateLabel.topAnchor.constraint(equalTo: rateContainer.topAnchor, constant: 4), rateLabel.leadingAnchor.constraint(equalTo: rateContainer.leadingAnchor, constant: 8), rateLabel.trailingAnchor.constraint(equalTo: rateContainer.trailingAnchor, constant: -8), - rateLabel.bottomAnchor.constraint(equalTo: rateContainer.bottomAnchor, constant: -4) + rateLabel.bottomAnchor.constraint(equalTo: rateContainer.bottomAnchor, constant: -4), + + swiftUIController.view.heightAnchor.constraint(equalToConstant: 100) ]) } diff --git a/DashWallet/Sources/UI/Home/Views/Cells/CoinJoinProgressView.swift b/DashWallet/Sources/UI/Home/Views/Cells/CoinJoinProgressView.swift index 00f174120..7d9010dbc 100644 --- a/DashWallet/Sources/UI/Home/Views/Cells/CoinJoinProgressView.swift +++ b/DashWallet/Sources/UI/Home/Views/Cells/CoinJoinProgressView.swift @@ -77,18 +77,18 @@ struct CoinJoinProgressInfo: View { .foregroundColor(textColor) .font(font) .padding(.leading, 4) + + Spacer() + Text("\(mixed, format: .number.precision(.fractionLength(0...3))) of \(total, format: .number.precision(.fractionLength(0...3)))") // TODO + .foregroundColor(textColor) + .font(font) + Image("icon_dash_currency") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: font.pointSize, height: font.pointSize) + .padding(.leading, 2) + .foregroundColor(textColor) } - - Spacer() - Text("\(mixed, format: .number.precision(.fractionLength(0...3))) of \(total, format: .number.precision(.fractionLength(0...3)))") // TODO - .foregroundColor(textColor) - .font(font) - Image("icon_dash_currency") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: font.pointSize, height: font.pointSize) - .padding(.leading, 2) - .foregroundColor(textColor) } } } diff --git a/DashWallet/Sources/UI/Home/Views/HomeView.swift b/DashWallet/Sources/UI/Home/Views/HomeView.swift index b2eb3f33b..e3640d809 100644 --- a/DashWallet/Sources/UI/Home/Views/HomeView.swift +++ b/DashWallet/Sources/UI/Home/Views/HomeView.swift @@ -335,9 +335,6 @@ struct TransactionList: View { .sheet(item: $selectedTxDataItem) { item in TransactionDetailsSheet(item: item) } - .onChange(of: viewModel.coinJoinItem) { new in - DSLogger.log("[SW] CoinJoin: on change of coinJoinItem: \(viewModel.coinJoinItem.description)") - } } @ViewBuilder diff --git a/DashWallet/Sources/UI/Payment Controller/Enter Amount/ProvideAmountViewController.swift b/DashWallet/Sources/UI/Payment Controller/Enter Amount/ProvideAmountViewController.swift index 98c06165a..85a591f41 100644 --- a/DashWallet/Sources/UI/Payment Controller/Enter Amount/ProvideAmountViewController.swift +++ b/DashWallet/Sources/UI/Payment Controller/Enter Amount/ProvideAmountViewController.swift @@ -16,6 +16,8 @@ // import UIKit +import SwiftUI +import Combine // MARK: - ProvideAmountViewControllerDelegate @@ -29,7 +31,6 @@ final class ProvideAmountViewController: SendAmountViewController { weak var delegate: ProvideAmountViewControllerDelegate? public var locksBalance = false - private var balanceLabel: UILabel! private let address: String private let contact: DWDPBasicUserItem? @@ -71,151 +72,46 @@ final class ProvideAmountViewController: SendAmountViewController { stackView.spacing = 26 stackView.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(stackView) - - let textContainer = UIStackView() - textContainer.axis = .vertical - textContainer.spacing = 4 - stackView.addArrangedSubview(textContainer) - - let sendContainer = UIView() - textContainer.addArrangedSubview(sendContainer) - - let titleLabel = UILabel() - titleLabel.translatesAutoresizingMaskIntoConstraints = false - titleLabel.font = .dw_font(forTextStyle: .largeTitle).withWeight(UIFont.Weight.bold.rawValue) - titleLabel.text = NSLocalizedString("Send", comment: "Send Screen") - sendContainer.addSubview(titleLabel) - - let toLabel = UILabel() - toLabel.translatesAutoresizingMaskIntoConstraints = false - toLabel.font = .dw_font(forTextStyle: .body) - toLabel.textColor = .dw_label() - toLabel.text = NSLocalizedString("to", comment: "Send Screen: to address") - sendContainer.addSubview(toLabel) - let destinationLabel = UILabel() - destinationLabel.translatesAutoresizingMaskIntoConstraints = false - destinationLabel.font = .dw_font(forTextStyle: .body) - destinationLabel.textColor = .dw_label() - destinationLabel.lineBreakMode = .byTruncatingMiddle - sendContainer.addSubview(destinationLabel) - - #if DASHPAY - let avatarView = DWDPAvatarView() + var destination = address + let balanceLabel = CoinJoinService.shared.mode == .none ? NSLocalizedString("Dash balance", comment: "") : NSLocalizedString("Mixed balance", comment: ""); + var avatarView: DWDPAvatarView? = nil +#if DASHPAY if let contact = contact { - destinationLabel.text = contact.username - avatarView.blockchainIdentity = contact.blockchainIdentity - avatarView.translatesAutoresizingMaskIntoConstraints = false - avatarView.backgroundMode = .random - avatarView.isUserInteractionEnabled = false - avatarView.isSmall = true - sendContainer.addSubview(avatarView) - } else { - destinationLabel.text = address - avatarView.isHidden = true + avatarView = DWDPAvatarView() + destination = contact.username + avatarView!.blockchainIdentity = contact.blockchainIdentity + avatarView!.translatesAutoresizingMaskIntoConstraints = false + avatarView!.backgroundMode = .random + avatarView!.isUserInteractionEnabled = false + avatarView!.isSmall = true } - #else - destinationLabel.text = address - #endif - - let balanceStackView = UIStackView() - balanceStackView.axis = .horizontal - balanceStackView.spacing = 2 - balanceStackView.alignment = .lastBaseline - textContainer.addArrangedSubview(balanceStackView) - - let balanceTitleLabel = UILabel() - balanceTitleLabel.translatesAutoresizingMaskIntoConstraints = false - balanceTitleLabel.font = .dw_font(forTextStyle: .subheadline) - balanceTitleLabel.textColor = .dw_secondaryText() - balanceTitleLabel.text = NSLocalizedString("Balance", comment: "Send Screen: to address") + ":" - balanceStackView.addArrangedSubview(balanceTitleLabel) - - balanceLabel = UILabel() - balanceLabel.translatesAutoresizingMaskIntoConstraints = false - balanceLabel.font = .dw_font(forTextStyle: .subheadline) - balanceLabel.textColor = .dw_secondaryText() - balanceStackView.addArrangedSubview(balanceLabel) - - let spacer = UIView() - spacer.translatesAutoresizingMaskIntoConstraints = false - spacer.backgroundColor = .clear - balanceStackView.addArrangedSubview(spacer) - - let configuration = UIImage.SymbolConfiguration(pointSize: 13, weight: .regular, scale: .small) - let showHideBalanceButton = UIButton(type: .custom) - showHideBalanceButton.translatesAutoresizingMaskIntoConstraints = false - showHideBalanceButton.backgroundColor = UIColor(red: 0.098, green: 0.11, blue: 0.122, alpha: 0.05) - showHideBalanceButton.layer.cornerRadius = 12 - showHideBalanceButton.setImage(UIImage(systemName: "eye.fill", withConfiguration: configuration), for: .normal) - showHideBalanceButton.tintColor = .dw_darkTitle() - showHideBalanceButton.addTarget(self, action: #selector(toggleBalanceVisibilityAction), for: .touchUpInside) - balanceStackView.addArrangedSubview(showHideBalanceButton) - - let extraSpaceView = UIView() - extraSpaceView.backgroundColor = .clear - balanceStackView.addArrangedSubview(extraSpaceView) +#endif + + let intro = ProvideAmountIntro( + destination: destination, + balanceLabel: balanceLabel, + model: self.model as! SendAmountModel, + avatarView: { UIViewWrapper(uiView: avatarView ?? EmptyView()) } + ) + let swiftUIController = UIHostingController(rootView: intro) + swiftUIController.view.backgroundColor = UIColor.dw_secondaryBackground() + + addChild(swiftUIController) + stackView.addArrangedSubview(swiftUIController.view) + swiftUIController.view.translatesAutoresizingMaskIntoConstraints = false + swiftUIController.didMove(toParent: self) amountView.removeFromSuperview() stackView.addArrangedSubview(amountView) - - titleLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) - toLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) - destinationLabel.setContentHuggingPriority(.defaultLow, for: .horizontal) - - var constraints = [ + + NSLayoutConstraint.activate([ stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10), stackView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor), stackView.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor), - - titleLabel.leadingAnchor.constraint(equalTo: sendContainer.leadingAnchor), - titleLabel.topAnchor.constraint(equalTo: sendContainer.topAnchor), - titleLabel.bottomAnchor.constraint(equalTo: sendContainer.bottomAnchor), - - toLabel.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor, constant: 8), - toLabel.lastBaselineAnchor.constraint(equalTo: titleLabel.lastBaselineAnchor), - - destinationLabel.lastBaselineAnchor.constraint(equalTo: titleLabel.lastBaselineAnchor), - - spacer.widthAnchor.constraint(equalToConstant: 6), - - showHideBalanceButton.widthAnchor.constraint(equalToConstant: 24), - showHideBalanceButton.heightAnchor.constraint(equalToConstant: 24), - ] - - #if DASHPAY - avatarView.setContentHuggingPriority(.defaultHigh, for: .horizontal) - - if contact != nil { - constraints.append(contentsOf: [ - avatarView.leadingAnchor.constraint(equalTo: toLabel.trailingAnchor, constant: 6), - avatarView.bottomAnchor.constraint(equalTo: sendContainer.bottomAnchor, constant: -2), - avatarView.widthAnchor.constraint(equalToConstant: 20), - avatarView.heightAnchor.constraint(equalToConstant: 20), - - destinationLabel.leadingAnchor.constraint(equalTo: avatarView.trailingAnchor, constant: 6), - destinationLabel.trailingAnchor.constraint(equalTo: sendContainer.trailingAnchor) - ]) - } else { - constraints.append(contentsOf: [ - destinationLabel.leadingAnchor.constraint(equalTo: toLabel.trailingAnchor, constant: 6), - destinationLabel.trailingAnchor.constraint(equalTo: sendContainer.trailingAnchor) - ]) - } - #else - constraints.append(contentsOf: [ - destinationLabel.leadingAnchor.constraint(equalTo: toLabel.trailingAnchor, constant: 2), - destinationLabel.trailingAnchor.constraint(equalTo: sendContainer.trailingAnchor) + swiftUIController.view.heightAnchor.constraint(equalToConstant: 100) ]) - #endif - - NSLayoutConstraint.activate(constraints) - } - - @objc - func walletBalanceDidChangeNotification(notification: Notification) { - updateBalance() } @objc @@ -224,7 +120,6 @@ final class ProvideAmountViewController: SendAmountViewController { guard let self else { return } self.isBalanceHidden.toggle() - self.updateBalance() } if locksBalance && isBalanceHidden { @@ -260,11 +155,6 @@ final class ProvideAmountViewController: SendAmountViewController { override func viewDidLoad() { super.viewDidLoad() - - NotificationCenter.default.addObserver(self, selector: #selector(walletBalanceDidChangeNotification(notification:)), - name: .balanceChangeNotification, object: nil) - - updateBalance() updateInitialAmount() } @@ -274,28 +164,6 @@ final class ProvideAmountViewController: SendAmountViewController { } extension ProvideAmountViewController { - private func updateBalance() { - let balance = model.walletBalance - - let fiat: String - - if let fiatAmount = try? CurrencyExchanger.shared.convertDash(amount: balance.dashAmount, to: App.fiatCurrency) { - fiat = NumberFormatter.fiatFormatter.string(from: fiatAmount as NSNumber)! - } else { - fiat = NSLocalizedString("Syncing...", comment: "Balance") - } - - let dashStr = balance.formattedDashAmount - let fiatStr = " ≈ \(fiat)" - let fullStr = "\(dashStr)\(fiatStr)" - - if isBalanceHidden { - balanceLabel.text = String(repeating: "*", count: fullStr.count + 4) - } else { - balanceLabel.text = fullStr - } - } - private func updateInitialAmount() { if let details = details { let totalAmount = details.outputAmounts.reduce(UInt64(0)) { sum, element in @@ -312,3 +180,20 @@ extension ProvideAmountViewController { extension Notification.Name { static var balanceChangeNotification: NSNotification.Name { .init("DSWalletBalanceChangedNotification") } } + +struct ProvideAmountIntro: View { + var destination: String? = nil + var balanceLabel: String + @StateObject var model: SendAmountModel + @ViewBuilder var avatarView: () -> Content + + var body: some View { + SendIntro( + title: NSLocalizedString("Send", comment: "Send Screen"), + destination: destination, + dashBalance: CoinJoinService.shared.mode == .none ? model.walletBalance : model.coinJoinBalance, + balanceLabel: balanceLabel + ":", + avatarView: avatarView + ) + } +} diff --git a/DashWallet/Sources/UI/Payments/Amount/Model/BaseAmountModel.swift b/DashWallet/Sources/UI/Payments/Amount/Model/BaseAmountModel.swift index edaf6c5ae..e656561df 100644 --- a/DashWallet/Sources/UI/Payments/Amount/Model/BaseAmountModel.swift +++ b/DashWallet/Sources/UI/Payments/Amount/Model/BaseAmountModel.swift @@ -45,12 +45,14 @@ struct AmountInputItem: Equatable { // MARK: - BaseAmountModel -class BaseAmountModel { +class BaseAmountModel: ObservableObject { + var cancellableBag = Set() var activeAmountType: AmountType { currentInputItem.isMain ? .main : .supplementary } var mainAmount: AmountObject! var supplementaryAmount: AmountObject! @Published var amount: AmountObject! + @Published var walletBalance: UInt64 = 0 var localCurrency: String { let locale = Locale.current as NSLocale @@ -134,6 +136,12 @@ class BaseAmountModel { supplementaryAmountValidator = DWAmountInputValidator(type: .localCurrency) updateAmountObjects(with: "0") + + NotificationCenter.default.publisher(for: NSNotification.Name.DSWalletBalanceDidChange) + .sink { [weak self] _ in self?.refreshBalance() } + .store(in: &cancellableBag) + + refreshBalance() } func select(inputItem: AmountInputItem) { @@ -234,6 +242,10 @@ class BaseAmountModel { internal func checkAmountForErrors() { } internal func selectAllFunds() { } + + private func refreshBalance() { + walletBalance = DWEnvironment.sharedInstance().currentWallet.balance + } } extension BaseAmountModel { @@ -245,10 +257,6 @@ extension BaseAmountModel { CurrencyExchanger.shared.hasRate(for: localCurrencyCode) } - var walletBalance: UInt64 { - DWEnvironment.sharedInstance().currentWallet.balance - } - var fiatWalletBalanceFormatted: String { guard let fiatAmount = try? Coinbase.shared.currencyExchanger.convertDash(amount: walletBalance.dashAmount, to: App.fiatCurrency) else { return "Invalid" diff --git a/DashWallet/Sources/UI/Payments/Amount/Model/Send/SendAmountModel.swift b/DashWallet/Sources/UI/Payments/Amount/Model/Send/SendAmountModel.swift index 2d38cb5e3..87d0d1330 100644 --- a/DashWallet/Sources/UI/Payments/Amount/Model/Send/SendAmountModel.swift +++ b/DashWallet/Sources/UI/Payments/Amount/Model/Send/SendAmountModel.swift @@ -21,10 +21,12 @@ import Foundation enum SendAmountError: Error, ColorizedText, LocalizedError { case insufficientFunds + case insufficientMixedFunds case syncingChain var errorDescription: String? { switch self { + case .insufficientMixedFunds: return NSLocalizedString("Insufficient mixed funds. Wait for CoinJoin mixing to finish or disable this feature in the settings to complete this transaction.", comment: "Send screen") case .insufficientFunds: return NSLocalizedString("Insufficient funds", comment: "Send screen") case .syncingChain: return NSLocalizedString("Wait until wallet is synced to complete the transaction", comment: "Send screen") @@ -34,6 +36,7 @@ enum SendAmountError: Error, ColorizedText, LocalizedError { var textColor: UIColor { switch self { case .insufficientFunds: return .systemRed + case .insufficientMixedFunds: return .systemRed case .syncingChain: return .secondaryLabel } } @@ -42,6 +45,8 @@ enum SendAmountError: Error, ColorizedText, LocalizedError { // MARK: - SendAmountModel class SendAmountModel: BaseAmountModel { + @Published var coinJoinBalance: UInt64 = 0 + override var isAllowedToContinue: Bool { super.isAllowedToContinue && !canShowInsufficientFunds && @@ -53,7 +58,7 @@ class SendAmountModel: BaseAmountModel { let plainAmount = amount.plainAmount let account = DWEnvironment.sharedInstance().currentAccount - let allAvailableFunds = account.maxOutputAmount + let allAvailableFunds = CoinJoinService.shared.mode == .none ? account.maxOutputAmount : coinJoinBalance return plainAmount > allAvailableFunds } @@ -65,6 +70,15 @@ class SendAmountModel: BaseAmountModel { initializeSyncingActivityMonitor() checkAmountForErrors() + + if CoinJoinService.shared.mode != .none { + CoinJoinService.shared.$coinJoinBalance + .removeDuplicates() + .sink { [weak self] balance in + self?.coinJoinBalance = balance + } + .store(in: &cancellableBag) + } } override func selectAllFunds() { @@ -77,7 +91,7 @@ class SendAmountModel: BaseAmountModel { internal func selectAllFundsWithoutAuth() { let account = DWEnvironment.sharedInstance().currentAccount - let allAvailableFunds = account.maxOutputAmount + let allAvailableFunds = CoinJoinService.shared.mode == .none ? account.maxOutputAmount : coinJoinBalance if allAvailableFunds > 0 { updateCurrentAmountObject(with: allAvailableFunds) @@ -93,7 +107,7 @@ class SendAmountModel: BaseAmountModel { } guard !canShowInsufficientFunds else { - error = SendAmountError.insufficientFunds + error = CoinJoinService.shared.mode == .none ? SendAmountError.insufficientFunds : SendAmountError.insufficientMixedFunds return } diff --git a/DashWallet/Sources/UI/Payments/Amount/Views/AmountView.swift b/DashWallet/Sources/UI/Payments/Amount/Views/AmountView.swift index c2bc2324f..3ba479dc3 100644 --- a/DashWallet/Sources/UI/Payments/Amount/Views/AmountView.swift +++ b/DashWallet/Sources/UI/Payments/Amount/Views/AmountView.swift @@ -175,8 +175,9 @@ extension AmountView { inputTypeSwitcher.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), inputTypeSwitcher.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - errorLabel.centerXAnchor.constraint(equalTo: centerXAnchor), - errorLabel.topAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 10), + errorStackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16), + errorStackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16), + errorStackView.topAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 10), ]) } } diff --git a/DashWallet/Sources/UI/SwiftUI Components/SendIntro.swift b/DashWallet/Sources/UI/SwiftUI Components/SendIntro.swift index 5a3866f9c..3af1ad2d2 100644 --- a/DashWallet/Sources/UI/SwiftUI Components/SendIntro.swift +++ b/DashWallet/Sources/UI/SwiftUI Components/SendIntro.swift @@ -17,12 +17,14 @@ import SwiftUI -struct SendIntro: View { +struct SendIntro: View { + @State private var balanceHidden: Bool = true + var title: String var destination: String? = nil - var dashBalance: Int64? = nil + var dashBalance: UInt64? = nil var balanceLabel: String? = nil - @State var balanceHidden: Bool = true + @ViewBuilder var avatarView: () -> Content var body: some View { VStack(alignment: .leading, spacing: 0) { @@ -33,9 +35,12 @@ struct SendIntro: View { if let destination = destination { HStack(spacing: 2) { Text(NSLocalizedString("to", comment: "Send Screen: to address")) + .font(.subheadline) Text(destination) .font(.subheadline) - .padding(.leading, 4) + .padding(.leading, 2) + avatarView() + .frame(width: 20, height: 20) } .padding(.top, 4) .padding(.bottom, 2) @@ -52,7 +57,7 @@ struct SendIntro: View { if balanceHidden { Text("***********").font(.subheadline) } else { - DashAmount(amount: dashBalance, font: .subheadline, showDirection: false) + DashAmount(amount: Int64(dashBalance), font: .subheadline, showDirection: false) Text("~").font(.subheadline) FormattedFiatText(from: dashBalance) } @@ -78,8 +83,8 @@ struct SendIntro: View { } @ViewBuilder - private func FormattedFiatText(from dashAmount: Int64) -> some View { - let text = (try? CurrencyExchanger.shared.convertDash(amount: abs(dashAmount.dashAmount), to: App.fiatCurrency).formattedFiatAmount) ?? NSLocalizedString("Not available", comment: "") + private func FormattedFiatText(from dashAmount: UInt64) -> some View { + let text = (try? CurrencyExchanger.shared.convertDash(amount: dashAmount.dashAmount, to: App.fiatCurrency).formattedFiatAmount) ?? NSLocalizedString("Not available", comment: "") Text(text) .font(.subheadline) diff --git a/DashWallet/Sources/UI/Views/BaseController/ActionButtonViewController.swift b/DashWallet/Sources/UI/Views/BaseController/ActionButtonViewController.swift index 8e1b24d51..1377b1855 100644 --- a/DashWallet/Sources/UI/Views/BaseController/ActionButtonViewController.swift +++ b/DashWallet/Sources/UI/Views/BaseController/ActionButtonViewController.swift @@ -104,15 +104,7 @@ class ActionButtonViewController: BaseViewController, ActivityIndicatorPreviewin ka_startObservingKeyboardNotifications() } } - - override func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) - - if isKeyboardNotificationsEnabled { - ka_stopObservingKeyboardNotifications() - } - } - + override func viewDidLoad() { super.viewDidLoad()