diff --git a/Atomex.Client.Wpf.Installer/Product.wxs b/Atomex.Client.Wpf.Installer/Product.wxs index 1748b7a..62e9b09 100644 --- a/Atomex.Client.Wpf.Installer/Product.wxs +++ b/Atomex.Client.Wpf.Installer/Product.wxs @@ -3,7 +3,7 @@ - + diff --git a/Atomex.Client.Wpf/Atomex.Client.Wpf.csproj b/Atomex.Client.Wpf/Atomex.Client.Wpf.csproj index 2309894..dc49e94 100644 --- a/Atomex.Client.Wpf/Atomex.Client.Wpf.csproj +++ b/Atomex.Client.Wpf/Atomex.Client.Wpf.csproj @@ -369,6 +369,7 @@ + diff --git a/Atomex.Client.Wpf/ViewModels/MessageViewModel.cs b/Atomex.Client.Wpf/ViewModels/MessageViewModel.cs index 43b9838..5d466d1 100644 --- a/Atomex.Client.Wpf/ViewModels/MessageViewModel.cs +++ b/Atomex.Client.Wpf/ViewModels/MessageViewModel.cs @@ -80,14 +80,11 @@ public MessageViewModel( _nextAction = nextAction; } - public static MessageViewModel Error(string text) - { - return Error(text, goBackPages: 1); - } + public static MessageViewModel Error(string text) => + Error(text, goBackPages: 1); - public static MessageViewModel Error(string text, int goBackPages) - { - return new MessageViewModel( + public static MessageViewModel Error(string text, int goBackPages) => + new MessageViewModel( title: Resources.SvError, text: text, backTitle: Resources.SvBack, @@ -98,22 +95,18 @@ public static MessageViewModel Error(string text, int goBackPages) Navigation.Back(); }, nextAction: null); - } - public static MessageViewModel Success(string text, Action nextAction) - { - return new MessageViewModel( + public static MessageViewModel Success(string text, Action nextAction) => + new MessageViewModel( title: Resources.SvSuccess, text: text, backTitle: null, nextTitle: Resources.SvOk, backAction: null, nextAction: nextAction); - } - public static MessageViewModel Message(string title, string text, int goBackPages) - { - return new MessageViewModel( + public static MessageViewModel Message(string title, string text, int goBackPages) => + new MessageViewModel( title: title, text: text, backTitle: Resources.SvBack, @@ -124,11 +117,9 @@ public static MessageViewModel Message(string title, string text, int goBackPage Navigation.Back(); }, nextAction: null); - } - public static MessageViewModel Success(string text, string baseUrl, string id, Action nextAction) - { - return new MessageViewModel( + public static MessageViewModel Success(string text, string baseUrl, string id, Action nextAction) => + new MessageViewModel( title: Resources.SvSuccess, text: text, baseUrl: baseUrl, @@ -137,7 +128,6 @@ public static MessageViewModel Success(string text, string baseUrl, string id, A nextTitle: Resources.SvOk, backAction: null, nextAction: nextAction); - } private ICommand _openTxInExplorerCommand; public ICommand OpenTxInExplorerCommand => _openTxInExplorerCommand ?? (_openTxInExplorerCommand = new RelayCommand((id) => diff --git a/Atomex.Client.Wpf/ViewModels/ReceiveViewModel.cs b/Atomex.Client.Wpf/ViewModels/ReceiveViewModel.cs index da28be6..f624721 100644 --- a/Atomex.Client.Wpf/ViewModels/ReceiveViewModel.cs +++ b/Atomex.Client.Wpf/ViewModels/ReceiveViewModel.cs @@ -7,8 +7,12 @@ using Atomex.Core.Entities; using Atomex.Client.Wpf.Common; using Atomex.Client.Wpf.ViewModels.Abstract; +using Atomex.Client.Wpf.ViewModels.SendViewModels; using Atomex.Common; using QRCoder; +using System; +using System.Windows.Input; +using Serilog; namespace Atomex.Client.Wpf.ViewModels { @@ -16,6 +20,8 @@ public class ReceiveViewModel : BaseViewModel { private const int PixelsPerModule = 20; + private IAtomexApp App { get; } + private List _fromCurrencies; public List FromCurrencies { @@ -31,30 +37,64 @@ public Currency Currency { _currency = value; OnPropertyChanged(nameof(Currency)); - #if DEBUG if (!Env.IsInDesignerMode()) + { #endif - Address = App.AtomexApp.Account + var activeAddresses = App.Account + .GetUnspentAddressesAsync(_currency) + .WaitForResult(); + + var freeAddress = App.Account .GetFreeExternalAddressAsync(_currency) - .WaitForResult() - .Address; + .WaitForResult(); + + FromAddressList = activeAddresses + .Select(wa => new WalletAddressViewModel(wa)) + .ToList() + .AddEx(new WalletAddressViewModel(freeAddress, isFreeAddress: true)); +#if DEBUG + } +#endif + } + } + + private List _fromAddressList; + public List FromAddressList + { + get => _fromAddressList; + private set + { + _fromAddressList = value; + OnPropertyChanged(nameof(FromAddressList)); + + SelectedAddress = GetDefaultAddress(); } } - private string _address; - public string Address + private WalletAddress _selectedAddress; + public WalletAddress SelectedAddress { - get => _address; + get => _selectedAddress; set { - _address = value; - OnPropertyChanged(nameof(Address)); + _selectedAddress = value; + OnPropertyChanged(nameof(SelectedAddress)); + + if (_selectedAddress != null) + CreateQrCodeAsync().FireAndForget(); - CreateQrCodeAsync().FireAndForget(); + Warning = string.Empty; } } + private string _warning; + public string Warning + { + get => _warning; + set { _warning = value; OnPropertyChanged(nameof(Warning)); } + } + public BitmapSource QrCode { get; private set; } public ReceiveViewModel() @@ -72,6 +112,8 @@ public ReceiveViewModel(IAtomexApp app) public ReceiveViewModel(IAtomexApp app, Currency currency) { + App = app ?? throw new ArgumentNullException(nameof(app)); + FromCurrencies = app.Account.Currencies .Where(c => c.IsTransactionsAvailable) .Select(CurrencyViewModelCreator.CreateViewModel) @@ -87,7 +129,7 @@ private async Task CreateQrCodeAsync() await Task.Factory.StartNew(() => { using (var qrGenerator = new QRCodeGenerator()) - using (var qrData = qrGenerator.CreateQrCode(Address, QRCodeGenerator.ECCLevel.Q)) + using (var qrData = qrGenerator.CreateQrCode(_selectedAddress.Address, QRCodeGenerator.ECCLevel.Q)) using (var qrCode = new QRCode(qrData)) qrCodeBitmap = qrCode.GetGraphic(PixelsPerModule); }); @@ -104,6 +146,35 @@ await Application.Current.Dispatcher.InvokeAsync(() => } } + private WalletAddress GetDefaultAddress() + { + if (Currency is Tezos || Currency is Ethereum) + { + var activeAddressViewModel = FromAddressList + .FirstOrDefault(vm => vm.WalletAddress.HasActivity && vm.WalletAddress.AvailableBalance() > 0); + + if (activeAddressViewModel != null) + return activeAddressViewModel.WalletAddress; + } + + return FromAddressList.First(vm => vm.IsFreeAddress).WalletAddress; + } + + private ICommand _copyCommand; + public ICommand CopyCommand => _copyCommand ?? (_copyCommand = new RelayCommand((s) => + { + try + { + Clipboard.SetText(SelectedAddress.Address); + + Warning = "Address successfully copied to clipboard."; + } + catch (Exception e) + { + Log.Error(e, "Copy to clipboard error"); + } + })); + private void DesignerMode() { FromCurrencies = DesignTime.Currencies @@ -111,7 +182,7 @@ private void DesignerMode() .ToList(); Currency = FromCurrencies.First().Currency; - Address = "mzztP8VVJYxV93EUiiYrJUbL55MLx7KLoM"; + // Address = "mzztP8VVJYxV93EUiiYrJUbL55MLx7KLoM"; } } } \ No newline at end of file diff --git a/Atomex.Client.Wpf/ViewModels/SendViewModels/DelegateViewModel.cs b/Atomex.Client.Wpf/ViewModels/SendViewModels/DelegateViewModel.cs index 882cb78..d507aa2 100644 --- a/Atomex.Client.Wpf/ViewModels/SendViewModels/DelegateViewModel.cs +++ b/Atomex.Client.Wpf/ViewModels/SendViewModels/DelegateViewModel.cs @@ -22,17 +22,6 @@ namespace Atomex.Client.Wpf.ViewModels.SendViewModels { - public class WalletAddressViewModel - { - public WalletAddress WalletAddress { get; set; } - - public string Address => WalletAddress.Address; - public decimal AvailableBalance => WalletAddress.AvailableBalance(); - - public WalletAddressViewModel(WalletAddress walletAddress) => - WalletAddress = walletAddress; - } - public class DelegateViewModel : BaseViewModel { private IAtomexApp App { get; } diff --git a/Atomex.Client.Wpf/ViewModels/SendViewModels/WalletAddressViewModel.cs b/Atomex.Client.Wpf/ViewModels/SendViewModels/WalletAddressViewModel.cs new file mode 100644 index 0000000..b8b19e7 --- /dev/null +++ b/Atomex.Client.Wpf/ViewModels/SendViewModels/WalletAddressViewModel.cs @@ -0,0 +1,19 @@ +using Atomex.Core.Entities; + +namespace Atomex.Client.Wpf.ViewModels.SendViewModels +{ + public class WalletAddressViewModel + { + public WalletAddress WalletAddress { get; } + public string Address => WalletAddress.Address; + public decimal AvailableBalance => WalletAddress.AvailableBalance(); + public string CurrencyFormat => WalletAddress.Currency.Format; + public bool IsFreeAddress { get; } + + public WalletAddressViewModel(WalletAddress walletAddress, bool isFreeAddress = false) + { + WalletAddress = walletAddress; + IsFreeAddress = isFreeAddress; + } + } +} \ No newline at end of file diff --git a/Atomex.Client.Wpf/Views/ReceiveView.xaml b/Atomex.Client.Wpf/Views/ReceiveView.xaml index d491cc2..ff5d05d 100644 --- a/Atomex.Client.Wpf/Views/ReceiveView.xaml +++ b/Atomex.Client.Wpf/Views/ReceiveView.xaml @@ -7,7 +7,9 @@ xmlns:helpers="clr-namespace:Atomex.Client.Wpf.Helpers" xmlns:viewModels="clr-namespace:Atomex.Client.Wpf.ViewModels" xmlns:common="clr-namespace:Atomex.Client.Wpf.Common" - mc:Ignorable="d" + xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks" + xmlns:converters="clr-namespace:Atomex.Client.Wpf.Converters" + mc:Ignorable="d" ShowTitleBar="False" Background="Transparent" @@ -17,12 +19,22 @@ UseLayoutRounding="True" common:ChildWindowHelper.IsOpen="True" - d:DesignWidth="600" + d:DesignWidth="630" d:DesignHeight="400" d:DataContext="{d:DesignInstance Type=viewModels:ReceiveViewModel, IsDesignTimeCreatable=True}"> + + + + + + + + + + + Width="630"> - + HorizontalAlignment="Center" + Margin="37 0 0 0"> - + helpers:ComboBoxHelper.CornerRadius="0 20 20 0" + Style="{StaticResource CustomComboBox}"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +