deterministic transaction selection.
MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Home/History/HistoryViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Home/History/HistoryViewModel.cs
@@ -65,27 +65,18 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Home.History
public ObservableCollection<HistoryItemViewModel> Transactions => _transactions;
+ public void SelectTransaction(uint256 txid)
+ {
+ SelectedItem =
+ Transactions.FirstOrDefault(x => x.TransactionSummary.TransactionId == txid);
+ }
+
protected override void OnActivated(CompositeDisposable disposables)
{
base.OnActivated(disposables);
RxApp.MainThreadScheduler.Schedule(async () => await UpdateAsync());
- _transactionSourceList
- .Connect()
- .Throttle(TimeSpan.FromMilliseconds(100))
- .Skip(2)
- .OnItemAdded(_ =>
- {
- lock (_transactionListLock)
- {
- var newPendingItem = Transactions.OrderByDescending(x => x.OrderIndex).FirstOrDefault(x => !x.IsConfirmed);
- SelectedItem = newPendingItem;
- }
- })
- .Subscribe()
- .DisposeWith(disposables);
-
_updateTrigger
.Subscribe(async _ => await UpdateAsync())
.DisposeWith(disposables);

MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Send/OptimisePrivacyViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Send/OptimisePrivacyViewModel.cs
@@ -28,10 +28,10 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
[AutoNotify] private bool _exactAmountWarningVisible;
private PrivacySuggestionControlViewModel? _defaultSelection;
- public OptimisePrivacyViewModel(Wallet wallet,
+ public OptimisePrivacyViewModel(WalletViewModel owner,
TransactionInfo transactionInfo, TransactionBroadcaster broadcaster, BuildTransactionResult requestedTransaction)
{
- _wallet = wallet;
+ _wallet = owner.Wallet;
_requestedTransaction = requestedTransaction;
_transactionInfo = transactionInfo;
@@ -45,13 +45,13 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
EnableBack = true;
NextCommand = ReactiveCommand.Create(
- () => OnNext(wallet, transactionInfo, broadcaster),
+ () => OnNext(owner, transactionInfo, broadcaster),
this.WhenAnyValue(x => x.SelectedPrivacySuggestion).Select(x => x is { }));
}
- private void OnNext(Wallet wallet, TransactionInfo transactionInfo, TransactionBroadcaster broadcaster)
+ private void OnNext(WalletViewModel owner, TransactionInfo transactionInfo, TransactionBroadcaster broadcaster)
{
- Navigate().To(new TransactionPreviewViewModel(wallet, transactionInfo, broadcaster,
+ Navigate().To(new TransactionPreviewViewModel(owner, transactionInfo, broadcaster,
SelectedPrivacySuggestion!.TransactionResult));
}

MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Send/PrivacyControlViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Send/PrivacyControlViewModel.cs
@@ -23,6 +23,7 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
[NavigationMetaData(Title = "Privacy Control")]
public partial class PrivacyControlViewModel : RoutableViewModel
{
+ private readonly WalletViewModel _owner;
private readonly Wallet _wallet;
private readonly SourceList<PocketViewModel> _pocketSource;
private readonly ReadOnlyObservableCollection<PocketViewModel> _pockets;
@@ -32,9 +33,10 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
[AutoNotify] private decimal _stillNeeded;
[AutoNotify] private bool _enoughSelected;
- public PrivacyControlViewModel(Wallet wallet, TransactionInfo transactionInfo, TransactionBroadcaster broadcaster)
+ public PrivacyControlViewModel(WalletViewModel owner, TransactionInfo transactionInfo, TransactionBroadcaster broadcaster)
{
- _wallet = wallet;
+ _owner = owner;
+ _wallet = owner.Wallet;
_pocketSource = new SourceList<PocketViewModel>();
@@ -66,7 +68,7 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
EnableBack = true;
NextCommand = ReactiveCommand.CreateFromTask(
- async () => await OnNextAsync(wallet, transactionInfo, broadcaster, _selectedList),
+ async () => await OnNextAsync(_owner, transactionInfo, broadcaster, _selectedList),
this.WhenAnyValue(x => x.EnoughSelected));
EnableAutoBusyOn(NextCommand);
@@ -74,7 +76,7 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
public ReadOnlyObservableCollection<PocketViewModel> Pockets => _pockets;
- private async Task OnNextAsync(Wallet wallet, TransactionInfo transactionInfo, TransactionBroadcaster broadcaster, IObservableList<PocketViewModel> selectedList)
+ private async Task OnNextAsync(WalletViewModel owner, TransactionInfo transactionInfo, TransactionBroadcaster broadcaster, IObservableList<PocketViewModel> selectedList)
{
transactionInfo.Coins = selectedList.Items.SelectMany(x => x.Coins).ToArray();
@@ -87,11 +89,11 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
{
if (transactionInfo.PayJoinClient is { })
{
- await BuildTransactionAsPayJoinAsync(wallet, transactionInfo, broadcaster);
+ await BuildTransactionAsPayJoinAsync(owner, transactionInfo, broadcaster);
}
else
{
- await BuildTransactionAsNormalAsync(wallet, transactionInfo, broadcaster);
+ await BuildTransactionAsNormalAsync(owner, transactionInfo, broadcaster);
}
}
catch (Exception ex)
@@ -102,22 +104,22 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
}
}
- private async Task BuildTransactionAsNormalAsync(Wallet wallet, TransactionInfo transactionInfo, TransactionBroadcaster broadcaster)
+ private async Task BuildTransactionAsNormalAsync(WalletViewModel owner, TransactionInfo transactionInfo, TransactionBroadcaster broadcaster)
{
try
{
var transactionResult = await Task.Run(() => TransactionHelpers.BuildTransaction(_wallet, transactionInfo));
- Navigate().To(new OptimisePrivacyViewModel(wallet, transactionInfo, broadcaster, transactionResult));
+ Navigate().To(new OptimisePrivacyViewModel(owner, transactionInfo, broadcaster, transactionResult));
}
catch (InsufficientBalanceException)
{
var transactionResult = await Task.Run(() => TransactionHelpers.BuildTransaction(_wallet, transactionInfo, subtractFee: true));
- var dialog = new InsufficientBalanceDialogViewModel(BalanceType.Pocket, transactionResult, wallet.Synchronizer.UsdExchangeRate);
+ var dialog = new InsufficientBalanceDialogViewModel(BalanceType.Pocket, transactionResult, owner.Wallet.Synchronizer.UsdExchangeRate);
var result = await NavigateDialogAsync(dialog, NavigationTarget.DialogScreen);
if (result.Result)
{
- Navigate().To(new OptimisePrivacyViewModel(wallet, transactionInfo, broadcaster, transactionResult));
+ Navigate().To(new OptimisePrivacyViewModel(owner, transactionInfo, broadcaster, transactionResult));
}
else
{
@@ -126,13 +128,13 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
}
}
- private async Task BuildTransactionAsPayJoinAsync(Wallet wallet, TransactionInfo transactionInfo, TransactionBroadcaster broadcaster)
+ private async Task BuildTransactionAsPayJoinAsync(WalletViewModel owner, TransactionInfo transactionInfo, TransactionBroadcaster broadcaster)
{
try
{
// Do not add the PayJoin client yet, it will be added before broadcasting.
var transactionResult = await Task.Run(() => TransactionHelpers.BuildTransaction(_wallet, transactionInfo));
- Navigate().To(new TransactionPreviewViewModel(wallet, transactionInfo, broadcaster, transactionResult));
+ Navigate().To(new TransactionPreviewViewModel(owner, transactionInfo, broadcaster, transactionResult));
}
catch (InsufficientBalanceException)
{

MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Send/SendSuccessViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Send/SendSuccessViewModel.cs
using ReactiveUI;
+using WalletWasabi.Blockchain.Transactions;
using WalletWasabi.Fluent.ViewModels.Navigation;
namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
@@ -6,7 +7,7 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
[NavigationMetaData(Title = "Payment successful")]
public partial class SendSuccessViewModel : RoutableViewModel
{
- public SendSuccessViewModel()
+ public SendSuccessViewModel(SmartTransaction finalTransaction)
{
NextCommand = ReactiveCommand.Create(OnNext);

MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Send/SendViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Send/SendViewModel.cs
@@ -184,7 +184,7 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
if (transactionInfo.Amount > totalMixedCoinsAmount)
{
- Navigate().To(new PrivacyControlViewModel(wallet, transactionInfo, broadcaster));
+ Navigate().To(new PrivacyControlViewModel(_owner, transactionInfo, broadcaster));
return;
}
@@ -211,7 +211,7 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
try
{
var txRes = await Task.Run(() => TransactionHelpers.BuildTransaction(wallet, transactionInfo));
- Navigate().To(new OptimisePrivacyViewModel(wallet, transactionInfo, broadcaster, txRes));
+ Navigate().To(new OptimisePrivacyViewModel(_owner, transactionInfo, broadcaster, txRes));
}
catch (InsufficientBalanceException)
{
@@ -221,11 +221,11 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
if (result.Result)
{
- Navigate().To(new OptimisePrivacyViewModel(wallet, transactionInfo, broadcaster, txRes));
+ Navigate().To(new OptimisePrivacyViewModel(_owner, transactionInfo, broadcaster, txRes));
}
else
{
- Navigate().To(new PrivacyControlViewModel(wallet, transactionInfo, broadcaster));
+ Navigate().To(new PrivacyControlViewModel(_owner, transactionInfo, broadcaster));
}
}
}
@@ -236,12 +236,12 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
{
// Do not add the PayJoin client yet, it will be added before broadcasting.
var txRes = await Task.Run(() => TransactionHelpers.BuildTransaction(wallet, transactionInfo));
- Navigate().To(new TransactionPreviewViewModel(wallet, transactionInfo, broadcaster, txRes));
+ Navigate().To(new TransactionPreviewViewModel(_owner, transactionInfo, broadcaster, txRes));
}
catch (InsufficientBalanceException)
{
await ShowErrorAsync("Transaction Building", "There are not enough private funds to cover the transaction fee", "Wasabi was unable to create your transaction.");
- Navigate().To(new PrivacyControlViewModel(wallet, transactionInfo, broadcaster));
+ Navigate().To(new PrivacyControlViewModel(_owner, transactionInfo, broadcaster));
}
}

MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Send/TransactionPreviewViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Send/TransactionPreviewViewModel.cs
@@ -21,16 +21,18 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
[NavigationMetaData(Title = "Transaction Preview")]
public partial class TransactionPreviewViewModel : RoutableViewModel
{
+ private readonly WalletViewModel _owner;
private readonly Wallet _wallet;
private readonly TransactionInfo _info;
[AutoNotify] private string _confirmationTimeText;
[AutoNotify] private SmartLabel _labels;
- public TransactionPreviewViewModel(Wallet wallet, TransactionInfo info, TransactionBroadcaster broadcaster,
+ public TransactionPreviewViewModel(WalletViewModel owner, TransactionInfo info, TransactionBroadcaster broadcaster,
BuildTransactionResult transaction)
{
- _wallet = wallet;
+ _owner = owner;
+ _wallet = owner.Wallet;
_info = info;
SetupCancel(enableCancel: true, enableCancelOnEscape: true, enableCancelOnPressed: false);
EnableBack = true;
@@ -38,20 +40,20 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
var destinationAmount = transaction.CalculateDestinationAmount().ToDecimal(MoneyUnit.BTC);
var btcAmountText = $"{destinationAmount} bitcoins ";
- var fiatAmountText = destinationAmount.GenerateFiatText(wallet.Synchronizer.UsdExchangeRate, "USD");
+ var fiatAmountText = destinationAmount.GenerateFiatText(_wallet.Synchronizer.UsdExchangeRate, "USD");
AmountText = $"{btcAmountText}{fiatAmountText}";
AddressText = info.Address.ToString();
var fee = transaction.Fee;
var btcFeeText = $"{fee.ToDecimal(MoneyUnit.Satoshi)} satoshis ";
- var fiatFeeText = fee.ToDecimal(MoneyUnit.BTC).GenerateFiatText(wallet.Synchronizer.UsdExchangeRate, "USD");
+ var fiatFeeText = fee.ToDecimal(MoneyUnit.BTC).GenerateFiatText(_wallet.Synchronizer.UsdExchangeRate, "USD");
FeeText = $"{btcFeeText}{fiatFeeText}";
PayJoinUrl = info.PayJoinClient?.PaymentUrl.AbsoluteUri;
IsPayJoin = PayJoinUrl is not null;
- NextCommand = ReactiveCommand.CreateFromTask(async () => await OnNextAsync(wallet, broadcaster, transaction));
+ NextCommand = ReactiveCommand.CreateFromTask(async () => await OnNextAsync(_wallet, broadcaster, transaction));
}
public string AmountText { get; }
@@ -86,7 +88,8 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
{
var finalTransaction = await GetFinalTransactionAsync(transactionAuthorizationInfo.Transaction, _info);
await SendTransactionAsync(wallet, broadcaster, finalTransaction);
- Navigate().To(new SendSuccessViewModel());
+ Navigate().To(new SendSuccessViewModel(finalTransaction));
+ _owner.History.SelectTransaction(finalTransaction.GetHash());
}
catch (Exception ex)
{

deterministic transaction selection.