Working privacy control.
MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Send/PocketViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Send/PocketViewModel.cs
+using NBitcoin;
+using WalletWasabi.Blockchain.TransactionOutputs;
+
namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
{
public partial class PocketViewModel : ViewModelBase
@@ -5,5 +8,14 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
[AutoNotify] private bool _isSelected;
[AutoNotify] private decimal _totalBtc;
[AutoNotify] private string _labels;
+
+ public PocketViewModel((string labels, ICoinsView coins) pocket)
+ {
+ Coins = pocket.coins;
+ _labels = string.Join(", ", pocket.labels);
+ _totalBtc = pocket.coins.TotalAmount().ToDecimal(MoneyUnit.BTC);
+ }
+
+ public ICoinsView Coins { get; }
}
}
\ No newline at end of file

MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Send/PrivacyControlViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Send/PrivacyControlViewModel.cs
+using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Disposables;
+using DynamicData;
+using DynamicData.Aggregation;
using NBitcoin;
-using WalletWasabi.Blockchain.TransactionOutputs;
+using ReactiveUI;
+using WalletWasabi.Blockchain.TransactionBroadcasting;
+using WalletWasabi.Blockchain.TransactionBuilding;
using WalletWasabi.Fluent.Helpers;
using WalletWasabi.Fluent.ViewModels.Navigation;
using WalletWasabi.Wallets;
@@ -13,30 +18,75 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
public partial class PrivacyControlViewModel : RoutableViewModel
{
private readonly Wallet _wallet;
- private readonly TransactionInfo _transactionInfo;
+ private readonly SourceList<PocketViewModel> _pocketSource;
+ private readonly ReadOnlyObservableCollection<PocketViewModel> _pockets;
- [AutoNotify] private ObservableCollection<PocketViewModel> _pockets;
+ [AutoNotify] private decimal _stillNeeded;
+ [AutoNotify] private bool _enoughSelected;
- public PrivacyControlViewModel(Wallet wallet, TransactionInfo transactionInfoInfo)
+
+ public PrivacyControlViewModel(Wallet wallet, TransactionInfo transactionInfo, TransactionBroadcaster broadcaster)
{
_wallet = wallet;
- _transactionInfo = transactionInfoInfo;
- _pockets = new ObservableCollection<PocketViewModel>();
+
+ _pocketSource = new SourceList<PocketViewModel>();
+
+ _pocketSource.Connect()
+ .Bind(out _pockets)
+ .Subscribe();
+
+ var selected = _pocketSource.Connect()
+ .AutoRefresh()
+ .Filter(x => x.IsSelected);
+
+ var selectedList = selected.AsObservableList();
+
+ selected.Sum(x => x.TotalBtc)
+ .Subscribe(x =>
+ {
+ StillNeeded = transactionInfo.Amount.ToDecimal(MoneyUnit.BTC) - x;
+ EnoughSelected = StillNeeded <= 0;
+ });
+
+ StillNeeded = transactionInfo.Amount.ToDecimal(MoneyUnit.BTC);
+
+ NextCommand = ReactiveCommand.Create(() =>
+ {
+ var coins = selectedList.Items.SelectMany(x => x.Coins);
+
+ var intent = new PaymentIntent(
+ transactionInfo.Address,
+ transactionInfo.Amount,
+ subtractFee: false,
+ transactionInfo.Labels);
+
+ var transactionResult = _wallet.BuildTransaction(
+ _wallet.Kitchen.SaltSoup(),
+ intent,
+ FeeStrategy.CreateFromFeeRate(transactionInfo.FeeRate),
+ true,
+ coins.Select(x => x.OutPoint));
+
+ Navigate().To(new TransactionPreviewViewModel(wallet, transactionInfo, broadcaster, transactionResult));
+
+
+ }, this.WhenAnyValue(x => x.EnoughSelected));
}
+ public ReadOnlyObservableCollection<PocketViewModel> Pockets => _pockets;
+
protected override void OnNavigatedTo(bool isInHistory, CompositeDisposable disposables)
{
base.OnNavigatedTo(isInHistory, disposables);
+ if (!isInHistory)
+ {
var pockets = _wallet.Coins.GetPockets(_wallet.ServiceConfiguration.GetMixUntilAnonymitySetValue());
foreach (var pocket in pockets)
{
- _pockets.Add(new PocketViewModel
- {
- Labels = string.Join(", ", pocket.Labels),
- TotalBtc = pocket.Coins.TotalAmount().ToDecimal(MoneyUnit.BTC)
- });
+ _pocketSource.Add(new PocketViewModel(pocket));
+ }
}
}
}

MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Send/SendViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Send/SendViewModel.cs
@@ -105,7 +105,7 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
var targetAnonymitySet = wallet.ServiceConfiguration.GetMixUntilAnonymitySetValue();
var mixedCoins = wallet.Coins.Where(x => x.HdPubKey.AnonymitySet >= targetAnonymitySet).ToList();
- if (mixedCoins.Any())
+ if (false)//(mixedCoins.Any())
{
var intent = new PaymentIntent(
destination: transactionInfo.Address,
@@ -132,7 +132,7 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
}
}
- Navigate().To(new PrivacyControlViewModel(wallet, transactionInfo));
+ Navigate().To(new PrivacyControlViewModel(wallet, transactionInfo, walletVm.TransactionBroadcaster));
}, this.WhenAnyValue(x=>x.Labels.Count).Any());
}

MODIFY
WalletWasabi.Fluent/Views/Wallets/Send/PrivacyControlView.axaml
WalletWasabi.Fluent/Views/Wallets/Send/PrivacyControlView.axaml
x:Class="WalletWasabi.Fluent.Views.Wallets.Send.PrivacyControlView">
<c:ContentArea Title="Privacy Control" Caption="Parts of your wallet are know to certain entities. Select the entities you can tolerate leaking information about this transaction:"
HorizontalAlignment="Left"
+ EnableBack="True"
EnableCancel="False"
EnableNext="True"
NextContent="Continue"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
- <DataGrid AutoGenerateColumns="False" Items="{Binding Pockets}">
+ <DockPanel>
+ <Panel DockPanel.Dock="Bottom">
+ <c:InfoMessage Foreground="{DynamicResource WarningMessageForeground}" HorizontalAlignment="Center" IsVisible="{Binding !EnoughSelected}">
+ <StackPanel Orientation="Horizontal">
+ <TextBlock Text="Insufficient funds selected. Select at least another "/>
+ <TextBlock Text="{Binding StillNeeded}" />
+ <TextBlock Text=" BTC" />
+ </StackPanel>
+ </c:InfoMessage>
+ </Panel>
+ <DataGrid AutoGenerateColumns="False"
+ Items="{Binding Pockets}">
<DataGrid.Columns >
<!-- <DataGridCheckBoxColumn x:DataType="vm:PocketViewModel" Binding="{Binding IsSelected}" IsReadOnly="False" CanUserSort="False" CanUserResize="False" CanUserReorder="False" /> -->
<DataGridTemplateColumn CanUserSort="False" CanUserResize="False" CanUserReorder="False">
<DataGridTextColumn x:DataType="vm:PocketViewModel" Header="Labels" Binding="{Binding Labels}" Width="*" />
</DataGrid.Columns>
</DataGrid>
+ </DockPanel>
</c:ContentArea>
</UserControl>

Working privacy control.