Fix a lot of bugs
MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/ClosedWalletViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/ClosedWalletViewModel.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Threading;
+using System.Threading.Tasks;
using WalletWasabi.Gui.Helpers;
using WalletWasabi.Logging;
using WalletWasabi.Wallets;
@@ -24,12 +25,16 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
await global.WalletManager.StartWalletAsync(Wallet);
}
- catch (Exception e)
+ catch (Exception ex) when (ex is TaskCanceledException)
{
- NotificationHelpers.Error($"Error loading Wallet: {Title}");
- Logger.LogError(e.Message);
+ Logger.LogTrace(ex);
}
- }, this.WhenAnyValue(x => x.IsBusy).Select(x => !x));
+ catch (Exception ex)
+ {
+ NotificationHelpers.Error($"Couldn't load wallet: {Title}. Reason: {ex.Message}");
+ Logger.LogError(ex);
+ }
+ }, this.WhenAnyValue(x => x.WalletState).Select(x => x == WalletState.Uninitialized));
}
public ReactiveCommand<Unit, Unit> OpenWalletCommand { get; }

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/WalletExplorerView.xaml
WalletWasabi.Gui/Controls/WalletExplorer/WalletExplorerView.xaml
x:Class="WalletWasabi.Gui.Controls.WalletExplorer.WalletExplorerView">
<UserControl.Resources>
<converters:BooleanStringConverter x:Key="BooleanStringConverter" />
+ <converters:WalletLoadingBoolConverter x:Key="WalletLoadingBoolConverter" />
</UserControl.Resources>
<UserControl.Styles>
<Style Selector="DrawingPresenter#PART_Spinner">
</StackPanel.ContextMenu>
<DrawingPresenter Width="16" Height="16" Drawing="{DynamicResource WalletExplorer_ClosedWallet}" />
<TextBlock Text="{Binding Title}" />
- <DrawingPresenter Name="PART_Spinner" Width="16" Height="16" IsVisible="{Binding IsBusy}" Drawing="{DynamicResource WalletExplorer_Spinner}" />
+ <DrawingPresenter Name="PART_Spinner" Width="16" Height="16" IsVisible="{Binding WalletState, Converter={StaticResource WalletLoadingBoolConverter}}" Drawing="{DynamicResource WalletExplorer_Spinner}" />
</StackPanel>
</TreeDataTemplate>
<TreeDataTemplate DataType="ViewModels:WalletViewModel" ItemsSource="{Binding Actions}">

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/WalletViewModelBase.cs
WalletWasabi.Gui/Controls/WalletExplorer/WalletViewModelBase.cs
@@ -12,7 +12,6 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
public class WalletViewModelBase : ViewModelBase, IComparable<WalletViewModelBase>, IDisposable
{
private bool _isExpanded;
- private bool _isBusy;
private string _title;
private WalletState _walletState;
private CompositeDisposable _disposables;
@@ -33,23 +32,6 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(x => WalletState = x.EventArgs)
.DisposeWith(_disposables);
-
- this.WhenAnyValue(x => x.WalletState)
- .Subscribe(x =>
- {
- switch (x)
- {
- case WalletState.Started:
- case WalletState.Stopped:
- case WalletState.Uninitialized:
- IsBusy = false;
- break;
-
- default:
- IsBusy = true;
- break;
- }
- });
}
public WalletState WalletState
@@ -74,12 +56,6 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
public string WalletName => Wallet.WalletName;
- public bool IsBusy
- {
- get => _isBusy;
- private set => this.RaiseAndSetIfChanged(ref _isBusy, value);
- }
-
public int CompareTo([AllowNull] WalletViewModelBase other)
{
if (WalletState != other.WalletState)
@@ -93,6 +69,8 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
return Title.CompareTo(other.Title);
}
+ public override string ToString() => WalletName;
+
#region IDisposable Support
protected virtual void Dispose(bool disposing)

ADD
WalletWasabi.Gui/Converters/WalletLoadingBoolConverter.cs
+using Avalonia.Data.Converters;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+using WalletWasabi.Exceptions;
+using WalletWasabi.Wallets;
+
+namespace WalletWasabi.Gui.Converters
+{
+ public class WalletLoadingBoolConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is WalletState state)
+ {
+ return state < WalletState.Started && state > WalletState.Uninitialized;
+ }
+ else
+ {
+ throw new TypeArgumentException(value, typeof(WalletState), nameof(value));
+ }
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}

MODIFY
WalletWasabi.Gui/Tabs/WalletManager/LoadWallets/LoadWalletView.xaml
WalletWasabi.Gui/Tabs/WalletManager/LoadWallets/LoadWalletView.xaml
</controls:GroupBox>
<DockPanel LastChildFill="True" Margin="20 10">
<Button IsVisible="{Binding IsPasswordRequired}" Content="Test Password" Command="{Binding TestPasswordCommand}" DockPanel.Dock="Right" Margin="10 0 0 0" />
- <Button Command="{Binding LoadCommand}" DockPanel.Dock="Right">
- <StackPanel Orientation="Horizontal">
- <controls:Spinner Height="20" Width="20" IsVisible="{Binding IsBusy}" Margin="0 0 8 0" />
- <TextBlock Text="{Binding LoadButtonText}" />
- </StackPanel>
- </Button>
+ <Button Command="{Binding LoadCommand}" Content="Load Wallet" DockPanel.Dock="Right" />
<Button IsVisible="{Binding IsDesktopWallet}" Command="{Binding OpenFolderCommand}" DockPanel.Dock="Right" Margin="0 0 10 0">
<StackPanel Orientation="Horizontal" Spacing="6">
<DrawingPresenter Drawing="{DynamicResource FolderOpen}" />

MODIFY
WalletWasabi.Gui/Tabs/WalletManager/LoadWallets/LoadWalletViewModel.cs
WalletWasabi.Gui/Tabs/WalletManager/LoadWallets/LoadWalletViewModel.cs
@@ -30,11 +30,6 @@ namespace WalletWasabi.Gui.Tabs.WalletManager.LoadWallets
private ReadOnlyObservableCollection<WalletViewModelBase> _wallets;
private string _password;
private WalletViewModelBase _selectedWallet;
- private bool _isWalletSelected;
- private bool _canLoadWallet;
- private bool _canTestPassword;
- private bool _isBusy;
- private string _loadButtonText;
public LoadWalletViewModel(WalletManagerViewModel owner, LoadWalletType loadWalletType)
: base(loadWalletType == LoadWalletType.Password ? "Test Password" : "Load Wallet")
@@ -48,32 +43,41 @@ namespace WalletWasabi.Gui.Tabs.WalletManager.LoadWallets
RootList = new SourceList<WalletViewModelBase>();
RootList.Connect()
.AutoRefresh(model => model.WalletState)
- .Filter(x => (!IsPasswordRequired || !x.Wallet.KeyManager.IsWatchOnly) && (IsPasswordRequired || x.WalletState == WalletState.Uninitialized))
- .Sort(SortExpressionComparer<WalletViewModelBase>.Descending(p => p.Wallet.KeyManager.GetLastAccessTime()), resort: ResortTrigger.AsObservable())
+ .Filter(x => (!IsPasswordRequired || !x.Wallet.KeyManager.IsWatchOnly))
+ .Sort(SortExpressionComparer<WalletViewModelBase>
+ .Ascending(p => p.WalletState).ThenByDescending(p => p.Wallet.KeyManager.GetLastAccessTime()),
+ resort: ResortTrigger.AsObservable())
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(out _wallets)
.DisposeMany()
.Subscribe();
- this.WhenAnyValue(x => x.SelectedWallet)
- .Subscribe(_ => TrySetWalletStates());
-
- this.WhenAnyValue(x => x.IsBusy)
- .Subscribe(_ => TrySetWalletStates());
-
Observable.FromEventPattern<Wallet>(Global.WalletManager, nameof(Global.WalletManager.WalletAdded))
.ObserveOn(RxApp.MainThreadScheduler)
.Select(x => x.EventArgs)
.Subscribe(wallet => RootList.Add(new WalletViewModelBase(wallet)));
+ this.WhenAnyValue(x => x.SelectedWallet)
+ .Where(x => x is null)
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(_ => SelectedWallet = Wallets.FirstOrDefault());
+
+ Wallets
+ .ToObservableChangeSet()
+ .ToCollection()
+ .Where(items => items.Any() && SelectedWallet is null)
+ .Select(items => items.First())
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(x => SelectedWallet = x);
+
+ LoadCommand = ReactiveCommand.CreateFromTask(LoadWalletAsync, this.WhenAnyValue(x => x.SelectedWallet, x => x?.WalletState).Select(x => x == WalletState.Uninitialized));
+ TestPasswordCommand = ReactiveCommand.Create(LoadKeyManager, this.WhenAnyValue(x => x.SelectedWallet).Select(x => x is { }));
+ OpenFolderCommand = ReactiveCommand.Create(OpenWalletsFolder);
+
RootList.AddRange(Global.WalletManager
.GetWallets()
.Select(x => new WalletViewModelBase(x)));
- LoadCommand = ReactiveCommand.CreateFromTask(LoadWalletAsync, this.WhenAnyValue(x => x.CanLoadWallet));
- TestPasswordCommand = ReactiveCommand.Create(LoadKeyManager, this.WhenAnyValue(x => x.CanTestPassword));
- OpenFolderCommand = ReactiveCommand.Create(OpenWalletsFolder);
-
Observable
.Merge(LoadCommand.ThrownExceptions)
.Merge(TestPasswordCommand.ThrownExceptions)
@@ -84,8 +88,6 @@ namespace WalletWasabi.Gui.Tabs.WalletManager.LoadWallets
Logger.LogError(ex);
NotificationHelpers.Error(ex.ToUserFriendlyString());
});
-
- SetLoadButtonText();
}
public LoadWalletType LoadWalletType { get; }
@@ -107,36 +109,6 @@ namespace WalletWasabi.Gui.Tabs.WalletManager.LoadWallets
set => this.RaiseAndSetIfChanged(ref _selectedWallet, value);
}
- public bool IsWalletSelected
- {
- get => _isWalletSelected;
- set => this.RaiseAndSetIfChanged(ref _isWalletSelected, value);
- }
-
- public string LoadButtonText
- {
- get => _loadButtonText;
- set => this.RaiseAndSetIfChanged(ref _loadButtonText, value);
- }
-
- public bool CanLoadWallet
- {
- get => _canLoadWallet;
- set => this.RaiseAndSetIfChanged(ref _canLoadWallet, value);
- }
-
- public bool CanTestPassword
- {
- get => _canTestPassword;
- set => this.RaiseAndSetIfChanged(ref _canTestPassword, value);
- }
-
- public bool IsBusy
- {
- get => _isBusy;
- set => this.RaiseAndSetIfChanged(ref _isBusy, value);
- }
-
public SourceList<WalletViewModelBase> RootList { get; private set; }
public ReactiveCommand<Unit, Unit> LoadCommand { get; }
public ReactiveCommand<Unit, KeyManager> TestPasswordCommand { get; }
@@ -149,29 +121,15 @@ namespace WalletWasabi.Gui.Tabs.WalletManager.LoadWallets
public ErrorDescriptors ValidatePassword() => PasswordHelper.ValidatePassword(Password);
- public void SetLoadButtonText()
- {
- var text = "Load Wallet";
- if (IsBusy)
- {
- text = "Loading...";
- }
-
- LoadButtonText = text;
- }
-
public override void OnCategorySelected()
{
Password = "";
-
- TrySetWalletStates();
}
public KeyManager LoadKeyManager()
{
try
{
- CanTestPassword = false;
var password = Guard.Correct(Password); // Do not let whitespaces to the beginning and to the end.
Password = ""; // Clear password field.
@@ -224,18 +182,10 @@ namespace WalletWasabi.Gui.Tabs.WalletManager.LoadWallets
return null;
}
- finally
- {
- CanTestPassword = IsWalletSelected;
- }
}
public async Task LoadWalletAsync()
{
- try
- {
- IsBusy = true;
-
var keyManager = LoadKeyManager();
if (keyManager is null)
{
@@ -265,38 +215,9 @@ namespace WalletWasabi.Gui.Tabs.WalletManager.LoadWallets
}
}
}
- finally
- {
- IsBusy = false;
- }
- }
public void OpenWalletsFolder() => IoHelpers.OpenFolderInFileExplorer(Global.WalletManager.WalletDirectories.WalletsDir);
- private bool TrySetWalletStates()
- {
- try
- {
- if (SelectedWallet is null)
- {
- SelectedWallet = Wallets.FirstOrDefault();
- }
-
- IsWalletSelected = SelectedWallet != null;
- CanTestPassword = IsWalletSelected;
- CanLoadWallet = SelectedWallet is { } ? !SelectedWallet.IsBusy && SelectedWallet.WalletState <= WalletState.Initialized : false;
-
- SetLoadButtonText();
- return true;
- }
- catch (Exception ex)
- {
- Logger.LogWarning(ex);
- }
-
- return false;
- }
-
#region IDisposable Support
private bool _disposedValue = false; // To detect redundant calls

Fix a lot of bugs