Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[UI] Exclude coins from coinjoin #12893

Merged
merged 34 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
36f0129
Exclude coins
SuperJMN Apr 18, 2024
4cf4c12
Fix build
SuperJMN Apr 18, 2024
019211b
Add "Forbidden" icon
SuperJMN Apr 22, 2024
72dfb8d
Add logic
SuperJMN Apr 22, 2024
35f7f27
Fix CF
SuperJMN Apr 22, 2024
c9e7b17
Implement selection policy
SuperJMN Apr 24, 2024
10ad5a6
New icon
SuperJMN Apr 24, 2024
d7504a2
Implement select all/none
SuperJMN Apr 24, 2024
1c1fffa
Change toggle selection button
SuperJMN Apr 24, 2024
96cdacd
minor fixes
soosr Apr 24, 2024
c14ce31
Merge from master
SuperJMN Apr 25, 2024
8b71718
Remove AppLifetime
SuperJMN Apr 25, 2024
27aaedd
Reviewed code
SuperJMN Apr 25, 2024
35b5c61
Fix messed up selection column after scrolling
soosr Apr 25, 2024
6dc81c4
Make checkbox disabled instead if hidden
soosr Apr 25, 2024
241513d
Don't show Banned when coin is excluded from CJ
SuperJMN Apr 25, 2024
0dbaa49
Merge branch 'features/exclude-coins' of https://github.com/SuperJMN/…
SuperJMN Apr 25, 2024
645a516
Attempt
SuperJMN Apr 25, 2024
f8390d3
Fixes
soosr Apr 25, 2024
9d17528
more cleanup and fix
soosr Apr 25, 2024
0a37995
fix init values
soosr Apr 25, 2024
53fb531
Fix indicator on pockets
soosr Apr 25, 2024
6885544
Fix multiple save at a time
soosr Apr 25, 2024
f1a589c
don"t use leaky abstraction
soosr Apr 25, 2024
6ac36ee
add missing dispose
soosr Apr 25, 2024
13c016e
Fix memory leak
soosr Apr 25, 2024
b88ddac
cleanup
soosr Apr 25, 2024
0be2ee9
Coinjoin -> coinjoin
soosr Apr 30, 2024
2e82fa8
Fix selection desync
SuperJMN Apr 30, 2024
34ca433
Add priority for excluded coins
SuperJMN Apr 30, 2024
6269c0b
Merge branch 'master' into pr/12893
soosr May 7, 2024
7f806d8
Fix initial coin selection
SuperJMN May 7, 2024
71dca70
Merge from master
SuperJMN May 10, 2024
58b01d8
Add Pockets is the source of truth
SuperJMN May 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 8 additions & 1 deletion WalletWasabi.Fluent/Controls/Button.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,14 @@
</Style>
</ControlTheme>

<ControlTheme x:Key="DialogSortButton" TargetType="Button" BasedOn="{StaticResource SortButtonBase}">
<ControlTheme x:Key="DialogButton" TargetType="Button" BasedOn="{StaticResource SortButtonBase}">
<Setter Property="Width" Value="35" />
<Setter Property="Height" Value="35" />
<Setter Property="CornerRadius" Value="100" />
<Setter Property="Padding" Value="-1 1 0 0" />
</ControlTheme>

<ControlTheme x:Key="DialogSortButton" TargetType="Button" BasedOn="{StaticResource DialogButton}">
<Setter Property="Width" Value="35" />
<Setter Property="Height" Value="35" />
<Setter Property="CornerRadius" Value="100" />
Expand Down
8 changes: 8 additions & 0 deletions WalletWasabi.Fluent/Icons/Icons.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@
<PathIcon Data="{StaticResource up_arrow}" />
<PathIcon Data="{StaticResource down_arrow}" />
<PathIcon Data="{StaticResource sorting}" />
<PathIcon Data="{StaticResource exclude_coins}" />
<PathIcon Data="{StaticResource forbidden}" />
<PathIcon Data="{StaticResource select_none}" />
<PathIcon Data="{StaticResource select_all}" />
</WrapPanel>
</Design.PreviewWith>
<Style>
Expand Down Expand Up @@ -197,6 +201,10 @@
<StreamGeometry x:Key="down_arrow">M3.93698 0.00427978L4.00059 0C4.23783 0 4.43389 0.176289 4.46488 0.405019L4.46919 0.468605L4.46863 8.40116L6.48013 6.38853C6.64646 6.22214 6.90681 6.2069 7.09032 6.34292L7.14287 6.38828C7.30931 6.55454 7.32456 6.8149 7.18848 6.9984L7.14318 7.05101L4.33386 9.86264C4.1676 10.029 3.90724 10.0443 3.72373 9.90826L3.67119 9.86289L0.857175 7.05126C0.6741 6.86832 0.673975 6.57166 0.8569 6.38853C1.02319 6.22214 1.28352 6.2069 1.46703 6.34292L1.51961 6.38828L3.53142 8.39866L3.53198 0.468605C3.53198 0.231366 3.70824 0.0353077 3.93698 0.00427978Z</StreamGeometry>
<StreamGeometry x:Key="sorting">M3.28077 0L3.21719 0.00427978C2.98846 0.0353077 2.81217 0.231366 2.81217 0.468605L2.81163 8.39866L0.79982 6.38828L0.747243 6.34292C0.563737 6.2069 0.303405 6.22214 0.137112 6.38853C-0.0458124 6.57166 -0.0456874 6.86832 0.137387 7.05126L2.95137 9.86289L3.00395 9.90826C3.18745 10.0443 3.44778 10.029 3.61408 9.86264L6.42339 7.05101L6.46869 6.9984C6.60477 6.8149 6.58953 6.55454 6.42308 6.38828L6.37053 6.34292C6.18703 6.2069 5.92667 6.22214 5.76035 6.38853L3.74884 8.40116L3.74938 0.468605L3.7451 0.405019C3.71407 0.176289 3.51801 0 3.28077 0ZM5.46706 0.312404C5.20827 0.312404 4.99846 0.522208 4.99846 0.781009C4.99846 1.03981 5.20827 1.24961 5.46706 1.24961H10.4655C10.7243 1.24961 10.9341 1.03981 10.9341 0.781009C10.9341 0.522208 10.7243 0.312404 10.4655 0.312404H5.46706ZM4.99846 2.65543C4.99846 2.39663 5.20827 2.18683 5.46706 2.18683H8.5911C8.84989 2.18683 9.0597 2.39663 9.0597 2.65543C9.0597 2.91423 8.84989 3.12404 8.5911 3.12404H5.46706C5.20827 3.12404 4.99846 2.91423 4.99846 2.65543ZM5.46706 4.06125C5.20827 4.06125 4.99846 4.27106 4.99846 4.52985C4.99846 4.78865 5.20827 4.99846 5.46706 4.99846H6.71668C6.97547 4.99846 7.18528 4.78865 7.18528 4.52985C7.18528 4.27106 6.97547 4.06125 6.71668 4.06125H5.46706Z</StreamGeometry>
<StreamGeometry x:Key="mail_unread">M16 6.5H5.25a1.75 1.75 0 0 0-1.744 1.606l-.004.1L11 12.153l6.03-3.174a3.489 3.489 0 0 0 2.97.985v6.786a3.25 3.25 0 0 1-3.066 3.245L16.75 20H5.25a3.25 3.25 0 0 1-3.245-3.066L2 16.75v-8.5a3.25 3.25 0 0 1 3.066-3.245L5.25 5h11.087A3.487 3.487 0 0 0 16 6.5Zm2.5 3.399-7.15 3.765a.75.75 0 0 1-.603.042l-.096-.042L3.5 9.9v6.85a1.75 1.75 0 0 0 1.606 1.744l.144.006h11.5a1.75 1.75 0 0 0 1.744-1.607l.006-.143V9.899ZM19.5 4a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Z</StreamGeometry>
<StreamGeometry x:Key="exclude_coins">M 14.75 0 C 12.5 0 10.57 1.34 9.72 3.28 C 9.69 3.34 9.66 3.42 9.63 3.5 C 9.47 3.9 9.36 4.32 9.3 4.77 C 9.29 4.84 9.29 4.91 9.28 5 C 9.26 5.15 9.25 5.33 9.25 5.5 c 0 0.54 0.08 1.06 0.22 1.55 c 0.25 0.84 0.68 1.59 1.26 2.2 c 0.4 0.42 0.86 0.79 1.37 1.07 c 0.78 0.43 1.69 0.68 2.65 0.68 c 0.26 0 0.51 -0.03 0.75 -0.06 h 0.01 c 0.26 -0.04 0.5 -0.09 0.74 -0.15 c 0.26 -0.08 0.51 -0.16 0.75 -0.27 c 0.26 -0.12 0.51 -0.25 0.75 -0.42 c 0.27 -0.17 0.52 -0.36 0.75 -0.58 c 0.28 -0.26 0.53 -0.56 0.75 -0.87 c 0.63 -0.88 1 -1.98 1 -3.15 c 0 -3.05 -2.46 -5.5 -5.5 -5.5 z m 2.67 3.53 l -4.64 4.64 c -0.09 0.1 -0.22 0.15 -0.35 0.15 c -0.13 0 -0.26 -0.05 -0.35 -0.15 c -0.2 -0.19 -0.2 -0.51 0 -0.7 l 4.64 -4.64 c 0.19 -0.2 0.51 -0.2 0.7 0 c 0.2 0.19 0.2 0.51 0 0.7 z m 0.88 7.41 c -0.5 0.32 -1.03 0.57 -1.58 0.74 c -0.25 0.34 -0.7 0.68 -1.29 0.98 c -1.39 0.7 -3.59 1.22 -6.18 1.22 c -2.59 0 -4.79 -0.52 -6.18 -1.22 C 2.48 12.36 2.03 12.02 1.78 11.68 C 1.59 11.45 1.5 11.22 1.5 10.99 v -0.88 c 0.09 0.07 0.18 0.13 0.28 0.19 c 0.39 0.26 0.83 0.49 1.33 0.69 c 0.8 0.33 1.73 0.59 2.76 0.77 c 1.04 0.18 2.17 0.28 3.38 0.28 c 1.21 0 2.34 -0.1 3.38 -0.28 c 0.09 -0.02 0.17 -0.03 0.26 -0.05 c -0.84 -0.24 -1.62 -0.66 -2.29 -1.23 c -0.44 0.04 -0.89 0.06 -1.35 0.06 c -1.23 0 -2.37 -0.12 -3.38 -0.32 C 4.75 10.01 3.8 9.69 3.07 9.32 C 2.48 9.02 2.03 8.68 1.78 8.34 C 1.59 8.11 1.5 7.88 1.5 7.65 C 1.5 6.39 4.34 4.98 8.29 4.79 C 8.34 4.26 8.46 3.75 8.65 3.27 C 3.68 3.41 0 5.26 0 7.65 v 6.68 c 0 2.5 3.98 4.39 9.25 4.39 c 5.27 0 9.25 -1.89 9.25 -4.39 V 10.8 c -0.06 0.05 -0.13 0.1 -0.2 0.14 z m -1.3 3.4 c 0 1.36 -3.31 2.89 -7.75 2.89 c -4.44 0 -7.75 -1.53 -7.75 -2.89 v -0.88 c 0.09 0.06 0.18 0.13 0.28 0.19 c 1.66 1.07 4.33 1.74 7.47 1.74 c 3.14 0 5.81 -0.67 7.47 -1.74 c 0.1 -0.06 0.19 -0.13 0.28 -0.19 z</StreamGeometry>
<StreamGeometry x:Key="forbidden">M 8.01608 0 C 12.1582 0 15.5161 3.35787 15.5161 7.5 C 15.5161 11.6421 12.1582 15 8.01608 15 C 3.87395 15 0.516083 11.6421 0.516083 7.5 C 0.516083 3.35787 3.87395 0 8.01608 0 Z M 12.9574 3.25666 L 3.77274 12.4413 C 4.91254 13.4211 6.39517 14.0132 8.01608 14.0132 C 11.6132 14.0132 14.5292 11.0971 14.5292 7.5 C 14.5292 5.87909 13.9371 4.39646 12.9574 3.25666 Z M 8.01608 0.986842 C 4.41896 0.986842 1.50292 3.90288 1.50292 7.5 C 1.50292 9.12091 2.09504 10.6035 3.07477 11.7433 L 12.2594 2.55868 C 11.1196 1.57896 9.63699 0.986842 8.01608 0.986842 Z</StreamGeometry>
<StreamGeometry x:Key="select_none">M 1.6875 0 C 0.811127 0 0.090651 0.668804 0.0078125 1.52344 C 0.00255634 1.57766 0 1.63189 0 1.6875 C -1.21318e-05 1.69014 0 1.69267 0 1.69531 L 0 11.4355 L 0 11.4434 C 0.000255319 11.4989 0.00231271 11.5533 0.0078125 11.6074 C 0.090651 12.4621 0.811127 13.1309 1.6875 13.1309 L 11.4355 13.1309 C 12.3119 13.1309 13.0304 12.4621 13.1133 11.6074 C 13.119 11.5507 13.123 11.4938 13.123 11.4355 L 13.123 1.69531 L 13.123 1.6875 C 13.1228 1.63195 13.1188 1.5776 13.1133 1.52344 C 13.0304 0.668808 12.3119 0 11.4355 0 L 1.6875 0 z M 1.6875 1.13281 L 4.47656 1.13281 L 11.4355 1.13281 C 11.7073 1.13281 11.9339 1.32571 11.9863 1.58203 C 11.9933 1.61615 11.9975 1.65144 11.998 1.6875 L 11.998 1.69531 L 11.998 11.4355 C 11.998 11.4744 11.9938 11.5122 11.9863 11.5488 C 11.9339 11.8051 11.7073 11.998 11.4355 11.998 L 1.6875 11.998 C 1.41567 11.998 1.18917 11.8051 1.13672 11.5488 C 1.12923 11.5122 1.125 11.4744 1.125 11.4355 L 1.125 1.69531 L 1.125 1.6875 C 1.1255 1.65144 1.12974 1.61615 1.13672 1.58203 C 1.18917 1.32571 1.41567 1.13281 1.6875 1.13281 z M 13.873 2.7207 L 13.873 2.72852 L 13.8711 4.27344 L 13.8711 4.2832 L 13.875 4.3125 L 13.875 4.32031 L 13.875 11.8125 L 13.875 11.8203 C 13.8747 11.8888 13.872 11.9567 13.8652 12.0234 C 13.7596 13.0635 12.8804 13.875 11.8125 13.875 L 4.29297 13.875 L 2.7207 13.873 C 2.72168 13.8758 2.72362 13.8781 2.72461 13.8809 L 2.7207 13.8809 C 2.95155 14.5378 3.57668 15.0078 4.3125 15.0078 L 11.8125 15.0078 C 13.4655 15.0078 14.8247 13.7497 14.9844 12.1387 C 14.9953 12.0315 15 11.9225 15 11.8125 L 15 4.32031 L 15 4.3125 C 14.9998 4.26652 14.998 4.22083 14.9941 4.17578 C 14.9907 4.13334 14.985 4.09228 14.9785 4.05078 C 14.8818 3.43142 14.4502 2.92353 13.873 2.7207 z</StreamGeometry>
<StreamGeometry x:Key="select_all">M 1.6875 0 C 0.755521 0 0 0.755521 0 1.6875 L 0 11.4355 C 0 12.3675 0.755521 13.123 1.6875 13.123 L 11.4355 13.123 C 12.3675 13.123 13.123 12.3675 13.123 11.4355 L 13.123 1.6875 C 13.123 0.755521 12.3675 0 11.4355 0 L 1.6875 0 z M 1.6875 1.125 L 11.4355 1.125 C 11.7461 1.125 11.998 1.37684 11.998 1.6875 L 11.998 11.4355 C 11.998 11.7461 11.7461 11.998 11.4355 11.998 L 1.6875 11.998 C 1.37684 11.998 1.125 11.7461 1.125 11.4355 L 1.125 1.6875 C 1.125 1.37684 1.37684 1.125 1.6875 1.125 z M 13.873 2.7207 L 13.8711 4.27344 L 13.875 4.3125 L 13.875 11.8125 C 13.875 12.9516 12.9516 13.875 11.8125 13.875 L 4.29297 13.875 L 2.7207 13.873 C 2.95155 14.5299 3.57668 15 4.3125 15 L 11.8125 15 C 13.5729 15 15 13.5729 15 11.8125 L 15 4.3125 C 15 3.57668 14.5299 2.95155 13.873 2.7207 z M 9 3.9375 C 8.85606 3.9375 8.7114 3.99173 8.60156 4.10156 L 5.68555 7.01953 L 4.94922 6.03711 L 4.89453 5.97461 C 4.69964 5.78172 4.38805 5.75632 4.16211 5.92578 C 3.91358 6.11215 3.86438 6.46434 4.05078 6.71289 L 5.17578 8.21289 L 5.23047 8.27539 C 5.44628 8.48876 5.80158 8.49536 6.02344 8.27344 L 9.39844 4.89844 L 9.45312 4.83398 C 9.61654 4.61377 9.59816 4.30126 9.39844 4.10156 C 9.2886 3.99173 9.14394 3.9375 9 3.9375 z</StreamGeometry>

<ControlTemplate x:Key="wasabi_logo_text_dynamic">
<StackPanel Margin="0,0,0,-5" TextElement.FontFamily="{StaticResource WasabiLogoTextFont}"
Expand Down
28 changes: 12 additions & 16 deletions WalletWasabi.Fluent/Models/Wallets/CoinModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
namespace WalletWasabi.Fluent.Models.Wallets;

[AutoInterface]
public partial class CoinModel : ReactiveObject, IDisposable
public partial class CoinModel : ReactiveObject
{
private readonly CompositeDisposable _disposable = new();
private bool _subscribedToCoinChanges;

[AutoNotify] private bool _isExcludedFromCoinJoin;
Expand All @@ -22,7 +21,7 @@ public partial class CoinModel : ReactiveObject, IDisposable
[AutoNotify] private int _anonScore;
[AutoNotify] private int _confirmations;
[AutoNotify] private bool _isConfirmed;

public CoinModel(SmartCoin coin, int anonScoreTarget)
{
Coin = coin;
Expand Down Expand Up @@ -68,37 +67,34 @@ public CoinModel(SmartCoin coin, int anonScoreTarget)

/// <summary>Subscribes to property changes of underlying SmartCoin.</summary>
/// <remarks>This method is not thread safe. Make sure it's not called concurrently.</remarks>
public void SubscribeToCoinChanges()
public void SubscribeToCoinChanges(CompositeDisposable disposable)
{
if (_subscribedToCoinChanges)
{
return;
}

this.WhenAnyValue(c => c.Coin.IsExcludedFromCoinJoin).BindTo(this, x => x.IsExcludedFromCoinJoin).DisposeWith(_disposable);
this.WhenAnyValue(c => c.Coin.Confirmed).BindTo(this, x => x.IsConfirmed).DisposeWith(_disposable);
this.WhenAnyValue(c => c.Coin.HdPubKey.AnonymitySet).Select(x => (int) x).BindTo(this, x => x.AnonScore).DisposeWith(_disposable);
this.WhenAnyValue(c => c.Coin.CoinJoinInProgress).BindTo(this, x => x.IsCoinJoinInProgress).DisposeWith(_disposable);
this.WhenAnyValue(c => c.Coin.IsBanned).BindTo(this, x => x.IsBanned).DisposeWith(_disposable);
this.WhenAnyValue(c => c.Coin.BannedUntilUtc).WhereNotNull().Subscribe(x => BannedUntilUtcToolTip = $"Can't participate in coinjoin until: {x:g}").DisposeWith(_disposable);
this.WhenAnyValue(c => c.Coin.IsExcludedFromCoinJoin).BindTo(this, x => x.IsExcludedFromCoinJoin).DisposeWith(disposable);
this.WhenAnyValue(c => c.Coin.Confirmed).BindTo(this, x => x.IsConfirmed).DisposeWith(disposable);
this.WhenAnyValue(c => c.Coin.HdPubKey.AnonymitySet).Select(x => (int)x).BindTo(this, x => x.AnonScore).DisposeWith(disposable);
this.WhenAnyValue(c => c.Coin.CoinJoinInProgress).BindTo(this, x => x.IsCoinJoinInProgress).DisposeWith(disposable);
this.WhenAnyValue(c => c.Coin.IsBanned).BindTo(this, x => x.IsBanned).DisposeWith(disposable);
this.WhenAnyValue(c => c.Coin.BannedUntilUtc).WhereNotNull().Subscribe(x => BannedUntilUtcToolTip = $"Can't participate in coinjoin until: {x:g}").DisposeWith(disposable);

this.WhenAnyValue(c => c.Coin.Height).Select(_ => Coin.GetConfirmations()).Subscribe(
confirmations =>
{
Confirmations = confirmations;
ConfirmedToolTip = TextHelpers.GetConfirmationText(confirmations);
}).DisposeWith(_disposable);
}).DisposeWith(disposable);

_subscribedToCoinChanges = true;
}

public bool IsSameAddress(ICoinModel anotherCoin) => anotherCoin is CoinModel cm && cm.Coin.HdPubKey == Coin.HdPubKey;

public bool IsSame(ICoinModel anotherCoin) => anotherCoin is CoinModel cm && cm.Coin.Outpoint == Coin.Outpoint;

// TODO: Leaky abstraction. This shouldn't exist.
public SmartCoin GetSmartCoin() => Coin;

public void Dispose()
{
_disposable.Dispose();
}
}
16 changes: 16 additions & 0 deletions WalletWasabi.Fluent/Models/Wallets/WalletCoinsModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Threading.Tasks;
using DynamicData;
using ReactiveUI;
using WalletWasabi.Blockchain.Analysis.Clustering;
Expand Down Expand Up @@ -52,6 +53,21 @@ public WalletCoinsModel(Wallet wallet, IWalletModel walletModel)

public IObservableCache<Pocket, LabelsArray> Pockets { get; }

public async Task UpdateExcludedCoinsFromCoinjoinAsync(ICoinModel[] coinsToExclude)
{
await Task.Run(() =>
{
// TODO: To keep models in sync with business objects. Should be automatic.
foreach (var coinModel in List.Items)
{
coinModel.IsExcludedFromCoinJoin = coinsToExclude.Any(x => x.IsSame(coinModel));
}
soosr marked this conversation as resolved.
Show resolved Hide resolved

var outPoints = coinsToExclude.Select(x => x.GetSmartCoin().Outpoint).ToArray();
_wallet.UpdateExcludedCoinsFromCoinJoin(outPoints);
});
}

public List<ICoinModel> GetSpentCoins(BuildTransactionResult? transaction)
{
var coins = (transaction?.SpentCoins ?? new List<SmartCoin>()).ToList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@

NavigateToSettingsCommand = coinJoinSettingsCommand;
CanNavigateToCoinjoinSettings = coinJoinSettingsCommand.CanExecute;
NavigateToExcludedCoinsCommand = ReactiveCommand.Create(() => UiContext.Navigate().To().ExcludedCoins(_wallet));

Check warning on line 168 in WalletWasabi.Fluent/ViewModels/Wallets/CoinJoinStateViewModel.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (master)

❌ Getting worse: Large Method

CoinJoinStateViewModel increases from 78 to 79 lines of code, threshold = 70. Large functions with many lines of code are generally harder to understand and lower the code health. Avoid adding more lines to this function.
}

private enum State
Expand Down Expand Up @@ -195,6 +196,8 @@

public ICommand NavigateToSettingsCommand { get; }

public ICommand NavigateToExcludedCoinsCommand { get; }

public bool IsAutoCoinJoinEnabled => _wallet.Settings.AutoCoinjoin;

public IObservable<bool> AutoCoinJoinObservable { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ private static int GetIndicatorPriority(CoinListItem x)
return 3;
}

if (!x.IsExcludedFromCoinJoin)
{
return 4;
}

return 0;
}

Expand Down
11 changes: 8 additions & 3 deletions WalletWasabi.Fluent/ViewModels/Wallets/Coins/CoinListItem.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using NBitcoin;
using ReactiveUI;
Expand All @@ -10,8 +11,10 @@

namespace WalletWasabi.Fluent.ViewModels.CoinControl.Core;

public abstract partial class CoinListItem : ViewModelBase, ITreeDataGridExpanderItem
public abstract partial class CoinListItem : ViewModelBase, ITreeDataGridExpanderItem, IDisposable
{
protected readonly CompositeDisposable _disposables = new();

private bool? _isSelected;

[AutoNotify] private bool _isParentSelected;
Expand All @@ -21,6 +24,8 @@ public abstract partial class CoinListItem : ViewModelBase, ITreeDataGridExpande
[AutoNotify] private bool _isControlPointerOver;
[AutoNotify] private bool _isExpanded;
[AutoNotify] private bool _isCoinjoining;
[AutoNotify] private bool _isExcludedFromCoinJoin;
[AutoNotify] private bool _canBeSelected;

protected CoinListItem()
{
Expand Down Expand Up @@ -94,8 +99,6 @@ public bool IsSelectedProxy

public bool IsLastChild { get; set; }

public bool CanBeSelected { get; protected set; }

public bool IgnorePrivacyMode { get; protected set; }

public bool? IsSelected
Expand All @@ -115,4 +118,6 @@ public bool IsSelectedProxy
public ScriptType? ScriptType { get; protected set; }

public virtual bool HasChildren() => Children.Count != 0;

public void Dispose() => _disposables.Dispose();
}
55 changes: 15 additions & 40 deletions WalletWasabi.Fluent/ViewModels/Wallets/Coins/CoinListViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,70 +48,50 @@
})
.AddKey(model => model.Coin.Key);

coinItems.OnItemAdded(model => model.Coin.SubscribeToCoinChanges())
coinItems.OnItemAdded(model => model.Coin.SubscribeToCoinChanges(_disposables))
.Subscribe()
.DisposeWith(_disposables);

changes
.Sort(SortExpressionComparer<CoinListItem>.Descending(x => x.AnonymityScore ?? x.Children.Min(c => c.AnonymityScore) ?? 0))
.DisposeMany()
.Bind(out _itemsCollection)
.Subscribe()
.DisposeWith(_disposables);

coinItems
.Bind(out var coinItemsCollection)
.Subscribe()
.DisposeWith(_disposables);

var selectedCoins = coinItems
.AutoRefresh(x => x.IsSelected)
.ToCollection()
.Select(GetSelectedCoins);

wallet.Coins.Pockets
.Connect()
.ToCollection()
.WithLatestFrom(selectedCoins, (pockets, sc) => (pockets, sc))
.Do(
tuple =>
{
var (pockets, sl) = tuple;
var oldExpandedItemsLabel = _itemsCollection.Where(x => x.IsExpanded).Select(x => x.Labels).ToArray();
RefreshFromPockets(sourceItems, pockets);
UpdateSelection(coinItemsCollection, sl.ToList());
RestoreExpandedRows(oldExpandedItemsLabel);
})
.Subscribe()
.DisposeWith(_disposables);

coinItems.AutoRefresh(x => x.IsSelected)
.Filter(x => x.IsSelected == true)
.Transform(x => x.Coin)
.Bind(out var selection)
.Subscribe()
.DisposeWith(_disposables);

Selection = selection;

TreeDataGridSource = CoinListDataGridSource.Create(_itemsCollection, _ignorePrivacyMode);
TreeDataGridSource.DisposeWith(_disposables);
CoinItems = coinItemsCollection;

wallet.Coins.Pockets
.Connect()
wallet.Coins.List
.Connect(suppressEmptyChangeSets: false)
.ToCollection()
.SkipWhile(pockets => pockets.Count == 0)
.Do(
pockets =>
_ =>
{
RefreshFromPockets(sourceItems, pockets);
UpdateSelection(coinItemsCollection, initialCoinSelection);
ExpandSelectedItems();
var oldSelection = Selection.ToArray();
var oldExpandedItemsLabel = _itemsCollection.Where(x => x.IsExpanded).Select(x => x.Labels).ToArray();
RefreshFromPockets(sourceItems, wallet.Coins.Pockets.Items);
UpdateSelection(coinItemsCollection, oldSelection);
RestoreExpandedRows(oldExpandedItemsLabel);
})
.Subscribe()
.DisposeWith(_disposables);


TreeDataGridSource = CoinListDataGridSource.Create(_itemsCollection, _ignorePrivacyMode);
TreeDataGridSource.DisposeWith(_disposables);
CoinItems = coinItemsCollection;

Check notice on line 94 in WalletWasabi.Fluent/ViewModels/Wallets/Coins/CoinListViewModel.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (master)

✅ Getting better: Large Method

CoinListViewModel decreases from 90 to 72 lines of code, threshold = 70. Large functions with many lines of code are generally harder to understand and lower the code health. Avoid adding more lines to this function.
_wallet = wallet;

ExpandAllCommand = ReactiveCommand.Create(
Expand Down Expand Up @@ -181,12 +161,7 @@
return new PocketViewModel(_wallet, pocket, _ignorePrivacyMode);
});

source.Edit(
x =>
{
x.Clear();
x.AddRange(newItems);
});
source.EditDiff(newItems);
}

private void RestoreExpandedRows(IEnumerable<LabelsArray> oldItemsLabels)
Expand Down
13 changes: 8 additions & 5 deletions WalletWasabi.Fluent/ViewModels/Wallets/Coins/CoinViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Reactive.Disposables;
using System.Reactive.Linq;
using ReactiveUI;
using WalletWasabi.Blockchain.Analysis.Clustering;
using WalletWasabi.Fluent.Helpers;
Expand All @@ -7,10 +8,8 @@

namespace WalletWasabi.Fluent.ViewModels.Wallets.Coins;

public class CoinViewModel : CoinListItem, IDisposable
public class CoinViewModel : CoinListItem
{
private readonly CompositeDisposable _disposables = new();

public CoinViewModel(LabelsArray labels, ICoinModel coin, bool ignorePrivacyMode = false)
{
Labels = labels;
Expand All @@ -26,10 +25,14 @@ public CoinViewModel(LabelsArray labels, ICoinModel coin, bool ignorePrivacyMode
IsSelected = false;
ScriptType = coin.ScriptType;
IgnorePrivacyMode = ignorePrivacyMode;
this.WhenAnyValue(x => x.Coin.IsExcludedFromCoinJoin).BindTo(this, x => x.IsExcludedFromCoinJoin).DisposeWith(_disposables);
this.WhenAnyValue(x => x.Coin.IsCoinJoinInProgress).BindTo(this, x => x.IsCoinjoining).DisposeWith(_disposables);
this.WhenAnyValue(x => x.Coin.IsCoinJoinInProgress, b => !b).BindTo(this, x => x.CanBeSelected).DisposeWith(_disposables);
soosr marked this conversation as resolved.
Show resolved Hide resolved
this.WhenAnyValue(x => x.CanBeSelected)
.Where(b => !b)
.Do(_ => IsSelected = false)
.Subscribe();
}

public ICoinModel Coin { get; }

public void Dispose() => _disposables.Dispose();
}