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] Make CJ settings more discoverable via MusicBox #12694

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 19 additions & 1 deletion WalletWasabi.Fluent/ViewModels/Wallets/CoinJoinStateViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
using Avalonia.Threading;
using ReactiveUI;
using WalletWasabi.Fluent.Extensions;
using WalletWasabi.Fluent.Models.UI;
using WalletWasabi.Fluent.Models.Wallets;
using WalletWasabi.Fluent.State;
using WalletWasabi.Fluent.ViewModels.Wallets.Settings;
using WalletWasabi.WabiSabi.Backend.Rounds;
using WalletWasabi.WabiSabi.Client.CoinJoinProgressEvents;
using WalletWasabi.WabiSabi.Client.StatusChangedEvents;
Expand Down Expand Up @@ -62,93 +64,105 @@
private DateTimeOffset _countDownStartTime;
private DateTimeOffset _countDownEndTime;

private CoinJoinStateViewModel(IWalletModel wallet)
public CoinJoinStateViewModel(UiContext uiContext, IWalletModel wallet, WalletSettingsViewModel settings)
{
UiContext = uiContext;
_wallet = wallet;

wallet.Coinjoin.StatusUpdated
.Do(ProcessStatusChange)
.Subscribe();

wallet.Privacy.IsWalletPrivate
.BindTo(this, x => x.AreAllCoinsPrivate);

var initialState =
wallet.Settings.AutoCoinjoin
? State.WaitingForAutoStart
: State.StoppedOrPaused;

if (wallet.IsHardwareWallet || wallet.IsWatchOnlyWallet)
{
initialState = State.Disabled;
}

if (wallet.Settings.IsCoinJoinPaused)
{
initialState = State.StoppedOrPaused;
}

_stateMachine = new StateMachine<State, Trigger>(initialState);

ConfigureStateMachine();

wallet.Balances
.Do(_ => _stateMachine.Fire(Trigger.BalanceChanged))
.Subscribe();

this.WhenAnyValue(x => x.AreAllCoinsPrivate)
.Do(_ => _stateMachine.Fire(Trigger.AreAllCoinsPrivateChanged))
.Subscribe();

PlayCommand = ReactiveCommand.CreateFromTask(async () =>
{
if (!wallet.Settings.IsCoinjoinProfileSelected)
{
await UiContext.Navigate().To().CoinJoinProfiles(wallet.Settings).GetResultAsync();
}

if (wallet.Settings.IsCoinjoinProfileSelected)
{
var overridePlebStop = _stateMachine.IsInState(State.PlebStopActive);
await wallet.Coinjoin.StartAsync(stopWhenAllMixed: !IsAutoCoinJoinEnabled, overridePlebStop);
}
});

var stopPauseCommandCanExecute =
this.WhenAnyValue(
x => x.IsInCriticalPhase,
x => x.PauseSpreading,
(isInCriticalPhase, pauseSpreading) => !isInCriticalPhase && !pauseSpreading);

StopPauseCommand = ReactiveCommand.CreateFromTask(wallet.Coinjoin.StopAsync, stopPauseCommandCanExecute);

AutoCoinJoinObservable = wallet.Settings.WhenAnyValue(x => x.AutoCoinjoin);

AutoCoinJoinObservable
.Skip(1) // The first one is triggered at the creation.
.Where(x => !x)
.Do(_ => _stateMachine.Fire(Trigger.AutoCoinJoinOff))
.Subscribe();

wallet.Settings.WhenAnyValue(x => x.PlebStopThreshold)
.SubscribeAsync(async _ =>
{
// Hack: we take the value from KeyManager but it is saved later.
await Task.Delay(1500);
_stateMachine.Fire(Trigger.PlebStopChanged);
});

_autoCoinJoinStartTimer = new DispatcherTimer { Interval = TimeSpan.FromMinutes(Random.Shared.Next(5, 16)) };
_autoCoinJoinStartTimer.Tick += async (_, _) =>
{
await wallet.Coinjoin.StartAsync(stopWhenAllMixed: false, false);
_autoCoinJoinStartTimer.Stop();
};

_countdownTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
_countdownTimer.Tick += (_, _) => _stateMachine.Fire(Trigger.Tick);

_stateMachine.Start();

var coinJoinSettingsCommand = ReactiveCommand.Create(
() =>
{
settings.SelectedTab = 1;
UiContext.Navigate(NavigationTarget.DialogScreen).To(settings);
},
Observable.Return(!_wallet.IsWatchOnlyWallet));

NavigateToSettingsCommand = coinJoinSettingsCommand;
CanNavigateToCoinjoinSettings = coinJoinSettingsCommand.CanExecute;

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

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (master)

❌ New issue: Large Method

CoinJoinStateViewModel has 78 lines, 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 All @@ -175,6 +189,10 @@
AreAllCoinsPrivateChanged
}

public IObservable<bool> CanNavigateToCoinjoinSettings { get; }

public ICommand NavigateToSettingsCommand { get; }

public bool IsAutoCoinJoinEnabled => _wallet.Settings.AutoCoinjoin;

public IObservable<bool> AutoCoinJoinObservable { get; }
Expand Down
11 changes: 2 additions & 9 deletions WalletWasabi.Fluent/ViewModels/Wallets/WalletViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
using System.Reactive.Linq;
Expand Down Expand Up @@ -101,15 +102,7 @@ public WalletViewModel(UiContext uiContext, IWalletModel walletModel, Wallet wal

WalletCoinsCommand = ReactiveCommand.Create(() => Navigate(NavigationTarget.DialogScreen).To().WalletCoins(WalletModel));

CoinJoinSettingsCommand = ReactiveCommand.Create(
() =>
{
Settings.SelectedTab = 1;
Navigate(NavigationTarget.DialogScreen).To(Settings);
},
Observable.Return(!WalletModel.IsWatchOnlyWallet));

CoinJoinStateViewModel = new CoinJoinStateViewModel(uiContext, WalletModel);
CoinJoinStateViewModel = new CoinJoinStateViewModel(uiContext, WalletModel, Settings);

Tiles = GetTiles().ToList();

Expand Down
31 changes: 12 additions & 19 deletions WalletWasabi.Fluent/Views/Wallets/MusicControlsView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -105,25 +105,18 @@
<PathIcon Data="{StaticResource stop_regular}" />
</Button>

<Ellipse Height="12" Width="12"
Classes.disabled="{Binding !AutoCoinJoinObservable^}"
Classes.waiting="{Binding IsAutoWaiting}">
<Ellipse.Styles>
<Style Selector="Ellipse">
<Setter Property="Fill" Value="{DynamicResource SystemAccentColor}" />
<Setter Property="ToolTip.Tip" Value="Auto-start coinjoin enabled" />
</Style>
<Style Selector="Ellipse.waiting">
<Setter Property="Fill" Value="{DynamicResource CoinjoinActiveColor}" />
<Setter Property="ToolTip.Tip"
Value="Auto-start coinjoin enabled. Press Play to start immediately." />
</Style>
<Style Selector="Ellipse.disabled">
<Setter Property="Fill" Value="{DynamicResource SystemBaseMediumColor}" />
<Setter Property="ToolTip.Tip" Value="Auto-start coinjoin disabled" />
</Style>
</Ellipse.Styles>
</Ellipse>
<Button Classes="plain" IsVisible="{Binding CanNavigateToCoinjoinSettings^}">
<Button.Flyout>
<MenuFlyout Placement="Top">
<MenuItem Header="Coinjoin Settings" Command="{Binding NavigateToSettingsCommand}">
<MenuItem.Icon>
<PathIcon Data="{StaticResource settings_general_regular}" />
</MenuItem.Icon>
</MenuItem>
</MenuFlyout>
</Button.Flyout>
<PathIcon Data="{StaticResource more_regular}" Opacity="0.6" />
</Button>
</StackPanel>
</Panel>
</Border>
Expand Down