From 8c35edb6e837349647f7c10a52ae643f9f9086ca Mon Sep 17 00:00:00 2001 From: d11n Date: Thu, 3 Oct 2024 14:35:01 +0200 Subject: [PATCH] UI: Additional improvements to the User Invitation flow (#6233) * UI: Additional improvements to the User Invitation flow Closes #6224. * Clear invitation token only after the user can sign in Fixes "404 Error on Follow-Up Visits" of #6236. * Minor spacing fix * Update accordion button --- .../TruncateCenter/TruncateCenter.cs | 4 +- .../Controllers/UIAccountController.cs | 27 +++-- BTCPayServer/UserManagerExtensions.cs | 6 + BTCPayServer/Views/Shared/ShowQR.cshtml | 23 ++-- BTCPayServer/Views/UIServer/ListUsers.cshtml | 114 ++++++++++-------- BTCPayServer/Views/UIServer/User.cshtml | 2 +- BTCPayServer/wwwroot/img/icon-sprite.svg | 4 +- .../wwwroot/main/bootstrap/bootstrap.css | 10 ++ 8 files changed, 116 insertions(+), 74 deletions(-) diff --git a/BTCPayServer/Components/TruncateCenter/TruncateCenter.cs b/BTCPayServer/Components/TruncateCenter/TruncateCenter.cs index 934676690..ce35bb995 100644 --- a/BTCPayServer/Components/TruncateCenter/TruncateCenter.cs +++ b/BTCPayServer/Components/TruncateCenter/TruncateCenter.cs @@ -34,10 +34,8 @@ public class TruncateCenter : ViewComponent }; if (!vm.IsVue) { - vm.Start = vm.IsTruncated ? text[..padding] : text; + vm.Start = vm.IsTruncated && !vm.Elastic ? "{text[..padding]}…" : text; vm.End = vm.IsTruncated ? text[^padding..] : string.Empty; - if (!vm.Elastic && vm.IsTruncated) - vm.Start = $"{vm.Start}…"; } return View(vm); } diff --git a/BTCPayServer/Controllers/UIAccountController.cs b/BTCPayServer/Controllers/UIAccountController.cs index f068594a4..1909b6d6f 100644 --- a/BTCPayServer/Controllers/UIAccountController.cs +++ b/BTCPayServer/Controllers/UIAccountController.cs @@ -706,6 +706,7 @@ namespace BTCPayServer.Controllers Severity = StatusMessageModel.StatusSeverity.Success, Message = "Your email has been confirmed." }); + await FinalizeInvitationIfApplicable(user); return RedirectToAction(nameof(Login), new { email = user.Email }); } @@ -812,6 +813,7 @@ namespace BTCPayServer.Controllers Severity = StatusMessageModel.StatusSeverity.Success, Message = hasPassword ? "Password successfully set." : "Account successfully created." }); + if (!hasPassword) await FinalizeInvitationIfApplicable(user); return RedirectToAction(nameof(Login)); } @@ -837,16 +839,6 @@ namespace BTCPayServer.Controllers var requiresEmailConfirmation = user.RequiresEmailConfirmation && !user.EmailConfirmed; var requiresSetPassword = !await _userManager.HasPasswordAsync(user); - - _eventAggregator.Publish(new UserInviteAcceptedEvent - { - User = user, - RequestUri = Request.GetAbsoluteRootUri() - }); - - // unset used token - await _userManager.UnsetInvitationTokenAsync(user.Id); - if (requiresEmailConfirmation) { return await RedirectToConfirmEmail(user); @@ -868,9 +860,22 @@ namespace BTCPayServer.Controllers Message = "Your password has been set by the user who invited you." }); + await FinalizeInvitationIfApplicable(user); return RedirectToAction(nameof(Login), new { email = user.Email }); } - + + private async Task FinalizeInvitationIfApplicable(ApplicationUser user) + { + if (!_userManager.HasInvitationToken(user)) return; + _eventAggregator.Publish(new UserInviteAcceptedEvent + { + User = user, + RequestUri = Request.GetAbsoluteRootUri() + }); + // unset used token + await _userManager.UnsetInvitationTokenAsync(user.Id); + } + private async Task RedirectToConfirmEmail(ApplicationUser user) { var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); diff --git a/BTCPayServer/UserManagerExtensions.cs b/BTCPayServer/UserManagerExtensions.cs index 08be237e1..967e911c4 100644 --- a/BTCPayServer/UserManagerExtensions.cs +++ b/BTCPayServer/UserManagerExtensions.cs @@ -32,6 +32,12 @@ namespace BTCPayServer return await userManager.SetInvitationTokenAsync(userId, null); } + public static bool HasInvitationToken(this UserManager userManager, ApplicationUser user, string? token = null) where TUser : class + { + var blob = user.GetBlob() ?? new UserBlob(); + return token == null ? !string.IsNullOrEmpty(blob.InvitationToken) : blob.InvitationToken == token; + } + private static async Task SetInvitationTokenAsync(this UserManager userManager, string userId, string? token) where TUser : class { var user = await userManager.FindByIdAsync(userId); diff --git a/BTCPayServer/Views/Shared/ShowQR.cshtml b/BTCPayServer/Views/Shared/ShowQR.cshtml index 4702adbba..6829ac185 100644 --- a/BTCPayServer/Views/Shared/ShowQR.cshtml +++ b/BTCPayServer/Views/Shared/ShowQR.cshtml @@ -8,11 +8,19 @@ -