diff --git a/BTCPayServer/Controllers/NotificationsController.cs b/BTCPayServer/Controllers/NotificationsController.cs index 30150d240..aa68db8be 100644 --- a/BTCPayServer/Controllers/NotificationsController.cs +++ b/BTCPayServer/Controllers/NotificationsController.cs @@ -1,18 +1,13 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Security.Claims; -using System.Text; using System.Threading.Tasks; using BTCPayServer.Data; using BTCPayServer.Filters; -using BTCPayServer.HostedServices; using BTCPayServer.Models.NotificationViewModels; using BTCPayServer.Security; using BTCPayServer.Services; using BTCPayServer.Services.Notifications; using BTCPayServer.Services.Notifications.Blobs; -using Google; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; @@ -21,6 +16,7 @@ namespace BTCPayServer.Controllers { [BitpayAPIConstraint(false)] [Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)] + [Route("[controller]/[action]")] public class NotificationsController : Controller { private readonly BTCPayServerEnvironment _env; @@ -58,7 +54,7 @@ namespace BTCPayServer.Controllers .Where(a => a.ApplicationUserId == userId) .Select(a => _notificationManager.ToViewModel(a)) .ToList(), - Total = _db.Notifications.Where(a => a.ApplicationUserId == userId).Count() + Total = _db.Notifications.Count(a => a.ApplicationUserId == userId) }; return View(model); @@ -81,24 +77,81 @@ namespace BTCPayServer.Controllers var notif = _db.Notifications.Single(a => a.Id == id && a.ApplicationUserId == userId); notif.Seen = !notif.Seen; await _db.SaveChangesAsync(); + _notificationManager.InvalidateNotificationCache(userId); + return RedirectToAction(nameof(Index)); } - return RedirectToAction(nameof(Index)); + return BadRequest(); } + [HttpGet] + public async Task NotificationPassThrough(string id) + { + if (ValidUserClaim(out var userId)) + { + var notif = _db.Notifications.Single(a => a.Id == id && a.ApplicationUserId == userId); + if (!notif.Seen) + { + notif.Seen = !notif.Seen; + await _db.SaveChangesAsync(); + _notificationManager.InvalidateNotificationCache(userId); + } + + var vm = _notificationManager.ToViewModel(notif); + if (string.IsNullOrEmpty(vm.ActionLink)) + { + return RedirectToAction(nameof(Index)); + } + + return Redirect(vm.ActionLink); + } + + return NotFound(); + } + + [HttpPost] public async Task MassAction(string command, string[] selectedItems) { + if (!ValidUserClaim(out var userId)) + { + return NotFound(); + } + + if (command.StartsWith("flip-individual", StringComparison.InvariantCulture)) + { + var id = command.Split(":")[1]; + return await FlipRead(id); + } + if (selectedItems != null) { - if (command == "delete" && ValidUserClaim(out var userId)) + var items = _db.Notifications.Where(a => a.ApplicationUserId == userId && selectedItems.Contains(a.Id)); + switch (command) { - var toRemove = _db.Notifications.Where(a => a.ApplicationUserId == userId && selectedItems.Contains(a.Id)); - _db.Notifications.RemoveRange(toRemove); - await _db.SaveChangesAsync(); + case "delete": + _db.Notifications.RemoveRange(items); - return RedirectToAction(nameof(Index)); + break; + case "mark-seen": + foreach (NotificationData notificationData in items) + { + notificationData.Seen = true; + } + + break; + case "mark-unseen": + foreach (NotificationData notificationData in items) + { + notificationData.Seen = false; + } + + break; } + + await _db.SaveChangesAsync(); + _notificationManager.InvalidateNotificationCache(userId); + return RedirectToAction(nameof(Index)); } return RedirectToAction(nameof(Index)); diff --git a/BTCPayServer/Services/Notifications/NotificationManager.cs b/BTCPayServer/Services/Notifications/NotificationManager.cs index 06764dbe1..b16ddb912 100644 --- a/BTCPayServer/Services/Notifications/NotificationManager.cs +++ b/BTCPayServer/Services/Notifications/NotificationManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Security.Claims; using System.Text; @@ -22,7 +23,8 @@ namespace BTCPayServer.Services.Notifications private readonly Dictionary _handlersByNotificationType; private readonly Dictionary _handlersByBlobType; - public NotificationManager(ApplicationDbContextFactory factory, UserManager userManager, IMemoryCache memoryCache, IEnumerable handlers) + public NotificationManager(ApplicationDbContextFactory factory, UserManager userManager, + IMemoryCache memoryCache, IEnumerable handlers) { _factory = factory; _userManager = userManager; @@ -32,19 +34,31 @@ namespace BTCPayServer.Services.Notifications } private const int _cacheExpiryMs = 5000; + public async Task GetSummaryNotifications(ClaimsPrincipal user) { var userId = _userManager.GetUserId(user); - - if (_memoryCache.TryGetValue(userId, out var obj)) + var cacheKey = GetNotificationsCacheId(userId); + if (_memoryCache.TryGetValue(cacheKey, out var obj)) return obj; var resp = await FetchNotificationsFromDb(userId); - _memoryCache.Set(userId, resp, new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromMilliseconds(_cacheExpiryMs))); + _memoryCache.Set(cacheKey, resp, + new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromMilliseconds(_cacheExpiryMs))); return resp; } + public void InvalidateNotificationCache(string userId) + { + _memoryCache.Remove(GetNotificationsCacheId(userId)); + } + + private static string GetNotificationsCacheId(string userId) + { + return $"notifications-{userId}"; + } + private async Task FetchNotificationsFromDb(string userId) { var resp = new NotificationSummaryViewModel(); @@ -59,10 +73,10 @@ namespace BTCPayServer.Services.Notifications try { resp.Last5 = (await _db.Notifications - .Where(a => a.ApplicationUserId == userId && !a.Seen) - .OrderByDescending(a => a.Created) - .Take(5) - .ToListAsync()) + .Where(a => a.ApplicationUserId == userId && !a.Seen) + .OrderByDescending(a => a.Created) + .Take(5) + .ToListAsync()) .Select(a => ToViewModel(a)) .ToList(); } @@ -90,12 +104,7 @@ namespace BTCPayServer.Services.Notifications { var handler = GetHandler(data.NotificationType); var notification = JsonConvert.DeserializeObject(ZipUtils.Unzip(data.Blob), handler.NotificationBlobType); - var obj = new NotificationViewModel - { - Id = data.Id, - Created = data.Created, - Seen = data.Seen - }; + var obj = new NotificationViewModel {Id = data.Id, Created = data.Created, Seen = data.Seen}; handler.FillViewModel(notification, obj); return obj; } @@ -106,6 +115,7 @@ namespace BTCPayServer.Services.Notifications return h; throw new InvalidOperationException($"No INotificationHandler found for {notificationId}"); } + public INotificationHandler GetHandler(Type blobType) { if (_handlersByBlobType.TryGetValue(blobType, out var h)) diff --git a/BTCPayServer/Services/Notifications/NotificationSender.cs b/BTCPayServer/Services/Notifications/NotificationSender.cs index 42a4df55b..f4520f825 100644 --- a/BTCPayServer/Services/Notifications/NotificationSender.cs +++ b/BTCPayServer/Services/Notifications/NotificationSender.cs @@ -51,6 +51,10 @@ namespace BTCPayServer.Services.Notifications } await db.SaveChangesAsync(); } + foreach (string user in users) + { + _notificationManager.InvalidateNotificationCache(user); + } } private async Task GetUsers(NotificationScope scope) diff --git a/BTCPayServer/Views/Notifications/Index.cshtml b/BTCPayServer/Views/Notifications/Index.cshtml index 1944bd6ab..3c9d9f6d7 100644 --- a/BTCPayServer/Views/Notifications/Index.cshtml +++ b/BTCPayServer/Views/Notifications/Index.cshtml @@ -9,7 +9,7 @@ {
- +
} @@ -29,6 +29,8 @@ Actions @@ -38,45 +40,45 @@
- - - - - - + + + + + + - @foreach (var item in Model.Items) - { - - - - - - - } + @foreach (var item in Model.Items) + { + + + + + + + }
- Date - - - - Message 
+ Date + + + + MessageActions
- - - - @item.Created.ToBrowserDate() - - - @item.Body - - @if (!String.IsNullOrEmpty(item.ActionLink)) - { - Action - } - else - { -   - } -
+ + + + @item.Created.ToBrowserDate() + + + @item.Body + + @if (!String.IsNullOrEmpty(item.ActionLink)) + { + Details + - + } + +
@@ -155,13 +157,39 @@
+