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] passphrase redesign #12731

Open
wants to merge 139 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 90 commits
Commits
Show all changes
139 commits
Select commit Hold shift + click to select a range
89bb964
Add Password property to WalletCreationOptions
wieslawsoltes Mar 26, 2024
3448669
Show password dialog after wallet name screen
wieslawsoltes Mar 26, 2024
3a90d12
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Mar 27, 2024
370e3f0
Fix dialog caption
wieslawsoltes Mar 27, 2024
cf6df4c
Rename Password to Passphrase
wieslawsoltes Mar 27, 2024
605739c
Redesign the RecoveryWordsView, add Passphrase and confirmation check…
wieslawsoltes Mar 27, 2024
b82dc01
Use correct converter
wieslawsoltes Mar 27, 2024
4d2e8da
Reset IsConfirmed on back navigation
wieslawsoltes Mar 27, 2024
3d2c2a0
Do not show when not present
wieslawsoltes Mar 27, 2024
4e602f7
Format xaml
wieslawsoltes Mar 27, 2024
1f35dd0
Add TextBlock.RecoveryWord style
wieslawsoltes Mar 27, 2024
63684f1
Add TextBlock.Passphrase style
wieslawsoltes Mar 27, 2024
1febeb1
Add passphrase confirmation into ConfirmRecoveryWordsView
wieslawsoltes Mar 28, 2024
5b95a6e
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Mar 30, 2024
318468f
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 2, 2024
34ecb71
Add RecoverWordViewModel
wieslawsoltes Apr 2, 2024
912f6b0
Initial refactoring of the Recover Wallet workflow
wieslawsoltes Apr 2, 2024
6139722
Fix FocusBehavior
wieslawsoltes Apr 2, 2024
ea4768a
Add word navigation
wieslawsoltes Apr 2, 2024
a2d611f
Unselect current word
wieslawsoltes Apr 2, 2024
3ac6a69
Add select word command
wieslawsoltes Apr 2, 2024
5fead52
Fix command binding
wieslawsoltes Apr 2, 2024
53c0f54
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 3, 2024
acf5fdc
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 3, 2024
6f79463
Set theme from style
wieslawsoltes Apr 3, 2024
93fc359
Use TagsBoxAutoCompleteBox with RecoveryAutoCompleteBoxTextBox theme
wieslawsoltes Apr 3, 2024
70a9b1d
Wrap Focus() call inside Dispatcher.UIThread.Post() to make it work
wieslawsoltes Apr 3, 2024
2b9b936
Disable FocusOnAttachedBehavior
wieslawsoltes Apr 3, 2024
8d586b2
Disable IsConfirmed validation for now
wieslawsoltes Apr 3, 2024
603e52e
Adjust layout for TextBlock and use Word for Text
wieslawsoltes Apr 3, 2024
9656807
Use Word for Text binding in TagsBoxAutoCompleteBox
wieslawsoltes Apr 3, 2024
6212067
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 4, 2024
86af7c9
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 6, 2024
1f32fd4
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 8, 2024
d8e4107
Introduce variable for can execute
wieslawsoltes Apr 8, 2024
31fcffa
Do not display Add Passphrase dialog anymore
wieslawsoltes Apr 8, 2024
ce1dc07
Add RecoverWalletSummaryViewModel
wieslawsoltes Apr 8, 2024
ef8ca4e
Navigate to RecoverWalletSummaryViewModel
wieslawsoltes Apr 8, 2024
0cf80a6
Add RecoverWalletSummaryView stub
wieslawsoltes Apr 8, 2024
3396782
Add TODO comments
wieslawsoltes Apr 8, 2024
b8d27d8
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 8, 2024
32124d1
Set initial value for ConfirmPassphrase
wieslawsoltes Apr 8, 2024
bc29b85
Add Mnemonics validation
wieslawsoltes Apr 8, 2024
0bf61e4
Remove unused property
wieslawsoltes Apr 8, 2024
e0edc77
Remove unused code
wieslawsoltes Apr 8, 2024
f019ea3
Remove unused code
wieslawsoltes Apr 8, 2024
f924f28
Rename
wieslawsoltes Apr 8, 2024
f3c7e84
Pass suggestions as argument
wieslawsoltes Apr 8, 2024
45b09a0
Remove unused property
wieslawsoltes Apr 8, 2024
fda9f70
Remove unused code
wieslawsoltes Apr 8, 2024
6c549ae
Set field instead of the property
wieslawsoltes Apr 8, 2024
9e7a353
Refactor summary view
wieslawsoltes Apr 8, 2024
265bc21
Rename properties
wieslawsoltes Apr 8, 2024
ae1c1ce
Add validation and properties to summary view
wieslawsoltes Apr 8, 2024
d245947
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 9, 2024
bc2da4b
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 9, 2024
fb15f3e
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 10, 2024
8372748
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 10, 2024
f788b4b
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 10, 2024
686231f
Remove unused xaml
wieslawsoltes Apr 10, 2024
c7126db
Add Passphrase, MinGapLimit and DerivationPath input fields
wieslawsoltes Apr 10, 2024
9f48aca
Adjust layout
wieslawsoltes Apr 11, 2024
eee1481
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 11, 2024
1229152
Use Passphrase and DerivationPath properties for options
wieslawsoltes Apr 11, 2024
3666f64
Remove todo comments
wieslawsoltes Apr 11, 2024
67351ee
Refactor can execute next command conditions
wieslawsoltes Apr 11, 2024
25950d9
Fix format
wieslawsoltes Apr 11, 2024
b1c2d79
Set Mnemonics from options is any
wieslawsoltes Apr 11, 2024
3681a70
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 11, 2024
1269f49
Set property directly using style instead using custom theme
wieslawsoltes Apr 11, 2024
d706d91
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 12, 2024
d800c2f
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 13, 2024
e2807f9
Add missing checkmarks and update layout
wieslawsoltes Apr 14, 2024
e634356
Adjust separators
wieslawsoltes Apr 14, 2024
d154cb4
Set Focusable to true
wieslawsoltes Apr 14, 2024
79676a0
Adjust panel MinWidth
wieslawsoltes Apr 14, 2024
655340f
Adjust CopyablePasswordTextBox
wieslawsoltes Apr 14, 2024
b9e00a1
Adjust CopyablePasswordTextBox
wieslawsoltes Apr 14, 2024
9e11713
Move advanced options to the bottom of the content area
wieslawsoltes Apr 14, 2024
4faffb0
Fix ContentArea bottom panel alignment issues
wieslawsoltes Apr 14, 2024
15a57f4
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 15, 2024
76b6271
Remove spacing from MinGapLimit/DerivationPath StackPanel
wieslawsoltes Apr 15, 2024
2cec115
Fix style
wieslawsoltes Apr 15, 2024
7e71d91
Refactor how recover wallet word input works
wieslawsoltes Apr 15, 2024
c3ad527
Cleanup RecoverWalletViewModel
wieslawsoltes Apr 15, 2024
5c894c4
Disable navigating back
wieslawsoltes Apr 15, 2024
18eaa3e
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 15, 2024
a40cac1
Merge branch 'master' into pr/12731
molnard Apr 16, 2024
c851648
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 18, 2024
2730613
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 18, 2024
ba45470
Make Passphrase same color as Index
wieslawsoltes Apr 18, 2024
0d7bd5d
Increase margin between warning and words
wieslawsoltes Apr 18, 2024
2fe4220
Remove fixed height to make layout more responsive
wieslawsoltes Apr 18, 2024
0db3503
Align popup placement to bottom edge aligned to the left
wieslawsoltes Apr 18, 2024
6b3f6fd
Do not show dialog just go to advanced view
wieslawsoltes Apr 18, 2024
b33c57d
Add word validation
wieslawsoltes Apr 18, 2024
1910751
Mark as handled even when not executing command
wieslawsoltes Apr 18, 2024
9ad92c3
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 23, 2024
d5913a9
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 23, 2024
2825c0a
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 23, 2024
62fc35e
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 24, 2024
998455b
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 24, 2024
45fcdcd
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 25, 2024
0208ffb
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 26, 2024
87ca02b
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 29, 2024
b2e52e5
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Apr 30, 2024
1cf0a96
Remove the custom derivation path from advanced wallet recovery
wieslawsoltes Apr 30, 2024
abc1789
Add a Caption to the recovery screen
wieslawsoltes Apr 30, 2024
ca66e25
Reduce vertical space to fit all the words on smaller heoghts
wieslawsoltes Apr 30, 2024
704f380
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes May 3, 2024
fef04b2
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes May 6, 2024
6fec866
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes May 7, 2024
763e1a6
Fix content area navigation using keyboard, the Next button should be…
wieslawsoltes May 7, 2024
8a16824
Pressing ENTER ticks the box, and pressing ENTER again executes the c…
wieslawsoltes May 7, 2024
27f2770
Add a back button to the advanced recovery word dialog
wieslawsoltes May 7, 2024
5257a80
Edit the previously filled words by clicking on them
wieslawsoltes May 7, 2024
de5517c
Fix CF
wieslawsoltes May 7, 2024
3305246
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes May 13, 2024
c887d91
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes May 15, 2024
b013db1
Add custom RecoverWordBox control
wieslawsoltes May 20, 2024
c0650d2
Enable Shift+Tab behavior
wieslawsoltes May 20, 2024
54b6145
Remove unused code
wieslawsoltes May 20, 2024
cd586be
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes May 20, 2024
93eae3b
Add DataValidationErrors
wieslawsoltes May 20, 2024
645bd2c
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes May 27, 2024
d474be1
Add ExecuteCommandBaseBehavior base class
wieslawsoltes May 27, 2024
be6c817
Add ExecuteCommandOnLostFocusBehavior
wieslawsoltes May 27, 2024
48b8602
Add IsEnabled property
wieslawsoltes May 27, 2024
2a65e2c
Add ExecuteCommandOnLostFocusBehavior for IsSelected
wieslawsoltes May 27, 2024
19974dc
Disable as its not working properly for now
wieslawsoltes May 27, 2024
4875fb5
Show Invalid recovery words entered error message
wieslawsoltes May 27, 2024
36d796e
Show Invalid recovery words entered error message
wieslawsoltes May 27, 2024
c28e73c
Jump back to the previous word when user presses BACKSPACE
wieslawsoltes May 27, 2024
26ec0a9
Move to item template
wieslawsoltes May 27, 2024
1b43631
Hack-fix for TextBox handling Back key
wieslawsoltes May 27, 2024
f517b43
Add additional IsEnabled conditions
wieslawsoltes May 27, 2024
9cad29d
Change FallbackValue
wieslawsoltes May 27, 2024
ed565c5
Use correct type
wieslawsoltes May 27, 2024
be94d58
Merge remote-tracking branch 'upstream/master' into vdg/ui-passphrase…
wieslawsoltes Jun 4, 2024
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
24 changes: 17 additions & 7 deletions WalletWasabi.Fluent/Behaviors/FocusBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Reactive;
using Avalonia.Threading;
using Avalonia.Xaml.Interactions.Custom;

namespace WalletWasabi.Fluent.Behaviors;
Expand All @@ -19,19 +21,27 @@ public bool IsFocused

protected override void OnAttached(CompositeDisposable disposables)
{
base.OnAttached();

if (AssociatedObject is not null)
{
AssociatedObject.AttachedToLogicalTree += (sender, e) =>
disposables.Add(this.GetObservable(IsFocusedProperty)
.Subscribe(focused =>
disposables.Add(AssociatedObject.GetObservable(Avalonia.Input.InputElement.IsFocusedProperty)
.Subscribe(new AnonymousObserver<bool>(
focused =>
{
if (!focused)
{
SetCurrentValue(IsFocusedProperty, false);
}
})));

disposables.Add(this.GetObservable(IsFocusedProperty)
.Subscribe(new AnonymousObserver<bool>(
focused =>
{
if (focused)
{
AssociatedObject.Focus();
Dispatcher.UIThread.Post(() => AssociatedObject?.Focus());
}
}));
})));
}
}
}
51 changes: 25 additions & 26 deletions WalletWasabi.Fluent/Controls/ContentArea.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,40 +27,39 @@
<ProgressRing Name="LoadingRing" IsIndeterminate="True" IsVisible="{TemplateBinding IsBusy}" />
<DockPanel Name="MainDockPanel" IsVisible="{Binding !#LoadingRing.IsVisible}">

<Panel DockPanel.Dock="Bottom" x:Name="PART_ButtonArea">
<DockPanel DockPanel.Dock="Bottom" x:Name="PART_ButtonArea">
<Button Name="PART_CancelButton"
Classes="invisible"
IsVisible="{TemplateBinding EnableCancel}"
Content="{TemplateBinding CancelContent}"
HorizontalAlignment="Left" Command="{Binding CancelCommand}">
HorizontalAlignment="Left" Command="{Binding CancelCommand}"
DockPanel.Dock="Left">
<Interaction.Behaviors>
<FocusOnAttachedBehavior
IsEnabled="{Binding FocusCancel, RelativeSource={RelativeSource TemplatedParent}}" />
</Interaction.Behaviors>
</Button>
<DockPanel>
<StackPanel Orientation="Horizontal" Spacing="30" DockPanel.Dock="Right">
<Button Name="PART_SkipButton"
IsVisible="{TemplateBinding EnableSkip}"
Content="{TemplateBinding SkipContent}"
Command="{Binding SkipCommand}"
Classes="activeHyperLink skip"
VerticalAlignment="Center" />
<Button Name="PART_NextButton" Classes="action"
IsVisible="{TemplateBinding EnableNext}"
Content="{TemplateBinding NextContent}"
Command="{Binding NextCommand}"
IsDefault="{Binding IsActive}">
<Interaction.Behaviors>
<FocusOnAttachedBehavior
IsEnabled="{Binding FocusNext, RelativeSource={RelativeSource TemplatedParent}}" />
</Interaction.Behaviors>
</Button>
</StackPanel>
<ContentPresenter Content="{TemplateBinding BottomContent}"
x:Name="PART_BottomContentPresenter" />
</DockPanel>
</Panel>
<StackPanel Orientation="Horizontal" Spacing="30" DockPanel.Dock="Right">
<Button Name="PART_SkipButton"
IsVisible="{TemplateBinding EnableSkip}"
Content="{TemplateBinding SkipContent}"
Command="{Binding SkipCommand}"
Classes="activeHyperLink skip"
VerticalAlignment="Center" />
<Button Name="PART_NextButton" Classes="action"
IsVisible="{TemplateBinding EnableNext}"
Content="{TemplateBinding NextContent}"
Command="{Binding NextCommand}"
IsDefault="{Binding IsActive}">
<Interaction.Behaviors>
<FocusOnAttachedBehavior
IsEnabled="{Binding FocusNext, RelativeSource={RelativeSource TemplatedParent}}" />
</Interaction.Behaviors>
</Button>
</StackPanel>
<ContentPresenter Content="{TemplateBinding BottomContent}"
x:Name="PART_BottomContentPresenter" />
</DockPanel>

<Panel DockPanel.Dock="Top">
<Panel IsHitTestVisible="False" Background="{TemplateBinding HeaderBackground}" />
Expand Down Expand Up @@ -118,7 +117,7 @@
<Setter Property="Margin" Value="31 11 31 21" />
</Style>

<Style Selector="^/template/ Panel#PART_ButtonArea">
<Style Selector="^/template/ DockPanel#PART_ButtonArea">
<Setter Property="Margin" Value="31,10" />
</Style>

Expand Down
2 changes: 1 addition & 1 deletion WalletWasabi.Fluent/Controls/TagsBoxAutoCompleteBox.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
Padding="{TemplateBinding Padding}"
Margin="0"
DataValidationErrors.Errors="{TemplateBinding (DataValidationErrors.Errors)}"
Theme="{StaticResource AutoCompleteBoxTextBox}" />
Theme="{StaticResource AutoCompleteBoxTextBox}"/>
<Popup x:Name="PART_Popup"
WindowManagerAddShadowHint="False"
MinWidth="{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}"
Expand Down
14 changes: 9 additions & 5 deletions WalletWasabi.Fluent/Models/WalletCreationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@

namespace WalletWasabi.Fluent.Models;

public abstract record WalletCreationOptions(string? WalletName = null)
public abstract record WalletCreationOptions(string? WalletName = null, string? Passphrase = null)
{
public record AddNewWallet(string? WalletName = null, string? Password = null, Mnemonic? Mnemonic = null) : WalletCreationOptions(WalletName)
public record AddNewWallet(string? WalletName = null, string? Passphrase = null, Mnemonic? Mnemonic = null)
: WalletCreationOptions(WalletName, Passphrase)
{
public AddNewWallet WithNewMnemonic()
{
return this with { Mnemonic = new Mnemonic(Wordlist.English, WordCount.Twelve) };
}
}

public record ConnectToHardwareWallet(string? WalletName = null, HwiEnumerateEntry? Device = null) : WalletCreationOptions(WalletName);
public record ConnectToHardwareWallet(string? WalletName = null, HwiEnumerateEntry? Device = null)
: WalletCreationOptions(WalletName);

public record ImportWallet(string? WalletName = null, string? FilePath = null) : WalletCreationOptions(WalletName);
public record ImportWallet(string? WalletName = null, string? FilePath = null)
: WalletCreationOptions(WalletName);

public record RecoverWallet(string? WalletName = null, string? Password = null, Mnemonic? Mnemonic = null, int? MinGapLimit = null) : WalletCreationOptions(WalletName);
public record RecoverWallet(string? WalletName = null, string? Passphrase = null, Mnemonic? Mnemonic = null, int? MinGapLimit = null, string? AccountKeyPath = null)
: WalletCreationOptions(WalletName, Passphrase);
}
18 changes: 16 additions & 2 deletions WalletWasabi.Fluent/Models/Wallets/WalletRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,27 @@ private async Task<IWalletSettingsModel> ImportWalletAsync(WalletCreationOptions

private async Task<IWalletSettingsModel> RecoverWalletAsync(WalletCreationOptions.RecoverWallet options)
{
var (walletName, password, mnemonic, minGapLimit) = options;
var (walletName, password, mnemonic, minGapLimit, path) = options;

ArgumentException.ThrowIfNullOrEmpty(walletName);
ArgumentNullException.ThrowIfNull(password);
ArgumentNullException.ThrowIfNull(mnemonic);
ArgumentNullException.ThrowIfNull(minGapLimit);

var accountKeyPath = AccountKeyPath;

if (!string.IsNullOrEmpty(path))
{
if (KeyPath.TryParse(path, out var userKeyPath) && userKeyPath is not null)
{
accountKeyPath = userKeyPath;
}
else
{
throw new InvalidOperationException("Could not parse key path.");
}
}

var keyManager = await Task.Run(() =>
{
var walletFilePath = Services.WalletManager.WalletDirectories.GetWalletFilePaths(walletName).walletFilePath;
Expand All @@ -164,7 +178,7 @@ private async Task<IWalletSettingsModel> RecoverWalletAsync(WalletCreationOption
mnemonic,
password,
Services.WalletManager.Network,
AccountKeyPath,
accountKeyPath,
null,
"", // Make sure it is not saved into a file yet.
minGapLimit.Value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ private async Task AutoLoginAsync(WalletCreationOptions? options)
var password =
options switch
{
WalletCreationOptions.AddNewWallet add => add.Password,
WalletCreationOptions.RecoverWallet rec => rec.Password,
WalletCreationOptions.AddNewWallet add => add.Passphrase,
WalletCreationOptions.RecoverWallet rec => rec.Passphrase,
WalletCreationOptions.ConnectToHardwareWallet => "",
_ => null
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
using DynamicData.Binding;
using ReactiveUI;
using WalletWasabi.Fluent.Models;
using WalletWasabi.Fluent.Validation;
using WalletWasabi.Fluent.ViewModels.Navigation;
using WalletWasabi.Models;
using WalletWasabi.Userfacing;

namespace WalletWasabi.Fluent.ViewModels.AddWallet.Create;

Expand All @@ -20,13 +23,19 @@ public partial class ConfirmRecoveryWordsViewModel : RoutableViewModel
[AutoNotify] private bool _isSkipEnabled;
[AutoNotify] private RecoveryWordViewModel _currentWord;
[AutoNotify] private List<RecoveryWordViewModel> _availableWords;
[AutoNotify(SetterModifier = AccessModifier.Private)] private string? _passphrase;
[AutoNotify] private string? _confirmPassphrase;
[AutoNotify(SetterModifier = AccessModifier.Private)] private bool _allWordsConfirmed;
[AutoNotify(SetterModifier = AccessModifier.Private)] private bool _passphraseConfirmed;

private ConfirmRecoveryWordsViewModel(WalletCreationOptions.AddNewWallet options, List<RecoveryWordViewModel> words)
{
_options = options;
_availableWords = new List<RecoveryWordViewModel>();
_words = words.OrderBy(x => x.Index).ToList();
_currentWord = words.First();

this.ValidateProperty(x => x.ConfirmPassphrase, ValidateConfirmPassphrase);
}

public ObservableCollectionExtended<RecoveryWordViewModel> ConfirmationWords { get; } = new();
Expand All @@ -53,11 +62,30 @@ protected override void OnNavigatedTo(bool isInHistory, CompositeDisposable disp

CancelCommand = ReactiveCommand.Create(OnCancel);

var nextCommandCanExecute =
confirmationWordsSourceList
confirmationWordsSourceList
.Connect()
.WhenValueChanged(x => x.IsConfirmed)
.Select(_ => confirmationWordsSourceList.Items.All(x => x.IsConfirmed));
.Subscribe(_ => AllWordsConfirmed = confirmationWordsSourceList.Items.All(x => x.IsConfirmed))
.DisposeWith(disposables);

if (string.IsNullOrEmpty(_options.Passphrase))
{
PassphraseConfirmed = true;
}
else
{
this.WhenAnyValue(x => x.ConfirmPassphrase)
.Subscribe(confirmPassphrase =>
{
PassphraseConfirmed = confirmPassphrase == _options.Passphrase;
})
.DisposeWith(disposables);
}

var nextCommandCanExecute = this.WhenAnyValue(
x => x.AllWordsConfirmed,
x => x.PassphraseConfirmed)
.Select(x => x.Item1 && x.Item2);

NextCommand = ReactiveCommand.CreateFromTask(OnNextAsync, nextCommandCanExecute);

Expand All @@ -83,10 +111,20 @@ protected override void OnNavigatedTo(bool isInHistory, CompositeDisposable disp

SetNextWord();

Passphrase = _options.Passphrase;

var enableCancel = UiContext.WalletRepository.HasWallet;
SetupCancel(enableCancel: false, enableCancelOnEscape: enableCancel, enableCancelOnPressed: false);
}

private void ValidateConfirmPassphrase(IValidationErrors errors)
{
if (!string.IsNullOrEmpty(ConfirmPassphrase) && Passphrase != ConfirmPassphrase)
{
errors.Add(ErrorSeverity.Error, PasswordHelper.MatchingMessage);
}
}

private void SetNextWord()
{
if (ConfirmationWords.FirstOrDefault(x => !x.IsConfirmed) is { } nextWord)
Expand Down Expand Up @@ -134,23 +172,13 @@ private void EnableAvailableWords(bool enable)

private async Task OnNextAsync()
{
var dialogCaption = "Store your passphrase safely, it cannot be reset if lost.\n" +
"It's needed to open and to recover your wallet.\n" +
"It's a recovery words extension for more security.";
var password = await Navigate().To().CreatePasswordDialog("Add Passphrase", dialogCaption, enableEmpty: true).GetResultAsync();

if (password is { })
{
IsBusy = true;
IsBusy = true;

var options = _options with { Password = password };
var walletSettings = await UiContext.WalletRepository.NewWalletAsync(_options);

var walletSettings = await UiContext.WalletRepository.NewWalletAsync(options);
IsBusy = false;

IsBusy = false;

await Navigate().To().CoinJoinProfiles(walletSettings, options).GetResultAsync();
}
await Navigate().To().CoinJoinProfiles(walletSettings, _options).GetResultAsync();
}

private void OnCancel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ namespace WalletWasabi.Fluent.ViewModels.AddWallet.Create;
[NavigationMetaData(Title = "Recovery Words")]
public partial class RecoveryWordsViewModel : RoutableViewModel
{
[AutoNotify] private bool _isConfirmed;

private RecoveryWordsViewModel(WalletCreationOptions.AddNewWallet options)
{
var (_, _, mnemonic) = options;
Expand All @@ -22,9 +24,13 @@ private RecoveryWordsViewModel(WalletCreationOptions.AddNewWallet options)

MnemonicWords = CreateList(mnemonic);

Passphrase = options.Passphrase;

EnableBack = true;

NextCommand = ReactiveCommand.Create(() => OnNext(options));
var canExecuteNext = this.WhenAnyValue(x => x.IsConfirmed);

NextCommand = ReactiveCommand.Create(() => OnNext(options), canExecuteNext);

CancelCommand = ReactiveCommand.Create(OnCancel);
CopyToClipboardCommand = ReactiveCommand.CreateFromTask(OnCopyToClipboardAsync);
Expand All @@ -34,6 +40,8 @@ private RecoveryWordsViewModel(WalletCreationOptions.AddNewWallet options)

public List<RecoveryWordViewModel> MnemonicWords { get; }

public string? Passphrase { get; }

private void OnNext(WalletCreationOptions.AddNewWallet options)
{
Navigate().To().ConfirmRecoveryWords(options, MnemonicWords);
Expand Down Expand Up @@ -75,6 +83,8 @@ protected override void OnNavigatedTo(bool isInHistory, CompositeDisposable disp
var enableCancel = UiContext.WalletRepository.HasWallet;
SetupCancel(enableCancel: enableCancel, enableCancelOnEscape: enableCancel, enableCancelOnPressed: false);

IsConfirmed = false;

base.OnNavigatedTo(isInHistory, disposables);
}

Expand Down