Refactor: Remove cshtml duplication for back/url buttons in wizards

This commit is contained in:
nicolas.dorier 2025-02-07 16:13:44 +09:00
parent 4fbcd89bb6
commit c37584328b
No known key found for this signature in database
GPG key ID: 6618763EF09186FE
17 changed files with 74 additions and 87 deletions

View file

@ -1960,7 +1960,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("SignTransaction")).Click();
// Back button should lead back to the previous page inside the send wizard
var backUrl = s.Driver.FindElement(By.Id("GoBack")).GetAttribute("href");
Assert.EndsWith($"/send?returnUrl={walletTransactionUri.AbsolutePath}", backUrl);
Assert.EndsWith($"/send?returnUrl={Uri.EscapeDataString(walletTransactionUri.AbsolutePath)}", backUrl);
// Cancel button should lead to the page that referred to the send wizard
var cancelUrl = s.Driver.FindElement(By.Id("CancelWizard")).GetAttribute("href");
Assert.EndsWith(walletTransactionUri.AbsolutePath, cancelUrl);

View file

@ -12,6 +12,10 @@ namespace Microsoft.AspNetCore.Mvc
public static class UrlHelperExtensions
{
#nullable enable
public static string? WalletSend(this IUrlHelper helper, WalletId walletId) => helper.Action(nameof(UIWalletsController.WalletSend), new { walletId });
public static string? WalletTransactions(this IUrlHelper helper, string walletId) => WalletTransactions(helper, WalletId.Parse(walletId));
public static string? WalletTransactions(this IUrlHelper helper, WalletId walletId)
=> helper.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
public static Uri ActionAbsolute(this IUrlHelper helper, HttpRequest request, string? action, string? controller, object? values)
=> request.GetAbsoluteUriNoPathBase(new Uri(helper.Action(action, controller, values) ?? "", UriKind.Relative));
public static Uri ActionAbsolute(this IUrlHelper helper, HttpRequest request, string? action, string? controller)

View file

@ -0,0 +1,25 @@
#nullable enable
using System;
using BTCPayServer.Controllers;
namespace BTCPayServer.Models
{
public interface IHasBackAndReturnUrl
{
string? BackUrl { get; set; }
string? ReturnUrl { get; set; }
(string? backUrl, string? returnUrl) NormalizeBackAndReturnUrl()
{
var backUrl = BackUrl;
if (backUrl is not null && ReturnUrl is not null)
{
var queryParam = $"returnUrl={Uri.EscapeDataString(ReturnUrl)}";
if (backUrl.Contains('?'))
backUrl = $"{backUrl}&{queryParam}";
else
backUrl = $"{backUrl}?{queryParam}";
}
return (backUrl, ReturnUrl);
}
}
}

View file

@ -4,7 +4,7 @@ using NBitcoin;
namespace BTCPayServer.Models.WalletViewModels
{
public class SignWithSeedViewModel
public class SignWithSeedViewModel : IHasBackAndReturnUrl
{
public SigningContextModel SigningContext { get; set; } = new SigningContextModel();

View file

@ -7,7 +7,7 @@ using NBitcoin;
namespace BTCPayServer.Models.WalletViewModels
{
public class WalletPSBTCombineViewModel
public class WalletPSBTCombineViewModel : IHasBackAndReturnUrl
{
public string OtherPSBT { get; set; }
[Display(Name = "PSBT to combine with…")]

View file

@ -5,7 +5,7 @@ using NBitcoin;
namespace BTCPayServer.Models.WalletViewModels
{
public class WalletPSBTReadyViewModel
public class WalletPSBTReadyViewModel : IHasBackAndReturnUrl
{
public SigningContextModel SigningContext { get; set; } = new SigningContextModel();
public string SigningKey { get; set; }
@ -27,6 +27,12 @@ namespace BTCPayServer.Models.WalletViewModels
public string BalanceChange { get; set; }
public IEnumerable<TransactionTagModel> Labels { get; set; } = new List<TransactionTagModel>();
}
public class AmountViewModel
{
public bool Positive { get; set; }
public string BalanceChange { get; set; }
}
public AmountViewModel ReplacementBalanceChange { get; set; }
public bool HasErrors => Inputs.Count == 0 || Inputs.Any(i => !string.IsNullOrEmpty(i.Error));
public string BalanceChange { get; set; }
public bool CanCalculateBalance { get; set; }

View file

@ -5,7 +5,7 @@ using BTCPayServer.Services.Labels;
namespace BTCPayServer.Models.WalletViewModels
{
public class WalletSendModel
public class WalletSendModel : IHasBackAndReturnUrl
{
public enum ThreeStateBool
{

View file

@ -1,6 +1,6 @@
namespace BTCPayServer.Models.WalletViewModels
{
public class WalletSendVaultModel
public class WalletSendVaultModel : IHasBackAndReturnUrl
{
public string WalletId { get; set; }
public string WebsocketPath { get; set; }

View file

@ -2,7 +2,7 @@ using System;
namespace BTCPayServer.Models.WalletViewModels
{
public class WalletSigningOptionsModel
public class WalletSigningOptionsModel : IHasBackAndReturnUrl
{
public SigningContextModel SigningContext { get; set; }
public string BackUrl { get; set; }

View file

@ -0,0 +1,15 @@
@model BTCPayServer.Models.IHasBackAndReturnUrl
@{
(var backUrl, var cancelUrl) = this.Model.NormalizeBackAndReturnUrl();
cancelUrl ??= Context.Request.Query["returnUrl"].ToString();
}
@if (backUrl != null)
{
<a href="@Url.EnsureLocal(backUrl, Context.Request)" id="GoBack">
<vc:icon symbol="back" />
</a>
}
<a href="@Url.EnsureLocal(cancelUrl, Context.Request)" id="CancelWizard" class="cancel">
<vc:icon symbol="cross" />
</a>

View file

@ -1,23 +1,14 @@
@using BTCPayServer.Controllers
@model SignWithSeedViewModel
@{
var walletId = Context.GetRouteValue("walletId").ToString();
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
var walletId = Context.GetRouteValue("walletId").ToString();
Model.ReturnUrl ??= Url.WalletTransactions(walletId);
Layout = "_LayoutWizard";
ViewData.SetActivePage(WalletsNavPages.Send, StringLocalizer["Sign PSBT"], walletId);
}
@section Navbar {
@if (backUrl != null)
{
<a href="@Url.EnsureLocal(backUrl, Context.Request)" id="GoBack">
<vc:icon symbol="back" />
</a>
}
<a href="@Url.EnsureLocal(cancelUrl, Context.Request)" id="CancelWizard" class="cancel">
<vc:icon symbol="cross" />
</a>
<partial name="_BackAndReturn" model="Model" />
}
<header class="text-center">

View file

@ -3,23 +3,14 @@
@model WalletPSBTViewModel
@{
var walletId = Context.GetRouteValue("walletId").ToString();
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
Model.ReturnUrl ??= Url.WalletTransactions(walletId);
Layout = "_LayoutWizard";
ViewData.SetActivePage(WalletsNavPages.PSBT, StringLocalizer["Decode PSBT"], walletId);
Csp.UnsafeEval();
}
@section Navbar {
@if (backUrl != null)
{
<a href="@Url.EnsureLocal(backUrl, Context.Request)" id="GoBack">
<vc:icon symbol="back" />
</a>
}
<a href="@Url.EnsureLocal(cancelUrl, Context.Request)" id="CancelWizard" class="cancel">
<vc:icon symbol="cross" />
</a>
<partial name="_BackAndReturn" model="Model" />
}
@section PageHeadContent {

View file

@ -2,22 +2,13 @@
@model WalletPSBTCombineViewModel
@{
var walletId = Context.GetRouteValue("walletId").ToString();
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
Model.ReturnUrl ??= Url.WalletTransactions(walletId);
Layout = "_LayoutWizard";
ViewData.SetActivePage(WalletsNavPages.PSBT, StringLocalizer["Combine PSBT"], walletId);
}
@section Navbar {
@if (backUrl != null)
{
<a href="@Url.EnsureLocal(backUrl, Context.Request)" id="GoBack">
<vc:icon symbol="back" />
</a>
}
<a href="@Url.EnsureLocal(cancelUrl, Context.Request)" id="CancelWizard" class="cancel">
<vc:icon symbol="cross" />
</a>
<partial name="_BackAndReturn" model="Model" />
}
<header class="text-center">

View file

@ -2,10 +2,9 @@
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
@model WalletPSBTViewModel
@{
var walletId = Context.GetRouteValue("walletId").ToString();
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new {walletId});
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
var isReady = !Model.HasErrors;
var walletId = Context.GetRouteValue("walletId").ToString();
Model.ReturnUrl ??= Url.WalletTransactions(walletId);
var isReady = !Model.HasErrors;
var isSignable = !isReady;
var needsExport = !isSignable && !isReady;
Layout = "_LayoutWizard";
@ -78,15 +77,7 @@
}
@section Navbar {
@if (backUrl != null)
{
<a href="@Url.EnsureLocal(backUrl, Context.Request)" id="GoBack">
<vc:icon symbol="back" />
</a>
}
<a href="@Url.EnsureLocal(cancelUrl, Context.Request)" id="CancelWizard" class="cancel">
<vc:icon symbol="cross" />
</a>
<partial name="_BackAndReturn" model="Model" />
}
<header class="text-center mb-3">

View file

@ -7,8 +7,7 @@
@model WalletSendModel
@{
var walletId = Context.GetRouteValue("walletId").ToString();
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
Model.ReturnUrl ??= Url.WalletTransactions(walletId);
Layout = "_LayoutWizard";
ViewData.SetActivePage(WalletsNavPages.Send, StringLocalizer["Send {0}", Model.CryptoCode], walletId);
Csp.Add("worker-src", "blob:");
@ -16,15 +15,7 @@
}
@section Navbar {
@if (backUrl != null)
{
<a href="@Url.EnsureLocal(backUrl, Context.Request)" id="GoBack">
<vc:icon symbol="back" />
</a>
}
<a href="@Url.EnsureLocal(cancelUrl, Context.Request)" id="CancelWizard" class="cancel">
<vc:icon symbol="cross" />
</a>
<partial name="_BackAndReturn" model="Model" />
}
@section PageHeadContent

View file

@ -2,22 +2,13 @@
@model WalletSendVaultModel
@{
var walletId = Context.GetRouteValue("walletId").ToString();
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
Model.ReturnUrl ??= Url.WalletTransactions(walletId);
Layout = "_LayoutWizard";
ViewData.SetActivePage(WalletsNavPages.Send, StringLocalizer["Sign the transaction"], walletId);
}
@section Navbar {
@if (backUrl != null)
{
<a href="@Url.EnsureLocal(backUrl, Context.Request)" id="GoBack">
<vc:icon symbol="back" />
</a>
}
<a href="@Url.EnsureLocal(cancelUrl, Context.Request)" id="CancelWizard" class="cancel">
<vc:icon symbol="cross" />
</a>
<partial name="_BackAndReturn" model="Model" />
}
<header class="text-center">

View file

@ -3,22 +3,13 @@
@inject BTCPayNetworkProvider BTCPayNetworkProvider
@{
var walletId = WalletId.Parse(Context.GetRouteValue("walletId").ToString());
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
Model.ReturnUrl ??= Url.WalletTransactions(walletId);
Layout = "_LayoutWizard";
ViewData.SetActivePage(WalletsNavPages.Send, StringLocalizer["Sign the transaction"], walletId.ToString());
}
@section Navbar {
@if (backUrl != null)
{
<a href="@Url.EnsureLocal(backUrl, Context.Request)" id="GoBack">
<vc:icon symbol="back" />
</a>
}
<a href="@Url.EnsureLocal(cancelUrl, Context.Request)" id="CancelWizard" class="cancel">
<vc:icon symbol="cross" />
</a>
<partial name="_BackAndReturn" model="Model" />
}
<header class="text-center">