confirm recovery word
MODIFY
WalletWasabi.Fluent/Icons/Icons.xaml
WalletWasabi.Fluent/Icons/Icons.xaml
<StreamGeometry x:Key="add_circle_regular">M14,2 C20.627417,2 26,7.372583 26,14 C26,20.627417 20.627417,26 14,26 C7.372583,26 2,20.627417 2,14 C2,7.372583 7.372583,2 14,2 Z M14,3.5 C8.20101013,3.5 3.5,8.20101013 3.5,14 C3.5,19.7989899 8.20101013,24.5 14,24.5 C19.7989899,24.5 24.5,19.7989899 24.5,14 C24.5,8.20101013 19.7989899,3.5 14,3.5 Z M14,8 C14.4142136,8 14.75,8.33578644 14.75,8.75 L14.75,13.25 L19.25,13.25 C19.6642136,13.25 20,13.5857864 20,14 C20,14.4142136 19.6642136,14.75 19.25,14.75 L14.75,14.75 L14.75,19.25 C14.75,19.6642136 14.4142136,20 14,20 C13.5857864,20 13.25,19.6642136 13.25,19.25 L13.25,14.75 L8.75,14.75 C8.33578644,14.75 8,14.4142136 8,14 C8,13.5857864 8.33578644,13.25 8.75,13.25 L13.25,13.25 L13.25,8.75 C13.25,8.33578644 13.5857864,8 14,8 Z</StreamGeometry>
<StreamGeometry x:Key="navigation_regular">M2.75254822,18 L21.2525482,18 C21.6667618,18 22.0025482,18.3357864 22.0025482,18.75 C22.0025482,19.1296958 21.7203943,19.443491 21.3543188,19.4931534 L21.2525482,19.5 L2.75254822,19.5 C2.33833466,19.5 2.00254822,19.1642136 2.00254822,18.75 C2.00254822,18.3703042 2.2847021,18.056509 2.65077766,18.0068466 L2.75254822,18 L21.2525482,18 L2.75254822,18 Z M2.75254822,11.5030063 L21.2525482,11.5030063 C21.6667618,11.5030063 22.0025482,11.8387927 22.0025482,12.2530063 C22.0025482,12.6327021 21.7203943,12.9464972 21.3543188,12.9961597 L21.2525482,13.0030063 L2.75254822,13.0030063 C2.33833466,13.0030063 2.00254822,12.6672199 2.00254822,12.2530063 C2.00254822,11.8733105 2.2847021,11.5595153 2.65077766,11.5098529 L2.75254822,11.5030063 L21.2525482,11.5030063 L2.75254822,11.5030063 Z M2.75168905,5.0032392 L21.251689,5.0032392 C21.6659026,5.0032392 22.001689,5.33902564 22.001689,5.7532392 C22.001689,6.13293497 21.7195352,6.44673016 21.3534596,6.49639258 L21.251689,6.5032392 L2.75168905,6.5032392 C2.33747549,6.5032392 2.00168905,6.16745276 2.00168905,5.7532392 C2.00168905,5.37354343 2.28384293,5.05974824 2.64991849,5.01008582 L2.75168905,5.0032392 L21.251689,5.0032392 L2.75168905,5.0032392 Z</StreamGeometry>
<StreamGeometry x:Key="arrow_left_regular">M12.7347,4.20949 C13.0332,3.92233 13.508,3.93153 13.7952,4.23005 C14.0823,4.52857 14.0731,5.00335 13.7746,5.29051 L5.50039,13.25 L24.2532,13.25 C24.6674,13.25 25.0032,13.5858 25.0032,13.9999982 C25.0032,14.4142 24.6674,14.75 24.2532,14.75 L5.50137,14.75 L13.7746,22.7085 C14.0731,22.9957 14.0823,23.4705 13.7952,23.769 C13.508,24.0675 13.0332,24.0767 12.7347,23.7896 L3.30673,14.7202 C2.89776,14.3268 2.89776,13.6723 3.30673,13.2788 L12.7347,4.20949 Z</StreamGeometry>
+ <StreamGeometry x:Key="checkmark_filled">M4.03033009,13.4696699 C3.73743687,13.1767767 3.26256313,13.1767767 2.96966991,13.4696699 C2.6767767,13.7625631 2.6767767,14.2374369 2.96966991,14.5303301 L9.96966991,21.5303301 C10.2625631,21.8232233 10.7374369,21.8232233 11.0303301,21.5303301 L25.0303301,7.53033009 C25.3232233,7.23743687 25.3232233,6.76256313 25.0303301,6.46966991 C24.7374369,6.1767767 24.2625631,6.1767767 23.9696699,6.46966991 L10.5,19.9393398 L4.03033009,13.4696699 Z</StreamGeometry>
</Style.Resources>
</Style>
</Styles>

ADD
WalletWasabi.Fluent/Models/RecoveryWord.cs
+using ReactiveUI;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using WalletWasabi.Gui.Validation;
+using WalletWasabi.Gui.ViewModels;
+using WalletWasabi.Models;
+
+namespace WalletWasabi.Fluent.Models
+{
+ public class RecoveryWord : ViewModelBase
+ {
+ private string _input;
+
+ public RecoveryWord(int index, string word)
+ {
+ Index = index;
+ Word = word;
+
+ this.ValidateProperty(x => x.Input, ValidateWord);
+ }
+
+ public string Input
+ {
+ get => _input;
+ set => this.RaiseAndSetIfChanged(ref _input, value);
+ }
+
+ public int Index { get; }
+ public string Word { get; }
+
+ private void ValidateWord(IValidationErrors errors)
+ {
+ if (string.IsNullOrWhiteSpace(Input))
+ {
+ return;
+ }
+
+ if (Input == Word)
+ {
+ return;
+ }
+
+ errors.Add(ErrorSeverity.Error, $"The input does not match to to the recovery word {Index}.");
+ }
+ }
+}

ADD
WalletWasabi.Fluent/ViewModels/Dialogs/CreateWallet/ConfirmRecoveryWordsViewModel.cs
+using ReactiveUI;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using WalletWasabi.Fluent.Models;
+using WalletWasabi.Gui.ViewModels;
+
+namespace WalletWasabi.Fluent.ViewModels.Dialogs.CreateWallet
+{
+ public class ConfirmRecoveryWordsViewModel : ViewModelBase, IRoutableViewModel
+ {
+ public ConfirmRecoveryWordsViewModel(IScreen screen, List<string> mnemonicWords)
+ {
+ HostScreen = screen;
+ ConfirmationWords = new List<RecoveryWord>();
+
+ SetConfirmationWords(mnemonicWords);
+ }
+
+ public string UrlPathSegment { get; }
+ public IScreen HostScreen { get; }
+ public List<RecoveryWord> ConfirmationWords { get; }
+
+ private void SetConfirmationWords(List<string> mnemonicWords)
+ {
+ var random = new Random();
+ var unsortedConfWords = new List<RecoveryWord>();
+
+ for (int i = 0; i < 4; i++)
+ {
+ int index;
+ while (true)
+ {
+ index = random.Next(0, 12);
+
+ if (!unsortedConfWords.Any(x => x.Index == index + 1))
+ {
+ break;
+ }
+ }
+
+ unsortedConfWords.Add(new RecoveryWord(index + 1, mnemonicWords[index]));
+ }
+
+ ConfirmationWords.AddRange(unsortedConfWords.OrderBy(x => x.Index));
+ }
+ }
+}

MODIFY
WalletWasabi.Fluent/ViewModels/Dialogs/CreateWallet/RecoveryWordsViewModel.cs
WalletWasabi.Fluent/ViewModels/Dialogs/CreateWallet/RecoveryWordsViewModel.cs
using System;
using System.Collections.Generic;
+using System.Reactive;
using System.Text;
+using System.Windows.Input;
using ReactiveUI;
using WalletWasabi.Gui.ViewModels;
@@ -19,8 +21,13 @@ namespace WalletWasabi.Fluent.ViewModels.Dialogs.CreateWallet
{
MnemonicWords.Add($"{i + 1}. Random");
}
+
+ ContinueCommand = ReactiveCommand.Create(() => screen.Router.Navigate.Execute(new ConfirmRecoveryWordsViewModel(HostScreen, MnemonicWords)));
}
+ public ICommand GoBackCommand => HostScreen.Router.NavigateBack;
+ public ICommand ContinueCommand { get; }
+
public List<string> MnemonicWords
{
get => _mnemonicWords;

MODIFY
WalletWasabi.Fluent/ViewModels/Dialogs/DialogViewModelBaseOfTResult.cs
WalletWasabi.Fluent/ViewModels/Dialogs/DialogViewModelBaseOfTResult.cs
@@ -13,15 +13,15 @@ namespace WalletWasabi.Fluent.ViewModels.Dialogs
/// <typeparam name="TResult">The type of the value to be returned when the dialog is finished.</typeparam>
public abstract class DialogViewModelBase<TResult> : DialogViewModelBase
{
- private readonly IDisposable _disposable;
- private readonly TaskCompletionSource<TResult> _currentTaskCompletionSource;
+ private readonly IDisposable Disposable;
+ private readonly TaskCompletionSource<TResult> CurrentTaskCompletionSource;
private bool _isDialogOpen;
protected DialogViewModelBase()
{
- _currentTaskCompletionSource = new TaskCompletionSource<TResult>();
+ CurrentTaskCompletionSource = new TaskCompletionSource<TResult>();
- _disposable = this.WhenAnyValue(x => x.IsDialogOpen)
+ Disposable = this.WhenAnyValue(x => x.IsDialogOpen)
.Skip(1) // Skip the initial value change (which is false).
.DistinctUntilChanged()
.Subscribe(OnIsDialogOpenChanged);
@@ -52,14 +52,14 @@ namespace WalletWasabi.Fluent.ViewModels.Dialogs
/// <param name="value">The return value of the dialog</param>
protected void Close(TResult value = default)
{
- if (_currentTaskCompletionSource.Task.IsCompleted)
+ if (CurrentTaskCompletionSource.Task.IsCompleted)
{
throw new InvalidOperationException("Dialog is already closed.");
}
- _currentTaskCompletionSource.SetResult(value);
+ CurrentTaskCompletionSource.SetResult(value);
- _disposable.Dispose();
+ Disposable.Dispose();
IsDialogOpen = false;
@@ -75,7 +75,7 @@ namespace WalletWasabi.Fluent.ViewModels.Dialogs
host.CurrentDialog = this;
IsDialogOpen = true;
- return _currentTaskCompletionSource.Task;
+ return CurrentTaskCompletionSource.Task;
}
/// <summary>

ADD
WalletWasabi.Fluent/Views/Dialogs/CreateWallet/ConfirmRecoveryWordsView.axaml
+<UserControl xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d"
+ d:DesignWidth="600" d:DesignHeight="500"
+ Width="600" Height="500"
+ x:Class="WalletWasabi.Fluent.Views.Dialogs.CreateWallet.ConfirmRecoveryWordsView">
+ <Panel Margin="25 35 25 20">
+
+ <Button Classes="invisible" Command="{Binding GoBackCommand}" DockPanel.Dock="Left" HorizontalAlignment="Left" VerticalAlignment="Top">
+ <PathIcon Data="{StaticResource arrow_left_regular}" />
+ </Button>
+
+ <StackPanel Margin="64 0 64 0">
+ <TextBlock Text="Confirm recovery words" FontSize="20" DockPanel.Dock="Top" FontWeight="SemiBold"/>
+ <TextBlock DockPanel.Dock="Top" TextWrapping="Wrap" FontSize="12" Margin="0 10 0 0" Opacity="0.6"
+ Text="Type the required recovery words" />
+
+ <ItemsControl Items="{Binding ConfirmationWords}" VerticalAlignment="Center" >
+ <ItemsControl.ItemTemplate>
+ <DataTemplate>
+ <StackPanel>
+ <TextBlock Text="{Binding Index, StringFormat=Recovery word {0}}" Margin="0 20 0 8"/>
+ <StackPanel Orientation="Horizontal">
+ <TextBox Watermark="Type the word here..." Text="{Binding Input}" Width="280" FontSize="12"/>
+ <PathIcon Foreground="Green" Data="{StaticResource checkmark_filled}" VerticalAlignment="Center" Width="12" Height="12" Margin="15 0 0 0"/>
+ </StackPanel>
+ </StackPanel>
+ </DataTemplate>
+ </ItemsControl.ItemTemplate>
+ </ItemsControl>
+
+
+ </StackPanel>
+
+ <Button Content="Cancel" Command="{Binding CancelCommand}" Classes="invisible" HorizontalAlignment="Left" VerticalAlignment="Bottom"/>
+ <Button Content="Finish" Command="{Binding ContinueCommand}" Classes="action" HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
+ </Panel>
+</UserControl>

ADD
WalletWasabi.Fluent/Views/Dialogs/CreateWallet/ConfirmRecoveryWordsView.axaml.cs
+using Avalonia.Markup.Xaml;
+using Avalonia.ReactiveUI;
+using WalletWasabi.Fluent.ViewModels.Dialogs.CreateWallet;
+
+namespace WalletWasabi.Fluent.Views.Dialogs.CreateWallet
+{
+ public class ConfirmRecoveryWordsView : ReactiveUserControl<ConfirmRecoveryWordsViewModel>
+ {
+ public ConfirmRecoveryWordsView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}

MODIFY
WalletWasabi.Fluent/Views/Dialogs/CreateWallet/RecoveryWordsView.axaml
WalletWasabi.Fluent/Views/Dialogs/CreateWallet/RecoveryWordsView.axaml
mc:Ignorable="d"
d:DesignWidth="600" d:DesignHeight="500"
Width="600" Height="500"
- x:Class="WalletWasabi.Fluent.Views.Dialogs.CreateWallet.RecoveryWordsView"
- Background="LightGray">
+ x:Class="WalletWasabi.Fluent.Views.Dialogs.CreateWallet.RecoveryWordsView">
<Panel Margin="25 35 25 20">
- <Button Classes="invisible" Command="{Binding Close}" DockPanel.Dock="Left" HorizontalAlignment="Left" VerticalAlignment="Top">
+ <Button Classes="invisible" Command="{Binding GoBackCommand}" DockPanel.Dock="Left" HorizontalAlignment="Left" VerticalAlignment="Top">
<PathIcon Data="{StaticResource arrow_left_regular}" />
</Button>
<DockPanel Margin="64 0 64 0">
- <TextBlock Text="Recovery words" FontSize="20" DockPanel.Dock="Top" FontWeight="SemiBold" Classes=""/>
+ <TextBlock Text="Recovery words" FontSize="20" DockPanel.Dock="Top" FontWeight="SemiBold"/>
<TextBlock DockPanel.Dock="Top" TextWrapping="Wrap" FontSize="12" Margin="0 10 0 0" Opacity="0.6"
Text="Write down the following numbered security words in order. These can be used to recover your wallet in the future. It is very important you keep them in a safe place." />
</ItemsControl>
</DockPanel>
- <Button Content="Cancel" Classes="invisible" HorizontalAlignment="Left" VerticalAlignment="Bottom"/>
- <Button Content="Continue" Classes="action" HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
+ <Button Content="Cancel" Command="{Binding CancelCommand}" Classes="invisible" HorizontalAlignment="Left" VerticalAlignment="Bottom"/>
+ <Button Content="Continue" Command="{Binding ContinueCommand}" Classes="action" HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
</Panel>
</UserControl>

MODIFY
WalletWasabi.Fluent/Views/Dialogs/CreateWallet/RecoveryWordsView.axaml.cs
WalletWasabi.Fluent/Views/Dialogs/CreateWallet/RecoveryWordsView.axaml.cs
-using Avalonia;
-using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
using WalletWasabi.Fluent.ViewModels.Dialogs.CreateWallet;

confirm recovery word