Skip to content

Commit

Permalink
Improve fix to CoinList crash (WalletWasabi#13315)
Browse files Browse the repository at this point in the history
  • Loading branch information
turbolay authored Aug 9, 2024
1 parent 1e51ede commit b4f43aa
Show file tree
Hide file tree
Showing 9 changed files with 24 additions and 57 deletions.
22 changes: 0 additions & 22 deletions WalletWasabi.Fluent/Extensions/ObservableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,26 +116,4 @@ public static IObservableCache<TObject, TKey> Fetch<TObject, TKey>(this IObserva
.DisposeMany()
.AsObservableCache();
}

public static (IObservableCache<TObject1, TKey1>, IObservableCache<TObject2, TKey2>) Fetch<TObject1, TKey1, TObject2, TKey2>(
this IObservable<Unit> signal,
(Func<IEnumerable<TObject1>> Source, Func<TObject1, TKey1> KeySelector, IEqualityComparer<TObject1>? EqualityComparer) first,
(Func<IEnumerable<TObject2>> Source, Func<TObject2, TKey2> KeySelector, IEqualityComparer<TObject2>? EqualityComparer) second)
where TKey1 : notnull
where TKey2 : notnull
where TObject1 : notnull
where TObject2 : notnull
{
var cache1 = signal.Select(_ => first.Source())
.EditDiff(first.KeySelector, first.EqualityComparer)
.DisposeMany()
.AsObservableCache();

var cache2 = signal.Select(_ => second.Source())
.EditDiff(second.KeySelector, second.EqualityComparer)
.DisposeMany()
.AsObservableCache();

return (cache1, cache2);
}
}
11 changes: 2 additions & 9 deletions WalletWasabi.Fluent/Models/Wallets/CoinListModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ public abstract partial class CoinListModel : IDisposable
{
private readonly CompositeDisposable _disposables = new();

private object Lock { get; } = new();

public CoinListModel(Wallet wallet, IWalletModel walletModel)
{
Wallet = wallet;
Expand All @@ -34,13 +32,8 @@ public CoinListModel(Wallet wallet, IWalletModel walletModel)
.Merge(isSelected)
.Publish();

(List, Pockets) = signals.Fetch(
first: (Source: CreateCoinModels, KeySelector: x => x.Key, EqualityComparer: null),
second: (Source: GetPockets, KeySelector: x => x.Labels, EqualityComparer: null)
);

List.DisposeWith(_disposables);
Pockets.DisposeWith(_disposables);
List = signals.Fetch(CreateCoinModels, x => x.Key).DisposeWith(_disposables);
Pockets = signals.Fetch(GetPockets, x => x.Labels).DisposeWith(_disposables);

signals
.Do(_ => Logger.LogDebug($"Refresh signal emitted in {walletModel.Name}"))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using System.Reactive.Linq;
using WalletWasabi.Blockchain.TransactionOutputs;
using WalletWasabi.Fluent.Helpers;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public SelectCoinsDialogViewModel(IWalletModel wallet, IList<ICoinModel> selecte
{
var transactionInfo = sendFlow.TransactionInfo ?? throw new InvalidOperationException($"Missing required TransactionInfo.");

CoinList = new CoinListViewModel(wallet, sendFlow.CoinList, selectedCoins, allowCoinjoiningCoinSelection: true, ignorePrivacyMode: true, allowSelection: true);
CoinList = new CoinListViewModel(sendFlow.CoinList, selectedCoins, allowCoinjoiningCoinSelection: true, ignorePrivacyMode: true, allowSelection: true);

EnoughSelected = CoinList.Selection.ToObservableChangeSet()
.ToCollection()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public WalletCoinsViewModel(UiContext uiContext, IWalletModel wallet)
_wallet = wallet;
SetupCancel(enableCancel: false, enableCancelOnEscape: true, enableCancelOnPressed: true);
NextCommand = CancelCommand;
CoinList = new CoinListViewModel(_wallet, _wallet.Coins, new List<ICoinModel>(), allowCoinjoiningCoinSelection: false, ignorePrivacyMode: false, allowSelection: false);
CoinList = new CoinListViewModel(_wallet.Coins, new List<ICoinModel>(), allowCoinjoiningCoinSelection: false, ignorePrivacyMode: false, allowSelection: false);
}

public CoinListViewModel CoinList { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@ public class CoinListViewModel : ViewModelBase, IDisposable
{
private readonly CompositeDisposable _disposables = new();
private readonly ReadOnlyObservableCollection<CoinListItem> _itemsCollection;
private readonly IWalletModel _wallet;
private readonly bool _ignorePrivacyMode;
private readonly bool _allowCoinjoiningCoinSelection;

[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Uses DisposeWith()")]
public CoinListViewModel(IWalletModel wallet, ICoinListModel availableCoins, IList<ICoinModel> initialCoinSelection, bool allowCoinjoiningCoinSelection, bool ignorePrivacyMode, bool allowSelection = true)
public CoinListViewModel(ICoinListModel availableCoins, IList<ICoinModel> initialCoinSelection, bool allowCoinjoiningCoinSelection, bool ignorePrivacyMode, bool allowSelection = true)
{
_wallet = wallet;
_ignorePrivacyMode = ignorePrivacyMode;
_allowCoinjoiningCoinSelection = allowCoinjoiningCoinSelection;

Expand Down Expand Up @@ -84,7 +82,7 @@ public CoinListViewModel(IWalletModel wallet, ICoinListModel availableCoins, ILi
{
IList<ICoinModel> oldSelection = Selection.ToArray();
var oldExpandedItemsLabel = _itemsCollection.Where(x => x.IsExpanded).Select(x => x.Labels).ToArray();
Rebuild(viewModels, pockets);
Rebuild(viewModels, pockets, availableCoins);
UpdateSelection(coinItemsCollection, oldSelection);
RestoreExpandedRows(oldExpandedItemsLabel);
})
Expand All @@ -95,8 +93,6 @@ public CoinListViewModel(IWalletModel wallet, ICoinListModel availableCoins, ILi
TreeDataGridSource.DisposeWith(_disposables);
CoinItems = coinItemsCollection;

_wallet = wallet;

ExpandAllCommand = ReactiveCommand.Create(
() =>
{
Expand Down Expand Up @@ -155,7 +151,7 @@ private static void UpdateSelection(IEnumerable<CoinViewModel> coinItems, IList<
}
}

private void Rebuild(ISourceList<CoinListItem> source, IEnumerable<Pocket> pockets)
private void Rebuild(ISourceList<CoinListItem> source, IEnumerable<Pocket> pockets, ICoinListModel availableCoins)
{
var newItems =
pockets.Select(pocket =>
Expand All @@ -164,12 +160,12 @@ private void Rebuild(ISourceList<CoinListItem> source, IEnumerable<Pocket> pocke
if (pocket.Coins.Count() == 1)
{
var coin = pocket.Coins.First();
var coinModel = _wallet.Coins.GetCoinModel(coin);
var coinModel = availableCoins.GetCoinModel(coin);

return (CoinListItem)new CoinViewModel(pocket.Labels, coinModel, _ignorePrivacyMode, _allowCoinjoiningCoinSelection);
}

return new PocketViewModel(_wallet, pocket, _allowCoinjoiningCoinSelection, _ignorePrivacyMode);
return new PocketViewModel(pocket, availableCoins, _allowCoinjoiningCoinSelection, _ignorePrivacyMode);
});

source.EditDiff(newItems, new LambdaComparer<CoinListItem>((a, b) => Equals(a?.Key, b?.Key)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Coins;

public class PocketViewModel : CoinListItem
{
public PocketViewModel(IWalletModel wallet, Pocket pocket, bool canSelectCoinjoiningCoins, bool ignorePrivacyMode)
public PocketViewModel(Pocket pocket, ICoinListModel availableCoins, bool canSelectCoinjoiningCoins, bool ignorePrivacyMode)
{
var pocketCoins = pocket.Coins.ToList();

Expand All @@ -30,9 +30,9 @@ public PocketViewModel(IWalletModel wallet, Pocket pocket, bool canSelectCoinjoi
Labels = pocket.Labels;
Children =
pocketCoins
.Select(wallet.Coins.GetCoinModel)
.Select(availableCoins.GetCoinModel)
.OrderByDescending(x => x.AnonScore)
.Select(coin => new CoinViewModel("", coin, canSelectCoinjoiningCoins, ignorePrivacyMode) { IsChild = true })
.Select(coin => new CoinViewModel("", coin, canSelectCoinjoiningCoins, ignorePrivacyMode) { IsChild = true })
.ToList();

Children
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
namespace WalletWasabi.Fluent.ViewModels.Wallets.Send;

[NavigationMetaData(
Title = "Manual Control",
IconName = "wallet_action_send",
NavBarPosition = NavBarPosition.None,
Searchable = false,
NavigationTarget = NavigationTarget.DialogScreen)]
public partial class ManualControlDialogViewModel: DialogViewModelBase<IEnumerable<SmartCoin>>
Title = "Manual Control",
IconName = "wallet_action_send",
NavBarPosition = NavBarPosition.None,
Searchable = false,
NavigationTarget = NavigationTarget.DialogScreen)]
public partial class ManualControlDialogViewModel : DialogViewModelBase<IEnumerable<SmartCoin>>
{
[AutoNotify] private bool _hasSelection;

Expand All @@ -30,7 +30,7 @@ public partial class ManualControlDialogViewModel: DialogViewModelBase<IEnumerab

private ManualControlDialogViewModel(IWalletModel walletModel, Wallet wallet)
{
CoinList = new CoinListViewModel(walletModel, walletModel.Coins, [], allowCoinjoiningCoinSelection: true, ignorePrivacyMode: true, allowSelection: true);
CoinList = new CoinListViewModel(walletModel.Coins, [], allowCoinjoiningCoinSelection: true, ignorePrivacyMode: true, allowSelection: true);

var nextCommandCanExecute =
CoinList.Selection
Expand All @@ -42,9 +42,9 @@ private ManualControlDialogViewModel(IWalletModel walletModel, Wallet wallet)

SelectedAmount =
CoinList.Selection
.ToObservableChangeSet()
.ToCollection()
.Select(c => c.Any() ? walletModel.AmountProvider.Create(c.TotalAmount()) : null);
.ToObservableChangeSet()
.ToCollection()
.Select(c => c.Any() ? walletModel.AmountProvider.Create(c.TotalAmount()) : null);

ToggleSelectionCommand = ReactiveCommand.Create(() => SelectAll(!CoinList.Selection.Any()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public ExcludedCoinsViewModel(IWalletModel wallet)
{
_wallet = wallet;
var initialCoins = wallet.Coins.List.Items.Where(x => x.IsExcludedFromCoinJoin);
CoinList = new CoinListViewModel(wallet, wallet.Coins, initialCoins.ToList(), allowCoinjoiningCoinSelection: false, ignorePrivacyMode: true);
CoinList = new CoinListViewModel(wallet.Coins, initialCoins.ToList(), allowCoinjoiningCoinSelection: false, ignorePrivacyMode: true);
SetupCancel(enableCancel: true, enableCancelOnEscape: true, enableCancelOnPressed: true);
NextCommand = ReactiveCommand.Create(() => Close());
ToggleSelectionCommand = ReactiveCommand.Create(() => SelectAll(!CoinList.Selection.Any()));
Expand Down

0 comments on commit b4f43aa

Please sign in to comment.