From ec68d2a0e67421a433e7b256b2d19e19b0785b8a Mon Sep 17 00:00:00 2001
From: Samuel Adams
Date: Fri, 26 Nov 2021 16:13:41 +0200
Subject: [PATCH] Remove Confirmed state in UI (#3090)
* Remove Confirmed state in UI
Closes #1789.
* Add infobox & improve refund tooltip
* Update BTCPayServer/Views/Invoice/ListInvoices.cshtml
Add @dennisreimann suggestion
Co-authored-by: d11n
* Add "don't show again" button
Adds a "Don't Show Again" button to the infobox. Also a bugfix that was preventing the new status from showing in the invoice details page.
* Add User blob and move invoice status notice to it
Co-authored-by: d11n
Co-authored-by: Kukks
---
BTCPayServer.Data/Data/ApplicationUser.cs | 2 +
.../Migrations/20211125081400_AddUserBlob.cs | 29 ++++++++
.../ApplicationDbContextModelSnapshot.cs | 3 +
.../Controllers/InvoiceController.UI.cs | 11 ++-
BTCPayServer/Controllers/ManageController.cs | 20 ++++++
BTCPayServer/Extensions/UserExtensions.cs | 33 +++++++++
BTCPayServer/Hosting/MigrationStartupTask.cs | 16 +++++
.../InvoicingModels/InvoiceDetailsModel.cs | 4 +-
.../Models/InvoicingModels/InvoicesModel.cs | 4 +-
BTCPayServer/Services/MigrationSettings.cs | 1 +
.../Views/Apps/UpdatePointOfSale.cshtml | 2 +-
BTCPayServer/Views/Invoice/Invoice.cshtml | 21 ++++--
.../Invoice/InvoiceStatusChangePartial.cshtml | 57 +++++++++++++++
.../Views/Invoice/ListInvoices.cshtml | 72 ++++++++++---------
14 files changed, 225 insertions(+), 50 deletions(-)
create mode 100644 BTCPayServer.Data/Migrations/20211125081400_AddUserBlob.cs
create mode 100644 BTCPayServer/Extensions/UserExtensions.cs
create mode 100644 BTCPayServer/Views/Invoice/InvoiceStatusChangePartial.cshtml
diff --git a/BTCPayServer.Data/Data/ApplicationUser.cs b/BTCPayServer.Data/Data/ApplicationUser.cs
index 07342221b..5cd889d56 100644
--- a/BTCPayServer.Data/Data/ApplicationUser.cs
+++ b/BTCPayServer.Data/Data/ApplicationUser.cs
@@ -19,6 +19,8 @@ namespace BTCPayServer.Data
public List Notifications { get; set; }
public List UserStores { get; set; }
public List Fido2Credentials { get; set; }
+
+ public byte[] Blob { get; set; }
public List> UserRoles { get; set; }
diff --git a/BTCPayServer.Data/Migrations/20211125081400_AddUserBlob.cs b/BTCPayServer.Data/Migrations/20211125081400_AddUserBlob.cs
new file mode 100644
index 000000000..dccd38850
--- /dev/null
+++ b/BTCPayServer.Data/Migrations/20211125081400_AddUserBlob.cs
@@ -0,0 +1,29 @@
+using System;
+using BTCPayServer.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+namespace BTCPayServer.Migrations
+{
+ [DbContext(typeof(ApplicationDbContext))]
+ [Migration("20211125081400_AddUserBlob")]
+ public partial class AddUserBlob : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "Blob",
+ table: "AspNetUsers",
+ nullable: true);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "Blob",
+ table: "AspNetUsers");
+ }
+ }
+}
diff --git a/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs b/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs
index 13d9a20af..60f6face0 100644
--- a/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs
+++ b/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs
@@ -105,6 +105,9 @@ namespace BTCPayServer.Migrations
b.Property("AccessFailedCount")
.HasColumnType("INTEGER");
+ b.Property("Blob")
+ .HasColumnType("BLOB");
+
b.Property("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
diff --git a/BTCPayServer/Controllers/InvoiceController.UI.cs b/BTCPayServer/Controllers/InvoiceController.UI.cs
index 39365ea53..baa5cd02e 100644
--- a/BTCPayServer/Controllers/InvoiceController.UI.cs
+++ b/BTCPayServer/Controllers/InvoiceController.UI.cs
@@ -104,7 +104,7 @@ namespace BTCPayServer.Controllers
StoreLink = Url.Action(nameof(StoresController.PaymentMethods), "Stores", new { storeId = store.Id }),
PaymentRequestLink = Url.Action(nameof(PaymentRequestController.ViewPaymentRequest), "PaymentRequest", new { id = invoice.Metadata.PaymentRequestId }),
Id = invoice.Id,
- State = invoiceState.ToString(),
+ State = invoiceState.Status.ToModernStatus().ToString(),
TransactionSpeed = invoice.SpeedPolicy == SpeedPolicy.HighSpeed ? "high" :
invoice.SpeedPolicy == SpeedPolicy.MediumSpeed ? "medium" :
invoice.SpeedPolicy == SpeedPolicy.LowMediumSpeed ? "low-medium" :
@@ -128,7 +128,7 @@ namespace BTCPayServer.Controllers
.Select(c => new Models.StoreViewModels.DeliveryViewModel(c))
.ToList(),
CanMarkInvalid = invoiceState.CanMarkInvalid(),
- CanMarkComplete = invoiceState.CanMarkComplete(),
+ CanMarkSettled = invoiceState.CanMarkComplete(),
};
model.Addresses = invoice.HistoricalAddresses.Select(h =>
new InvoiceDetailsModel.AddressModel
@@ -763,7 +763,7 @@ namespace BTCPayServer.Controllers
RedirectUrl = invoice.RedirectURL?.AbsoluteUri ?? string.Empty,
AmountCurrency = _CurrencyNameTable.DisplayFormatCurrency(invoice.Price, invoice.Currency),
CanMarkInvalid = state.CanMarkInvalid(),
- CanMarkComplete = state.CanMarkComplete(),
+ CanMarkSettled = state.CanMarkComplete(),
Details = InvoicePopulatePayments(invoice),
});
}
@@ -936,10 +936,10 @@ namespace BTCPayServer.Controllers
await _InvoiceRepository.MarkInvoiceStatus(invoiceId, InvoiceStatus.Invalid);
model.StatusString = new InvoiceState("invalid", "marked").ToString();
}
- else if (newState == "complete")
+ else if (newState == "settled")
{
await _InvoiceRepository.MarkInvoiceStatus(invoiceId, InvoiceStatus.Settled);
- model.StatusString = new InvoiceState("complete", "marked").ToString();
+ model.StatusString = new InvoiceState("settled", "marked").ToString();
}
return Json(model);
@@ -999,6 +999,5 @@ namespace BTCPayServer.Controllers
return result;
}
}
-
}
}
diff --git a/BTCPayServer/Controllers/ManageController.cs b/BTCPayServer/Controllers/ManageController.cs
index 44a2eabc4..990383c46 100644
--- a/BTCPayServer/Controllers/ManageController.cs
+++ b/BTCPayServer/Controllers/ManageController.cs
@@ -85,6 +85,26 @@ namespace BTCPayServer.Controllers
return View(model);
}
+ [HttpPost]
+ [ValidateAntiForgeryToken]
+ public async Task DisableShowInvoiceStatusChangeHint()
+ {
+
+ var user = await _userManager.GetUserAsync(User);
+ if (user == null)
+ {
+ throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
+ }
+
+ var blob = user.GetBlob();
+ blob.ShowInvoiceStatusChangeHint = false;
+ if (user.SetBlob(blob))
+ {
+ await _userManager.UpdateAsync(user);
+ }
+ return RedirectToAction(nameof(Index));
+ }
+
[HttpPost]
[ValidateAntiForgeryToken]
public async Task Index(IndexViewModel model)
diff --git a/BTCPayServer/Extensions/UserExtensions.cs b/BTCPayServer/Extensions/UserExtensions.cs
new file mode 100644
index 000000000..aceb8f171
--- /dev/null
+++ b/BTCPayServer/Extensions/UserExtensions.cs
@@ -0,0 +1,33 @@
+using System.Linq;
+using BTCPayServer.Data;
+using BTCPayServer.Services.Invoices;
+using Newtonsoft.Json.Linq;
+
+namespace BTCPayServer
+{
+ public static class UserExtensions
+ {
+ public static UserBlob GetBlob(this ApplicationUser user)
+ {
+ var result = user.Blob == null
+ ? new UserBlob()
+ : JObject.Parse(ZipUtils.Unzip(user.Blob)).ToObject();
+ return result;
+ }
+ public static bool SetBlob(this ApplicationUser user, UserBlob blob)
+ {
+ var newBytes = InvoiceRepository.ToBytes(blob);
+ if (user.Blob != null && newBytes.SequenceEqual(user.Blob))
+ {
+ return false;
+ }
+ user.Blob = newBytes;
+ return true;
+ }
+ }
+
+ public class UserBlob
+ {
+ public bool ShowInvoiceStatusChangeHint { get; set; }
+ }
+}
diff --git a/BTCPayServer/Hosting/MigrationStartupTask.cs b/BTCPayServer/Hosting/MigrationStartupTask.cs
index 37aaed634..6f76fe9fe 100644
--- a/BTCPayServer/Hosting/MigrationStartupTask.cs
+++ b/BTCPayServer/Hosting/MigrationStartupTask.cs
@@ -163,6 +163,12 @@ namespace BTCPayServer.Hosting
settings.MigratePayoutDestinationId = true;
await _Settings.UpdateSetting(settings);
}
+ if (!settings.AddInitialUserBlob)
+ {
+ await AddInitialUserBlob();
+ settings.AddInitialUserBlob = true;
+ await _Settings.UpdateSetting(settings);
+ }
}
catch (Exception ex)
{
@@ -171,6 +177,16 @@ namespace BTCPayServer.Hosting
}
}
+ private async Task AddInitialUserBlob()
+ {
+ await using var ctx = _DBContextFactory.CreateContext();
+ foreach (var user in await ctx.Users.AsQueryable().ToArrayAsync())
+ {
+ user.SetBlob(new UserBlob() { ShowInvoiceStatusChangeHint = true });
+ }
+ await ctx.SaveChangesAsync();
+ }
+
private async Task MigratePayoutDestinationId()
{
await using var ctx = _DBContextFactory.CreateContext();
diff --git a/BTCPayServer/Models/InvoicingModels/InvoiceDetailsModel.cs b/BTCPayServer/Models/InvoicingModels/InvoiceDetailsModel.cs
index a82e90a9a..878827d69 100644
--- a/BTCPayServer/Models/InvoicingModels/InvoiceDetailsModel.cs
+++ b/BTCPayServer/Models/InvoicingModels/InvoiceDetailsModel.cs
@@ -129,8 +129,8 @@ namespace BTCPayServer.Models.InvoicingModels
public bool Archived { get; set; }
public bool CanRefund { get; set; }
public bool ShowCheckout { get; set; }
- public bool CanMarkComplete { get; set; }
+ public bool CanMarkSettled { get; set; }
public bool CanMarkInvalid { get; set; }
- public bool CanMarkStatus => CanMarkComplete || CanMarkInvalid;
+ public bool CanMarkStatus => CanMarkSettled || CanMarkInvalid;
}
}
diff --git a/BTCPayServer/Models/InvoicingModels/InvoicesModel.cs b/BTCPayServer/Models/InvoicingModels/InvoicesModel.cs
index af6b69ea3..35cbbbe70 100644
--- a/BTCPayServer/Models/InvoicingModels/InvoicesModel.cs
+++ b/BTCPayServer/Models/InvoicingModels/InvoicesModel.cs
@@ -20,9 +20,9 @@ namespace BTCPayServer.Models.InvoicingModels
public string InvoiceId { get; set; }
public InvoiceState Status { get; set; }
- public bool CanMarkComplete { get; set; }
+ public bool CanMarkSettled { get; set; }
public bool CanMarkInvalid { get; set; }
- public bool CanMarkStatus => CanMarkComplete || CanMarkInvalid;
+ public bool CanMarkStatus => CanMarkSettled || CanMarkInvalid;
public bool ShowCheckout { get; set; }
public string ExceptionStatus { get; set; }
public string AmountCurrency { get; set; }
diff --git a/BTCPayServer/Services/MigrationSettings.cs b/BTCPayServer/Services/MigrationSettings.cs
index 1b69c735d..38b7b2ecb 100644
--- a/BTCPayServer/Services/MigrationSettings.cs
+++ b/BTCPayServer/Services/MigrationSettings.cs
@@ -27,5 +27,6 @@ namespace BTCPayServer.Services
public int? MigratedInvoiceTextSearchPages { get; set; }
public bool MigrateAppCustomOption { get; set; }
public bool MigratePayoutDestinationId { get; set; }
+ public bool AddInitialUserBlob { get; set; }
}
}
diff --git a/BTCPayServer/Views/Apps/UpdatePointOfSale.cshtml b/BTCPayServer/Views/Apps/UpdatePointOfSale.cshtml
index 520cf0301..f81a979d5 100644
--- a/BTCPayServer/Views/Apps/UpdatePointOfSale.cshtml
+++ b/BTCPayServer/Views/Apps/UpdatePointOfSale.cshtml
@@ -200,7 +200,7 @@
Send a GET request to https://btcpay.example.com/invoices/{invoiceId} with Content-Type: application/json; Authorization: Basic YourLegacyAPIkey", Legacy API key can be created with Access Tokens in Store settings
-
Verify that the orderId is from your backend, that the price is correct and that status is either confirmed or complete
+
Verify that the orderId is from your backend, that the price is correct and that status is settled