Skip to content

Commit

Permalink
Merge branch 'master' into xaml-formatting-2
Browse files Browse the repository at this point in the history
  • Loading branch information
yahiheb committed Jul 23, 2019
2 parents 44cbf82 + 6da51c7 commit f7a8252
Show file tree
Hide file tree
Showing 20 changed files with 955 additions and 8 deletions.
8 changes: 8 additions & 0 deletions WalletWasabi.Gui/Controls/LockScreen/ILockScreenViewModel.cs
@@ -0,0 +1,8 @@
using System;

namespace WalletWasabi.Gui.Controls.LockScreen
{
public interface ILockScreenViewModel : IDisposable
{
}
}
14 changes: 14 additions & 0 deletions WalletWasabi.Gui/Controls/LockScreen/LockScreen.xaml
@@ -0,0 +1,14 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:lockscreen="clr-namespace:WalletWasabi.Gui.Controls.LockScreen;assembly=WalletWasabi.Gui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
IsHitTestVisible="{Binding IsLocked}">
<UserControl.DataTemplates>
<DataTemplate DataType="{x:Type lockscreen:PinLockScreenViewModel}">
<lockscreen:PinLockScreen />
</DataTemplate>
<DataTemplate DataType="{x:Type lockscreen:SlideLockScreenViewModel}">
<lockscreen:SlideLockScreen />
</DataTemplate>
</UserControl.DataTemplates>
<ContentControl Content="{Binding ActiveLockScreen}" />
</UserControl>
22 changes: 22 additions & 0 deletions WalletWasabi.Gui/Controls/LockScreen/LockScreen.xaml.cs
@@ -0,0 +1,22 @@
using System;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;

namespace WalletWasabi.Gui.Controls.LockScreen
{
public class LockScreen : UserControl
{
public LockScreen() : base()
{
InitializeComponent();
}

private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}
74 changes: 74 additions & 0 deletions WalletWasabi.Gui/Controls/LockScreen/LockScreenViewModel.cs
@@ -0,0 +1,74 @@
using System;
using ReactiveUI;
using System.Reactive.Disposables;
using WalletWasabi.Helpers;
using System.Reactive.Linq;
using WalletWasabi.Gui.ViewModels;

namespace WalletWasabi.Gui.Controls.LockScreen
{
public class LockScreenViewModel : ViewModelBase
{
private CompositeDisposable Disposables { get; }

public Global Global { get; }

public LockScreenViewModel(Global global)
{
Global = Guard.NotNull(nameof(Global), global);
Disposables = new CompositeDisposable();
}

private ILockScreenViewModel _activeLockScreen;

public ILockScreenViewModel ActiveLockScreen
{
get => _activeLockScreen;
set => this.RaiseAndSetIfChanged(ref _activeLockScreen, value);
}

private ObservableAsPropertyHelper<string> _pinHash;
public string PinHash => _pinHash?.Value ?? default;

private bool _isLocked;

public bool IsLocked
{
get => _isLocked;
set => this.RaiseAndSetIfChanged(ref _isLocked, value);
}

public void Initialize()
{
Global.UiConfig.WhenAnyValue(x => x.LockScreenActive)
.ObserveOn(RxApp.MainThreadScheduler)
.BindTo(this, y => y.IsLocked)
.DisposeWith(Disposables);

this.WhenAnyValue(x => x.IsLocked)
.ObserveOn(RxApp.MainThreadScheduler)
.BindTo(Global.UiConfig, y => y.LockScreenActive)
.DisposeWith(Disposables);

_pinHash = Global.UiConfig
.WhenAnyValue(x => x.LockScreenPinHash)
.ObserveOn(RxApp.MainThreadScheduler)
.Do(x => CheckLockScreenType(x))
.ToProperty(this, x => x.PinHash);
}

private void CheckLockScreenType(string currentHash)
{
ActiveLockScreen?.Dispose();

if (currentHash != string.Empty)
{
ActiveLockScreen = new PinLockScreenViewModel(this);
}
else
{
ActiveLockScreen = new SlideLockScreenViewModel(this);
}
}
}
}
68 changes: 68 additions & 0 deletions WalletWasabi.Gui/Controls/LockScreen/PinLockScreen.xaml
@@ -0,0 +1,68 @@
<lockscreen:PinLockScreen xmlns="https://github.com/avaloniaui"
xmlns:controls="clr-namespace:WalletWasabi.Gui.Controls;assembly=WalletWasabi.Gui"
xmlns:lockscreen="clr-namespace:WalletWasabi.Gui.Controls.LockScreen;assembly=WalletWasabi.Gui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
IsLocked="{Binding IsLocked}">
<lockscreen:PinLockScreen.Styles>

<Style Selector="Grid.Shade">
<Setter Property="Opacity" Value="0" />
<Setter Property="TranslateTransform.Y" Value="-100" />
</Style>

<Style Selector="lockscreen|PinLockScreen[IsLocked=true] Grid.Shade">
<Style.Animations>
<Animation FillMode="Both" Duration="0:0:1.5" Easing="QuarticEaseInOut">
<KeyFrame Cue="100%">
<Setter Property="Opacity" Value="1" />
<Setter Property="TranslateTransform.Y" Value="0" />
</KeyFrame>
</Animation>
</Style.Animations>
</Style>

<Style Selector="lockscreen|PinLockScreen[IsLocked=false] Grid.Shade">
<Style.Animations>
<Animation FillMode="Both" Duration="0:0:1.5" Easing="QuarticEaseInOut">
<KeyFrame Cue="100%">
<Setter Property="Opacity" Value="0" />
<Setter Property="TranslateTransform.Y" Value="-100" />
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</lockscreen:PinLockScreen.Styles>
<Grid>
<Grid Classes="Shade" Background="{DynamicResource ThemeBackgroundBrush}">
<controls:GroupBox VerticalAlignment="Center" HorizontalAlignment="Center" TextBlock.FontSize="25" Padding="20" Margin="10">
<DockPanel LastChildFill ="True">
<controls:NoparaPasswordBox x:Name="InputField" Password="{Binding PinInput}" Width="300" DockPanel.Dock="Top" Margin="4 0 4 20" Watermark="PIN" UseFloatingWatermark="True" />
<Grid DockPanel.Dock="Bottom">
<TextBlock Text="Wrong PIN!"
Margin="0,12,0,0" VerticalAlignment="Center" FontSize="20" Classes="warningMessage" IsVisible="{Binding WarningMessageVisible}" />
</Grid>
<Grid RowDefinitions="*,*,*,*" ColumnDefinitions="*,*,*">
<Grid.Styles>
<Style Selector="Button">
<Setter Property="Margin" Value="4" />
<Setter Property="Focusable" Value="False" />
</Style>
</Grid.Styles>
<Button Grid.Row="0" Grid.Column="0" Content="7" Command="{Binding KeyPadCommand}" CommandParameter="7" />
<Button Grid.Row="0" Grid.Column="1" Content="8" Command="{Binding KeyPadCommand}" CommandParameter="8" />
<Button Grid.Row="0" Grid.Column="2" Content="9" Command="{Binding KeyPadCommand}" CommandParameter="9" />
<Button Grid.Row="1" Grid.Column="0" Content="4" Command="{Binding KeyPadCommand}" CommandParameter="4" />
<Button Grid.Row="1" Grid.Column="1" Content="5" Command="{Binding KeyPadCommand}" CommandParameter="5" />
<Button Grid.Row="1" Grid.Column="2" Content="6" Command="{Binding KeyPadCommand}" CommandParameter="6" />
<Button Grid.Row="2" Grid.Column="0" Content="1" Command="{Binding KeyPadCommand}" CommandParameter="1" />
<Button Grid.Row="2" Grid.Column="1" Content="2" Command="{Binding KeyPadCommand}" CommandParameter="2" />
<Button Grid.Row="2" Grid.Column="2" Content="3" Command="{Binding KeyPadCommand}" CommandParameter="3" />
<Button Grid.Row="3" Grid.Column="0" Content="CLEAR" Command="{Binding KeyPadCommand}" CommandParameter="CLEAR" />
<Button Grid.Row="3" Grid.Column="1" Content="0" Command="{Binding KeyPadCommand}" CommandParameter="0" />
<Button Grid.Row="3" Grid.Column="2" Content="BACK" Command="{Binding KeyPadCommand}" CommandParameter="BACK" />
</Grid>
</DockPanel>
</controls:GroupBox>
</Grid>
</Grid>
</lockscreen:PinLockScreen>
61 changes: 61 additions & 0 deletions WalletWasabi.Gui/Controls/LockScreen/PinLockScreen.xaml.cs
@@ -0,0 +1,61 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia;
using Avalonia.Input;
using ReactiveUI;
using System.Reactive.Linq;
using System;
using Avalonia.LogicalTree;

namespace WalletWasabi.Gui.Controls.LockScreen
{
public class PinLockScreen : UserControl
{
public static readonly DirectProperty<PinLockScreen, bool> IsLockedProperty =
AvaloniaProperty.RegisterDirect<PinLockScreen, bool>(nameof(IsLocked),
o => o.IsLocked,
(o, v) => o.IsLocked = v);

private bool _isLocked;

public bool IsLocked
{
get => _isLocked;
set => SetAndRaise(IsLockedProperty, ref _isLocked, value);
}

public PinLockScreen() : base()
{
InitializeComponent();

var inputField = this.FindControl<NoparaPasswordBox>("InputField");

this.WhenAnyValue(x => x.IsLocked)
.Where(x => x)
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(x => inputField.Focus());
}

protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);

// When the control first created on AppStart set the Focus of the password box.
// If you just simply set the Focus without delay it won't work.
Observable
.Interval(TimeSpan.FromSeconds(1))
.Take(1)
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(x =>
{
var inputField = this.FindControl<NoparaPasswordBox>("InputField");
inputField.Focus();
});
}

private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}
113 changes: 113 additions & 0 deletions WalletWasabi.Gui/Controls/LockScreen/PinLockScreenViewModel.cs
@@ -0,0 +1,113 @@
using WalletWasabi.Gui.ViewModels;
using WalletWasabi.Helpers;
using System;
using System.Reactive;
using ReactiveUI;
using System.Reactive.Disposables;
using System.Reactive.Linq;

namespace WalletWasabi.Gui.Controls.LockScreen
{
public class PinLockScreenViewModel : ViewModelBase, ILockScreenViewModel
{
private LockScreenViewModel ParentVM { get; }

private CompositeDisposable Disposables { get; }

public ReactiveCommand<string, Unit> KeyPadCommand { get; }

private ObservableAsPropertyHelper<bool> _isLocked;
public bool IsLocked => _isLocked?.Value ?? false;

private string _pinInput;

public string PinInput
{
get => _pinInput;
set => this.RaiseAndSetIfChanged(ref _pinInput, value);
}

private bool _warningMessageVisible;

public bool WarningMessageVisible
{
get => _warningMessageVisible;
set => this.RaiseAndSetIfChanged(ref _warningMessageVisible, value);
}

public PinLockScreenViewModel(LockScreenViewModel lockScreenViewModel)
{
ParentVM = Guard.NotNull(nameof(lockScreenViewModel), lockScreenViewModel);

Disposables = new CompositeDisposable();

KeyPadCommand = ReactiveCommand.Create<string>((arg) =>
{
if (arg == "BACK")
{
if (PinInput.Length > 0)
{
PinInput = PinInput.Substring(0, PinInput.Length - 1);
WarningMessageVisible = false;
}
}
else if (arg == "CLEAR")
{
PinInput = string.Empty;
WarningMessageVisible = false;
}
else
{
PinInput += arg;
}
});

this.WhenAnyValue(x => x.PinInput)
.Throttle(TimeSpan.FromSeconds(1))
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(x =>
{
if (string.IsNullOrWhiteSpace(x))
{
WarningMessageVisible = false;
}
else if (ParentVM.PinHash == HashHelpers.GenerateSha256Hash(x))
{
WarningMessageVisible = false;
}
else
{
WarningMessageVisible = true;
}
});

this.WhenAnyValue(x => x.PinInput)
.Select(Guard.Correct)
.Where(x => x != string.Empty)
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(x =>
{
if (ParentVM.PinHash == HashHelpers.GenerateSha256Hash(x))
{
ParentVM.IsLocked = false;
}
});

_isLocked = ParentVM
.WhenAnyValue(x => x.IsLocked)
.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.IsLocked)
.DisposeWith(Disposables);

this.WhenAnyValue(x => x.IsLocked)
.Where(x => !x)
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ => PinInput = string.Empty);
}

public void Dispose()
{
Disposables?.Dispose();
}
}
}

0 comments on commit f7a8252

Please sign in to comment.