Finish Manual CoinJoin Profiles
MODIFY
WalletWasabi.Fluent/ViewModels/CoinJoinProfiles/CoinJoinProfilesViewModel.cs
WalletWasabi.Fluent/ViewModels/CoinJoinProfiles/CoinJoinProfilesViewModel.cs
using ReactiveUI;
using System.Collections.Generic;
+using System.Linq;
using System.Reactive.Disposables;
using System.Threading.Tasks;
using System.Windows.Input;
@@ -13,7 +14,7 @@ namespace WalletWasabi.Fluent.ViewModels.CoinJoinProfiles;
[NavigationMetaData(Title = "CoinJoin Profiles")]
public partial class CoinJoinProfilesViewModel : RoutableViewModel
{
- [AutoNotify] private CoinJoinProfileViewModelBase _selectedProfile;
+ [AutoNotify] private CoinJoinProfileViewModelBase? _selectedProfile;
public CoinJoinProfilesViewModel(KeyManager keyManager)
{
@@ -38,20 +39,25 @@ public partial class CoinJoinProfilesViewModel : RoutableViewModel
public List<CoinJoinProfileViewModelBase> Profiles { get; }
+ public ManualCoinJoinProfile? SelectedManualProfile { get; private set; }
+
private async Task OnManualSetupAsync()
{
- var dialog = new ManualCoinJoinProfileDialogViewModel();
+ var current = SelectedProfile ?? SelectedManualProfile ?? Profiles.First();
+ var dialog = new ManualCoinJoinProfileDialogViewModel(current);
var dialogResult = await NavigateDialogAsync(dialog, NavigationTarget.CompactDialogScreen);
- if (dialogResult.Result is { })
+ if (dialogResult.Result is ManualCoinJoinProfile profile)
{
+ SelectedProfile = null;
+ SelectedManualProfile = profile;
}
}
private void OnNext(KeyManager keyManager)
{
- var selected = SelectedProfile;
+ var selected = SelectedProfile ?? SelectedManualProfile ?? Profiles.First();
keyManager.SetAnonScoreTargets(selected.MinAnonScoreTarget, selected.MaxAnonScoreTarget, toFile: false);
keyManager.SetFeeRateAverageTimeFrame(selected.FeeRateAverageTimeFrameHours, toFile: false);

MODIFY
WalletWasabi.Fluent/ViewModels/Dialogs/ManualCoinJoinProfileDialogViewModel.cs
WalletWasabi.Fluent/ViewModels/Dialogs/ManualCoinJoinProfileDialogViewModel.cs
+using ReactiveUI;
+using System.Linq;
+using WalletWasabi.Fluent.Validation;
using WalletWasabi.Fluent.ViewModels.CoinJoinProfiles;
using WalletWasabi.Fluent.ViewModels.Dialogs.Base;
+using WalletWasabi.Models;
namespace WalletWasabi.Fluent.ViewModels.Dialogs;
[NavigationMetaData(Title = "Manual CoinJoin Profile")]
public partial class ManualCoinJoinProfileDialogViewModel : DialogViewModelBase<ManualCoinJoinProfile?>
{
- public ManualCoinJoinProfileDialogViewModel()
+ [AutoNotify] private string _minAnonScoreTarget;
+ [AutoNotify] private string _maxAnonScoreTarget;
+ [AutoNotify] private TimeFrameItem[] _timeFrames;
+ [AutoNotify] private TimeFrameItem _selectedTimeFrame;
+
+ public ManualCoinJoinProfileDialogViewModel(CoinJoinProfileViewModelBase current)
+ {
+ _minAnonScoreTarget = current.MinAnonScoreTarget.ToString();
+ _maxAnonScoreTarget = current.MaxAnonScoreTarget.ToString();
+ _timeFrames = new[]
{
+ new TimeFrameItem("None", TimeSpan.Zero),
+ new TimeFrameItem("Daily", TimeSpan.FromDays(1)),
+ new TimeFrameItem("Weekly", TimeSpan.FromDays(7)),
+ new TimeFrameItem("Monthly", TimeSpan.FromDays(30))
+ };
+
+ _selectedTimeFrame = _timeFrames.FirstOrDefault(tf => tf.TimeFrame == TimeSpan.FromHours(current.FeeRateAverageTimeFrameHours)) ?? _timeFrames.First();
+
SetupCancel(enableCancel: true, enableCancelOnEscape: true, enableCancelOnPressed: true);
EnableBack = false;
+
+ NextCommand = ReactiveCommand.Create(() =>
+ {
+ var min = int.Parse(MinAnonScoreTarget);
+ var max = int.Parse(MaxAnonScoreTarget);
+ var hours = (int)Math.Floor(SelectedTimeFrame.TimeFrame.TotalHours);
+
+ Close(DialogResultKind.Normal, new ManualCoinJoinProfile(min, max, hours));
+ });
+
+ this.ValidateProperty(x => x.MinAnonScoreTarget, errors => ValidateInteger(MinAnonScoreTarget, nameof(MinAnonScoreTarget), errors));
+ this.ValidateProperty(x => x.MaxAnonScoreTarget, errors => ValidateInteger(MaxAnonScoreTarget, nameof(MaxAnonScoreTarget), errors));
+ }
+
+ private void ValidateInteger(string text, string propertyName, IValidationErrors errors)
+ {
+ if (int.TryParse(text, out _))
+ {
+ return;
+ }
+
+ errors.Add(ErrorSeverity.Error, $"Field {propertyName} is not an integer.");
+ }
+
+ public record TimeFrameItem(string Name, TimeSpan TimeFrame)
+ {
+ public override string ToString()
+ {
+ return Name;
+ }
}
}

MODIFY
WalletWasabi.Fluent/Views/Dialogs/ManualCoinJoinProfileDialogView.axaml
WalletWasabi.Fluent/Views/Dialogs/ManualCoinJoinProfileDialogView.axaml
x:DataType="dialogs:ManualCoinJoinProfileDialogViewModel"
x:CompileBindings="True"
x:Class="WalletWasabi.Fluent.Views.Dialogs.ManualCoinJoinProfileDialogView">
- <c:ContentArea Width="500" Height="300"
+ <c:ContentArea Width="500" Height="400"
Title="{Binding Title}"
CancelContent="Cancel"
EnableCancel="{Binding EnableCancel}"
EnableBack="{Binding EnableBack}"
EnableNext="True" NextContent="Done">
-
+ <Grid>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="200" />
+ <ColumnDefinition Width="200" />
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition />
+ <RowDefinition />
+ <RowDefinition />
+ </Grid.RowDefinitions>
+ <TextBlock Grid.Row="0" Grid.Column="0" Text="MinAnonScoreTarget" />
+ <TextBlock Grid.Row="1" Grid.Column="0" Text="MaxAnonScoreTarget" />
+ <TextBlock Grid.Row="2" Grid.Column="0" Text="Average time frame" />
+ <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding MinAnonScoreTarget}" />
+ <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding MaxAnonScoreTarget}" />
+ <ComboBox Grid.Row="2" Grid.Column="1" HorizontalAlignment="Stretch"
+ Items="{Binding TimeFrames}"
+ SelectedItem="{Binding SelectedTimeFrame}" />
+ </Grid>
</c:ContentArea>
</UserControl>

Finish Manual CoinJoin Profiles