diff --git a/src/aoWebWallet/Models/ActionParam.cs b/src/aoWebWallet/Models/ActionParam.cs index 6798bc6..822c03f 100644 --- a/src/aoWebWallet/Models/ActionParam.cs +++ b/src/aoWebWallet/Models/ActionParam.cs @@ -1,6 +1,5 @@  -using aoWebWallet.Pages; -using System.Net; +using System.Text; namespace aoWebWallet.Models { @@ -20,6 +19,12 @@ public class AoAction if (Target == null) return "No Target process specified."; + foreach(var input in AllInputs) + { + if (string.IsNullOrEmpty(input.Value)) + return $"Please enter a value for {input.Key}"; + } + return null; } @@ -32,6 +37,114 @@ public class AoAction { return AllWithoutTarget.Select(x => new ArweaveBlazor.Models.Tag { Name = x.Key, Value = x.Value ?? string.Empty }).ToList(); } + + + public string ToQueryString() + { + if (Target == null) + return string.Empty; + + StringBuilder sb = new StringBuilder(); + + sb.Append($"{Target.Key}={Target.Value}&"); + + foreach (var param in this.Filled) + { + sb.Append($"{param.Key}={param.Value}&"); + } + + foreach (var param in this.AllInputs) + { + var args = string.Join(';', param.Args); + if (args.Length > 0) + { + sb.Append($"X-{param.ParamType}={param.Key};{args}&"); + } + else + { + sb.Append($"X-{param.ParamType}={param.Key}&"); + } + } + + return sb.ToString().TrimEnd('&'); + } + + public static AoAction CreateFromQueryString(string qstring) + { + // Parsing query string + var queryStringValues = System.Web.HttpUtility.ParseQueryString(qstring); + + AoAction action = new AoAction(); + + foreach (var key in queryStringValues.AllKeys) + { + if (key == null) + continue; + + var values = queryStringValues.GetValues(key); + if (values == null || !values.Any()) + continue; + + foreach (var val in values) + { + string actionKey = key; + string? actionValue = val.ToString(); + ActionParamType actionParamType = ActionParamType.Filled; + + var actionValueSplit = actionValue.Split(';', StringSplitOptions.RemoveEmptyEntries); + actionValue = actionValueSplit.First(); + List args = actionValueSplit.Skip(1).ToList(); + + if (key.Equals("Target", StringComparison.InvariantCultureIgnoreCase)) + actionParamType = ActionParamType.Target; + if (key.Equals("X-Quantity", StringComparison.InvariantCultureIgnoreCase)) + actionParamType = ActionParamType.Quantity; + if (key.Equals("X-Balance", StringComparison.InvariantCultureIgnoreCase)) + actionParamType = ActionParamType.Balance; + else if (key.Equals("X-Process", StringComparison.InvariantCultureIgnoreCase)) + actionParamType = ActionParamType.Process; + else if (key.Equals("X-Integer", StringComparison.InvariantCultureIgnoreCase)) + actionParamType = ActionParamType.Integer; + else if (key.Equals("X-Input", StringComparison.InvariantCultureIgnoreCase)) + actionParamType = ActionParamType.Input; + + if (actionParamType != ActionParamType.Filled + && actionParamType != ActionParamType.Target) + { + actionKey = actionValue; + actionValue = null; + } + + Console.WriteLine($"Val: {actionValue} args: {args.Count()}"); + + action.Params.Add(new ActionParam + { + Key = actionKey, + Value = actionValue, + Args = args, + ParamType = actionParamType + }); + + } + } + + return action; + } + + public static AoAction CreateForTokenTransaction(string tokenId) + { + return new AoAction + { + Params = new List + { + new ActionParam { Key= "Target", ParamType = ActionParamType.Target, Value= tokenId }, + new ActionParam { Key= "Action", ParamType = ActionParamType.Filled, Value= "Transfer" }, + new ActionParam { Key= "Recipient", ParamType = ActionParamType.Process }, + new ActionParam { Key= "Quantity", ParamType = ActionParamType.Balance, Args = new List { tokenId } } + } + + }; + } } public class ActionParam diff --git a/src/aoWebWallet/Pages/ActionPage.razor b/src/aoWebWallet/Pages/ActionPage.razor index 371a5b7..fc964cf 100644 --- a/src/aoWebWallet/Pages/ActionPage.razor +++ b/src/aoWebWallet/Pages/ActionPage.razor @@ -5,16 +5,16 @@ @inject ISnackbar Snackbar @inject NavigationManager NavigationManager @inject TokenDataService dataService +@inject TransactionService transactionService; +@inject WalletDetailViewModel WalletDetailViewModel @Program.PageTitlePostFix - Action Page + New transaction - TODO: Select a wallet, or create a new wallet - @if (BindingContext.WalletList.Data != null) { if (!BindingContext.WalletList.Data.Any()) @@ -59,6 +59,7 @@ @if (!readOnly) { Preview + @validation } else { @@ -71,12 +72,14 @@ @code { + private string? validation; private string? selectedWallet; private bool readOnly = false; private void Preview() { - readOnly = true; + validation = AoAction.IsValid(); + readOnly = string.IsNullOrEmpty(validation); } private void Cancel() { diff --git a/src/aoWebWallet/Pages/ActionPage.razor.cs b/src/aoWebWallet/Pages/ActionPage.razor.cs index a1bb6b2..03a4344 100644 --- a/src/aoWebWallet/Pages/ActionPage.razor.cs +++ b/src/aoWebWallet/Pages/ActionPage.razor.cs @@ -1,28 +1,23 @@ using aoWebWallet.Models; -using aoWebWallet.Services; using aoWebWallet.ViewModels; using Microsoft.AspNetCore.Components.Routing; -using System.Net; namespace aoWebWallet.Pages { public partial class ActionPage : MvvmComponentBase { - private readonly TransactionService transactionService; - public AoAction AoAction { get; set; } = new(); - public ActionPage(TransactionService transactionService) - { - this.transactionService = transactionService; - } - protected override void OnInitialized() { GetQueryStringValues(); //WatchDataLoaderVM(BindingContext.TokenList); WatchDataLoaderVM(BindingContext.WalletList); + //Auto select wallet + if(!string.IsNullOrEmpty(WalletDetailViewModel.SelectedWallet?.Wallet.Address)) + selectedWallet = WalletDetailViewModel.SelectedWallet?.Wallet.Address; + NavigationManager.LocationChanged += NavigationManager_LocationChanged; base.OnInitialized(); @@ -52,60 +47,7 @@ private async void GetQueryStringValues() var uri = new Uri(NavigationManager.Uri); var query = uri.Query; - // Parsing query string - var queryStringValues = System.Web.HttpUtility.ParseQueryString(query); - - AoAction = new AoAction(); - - foreach (var key in queryStringValues.AllKeys) - { - if (key == null) - continue; - - var values = queryStringValues.GetValues(key); - if (values == null || !values.Any()) - continue; - - foreach(var val in values) - { - string actionKey = key; - string? actionValue = val.ToString(); - ActionParamType actionParamType = ActionParamType.Filled; - - var actionValueSplit = actionValue.Split(';', StringSplitOptions.RemoveEmptyEntries); - actionValue = actionValueSplit.FirstOrDefault(); - List args = actionValueSplit.Skip(1).ToList(); - - if (key.Equals("Target", StringComparison.InvariantCultureIgnoreCase)) - actionParamType = ActionParamType.Target; - if (key.Equals("X-Quantity", StringComparison.InvariantCultureIgnoreCase)) - actionParamType = ActionParamType.Quantity; - if (key.Equals("X-Balance", StringComparison.InvariantCultureIgnoreCase)) - actionParamType = ActionParamType.Balance; - else if (key.Equals("X-Process", StringComparison.InvariantCultureIgnoreCase)) - actionParamType = ActionParamType.Process; - else if (key.Equals("X-Int", StringComparison.InvariantCultureIgnoreCase)) - actionParamType = ActionParamType.Integer; - else if (key.Equals("X-Input", StringComparison.InvariantCultureIgnoreCase)) - actionParamType = ActionParamType.Input; - - if (actionParamType != ActionParamType.Filled - && actionParamType != ActionParamType.Target) - { - actionKey = val; - actionValue = null; - } - - AoAction.Params.Add(new ActionParam - { - Key = actionKey, - Value = actionValue, - Args = args, - ParamType = actionParamType - }); - - } - } + AoAction = AoAction.CreateFromQueryString(query); //Add and load tokens var tokens = AoAction diff --git a/src/aoWebWallet/Pages/WalletDetail.razor b/src/aoWebWallet/Pages/WalletDetail.razor index ef6c05b..afbbf28 100644 --- a/src/aoWebWallet/Pages/WalletDetail.razor +++ b/src/aoWebWallet/Pages/WalletDetail.razor @@ -237,12 +237,19 @@ private void Send(BalanceDataViewModel? balanceDataVM) { - var parameters = new DialogParameters { - { x => x.SelectedBalanceDataVM, balanceDataVM }, - { x => x.SelectedWallet, BindingContext.SelectedWallet } - }; - var options = new DialogOptions { CloseOnEscapeKey = true }; - DialogService.Show("Transfer Token", parameters, options); + if(balanceDataVM?.Token == null) + return; + + var aoAction = AoAction.CreateForTokenTransaction(balanceDataVM.Token.TokenId); + + NavigationManager.NavigateTo($"/action?{aoAction.ToQueryString()}"); + + // var parameters = new DialogParameters { + // { x => x.SelectedBalanceDataVM, balanceDataVM }, + // { x => x.SelectedWallet, BindingContext.SelectedWallet } + // }; + // var options = new DialogOptions { CloseOnEscapeKey = true }; + // DialogService.Show("Transfer Token", parameters, options); } private async Task RefreshBalances() diff --git a/src/aoWebWallet/Shared/ActionEditor.razor b/src/aoWebWallet/Shared/ActionEditor.razor index fa19e30..6eccb07 100644 --- a/src/aoWebWallet/Shared/ActionEditor.razor +++ b/src/aoWebWallet/Shared/ActionEditor.razor @@ -1,17 +1,15 @@ @using aoWebWallet.Models - - Target: @AoAction.Target?.Value - + + Target @AoAction.Target?.Value + - @foreach (var value in AoAction.Filled) + @foreach (var ActionParam in AoAction.Filled) { -

@value.Key = @value.Value | @value.ParamType

- @foreach (var arg in value.Args) - { - @arg - } + + @ActionParam.Key @ActionParam.Value + }
diff --git a/src/aoWebWallet/Shared/Components/ActionInputComponent.razor b/src/aoWebWallet/Shared/Components/ActionInputComponent.razor index dd0f083..4a3d20c 100644 --- a/src/aoWebWallet/Shared/Components/ActionInputComponent.razor +++ b/src/aoWebWallet/Shared/Components/ActionInputComponent.razor @@ -1,9 +1,10 @@ @using aoWebWallet.Models -

@ActionParam.Key = @ActionParam.Value | @ActionParam.ParamType

+@*

@ActionParam.Key = @ActionParam.Value | @ActionParam.ParamType

*@ + @if(ReadOnly) { -

@ActionParam.Key = @ActionParam.Value

+ @ActionParam.Key @ActionParam.Value } else { @@ -20,7 +21,7 @@ else } } - +
@code { diff --git a/src/aoWebWallet/Shared/Components/ActionQuantityComponent.razor b/src/aoWebWallet/Shared/Components/ActionQuantityComponent.razor index 9c5c6e5..8624ad7 100644 --- a/src/aoWebWallet/Shared/Components/ActionQuantityComponent.razor +++ b/src/aoWebWallet/Shared/Components/ActionQuantityComponent.razor @@ -2,11 +2,13 @@ @using aoWebWallet.Models @inject TokenDataService tokenDataService @inject TokenClient tokenClient -

@ActionParam.Key = @ActionParam.Value | @ActionParam.ParamType

+@*

@ActionParam.Key = @ActionParam.Value | @ActionParam.ParamType

*@ + @if(Token == null) { Loading token data... + return; } @if (ActionParam.ParamType == ActionParamType.Balance && string.IsNullOrEmpty(Address)) @@ -17,27 +19,33 @@ @if (ActionParam.ParamType == ActionParamType.Balance && BalanceData == null) { Loading balance... + return; } @if (ReadOnly) { -

@ActionParam.Key = @BalanceHelper.FormatBalance(long.Parse(ActionParam.Value ?? "0"), Token?.TokenData?.Denomination ?? 0)

-} -else -{ - if (ActionParam.ParamType == ActionParamType.Quantity -|| ActionParam.ParamType == ActionParamType.Balance) + @ActionParam.Key @BalanceHelper.FormatBalance(long.Parse(ActionParam.Value ?? "0"), Token?.TokenData?.Denomination ?? 0) @Token?.TokenData?.Ticker + } + else { - + if (ActionParam.ParamType == ActionParamType.Quantity + || ActionParam.ParamType == ActionParamType.Balance) + { + var label = $"{ActionParam.Key} ({Token?.TokenData?.Ticker})"; + + + + @Token?.TokenData?.Ticker + if (ActionParam.ParamType == ActionParamType.Balance) { - Balance available: @BalanceHelper.FormatBalance(BalanceData?.Balance, Token?.TokenData?.Denomination ?? 1) + Balance available: @BalanceHelper.FormatBalance(BalanceData?.Balance, Token?.TokenData?.Denomination ?? 1) @Token?.TokenData?.Ticker } } } - +
@code { @@ -63,9 +71,9 @@ else protected override void OnAfterRender(bool firstRender) { - if (!(mudTextField?.ValidationErrors.Any() ?? false)) + if (!(mudTextField?.ValidationErrors.Any() ?? false) && Token?.TokenData?.Denomination != null) { - mudTextField?.SetText(ActionParam.Value); + mudTextField?.SetText(@BalanceHelper.FormatBalance(long.Parse(ActionParam.Value ?? "0"), Token.TokenData.Denomination.Value)); } base.OnAfterRender(firstRender); diff --git a/src/aoWebWallet/ViewModels/WalletDetailViewModel.cs b/src/aoWebWallet/ViewModels/WalletDetailViewModel.cs index df882c2..dc15de0 100644 --- a/src/aoWebWallet/ViewModels/WalletDetailViewModel.cs +++ b/src/aoWebWallet/ViewModels/WalletDetailViewModel.cs @@ -1,6 +1,7 @@ using aoWebWallet.Models; using aoWebWallet.Services; using ArweaveAO; +using ArweaveBlazor; using CommunityToolkit.Mvvm.ComponentModel; using MudBlazor; using System.Collections.ObjectModel; @@ -19,6 +20,7 @@ public partial class WalletDetailViewModel : ObservableObject private readonly TokenDataService dataService; private readonly TokenClient tokenClient; private readonly StorageService storageService; + private readonly ArweaveService arweaveService; private readonly ISnackbar snackbar; private readonly MemoryDataCache memoryDataCache; @@ -32,6 +34,12 @@ public partial class WalletDetailViewModel : ObservableObject [ObservableProperty] private bool canClaim3; + [ObservableProperty] + public string? activeArConnectAddress; + + [ObservableProperty] + public bool? hasArConnectExtension; + public int? SelectedWalletIndex { get; set; } @@ -50,6 +58,7 @@ public WalletDetailViewModel(MainViewModel mainViewModel, TokenDataService dataService, TokenClient tokenClient, StorageService storageService, + ArweaveService arweaveService, ISnackbar snackbar, MemoryDataCache memoryDataCache) { @@ -58,6 +67,7 @@ public WalletDetailViewModel(MainViewModel mainViewModel, this.dataService = dataService; this.tokenClient = tokenClient; this.storageService = storageService; + this.arweaveService = arweaveService; this.snackbar = snackbar; this.memoryDataCache = memoryDataCache; } @@ -72,13 +82,32 @@ public async Task Initialize(string address) await LoadSelectedWalletProcessData(address); await LoadSelectedWalletOwnerData(address); - mainViewModel.CheckHasArConnectExtension(); + CheckHasArConnectExtension(); SetClaims(); mainViewModel.AddToLog(ActivityLogType.ViewAddress, address); } + public async Task CheckHasArConnectExtension() + { + HasArConnectExtension = await arweaveService.HasArConnectAsync(); + await GetActiveArConnectAddress(); + } + + public async Task GetActiveArConnectAddress() + { + if (HasArConnectExtension.HasValue && HasArConnectExtension.Value) + { + ActiveArConnectAddress = await arweaveService.GetActiveAddress(); + + if (this.SelectedWallet != null) + { + this.SelectedWallet.IsConnected = SelectedWallet.Wallet.Address == ActiveArConnectAddress; + } + } + } + public async Task RefreshTokenTransferList() { if (selectedAddress != null) @@ -153,6 +182,11 @@ private async Task SelectWallet(string? address) this.LoadBalanceDataList(address); this.LoadTokenTransferList(address); + if (this.SelectedWallet != null) + { + this.SelectedWallet.IsConnected = SelectedWallet.Wallet.Address == ActiveArConnectAddress; + } + } else {