port existing wallet view models.
ADD
WalletWasabi.Fluent/ViewModels/ClosedWalletViewModel.cs
+using ReactiveUI;
+using System;
+using System.Linq;
+using System.Reactive;
+using System.Reactive.Linq;
+using System.Threading.Tasks;
+using WalletWasabi.Gui.Helpers;
+using WalletWasabi.Logging;
+using WalletWasabi.Wallets;
+
+namespace WalletWasabi.Fluent.ViewModels
+{
+ public class ClosedWalletViewModel : WalletViewModelBase
+ {
+ protected ClosedWalletViewModel(WalletManager walletManager, Wallet wallet) : base(wallet)
+ {
+ OpenWalletCommand = ReactiveCommand.CreateFromTask(
+ async () =>
+ {
+ try
+ {
+ if (wallet.KeyManager.PasswordVerified is true)
+ {
+ // TODO ... new UX will test password earlier...
+ }
+
+ await Task.Run(async () => await walletManager.StartWalletAsync(Wallet));
+ }
+ catch (OperationCanceledException ex)
+ {
+ Logger.LogTrace(ex);
+ }
+ catch (Exception ex)
+ {
+ NotificationHelpers.Error($"Couldn't load wallet. Reason: {ex.ToUserFriendlyString()}", sender: wallet);
+ Logger.LogError(ex);
+ }
+ },
+ this.WhenAnyValue(x => x.WalletState).Select(x => x == WalletState.Uninitialized));
+ }
+
+ public ReactiveCommand<Unit, Unit> OpenWalletCommand { get; }
+
+ public static WalletViewModelBase Create(WalletManager walletManager, Wallet wallet)
+ {
+ return wallet.KeyManager.IsHardwareWallet
+ ? new ClosedHardwareWalletViewModel(walletManager, wallet)
+ : wallet.KeyManager.IsWatchOnly
+ ? new ClosedWatchOnlyWalletViewModel(walletManager, wallet)
+ : new ClosedWalletViewModel(walletManager, wallet);
+ }
+ }
+}

ADD
WalletWasabi.Fluent/ViewModels/WalletViewModelBase.cs
+using ReactiveUI;
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
+using WalletWasabi.Helpers;
+using WalletWasabi.Wallets;
+
+namespace WalletWasabi.Fluent.ViewModels
+{
+ public abstract class WalletViewModelBase : NavBarItemViewModel, IComparable<WalletViewModelBase>, IDisposable
+ {
+
+ private string _titleTip;
+ private WalletState _walletState;
+ private CompositeDisposable _disposables;
+ private bool _disposedValue;
+
+ public WalletViewModelBase(Wallet wallet)
+ {
+ Wallet = Guard.NotNull(nameof(wallet), wallet);
+
+ _disposables = new CompositeDisposable();
+
+ Title = WalletName;
+ var isHardware = Wallet.KeyManager.IsHardwareWallet;
+ var isWatch = Wallet.KeyManager.IsWatchOnly;
+ TitleTip = isHardware ? "Hardware Wallet" : isWatch ? "Watch Only Wallet" : "Hot Wallet";
+
+ WalletState = wallet.State;
+
+ Observable.FromEventPattern<WalletState>(wallet, nameof(wallet.StateChanged))
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(x => WalletState = x.EventArgs)
+ .DisposeWith(_disposables);
+ }
+
+ public WalletState WalletState
+ {
+ get => _walletState;
+ private set => this.RaiseAndSetIfChanged(ref _walletState, value);
+ }
+
+ public Wallet Wallet { get; }
+
+ public string TitleTip
+ {
+ get => _titleTip;
+ set => this.RaiseAndSetIfChanged(ref _titleTip, value);
+ }
+
+ public string WalletName => Wallet.WalletName;
+
+ public int CompareTo([AllowNull] WalletViewModelBase other)
+ {
+ if (WalletState != other.WalletState)
+ {
+ if (WalletState == WalletState.Started || other.WalletState == WalletState.Started)
+ {
+ return other.WalletState.CompareTo(WalletState);
+ }
+ }
+
+ return Title.CompareTo(other.Title);
+ }
+
+ public override int CompareTo([AllowNull] NavBarItemViewModel other)
+ {
+ if(other is WalletViewModelBase wvmb)
+ {
+ return CompareTo(wvmb);
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ public override string ToString() => WalletName;
+
+ #region IDisposable Support
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposedValue)
+ {
+ if (disposing)
+ {
+ _disposables?.Dispose();
+ _disposables = null;
+ }
+
+ _disposedValue = true;
+ }
+ }
+
+ // This code added to correctly implement the disposable pattern.
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ Dispose(true);
+ }
+
+ #endregion IDisposable Support
+ }
+}

port existing wallet view models.