From 60cfea9f9408aa648c36cf4dc9e029e2529c90ba Mon Sep 17 00:00:00 2001 From: Nicolas Dorier Date: Tue, 4 Apr 2023 10:45:40 +0900 Subject: [PATCH] Add presets in the checkout appearance (#4756) --- BTCPayServer.Tests/Checkoutv2Tests.cs | 9 +++- BTCPayServer.Tests/Extensions.cs | 6 ++- BTCPayServer/Components/Icon/IconViewModel.cs | 2 + .../Controllers/UIInvoiceController.UI.cs | 2 + .../Controllers/UIStoresController.cs | 4 ++ .../Controllers/UIUserStoresController.cs | 8 +++- BTCPayServer/Data/StoreBlob.cs | 8 ++++ .../Models/InvoicingModels/PaymentModel.cs | 2 + .../CheckoutAppearanceViewModel.cs | 6 +++ .../Services/Stores/StoreRepository.cs | 12 ------ .../BitcoinLikeMethodCheckout-v2.cshtml | 2 +- .../Bitcoin/BitcoinLikeMethodCheckout.cshtml | 2 +- .../LightningLikeMethodCheckout-v2.cshtml | 2 +- .../LightningLikeMethodCheckout.cshtml | 2 +- .../Views/UIInvoice/CheckoutV2.cshtml | 7 ++- .../Views/UIStores/CheckoutAppearance.cshtml | 43 ++++++++++++++++++- .../Views/UIUserStores/CreateStore.cshtml | 23 +++++----- BTCPayServer/wwwroot/main/site.css | 25 ++++++++--- 18 files changed, 123 insertions(+), 42 deletions(-) diff --git a/BTCPayServer.Tests/Checkoutv2Tests.cs b/BTCPayServer.Tests/Checkoutv2Tests.cs index 6013ac1d1..d2123c75f 100644 --- a/BTCPayServer.Tests/Checkoutv2Tests.cs +++ b/BTCPayServer.Tests/Checkoutv2Tests.cs @@ -51,7 +51,14 @@ namespace BTCPayServer.Tests s.Driver.SetCheckbox(By.Id("LNURLStandardInvoiceEnabled"), true); s.Driver.FindElement(By.Id("save")).Click(); Assert.Contains("BTC Lightning settings successfully updated", s.FindAlertMessage().Text); - + + s.GoToStore(StoreNavPages.CheckoutAppearance); + s.Driver.WaitForAndClick(By.Id("Presets")); + s.Driver.WaitForAndClick(By.Id("Presets_InStore")); + Assert.True(s.Driver.SetCheckbox(By.Id("ShowPayInWalletButton"), true)); + s.Driver.FindElement(By.Id("Save")).SendKeys(Keys.Enter); + Assert.Contains("Store successfully updated", s.FindAlertMessage().Text); + // Top up/zero amount invoices var invoiceId = s.CreateInvoice(amount: null); s.GoToInvoiceCheckout(invoiceId); diff --git a/BTCPayServer.Tests/Extensions.cs b/BTCPayServer.Tests/Extensions.cs index ee9540784..e3d4d1e06 100644 --- a/BTCPayServer.Tests/Extensions.cs +++ b/BTCPayServer.Tests/Extensions.cs @@ -199,11 +199,15 @@ retry: return true; } - public static void SetCheckbox(this IWebDriver driver, By selector, bool value) + public static bool SetCheckbox(this IWebDriver driver, By selector, bool value) { var element = driver.FindElement(selector); if (value != element.Selected) + { driver.WaitForAndClick(selector); + return true; + } + return false; } } } diff --git a/BTCPayServer/Components/Icon/IconViewModel.cs b/BTCPayServer/Components/Icon/IconViewModel.cs index c67192fd0..b8de54b3d 100644 --- a/BTCPayServer/Components/Icon/IconViewModel.cs +++ b/BTCPayServer/Components/Icon/IconViewModel.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace BTCPayServer.Components.Icon { public class IconViewModel diff --git a/BTCPayServer/Controllers/UIInvoiceController.UI.cs b/BTCPayServer/Controllers/UIInvoiceController.UI.cs index e43b18e26..67f86730a 100644 --- a/BTCPayServer/Controllers/UIInvoiceController.UI.cs +++ b/BTCPayServer/Controllers/UIInvoiceController.UI.cs @@ -793,6 +793,8 @@ namespace BTCPayServer.Controllers OrderId = invoice.Metadata.OrderId, InvoiceId = invoice.Id, DefaultLang = lang ?? invoice.DefaultLanguage ?? storeBlob.DefaultLang ?? "en", + ShowPayInWalletButton = storeBlob.ShowPayInWalletButton, + ShowStoreHeader = storeBlob.ShowStoreHeader, CustomCSSLink = storeBlob.CustomCSS, CustomLogoLink = storeBlob.CustomLogo, LogoFileId = storeBlob.LogoFileId, diff --git a/BTCPayServer/Controllers/UIStoresController.cs b/BTCPayServer/Controllers/UIStoresController.cs index 21f83c442..07c8b7512 100644 --- a/BTCPayServer/Controllers/UIStoresController.cs +++ b/BTCPayServer/Controllers/UIStoresController.cs @@ -388,6 +388,8 @@ namespace BTCPayServer.Controllers vm.UseNewCheckout = storeBlob.CheckoutType == Client.Models.CheckoutType.V2; vm.CelebratePayment = storeBlob.CelebratePayment; vm.OnChainWithLnInvoiceFallback = storeBlob.OnChainWithLnInvoiceFallback; + vm.ShowPayInWalletButton = storeBlob.ShowPayInWalletButton; + vm.ShowStoreHeader = storeBlob.ShowStoreHeader; vm.LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi; vm.RequiresRefundEmail = storeBlob.RequiresRefundEmail; vm.LazyPaymentMethods = storeBlob.LazyPaymentMethods; @@ -505,6 +507,8 @@ namespace BTCPayServer.Controllers }); } + blob.ShowPayInWalletButton = model.ShowPayInWalletButton; + blob.ShowStoreHeader = model.ShowStoreHeader; blob.CheckoutType = model.UseNewCheckout ? Client.Models.CheckoutType.V2 : Client.Models.CheckoutType.V1; blob.CelebratePayment = model.CelebratePayment; blob.OnChainWithLnInvoiceFallback = model.OnChainWithLnInvoiceFallback; diff --git a/BTCPayServer/Controllers/UIUserStoresController.cs b/BTCPayServer/Controllers/UIUserStoresController.cs index e87cdbe0e..d2520444f 100644 --- a/BTCPayServer/Controllers/UIUserStoresController.cs +++ b/BTCPayServer/Controllers/UIUserStoresController.cs @@ -60,9 +60,13 @@ namespace BTCPayServer.Controllers return View(vm); } - var store = await _repo.CreateStore(GetUserId(), vm.Name, vm.DefaultCurrency, vm.PreferredExchange); + var store = new StoreData { StoreName = vm.Name }; + var blob = store.GetStoreBlob(); + blob.DefaultCurrency = vm.DefaultCurrency; + blob.PreferredExchange = vm.PreferredExchange; + store.SetStoreBlob(blob); + await _repo.CreateStore(GetUserId(), store); CreatedStoreId = store.Id; - TempData[WellKnownTempData.SuccessMessage] = "Store successfully created"; return RedirectToAction(nameof(UIStoresController.Dashboard), "UIStores", new { diff --git a/BTCPayServer/Data/StoreBlob.cs b/BTCPayServer/Data/StoreBlob.cs index 7150cbea2..b2f35ed1d 100644 --- a/BTCPayServer/Data/StoreBlob.cs +++ b/BTCPayServer/Data/StoreBlob.cs @@ -218,6 +218,14 @@ namespace BTCPayServer.Data public string BrandColor { get; set; } public string LogoFileId { get; set; } public string CssFileId { get; set; } + + [DefaultValue(true)] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] + public bool ShowPayInWalletButton { get; set; } = true; + + [DefaultValue(true)] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] + public bool ShowStoreHeader { get; set; } = true; [DefaultValue(true)] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] diff --git a/BTCPayServer/Models/InvoicingModels/PaymentModel.cs b/BTCPayServer/Models/InvoicingModels/PaymentModel.cs index 42149139a..b91558390 100644 --- a/BTCPayServer/Models/InvoicingModels/PaymentModel.cs +++ b/BTCPayServer/Models/InvoicingModels/PaymentModel.cs @@ -29,6 +29,8 @@ namespace BTCPayServer.Models.InvoicingModels public string BrandColor { get; set; } public string HtmlTitle { get; set; } public string DefaultLang { get; set; } + public bool ShowPayInWalletButton { get; set; } + public bool ShowStoreHeader { get; set; } public List AvailableCryptos { get; set; } = new(); public bool IsModal { get; set; } public bool IsUnsetTopUp { get; set; } diff --git a/BTCPayServer/Models/StoreViewModels/CheckoutAppearanceViewModel.cs b/BTCPayServer/Models/StoreViewModels/CheckoutAppearanceViewModel.cs index 674ccb652..59ddbb610 100644 --- a/BTCPayServer/Models/StoreViewModels/CheckoutAppearanceViewModel.cs +++ b/BTCPayServer/Models/StoreViewModels/CheckoutAppearanceViewModel.cs @@ -26,6 +26,12 @@ namespace BTCPayServer.Models.StoreViewModels [Display(Name = "Unify on-chain and lightning payment URL/QR code")] public bool OnChainWithLnInvoiceFallback { get; set; } + [Display(Name = "Show \"Pay in wallet\" button")] + public bool ShowPayInWalletButton { get; set; } + + [Display(Name = "Show the store header")] + public bool ShowStoreHeader { get; set; } + [Display(Name = "Display Lightning payment amounts in Satoshis")] public bool LightningAmountInSatoshi { get; set; } diff --git a/BTCPayServer/Services/Stores/StoreRepository.cs b/BTCPayServer/Services/Stores/StoreRepository.cs index acf7150cd..c96627d39 100644 --- a/BTCPayServer/Services/Stores/StoreRepository.cs +++ b/BTCPayServer/Services/Stores/StoreRepository.cs @@ -189,18 +189,6 @@ namespace BTCPayServer.Services.Stores await ctx.SaveChangesAsync(); } - public async Task CreateStore(string ownerId, string name, string defaultCurrency, string preferredExchange) - { - var store = new StoreData { StoreName = name }; - var blob = store.GetStoreBlob(); - blob.DefaultCurrency = defaultCurrency; - blob.PreferredExchange = preferredExchange; - store.SetStoreBlob(blob); - - await CreateStore(ownerId, store); - return store; - } - public async Task GetWebhooks(string storeId) { using var ctx = _ContextFactory.CreateContext(); diff --git a/BTCPayServer/Views/Shared/Bitcoin/BitcoinLikeMethodCheckout-v2.cshtml b/BTCPayServer/Views/Shared/Bitcoin/BitcoinLikeMethodCheckout-v2.cshtml index 8509b72b6..1fe8e38aa 100644 --- a/BTCPayServer/Views/Shared/Bitcoin/BitcoinLikeMethodCheckout-v2.cshtml +++ b/BTCPayServer/Views/Shared/Bitcoin/BitcoinLikeMethodCheckout-v2.cshtml @@ -28,7 +28,7 @@ - @await Component.InvokeAsync("UiExtensionPoint", new {location = "checkout-v2-bitcoin-post-content", model = Model}) diff --git a/BTCPayServer/Views/Shared/Bitcoin/BitcoinLikeMethodCheckout.cshtml b/BTCPayServer/Views/Shared/Bitcoin/BitcoinLikeMethodCheckout.cshtml index 207ece831..3d5562466 100644 --- a/BTCPayServer/Views/Shared/Bitcoin/BitcoinLikeMethodCheckout.cshtml +++ b/BTCPayServer/Views/Shared/Bitcoin/BitcoinLikeMethodCheckout.cshtml @@ -14,7 +14,7 @@ @await Html.PartialAsync("~/Views/UIInvoice/Checkout-Spinner.cshtml") -
+
{{$t("Open in wallet")}} diff --git a/BTCPayServer/Views/Shared/Lightning/LightningLikeMethodCheckout-v2.cshtml b/BTCPayServer/Views/Shared/Lightning/LightningLikeMethodCheckout-v2.cshtml index e9fc831f0..651e19398 100644 --- a/BTCPayServer/Views/Shared/Lightning/LightningLikeMethodCheckout-v2.cshtml +++ b/BTCPayServer/Views/Shared/Lightning/LightningLikeMethodCheckout-v2.cshtml @@ -18,7 +18,7 @@
- @await Component.InvokeAsync("UiExtensionPoint", new {location = "checkout-v2-lightning-post-content", model = Model})
diff --git a/BTCPayServer/Views/Shared/Lightning/LightningLikeMethodCheckout.cshtml b/BTCPayServer/Views/Shared/Lightning/LightningLikeMethodCheckout.cshtml index 7a3c9b5a4..4dc6cf572 100644 --- a/BTCPayServer/Views/Shared/Lightning/LightningLikeMethodCheckout.cshtml +++ b/BTCPayServer/Views/Shared/Lightning/LightningLikeMethodCheckout.cshtml @@ -26,7 +26,7 @@ @await Html.PartialAsync("~/Views/UIInvoice/Checkout-Spinner.cshtml") -
+
{{$t("Open in wallet")}} diff --git a/BTCPayServer/Views/UIInvoice/CheckoutV2.cshtml b/BTCPayServer/Views/UIInvoice/CheckoutV2.cshtml index fe74b3272..a34189858 100644 --- a/BTCPayServer/Views/UIInvoice/CheckoutV2.cshtml +++ b/BTCPayServer/Views/UIInvoice/CheckoutV2.cshtml @@ -40,8 +40,11 @@
- -
+ @if (Model.ShowStoreHeader) + { + + } +
} -

Checkout

+

+ Checkout + +

+
@@ -90,7 +121,15 @@
+
+ + +
+
+ + +
diff --git a/BTCPayServer/Views/UIUserStores/CreateStore.cshtml b/BTCPayServer/Views/UIUserStores/CreateStore.cshtml index 01ecfe8b3..3fcff7a35 100644 --- a/BTCPayServer/Views/UIUserStores/CreateStore.cshtml +++ b/BTCPayServer/Views/UIUserStores/CreateStore.cshtml @@ -4,18 +4,18 @@ } @section PageFootContent { - + + const exchanges = @Safe.Json(StoreBlob.RecommendedExchanges); + const recommended = document.querySelector("#PreferredExchange option[value='']") + const updateRecommended = currency => { + const source = exchanges[currency] || 'coingecko' + const name = source.charAt(0).toUpperCase() + source.slice(1) + recommended.innerText = `${name} (Recommended)` + } + updateRecommended(@Safe.Json(Model.DefaultCurrency)) + delegate('change', '#DefaultCurrency', e => updateRecommended(e.target.value)) + } @@ -41,6 +41,7 @@
The recommended price source gets chosen based on the default currency.
+
diff --git a/BTCPayServer/wwwroot/main/site.css b/BTCPayServer/wwwroot/main/site.css index dbec77f69..d5e67846d 100644 --- a/BTCPayServer/wwwroot/main/site.css +++ b/BTCPayServer/wwwroot/main/site.css @@ -590,14 +590,21 @@ svg.icon-note { } .btcpay-list-select-item { display: flex; + flex-wrap: wrap; flex: 1 1 45%; align-items: center; - border: 1px solid var(--btcpay-form-border); padding: .75rem var(--btcpay-space-s); + cursor: pointer; +} +label.btcpay-list-select-item { + border: 1px solid var(--btcpay-form-border); background-color: var(--btcpay-form-bg); border-radius: var(--btcpay-border-radius); transition: border-color 0.15s ease-in-out; - cursor: pointer; +} +label.btcpay-list-select-item:hover { + border-color: var(--btcpay-form-border-hover); + background-color: var(--btcpay-form-bg-hover); } @media (max-width: 575px) { .btcpay-list-select-item { @@ -609,13 +616,17 @@ svg.icon-note { height: 1.5rem; margin: 0 var(--btcpay-space-s); } -.btcpay-list-select-item:hover { - border-color: var(--btcpay-form-border-hover); - background-color: var(--btcpay-form-bg-hover); -} -input:checked + .btcpay-list-select-item { +input:checked + label.btcpay-list-select-item { border-color: var(--btcpay-form-border-focus); } +.btcpay-list-select-item .note { + color: var(--btcpay-body-text-muted); + flex-basis: 100%; + margin-left: 2.5rem; +} +.btcpay-list-select-item:active { + background-color: var(--btcpay-form-bg-hover); +} /* Public pages */ .public-page-wrap {