Rewrite the Notification dropdown with Blazor (#5325)

* Rewrite the Notification dropdown with Blazor

* Test fix

---------

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
This commit is contained in:
Nicolas Dorier
2023-09-18 10:55:05 +09:00
committed by GitHub
parent e694568674
commit 44df8cf0c5
15 changed files with 223 additions and 265 deletions

View File

@@ -4,51 +4,46 @@ using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Components.Notifications;
using BTCPayServer.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;
using Org.BouncyCastle.Crypto.Generators;
namespace BTCPayServer.Services.Notifications
{
public class NotificationManager
{
private readonly ApplicationDbContextFactory _factory;
private readonly UserManager<ApplicationUser> _userManager;
private readonly IMemoryCache _memoryCache;
private readonly EventAggregator _eventAggregator;
private readonly Dictionary<string, INotificationHandler> _handlersByNotificationType;
public NotificationManager(ApplicationDbContextFactory factory, UserManager<ApplicationUser> userManager,
public NotificationManager(ApplicationDbContextFactory factory,
IMemoryCache memoryCache, IEnumerable<INotificationHandler> handlers, EventAggregator eventAggregator)
{
_factory = factory;
_userManager = userManager;
_memoryCache = memoryCache;
_eventAggregator = eventAggregator;
_handlersByNotificationType = handlers.ToDictionary(h => h.NotificationType);
}
private const int _cacheExpiryMs = 5000;
public async Task<NotificationsViewModel> GetSummaryNotifications(ClaimsPrincipal user)
public async Task<(List<NotificationViewModel> Items, int? Count)> GetSummaryNotifications(string userId, bool cachedOnly)
{
var userId = _userManager.GetUserId(user);
var cacheKey = GetNotificationsCacheId(userId);
if (cachedOnly)
return _memoryCache.Get<(List<NotificationViewModel> Items, int? Count)>(cacheKey);
return await _memoryCache.GetOrCreateAsync(cacheKey, async entry =>
{
var resp = await GetNotifications(new NotificationsQuery
var res = await GetNotifications(new NotificationsQuery
{
Seen = false,
Skip = 0,
Take = 5,
UserId = userId
});
entry.SetAbsoluteExpiration(TimeSpan.FromMilliseconds(_cacheExpiryMs));
var res = new NotificationsViewModel { Last5 = resp.Items, UnseenCount = resp.Count.Value };
entry.Value = res;
return res;
});
@@ -67,7 +62,7 @@ namespace BTCPayServer.Services.Notifications
{
return $"notifications-{userId}";
}
public const int MaxUnseen = 100;
public async Task<(List<NotificationViewModel> Items, int? Count)> GetNotifications(NotificationsQuery query)
{
await using var dbContext = _factory.CreateContext();
@@ -80,6 +75,27 @@ namespace BTCPayServer.Services.Notifications
{
// Unseen notifications aren't likely to be too huge, so count should be fast
count = await queryables.withoutPaging.CountAsync();
if (count >= MaxUnseen)
{
// If we have too much unseen notifications, we don't want to show the exact count
// because it would be too long to display, so we just show 99+
// Then cleanup a bit the database by removing the oldest notifications, as it would be expensive to fetch every time
if (count >= MaxUnseen + (MaxUnseen / 2))
{
nextBatch:
var seenToRemove = await queryables.withoutPaging.OrderByDescending(data => data.Created).Skip(MaxUnseen).Take(1000).ToListAsync();
if (seenToRemove.Count > 0)
{
foreach (var seen in seenToRemove)
{
seen.Seen = true;
}
await dbContext.SaveChangesAsync();
goto nextBatch;
}
}
count = MaxUnseen;
}
}
return (Items: items, Count: count);
}