Add CoinSelectorViewModel
RENAME
WalletWasabi.Fluent/ViewModels/CoinControl/CoinSelector.cs
WalletWasabi.Fluent/ViewModels/CoinControl/CoinSelectorViewModel.cs
using System.Collections.Generic;
-using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
@@ -10,30 +9,30 @@ using DynamicData;
using NBitcoin;
using ReactiveUI;
using WalletWasabi.Blockchain.TransactionOutputs;
+using WalletWasabi.Fluent.Extensions;
using WalletWasabi.Fluent.Helpers;
using WalletWasabi.Fluent.Models;
using WalletWasabi.Fluent.TreeDataGrid;
using WalletWasabi.Fluent.ViewModels.CoinControl.Core;
-using WalletWasabi.Fluent.ViewModels.Dialogs.Base;
using WalletWasabi.Fluent.ViewModels.Wallets;
using WalletWasabi.Fluent.Views.CoinControl.Core.Cells;
using WalletWasabi.Fluent.Views.CoinControl.Core.Headers;
namespace WalletWasabi.Fluent.ViewModels.CoinControl;
-public class CoinSelector : ViewModelBase
+public class CoinSelectorViewModel : ViewModelBase, IDisposable
{
private readonly BehaviorSubject<IEnumerable<SmartCoin>> _selectedCoinsSubject;
- public CoinSelector(WalletViewModel walletViewModel, IEnumerable<SmartCoin> selectedCoins)
+ public CoinSelectorViewModel(WalletViewModelBase walletViewModel, IEnumerable<SmartCoin> initialCoinSelection)
{
var pockets = walletViewModel.Wallet.GetPockets();
var pocketItems = CreatePocketItems(pockets);
- SyncSelectedItems(pocketItems, selectedCoins);
+ SyncSelectedItems(pocketItems, initialCoinSelection);
var pocketColumn = PocketColumn();
- Source = new HierarchicalTreeDataGridSource<CoinControlItemViewModelBase>(pocketItems)
+ TreeDataGridSource = new HierarchicalTreeDataGridSource<CoinControlItemViewModelBase>(pocketItems)
{
Columns =
{
@@ -50,23 +49,34 @@ public class CoinSelector : ViewModelBase
.OfType<CoinCoinControlItemViewModel>()
.ToList();
- _selectedCoinsSubject = new BehaviorSubject<IEnumerable<SmartCoin>>(new List<SmartCoin>());
-
- SelectedCoins = coins
+ SelectedCoinsChanged = coins
.AsObservableChangeSet(x => x.SmartCoin)
- .AutoRefresh(x => x.IsSelected)
+ .AutoRefresh(x => x.IsSelected, TimeSpan.FromMilliseconds(100), scheduler: RxApp.MainThreadScheduler)
.ToCollection()
.Select(x => x.Where(m => m.IsSelected == true))
- .Select(models => models.Select(x => x.SmartCoin));
+ .Select(models => models.Select(x => x.SmartCoin))
+ .ReplayLastActive();
- SelectedCoins.Subscribe(_selectedCoinsSubject);
+ _selectedCoinsSubject = new BehaviorSubject<IEnumerable<SmartCoin>>(new List<SmartCoin>());
+ SelectedCoinsChanged.Subscribe(_selectedCoinsSubject);
}
- public IEnumerable<SmartCoin> SelectedCoinsValue => _selectedCoinsSubject.Value;
+ public IEnumerable<SmartCoin> SelectedCoins => _selectedCoinsSubject.Value;
+
+ public IObservable<IEnumerable<SmartCoin>> SelectedCoinsChanged { get; }
- public IObservable<IEnumerable<SmartCoin>> SelectedCoins { get; }
+ public HierarchicalTreeDataGridSource<CoinControlItemViewModelBase> TreeDataGridSource { get; }
- public HierarchicalTreeDataGridSource<CoinControlItemViewModelBase> Source { get; }
+ public void Dispose()
+ {
+ _selectedCoinsSubject?.Dispose();
+ foreach (var pocket in TreeDataGridSource.Items.OfType<PocketCoinControlItemViewModel>())
+ {
+ pocket.Dispose();
+ }
+
+ TreeDataGridSource.Dispose();
+ }
private static IList<CoinCoinControlItemViewModel> GetAllCoinItems(IEnumerable<PocketCoinControlItemViewModel> pockets)
{
@@ -76,14 +86,6 @@ public class CoinSelector : ViewModelBase
.ToList();
}
- private static IList<SmartCoin> GetSelectedCoinItems(IEnumerable<PocketCoinControlItemViewModel> pocketItems)
- {
- return GetAllCoinItems(pocketItems)
- .Where(x => x.IsSelected == true)
- .Select(x => x.SmartCoin)
- .ToList();
- }
-
private static IColumn<CoinControlItemViewModelBase> ChildrenColumn()
{
return new HierarchicalExpanderColumn<CoinControlItemViewModelBase>(

MODIFY
WalletWasabi.Fluent/ViewModels/CoinControl/SelectCoinsDialogViewModel.cs
WalletWasabi.Fluent/ViewModels/CoinControl/SelectCoinsDialogViewModel.cs
using System.Collections.Generic;
-using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
-using Avalonia.Controls;
-using Avalonia.Controls.Models.TreeDataGrid;
-using Avalonia.Controls.Templates;
-using DynamicData;
using NBitcoin;
using ReactiveUI;
using WalletWasabi.Blockchain.TransactionOutputs;
using WalletWasabi.Fluent.Helpers;
-using WalletWasabi.Fluent.Models;
-using WalletWasabi.Fluent.TreeDataGrid;
-using WalletWasabi.Fluent.ViewModels.CoinControl.Core;
using WalletWasabi.Fluent.ViewModels.Dialogs.Base;
using WalletWasabi.Fluent.ViewModels.Wallets;
using WalletWasabi.Fluent.ViewModels.Wallets.Send;
-using WalletWasabi.Fluent.Views.CoinControl.Core.Cells;
-using WalletWasabi.Fluent.Views.CoinControl.Core.Headers;
namespace WalletWasabi.Fluent.ViewModels.CoinControl;
@@ -38,19 +28,19 @@ public partial class SelectCoinsDialogViewModel : DialogViewModelBase<IEnumerabl
_walletViewModel = walletViewModel;
_transactionInfo = transactionInfo;
- CoinSelector = new CoinSelector(walletViewModel, selectedCoins);
+ CoinSelector = new CoinSelectorViewModel(walletViewModel, selectedCoins);
- RequiredAmount = CoinSelector.SelectedCoins.Select(c => GetRequiredAmount(c));
- SelectedAmount = CoinSelector.SelectedCoins.Select(c => new Money(c.Sum(x => x.Amount)));
+ RequiredAmount = CoinSelector.SelectedCoinsChanged.Select(GetRequiredAmount);
+ SelectedAmount = CoinSelector.SelectedCoinsChanged.Select(c => new Money(c.Sum(x => x.Amount)));
RemainingAmount = SelectedAmount.CombineLatest(RequiredAmount, (selected, remaining) => remaining - selected);
EnoughSelected = RemainingAmount.Select(remaining => remaining <= Money.Zero);
SetupCancel(false, true, false);
EnableBack = true;
- NextCommand = ReactiveCommand.Create(() => Close(DialogResultKind.Normal, CoinSelector.SelectedCoinsValue), EnoughSelected);
+ NextCommand = ReactiveCommand.Create(() => Close(DialogResultKind.Normal, CoinSelector.SelectedCoins), EnoughSelected);
}
- public CoinSelector CoinSelector { get; set; }
+ public CoinSelectorViewModel CoinSelector { get; }
public IObservable<Money> RemainingAmount { get; }
@@ -64,168 +54,12 @@ public partial class SelectCoinsDialogViewModel : DialogViewModelBase<IEnumerabl
return minimumAmount;
}
- public IObservable<bool> EnoughSelected { get; }
-
- public HierarchicalTreeDataGridSource<CoinControlItemViewModelBase> Source { get; }
-
- private static IList<CoinCoinControlItemViewModel> GetAllCoinItems(IEnumerable<PocketCoinControlItemViewModel> pockets)
- {
- return pockets
- .SelectMany(x => x.Children)
- .OfType<CoinCoinControlItemViewModel>()
- .ToList();
- }
-
- private static IList<SmartCoin> GetSelectedCoinItems(IEnumerable<PocketCoinControlItemViewModel> pocketItems)
- {
- return GetAllCoinItems(pocketItems)
- .Where(x => x.IsSelected == true)
- .Select(x => x.SmartCoin)
- .ToList();
- }
+ private IObservable<bool> EnoughSelected { get; }
protected override void OnNavigatedFrom(bool isInHistory)
{
- foreach (var pocket in Source.Items.OfType<PocketCoinControlItemViewModel>())
- {
- pocket.Dispose();
- }
-
- Source.Dispose();
+ CoinSelector.Dispose();
base.OnNavigatedFrom(isInHistory);
}
-
- private static IColumn<CoinControlItemViewModelBase> ChildrenColumn()
- {
- return new HierarchicalExpanderColumn<CoinControlItemViewModelBase>(
- SelectionColumn(),
- group => group.Children,
- node => node.Children.Count > 1,
- node => node.IsExpanded);
- }
-
- private static TemplateColumn<CoinControlItemViewModelBase> SelectionColumn()
- {
- return new TemplateColumn<CoinControlItemViewModelBase>(
- "",
- new FuncDataTemplate<CoinControlItemViewModelBase>(
- (_, _) => new SelectionCellView(),
- true),
- GridLength.Auto);
- }
-
- private static void SyncSelectedItems(IEnumerable<PocketCoinControlItemViewModel> items, IEnumerable<SmartCoin> selectedCoins)
- {
- var allCoins = GetAllCoinItems(items);
- var selected = allCoins.Where(x => selectedCoins.Any(other => other == x.SmartCoin));
- foreach (var viewModel in selected)
- {
- viewModel.IsSelected = true;
- }
- }
-
- private static IColumn<CoinControlItemViewModelBase> AmountColumn()
- {
- return new PlainTextColumn<CoinControlItemViewModelBase>(
- "Amount",
- node => node.Amount.ToFormattedString(),
- GridLength.Auto,
- new ColumnOptions<CoinControlItemViewModelBase>
- {
- CompareAscending = SortAscending<CoinControlItemViewModelBase, Money>(x => x.Amount),
- CompareDescending = SortDescending<CoinControlItemViewModelBase, Money>(x => x.Amount)
- });
- }
-
- private static IColumn<CoinControlItemViewModelBase> IndicatorsColumn()
- {
- return new TemplateColumn<CoinControlItemViewModelBase>(
- "",
- new FuncDataTemplate<CoinControlItemViewModelBase>((_, _) => new IndicatorsCellView(), true),
- GridLength.Auto,
- new ColumnOptions<CoinControlItemViewModelBase>
- {
- CompareAscending = SortAscending<CoinControlItemViewModelBase, int>(GetIndicatorPriority),
- CompareDescending = SortDescending<CoinControlItemViewModelBase, int>(GetIndicatorPriority)
- });
- }
-
- private static IColumn<CoinControlItemViewModelBase> AnonymityScoreColumn()
- {
- return new PlainTextColumn<CoinControlItemViewModelBase>(
- new AnonymityScoreHeaderView(),
- node => node is CoinCoinControlItemViewModel coin ? coin.AnonymityScore.ToString() : "",
- GridLength.Auto,
- new TextColumnOptions<CoinControlItemViewModelBase>
- {
- CompareAscending = SortAscending<CoinControlItemViewModelBase, int?>(b => b.AnonymityScore),
- CompareDescending = SortDescending<CoinControlItemViewModelBase, int?>(b => b.AnonymityScore)
- });
- }
-
- private static IColumn<CoinControlItemViewModelBase> PocketColumn()
- {
- return new TemplateColumn<CoinControlItemViewModelBase>(
- "Pocket",
- new FuncDataTemplate<CoinControlItemViewModelBase>((_, _) => new LabelsCellView(), true),
- GridLength.Star,
- new ColumnOptions<CoinControlItemViewModelBase>
- {
- CompareAscending = SortAscending<CoinControlItemViewModelBase, int>(GetLabelPriority),
- CompareDescending = SortDescending<CoinControlItemViewModelBase, int>(GetLabelPriority)
- });
- }
-
- private static int GetLabelPriority(CoinControlItemViewModelBase coin)
- {
- if (coin.Labels == CoinPocketHelper.PrivateFundsText)
- {
- return 3;
- }
-
- if (coin.Labels == CoinPocketHelper.SemiPrivateFundsText)
- {
- return 2;
- }
-
- return 1;
- }
-
- private static int GetIndicatorPriority(CoinControlItemViewModelBase x)
- {
- if (x.IsCoinjoining)
- {
- return 1;
- }
-
- if (x.BannedUntilUtc.HasValue)
- {
- return 2;
- }
-
- if (!x.IsConfirmed)
- {
- return 3;
- }
-
- return 0;
- }
-
- private static IReadOnlyCollection<PocketCoinControlItemViewModel> CreatePocketItems(IEnumerable<Pocket> pockets)
- {
- return pockets
- .Select(pocket => new PocketCoinControlItemViewModel(pocket))
- .ToList();
- }
-
- private static Comparison<TSource?> SortAscending<TSource, TProperty>(Func<TSource, TProperty> selector)
- {
- return (x, y) => Comparer<TProperty>.Default.Compare(selector(x!), selector(y!));
- }
-
- private static Comparison<TSource?> SortDescending<TSource, TProperty>(Func<TSource, TProperty?> selector)
- {
- return (x, y) => Comparer<TProperty>.Default.Compare(selector(y!), selector(x!));
- }
}

MODIFY
WalletWasabi.Fluent/Views/CoinControl/SelectCoinsDialogView.axaml
WalletWasabi.Fluent/Views/CoinControl/SelectCoinsDialogView.axaml
</controls:ContentArea.Styles>
- <TreeDataGrid Source="{Binding CoinSelector.Source}" CanUserSortColumns="True">
+ <TreeDataGrid Source="{Binding CoinSelector.TreeDataGridSource}" CanUserSortColumns="True">
<TreeDataGrid.ElementFactory>
<treeDataGrid:PlainTextElementFactory />
</TreeDataGrid.ElementFactory>

Add CoinSelectorViewModel