diff --git a/src/Angor/Avalonia/AngorApp/AngorApp.csproj b/src/Angor/Avalonia/AngorApp/AngorApp.csproj index 3b2e725bb..47f8a785a 100644 --- a/src/Angor/Avalonia/AngorApp/AngorApp.csproj +++ b/src/Angor/Avalonia/AngorApp/AngorApp.csproj @@ -18,6 +18,7 @@ + diff --git a/src/Angor/Avalonia/AngorApp/Composition/CompositionRoot.cs b/src/Angor/Avalonia/AngorApp/Composition/CompositionRoot.cs index 4a4d83afb..7d317c53d 100644 --- a/src/Angor/Avalonia/AngorApp/Composition/CompositionRoot.cs +++ b/src/Angor/Avalonia/AngorApp/Composition/CompositionRoot.cs @@ -13,6 +13,8 @@ using AngorApp.UI.Shell; using Microsoft.Extensions.DependencyInjection; using Serilog; +using Zafiro.UI.Navigation; +using Branta.V2.Extensions; namespace AngorApp.Composition; @@ -51,8 +53,14 @@ public static IShellViewModel CreateMainViewModel(Control topLevelView, string p .AddModelServices() .AddViewModels() .AddUIServices(topLevelView, profileContext, applicationStorage); + + services.ConfigureBrantaServices(new Branta.Classes.BrantaClientOptions() { + BaseUrl = System.Diagnostics.Debugger.IsAttached + ? Branta.Enums.BrantaServerBaseUrl.Staging + : Branta.Enums.BrantaServerBaseUrl.Production + }); - services.AddAnnotatedSections(logger); + services.AddNavigator(logger); services.AddSecurityContext(); RegisterWalletServices(services, logger, network); FundingContextServices.Register(services, logger); diff --git a/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/AddressAndAmount/AddressAndAmountView.axaml b/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/AddressAndAmount/AddressAndAmountView.axaml index 6754cbbbb..6daf26a91 100644 --- a/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/AddressAndAmount/AddressAndAmountView.axaml +++ b/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/AddressAndAmount/AddressAndAmountView.axaml @@ -9,8 +9,11 @@ d:DesignWidth="300" x:Class="AngorApp.UI.Flows.SendWalletMoney.AddressAndAmount.AddressAndAmountView" x:DataType="ad:IAddressAndAmountViewModel"> - - + + + + + @@ -25,7 +28,36 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/AddressAndAmount/AddressAndAmountViewModel.cs b/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/AddressAndAmount/AddressAndAmountViewModel.cs index dfc9f67bd..a108bf02a 100644 --- a/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/AddressAndAmount/AddressAndAmountViewModel.cs +++ b/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/AddressAndAmount/AddressAndAmountViewModel.cs @@ -1,3 +1,5 @@ +using Branta.V2.Classes; +using Branta.V2.Models; using ReactiveUI.Validation.Extensions; using ReactiveUI.Validation.Helpers; @@ -7,14 +9,22 @@ public partial class AddressAndAmountViewModel : ReactiveValidationObject, IAddr { [Reactive] private string? address; [Reactive] private long? amount; + [Reactive] private bool brantaLoading = false; + [Reactive] public bool showBrantaVerification = false; + [Reactive] public bool brantaPaymentsFound = false; + [Reactive] public List? brantaPayments = []; [ObservableAsProperty] private IAmountUI? walletBalance; + private readonly BrantaClient _brantaClient; - public AddressAndAmountViewModel(IWallet wallet) + + public AddressAndAmountViewModel(IWallet wallet, BrantaClient brantaClient) { + _brantaClient = brantaClient; + walletBalanceHelper = wallet.WhenAnyValue(w => w.Balance) .ToProperty(this, model => model.WalletBalance); - + this.ValidationRule(x => x.Amount, x => x is null or > 0, _ => "Amount must be greater than zero"); this.ValidationRule(x => x.Amount, x => x is not null, _ => "Please, specify an amount"); var isValidAmount = this.WhenAnyValue(x => x.Amount, x => x.WalletBalance, (amount, balance) => amount is null || amount <= balance.Sats); @@ -22,7 +32,32 @@ public AddressAndAmountViewModel(IWallet wallet) this.ValidationRule(x => x.Address, x => !string.IsNullOrWhiteSpace(x), _ => "Please, specify an address"); this.ValidationRule(x => x.Address, x => string.IsNullOrWhiteSpace(x) || wallet.IsAddressValid(x).IsSuccess, message => wallet.IsAddressValid(message).Error); + + this.WhenAnyValue(x => x.Address) + .Subscribe(async newAddress => { + await OnAddressChangedAsync(newAddress); + }); } public IObservable IsValid => this.IsValid(); + + private async Task OnAddressChangedAsync(string? newAddress) + { + BrantaLoading = true; + + if (string.IsNullOrEmpty(newAddress)) { + BrantaPayments = null; + ShowBrantaVerification = false; + BrantaLoading = false; + return; + } + + var payments = await _brantaClient.GetPaymentsAsync(newAddress); + + BrantaPayments = payments; + BrantaPaymentsFound = payments.Count != 0; + ShowBrantaVerification = true; + + BrantaLoading = false; + } } diff --git a/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/AddressAndAmount/AddressAndAmountViewModelSample.cs b/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/AddressAndAmount/AddressAndAmountViewModelSample.cs index 1faaf3a9e..c641b5283 100644 --- a/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/AddressAndAmount/AddressAndAmountViewModelSample.cs +++ b/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/AddressAndAmount/AddressAndAmountViewModelSample.cs @@ -1,3 +1,5 @@ +using Branta.V2.Models; + namespace AngorApp.UI.Flows.SendWalletMoney.AddressAndAmount; public class AddressAndAmountViewModelSample : IAddressAndAmountViewModel @@ -8,4 +10,8 @@ public class AddressAndAmountViewModelSample : IAddressAndAmountViewModel public long? Amount { get; set; } = 100; public string? Address { get; set; } = "testaddress"; public IAmountUI WalletBalance { get; } = new AmountUI(113000); + public bool ShowBrantaVerification { get; set; } = false; + public bool BrantaLoading { get; set; } = false; + public bool BrantaPaymentsFound { get; set; } = false; + public List? BrantaPayments { get; set; } = []; } \ No newline at end of file diff --git a/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/AddressAndAmount/IAddressAndAmountViewModel.cs b/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/AddressAndAmount/IAddressAndAmountViewModel.cs index 6086c7bbe..e219e0daf 100644 --- a/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/AddressAndAmount/IAddressAndAmountViewModel.cs +++ b/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/AddressAndAmount/IAddressAndAmountViewModel.cs @@ -1,3 +1,5 @@ +using Branta.V2.Models; + namespace AngorApp.UI.Flows.SendWalletMoney.AddressAndAmount; public interface IAddressAndAmountViewModel @@ -5,4 +7,8 @@ public interface IAddressAndAmountViewModel public long? Amount { get; set; } public string? Address { get; set; } public IAmountUI WalletBalance { get; } + public bool ShowBrantaVerification { get; set; } + public bool BrantaLoading { get; set; } + public bool BrantaPaymentsFound { get; set; } + public List? BrantaPayments { get; set; } } \ No newline at end of file diff --git a/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/SendMoneyFlow.cs b/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/SendMoneyFlow.cs index db8ad9de9..b7f751104 100644 --- a/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/SendMoneyFlow.cs +++ b/src/Angor/Avalonia/AngorApp/UI/Flows/SendWalletMoney/SendMoneyFlow.cs @@ -1,6 +1,7 @@ using Angor.Sdk.Wallet.Application; using AngorApp.Model.Contracts.Flows; using AngorApp.UI.Shared.Controls.Common.Success; +using Branta.V2.Classes; using Zafiro.Avalonia.Dialogs.Wizards.Slim; using Zafiro.UI.Wizards.Slim.Builder; using AddressAndAmountViewModel = AngorApp.UI.Flows.SendWalletMoney.AddressAndAmount.AddressAndAmountViewModel; @@ -8,12 +9,12 @@ namespace AngorApp.UI.Flows.SendWalletMoney; -public class SendMoneyFlow(IWalletAppService walletAppService, UIServices uiServices) : ISendMoneyFlow +public class SendMoneyFlow(IWalletAppService walletAppService, UIServices uiServices, BrantaClient brantaClient) : ISendMoneyFlow { public async Task SendMoney(IWallet sourceWallet) { var wizard = WizardBuilder - .StartWith(() => new AddressAndAmountViewModel(sourceWallet), "Amount and address").Next(model => (model.Amount, model.Address)).WhenValid() + .StartWith(() => new AddressAndAmountViewModel(sourceWallet, brantaClient), "Amount and address").Next(model => (model.Amount, model.Address)).WhenValid() .Then(sendData => new TransactionDraftViewModel(sourceWallet.Id, walletAppService, new SendAmount("Test", sendData.Amount.Value, sendData.Address), uiServices), "Summary").NextCommand(model => model.Confirm.Enhance("Confirm")) .Then(_ => new SuccessViewModel("Transaction sent!"), "Transaction sent").NextUnit("Close").Always() .WithCompletionFinalStep(); diff --git a/src/Angor/Avalonia/AngorApp/UI/Shared/Controls/UrlToBitmapConverter.cs b/src/Angor/Avalonia/AngorApp/UI/Shared/Controls/UrlToBitmapConverter.cs new file mode 100644 index 000000000..87eef7564 --- /dev/null +++ b/src/Angor/Avalonia/AngorApp/UI/Shared/Controls/UrlToBitmapConverter.cs @@ -0,0 +1,37 @@ +using Avalonia.Data.Converters; +using Avalonia.Media.Imaging; +using System.Globalization; +using System.Net.Http; + +namespace AngorApp.UI.Shared.Controls; + +public class UrlToBitmapConverter : IValueConverter +{ + private static readonly HttpClient httpClient = new HttpClient(); + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is string url && !string.IsNullOrEmpty(url)) { + try { + // For web URLs + if (url.StartsWith("http://") || url.StartsWith("https://")) { + var data = httpClient.GetByteArrayAsync(url).Result; + using var stream = new System.IO.MemoryStream(data); + return new Bitmap(stream); + } + + // For local resources + return new Bitmap(url); + } + catch { + return null; + } + } + return null; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} diff --git a/src/Angor/Avalonia/Directory.Packages.props b/src/Angor/Avalonia/Directory.Packages.props index 14aedcd61..df47009ef 100644 --- a/src/Angor/Avalonia/Directory.Packages.props +++ b/src/Angor/Avalonia/Directory.Packages.props @@ -15,6 +15,7 @@ +