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
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(); s.Driver.FindElement(By.Id("SignTransaction")).Click();
// Back button should lead back to the previous page inside the send wizard // Back button should lead back to the previous page inside the send wizard
var backUrl = s.Driver.FindElement(By.Id("GoBack")).GetAttribute("href"); 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 // Cancel button should lead to the page that referred to the send wizard
var cancelUrl = s.Driver.FindElement(By.Id("CancelWizard")).GetAttribute("href"); var cancelUrl = s.Driver.FindElement(By.Id("CancelWizard")).GetAttribute("href");
Assert.EndsWith(walletTransactionUri.AbsolutePath, cancelUrl); Assert.EndsWith(walletTransactionUri.AbsolutePath, cancelUrl);

View File

@@ -12,6 +12,10 @@ namespace Microsoft.AspNetCore.Mvc
public static class UrlHelperExtensions public static class UrlHelperExtensions
{ {
#nullable enable #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) 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)); => request.GetAbsoluteUriNoPathBase(new Uri(helper.Action(action, controller, values) ?? "", UriKind.Relative));
public static Uri ActionAbsolute(this IUrlHelper helper, HttpRequest request, string? action, string? controller) 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 namespace BTCPayServer.Models.WalletViewModels
{ {
public class SignWithSeedViewModel public class SignWithSeedViewModel : IHasBackAndReturnUrl
{ {
public SigningContextModel SigningContext { get; set; } = new SigningContextModel(); public SigningContextModel SigningContext { get; set; } = new SigningContextModel();

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@ using System;
namespace BTCPayServer.Models.WalletViewModels namespace BTCPayServer.Models.WalletViewModels
{ {
public class WalletSigningOptionsModel public class WalletSigningOptionsModel : IHasBackAndReturnUrl
{ {
public SigningContextModel SigningContext { get; set; } public SigningContextModel SigningContext { get; set; }
public string BackUrl { 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 @using BTCPayServer.Controllers
@model SignWithSeedViewModel @model SignWithSeedViewModel
@{ @{
var walletId = Context.GetRouteValue("walletId").ToString(); var walletId = Context.GetRouteValue("walletId").ToString();
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId }); Model.ReturnUrl ??= Url.WalletTransactions(walletId);
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
Layout = "_LayoutWizard"; Layout = "_LayoutWizard";
ViewData.SetActivePage(WalletsNavPages.Send, StringLocalizer["Sign PSBT"], walletId); ViewData.SetActivePage(WalletsNavPages.Send, StringLocalizer["Sign PSBT"], walletId);
} }
@section Navbar { @section Navbar {
@if (backUrl != null) <partial name="_BackAndReturn" model="Model" />
{
<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>
} }
<header class="text-center"> <header class="text-center">

View File

@@ -3,23 +3,14 @@
@model WalletPSBTViewModel @model WalletPSBTViewModel
@{ @{
var walletId = Context.GetRouteValue("walletId").ToString(); var walletId = Context.GetRouteValue("walletId").ToString();
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId }); Model.ReturnUrl ??= Url.WalletTransactions(walletId);
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
Layout = "_LayoutWizard"; Layout = "_LayoutWizard";
ViewData.SetActivePage(WalletsNavPages.PSBT, StringLocalizer["Decode PSBT"], walletId); ViewData.SetActivePage(WalletsNavPages.PSBT, StringLocalizer["Decode PSBT"], walletId);
Csp.UnsafeEval(); Csp.UnsafeEval();
} }
@section Navbar { @section Navbar {
@if (backUrl != null) <partial name="_BackAndReturn" model="Model" />
{
<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>
} }
@section PageHeadContent { @section PageHeadContent {

View File

@@ -2,22 +2,13 @@
@model WalletPSBTCombineViewModel @model WalletPSBTCombineViewModel
@{ @{
var walletId = Context.GetRouteValue("walletId").ToString(); var walletId = Context.GetRouteValue("walletId").ToString();
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId }); Model.ReturnUrl ??= Url.WalletTransactions(walletId);
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
Layout = "_LayoutWizard"; Layout = "_LayoutWizard";
ViewData.SetActivePage(WalletsNavPages.PSBT, StringLocalizer["Combine PSBT"], walletId); ViewData.SetActivePage(WalletsNavPages.PSBT, StringLocalizer["Combine PSBT"], walletId);
} }
@section Navbar { @section Navbar {
@if (backUrl != null) <partial name="_BackAndReturn" model="Model" />
{
<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>
} }
<header class="text-center"> <header class="text-center">

View File

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

View File

@@ -7,8 +7,7 @@
@model WalletSendModel @model WalletSendModel
@{ @{
var walletId = Context.GetRouteValue("walletId").ToString(); var walletId = Context.GetRouteValue("walletId").ToString();
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId }); Model.ReturnUrl ??= Url.WalletTransactions(walletId);
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
Layout = "_LayoutWizard"; Layout = "_LayoutWizard";
ViewData.SetActivePage(WalletsNavPages.Send, StringLocalizer["Send {0}", Model.CryptoCode], walletId); ViewData.SetActivePage(WalletsNavPages.Send, StringLocalizer["Send {0}", Model.CryptoCode], walletId);
Csp.Add("worker-src", "blob:"); Csp.Add("worker-src", "blob:");
@@ -16,15 +15,7 @@
} }
@section Navbar { @section Navbar {
@if (backUrl != null) <partial name="_BackAndReturn" model="Model" />
{
<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>
} }
@section PageHeadContent @section PageHeadContent

View File

@@ -2,22 +2,13 @@
@model WalletSendVaultModel @model WalletSendVaultModel
@{ @{
var walletId = Context.GetRouteValue("walletId").ToString(); var walletId = Context.GetRouteValue("walletId").ToString();
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId }); Model.ReturnUrl ??= Url.WalletTransactions(walletId);
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
Layout = "_LayoutWizard"; Layout = "_LayoutWizard";
ViewData.SetActivePage(WalletsNavPages.Send, StringLocalizer["Sign the transaction"], walletId); ViewData.SetActivePage(WalletsNavPages.Send, StringLocalizer["Sign the transaction"], walletId);
} }
@section Navbar { @section Navbar {
@if (backUrl != null) <partial name="_BackAndReturn" model="Model" />
{
<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>
} }
<header class="text-center"> <header class="text-center">

View File

@@ -3,22 +3,13 @@
@inject BTCPayNetworkProvider BTCPayNetworkProvider @inject BTCPayNetworkProvider BTCPayNetworkProvider
@{ @{
var walletId = WalletId.Parse(Context.GetRouteValue("walletId").ToString()); var walletId = WalletId.Parse(Context.GetRouteValue("walletId").ToString());
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId }); Model.ReturnUrl ??= Url.WalletTransactions(walletId);
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
Layout = "_LayoutWizard"; Layout = "_LayoutWizard";
ViewData.SetActivePage(WalletsNavPages.Send, StringLocalizer["Sign the transaction"], walletId.ToString()); ViewData.SetActivePage(WalletsNavPages.Send, StringLocalizer["Sign the transaction"], walletId.ToString());
} }
@section Navbar { @section Navbar {
@if (backUrl != null) <partial name="_BackAndReturn" model="Model" />
{
<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>
} }
<header class="text-center"> <header class="text-center">