refactor disposing
MODIFY
WalletWasabi.Fluent/ViewModels/AddWallet/HardwareWallet/ConnectHardwareWalletViewModel.cs
WalletWasabi.Fluent/ViewModels/AddWallet/HardwareWallet/ConnectHardwareWalletViewModel.cs
+using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reactive.Disposables;
-using System.Threading.Tasks;
-using System.Timers;
using System.Windows.Input;
using ReactiveUI;
using WalletWasabi.Fluent.ViewModels.NavBar;
@@ -55,7 +54,7 @@ namespace WalletWasabi.Fluent.ViewModels.AddWallet.HardwareWallet
public WalletViewModelBase? ExistingWallet { get; set; }
- public HardwareWalletOperations HardwareWalletOperations { get; set; }
+ public HardwareWalletOperations? HardwareWalletOperations { get; set; }
public ICommand OpenBrowserCommand { get; }
@@ -63,13 +62,18 @@ namespace WalletWasabi.Fluent.ViewModels.AddWallet.HardwareWallet
private void RunDetection()
{
+ if (HardwareWalletOperations is null)
+ {
+ return;
+ }
+
IsSearching = true;
ExistingWalletFound = false;
Message = "";
HardwareWalletOperations.StartDetection();
}
- private void OnPassphraseNeeded(object sender, ElapsedEventArgs e)
+ private void OnPassphraseNeeded(object? sender, EventArgs e)
{
IsSearching = false;
Message = "Check your device and enter your passphrase.";
@@ -77,6 +81,11 @@ namespace WalletWasabi.Fluent.ViewModels.AddWallet.HardwareWallet
private void OnDetectionCompleted(object? sender, HwiEnumerateEntry[] devices)
{
+ if (HardwareWalletOperations is null)
+ {
+ return;
+ }
+
IsSearching = false;
if (devices.Length == 0)
@@ -110,7 +119,7 @@ namespace WalletWasabi.Fluent.ViewModels.AddWallet.HardwareWallet
else
{
Message = "Check your device and finish the initialization.";
- Task.Run(() => HardwareWalletOperations.InitHardwareWalletAsync(device));
+ HardwareWalletOperations.InitHardwareWallet(device);
}
return;
@@ -143,15 +152,15 @@ namespace WalletWasabi.Fluent.ViewModels.AddWallet.HardwareWallet
HardwareWalletOperations = new HardwareWalletOperations(WalletManager);
HardwareWalletOperations.DetectionCompleted += OnDetectionCompleted;
- HardwareWalletOperations.PassphraseTimer.Elapsed += OnPassphraseNeeded;
+ HardwareWalletOperations.PassphraseNeeded += OnPassphraseNeeded;
RunDetection();
- Disposable.Create(() =>
+ Disposable.Create(async () =>
{
HardwareWalletOperations.DetectionCompleted -= OnDetectionCompleted;
- HardwareWalletOperations.PassphraseTimer.Elapsed -= OnPassphraseNeeded;
- HardwareWalletOperations.Dispose();
+ HardwareWalletOperations.PassphraseNeeded -= OnPassphraseNeeded;
+ await HardwareWalletOperations.DisposeAsync();
})
.DisposeWith(disposable);
}

MODIFY
WalletWasabi.Fluent/ViewModels/AddWallet/HardwareWallet/DetectedHardwareWalletViewModel.cs
WalletWasabi.Fluent/ViewModels/AddWallet/HardwareWallet/DetectedHardwareWalletViewModel.cs
using System;
+using System.Reactive.Disposables;
using System.Windows.Input;
using ReactiveUI;
using WalletWasabi.Extensions;
@@ -14,7 +15,9 @@ namespace WalletWasabi.Fluent.ViewModels.AddWallet.HardwareWallet
public DetectedHardwareWalletViewModel(WalletManager walletManager, string walletName, HwiEnumerateEntry device)
{
Title = "Hardware Wallet";
+ WalletManager = walletManager;
WalletName = walletName;
+ HardwareWalletOperations = new HardwareWalletOperations(WalletManager);
Type = device.Model switch
{
@@ -30,9 +33,7 @@ namespace WalletWasabi.Fluent.ViewModels.AddWallet.HardwareWallet
{
try
{
- using var hwo = new HardwareWalletOperations(walletManager);
-
- var km = await hwo.GenerateWalletAsync(WalletName, device);
+ var km = await HardwareWalletOperations.GenerateWalletAsync(WalletName, device);
km.SetIcon(Type);
Navigate().To(new AddedWalletPageViewModel(walletManager, km));
@@ -50,6 +51,7 @@ namespace WalletWasabi.Fluent.ViewModels.AddWallet.HardwareWallet
EnableAutoBusyOn(NextCommand);
}
+ public WalletManager WalletManager { get; }
public string WalletName { get; }
public WalletType Type { get; }
@@ -57,5 +59,14 @@ namespace WalletWasabi.Fluent.ViewModels.AddWallet.HardwareWallet
public string TypeName { get; }
public ICommand NoCommand { get; }
+
+ private HardwareWalletOperations HardwareWalletOperations { get; set; }
+
+ protected override void OnNavigatedTo(bool inStack, CompositeDisposable disposable)
+ {
+ base.OnNavigatedTo(inStack, disposable);
+
+ Disposable.Create(async () => await HardwareWalletOperations.DisposeAsync()).DisposeWith(disposable);
+ }
}
}

MODIFY
WalletWasabi.Fluent/ViewModels/AddWallet/HardwareWallet/HardwareWalletOperations.cs
WalletWasabi.Fluent/ViewModels/AddWallet/HardwareWallet/HardwareWalletOperations.cs
using System;
-using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using System.Timers;
using NBitcoin;
using WalletWasabi.Blockchain.Keys;
using WalletWasabi.Hwi;
@@ -13,35 +13,38 @@ using Timer = System.Timers.Timer;
namespace WalletWasabi.Fluent.ViewModels.AddWallet.HardwareWallet
{
- public class HardwareWalletOperations : IDisposable
+ public class HardwareWalletOperations : IAsyncDisposable
{
public event EventHandler<HwiEnumerateEntry[]>? DetectionCompleted;
+ public event EventHandler? PassphraseNeeded;
public HardwareWalletOperations(WalletManager walletManager)
{
WalletManager = walletManager;
Client = new HwiClient(WalletManager.Network);
DisposeCts = new CancellationTokenSource();
- PassphraseTimer = new Timer(8000) {AutoReset = false};
}
public WalletManager WalletManager { get; }
public HwiClient Client { get; }
- private CancellationTokenSource? DetectionCts { get; set; }
-
private CancellationTokenSource DisposeCts { get; }
public Task? DetectionTask { get; set; }
- public Timer PassphraseTimer { get; }
+ public Task? InitTask { get; set; }
private void OnDetectionCompleted(HwiEnumerateEntry[] wallets)
{
DetectionCompleted?.Invoke(this, wallets);
}
+ private void OnPassphraseNeeded(object sender, ElapsedEventArgs e)
+ {
+ PassphraseNeeded?.Invoke(this, EventArgs.Empty);
+ }
+
public async Task<KeyManager> GenerateWalletAsync(string walletName, HwiEnumerateEntry device)
{
if (device.Fingerprint is null)
@@ -61,7 +64,12 @@ namespace WalletWasabi.Fluent.ViewModels.AddWallet.HardwareWallet
return KeyManager.CreateNewHardwareWalletWatchOnly(fingerPrint, extPubKey, path);
}
- public async Task InitHardwareWalletAsync(HwiEnumerateEntry device)
+ public void InitHardwareWallet(HwiEnumerateEntry device)
+ {
+ InitTask = InitHardwareWalletAsync(device);
+ }
+
+ private async Task InitHardwareWalletAsync(HwiEnumerateEntry device)
{
using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(21));
using var initCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, DisposeCts.Token);
@@ -79,37 +87,28 @@ namespace WalletWasabi.Fluent.ViewModels.AddWallet.HardwareWallet
}
}
- public async Task StopDetectionAsync()
- {
- if (DetectionTask is { } task && DetectionCts is { } cts)
- {
- cts.Cancel();
- await task;
- }
- }
-
public void StartDetection()
{
- if (DisposeCts is { } cts)
- {
- DetectionCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token);
- DetectionTask = HardwareWalletDetectionAsync(DetectionCts);
- }
+ DetectionTask = RunDetectionAsync();
}
- protected async Task HardwareWalletDetectionAsync(CancellationTokenSource detectionCts)
+ protected async Task RunDetectionAsync()
{
+ using var passphraseTimer = new Timer(8000) {AutoReset = false};
+ passphraseTimer.Elapsed += OnPassphraseNeeded;
+
try
{
- using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(3));
+ using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(3));
+ using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, DisposeCts.Token);
- PassphraseTimer.Start();
+ passphraseTimer.Start();
var detectedHardwareWallets =
(await Client.EnumerateAsync(timeoutCts.Token)
.ConfigureAwait(false))
.ToArray();
- detectionCts.Token.ThrowIfCancellationRequested();
+ DisposeCts.Token.ThrowIfCancellationRequested();
OnDetectionCompleted(detectedHardwareWallets);
}
@@ -122,21 +121,27 @@ namespace WalletWasabi.Fluent.ViewModels.AddWallet.HardwareWallet
}
finally
{
- PassphraseTimer.Stop();
+ passphraseTimer.Stop();
}
}
- public void Dispose()
+ public async ValueTask DisposeAsync()
{
- Task.Run(async () =>
+ if (DetectionTask is { } detectionTask)
{
- await StopDetectionAsync();
+ DisposeCts.Cancel();
+ await detectionTask;
+ detectionTask.Dispose();
+ }
+
+ if (InitTask is { } initTask)
+ {
+ DisposeCts.Cancel();
+ await initTask;
+ initTask.Dispose();
+ }
DisposeCts.Dispose();
- DetectionCts?.Dispose();
- DetectionTask?.Dispose();
- PassphraseTimer.Dispose();
- });
}
}
}

refactor disposing