Files
RolemasterDB/src/RolemasterDb.App/Frontend/AppState/RecentTablesState.cs

109 lines
3.0 KiB
C#

using System.Text.Json;
namespace RolemasterDb.App.Frontend.AppState;
public sealed class RecentTablesState(BrowserStorageService browserStorage)
{
private const int MaxItems = 8;
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web);
private bool isInitialized;
public IReadOnlyList<RecentTableEntry> Items { get; private set; } = [];
public event Action? Changed;
public async Task InitializeAsync()
{
if (isInitialized)
{
return;
}
try
{
var storedValue = await browserStorage.GetItemAsync(BrowserStorageKeys.RecentTables);
Items = DeserializeItems(storedValue);
isInitialized = true;
Changed?.Invoke();
}
catch (JsonException)
{
Items = [];
isInitialized = true;
Changed?.Invoke();
}
catch (InvalidOperationException)
{
// JS interop is unavailable during prerender. Retry on the next interactive render.
}
}
public async Task RecordVisitAsync(string slug, string label, string family, int curationPercentage)
{
if (string.IsNullOrWhiteSpace(slug) || string.IsNullOrWhiteSpace(label))
{
return;
}
await InitializeAsync();
var updatedItems = new List<RecentTableEntry>
{
new(
slug.Trim(),
label.Trim(),
family.Trim(),
curationPercentage,
DateTimeOffset.UtcNow)
};
foreach (var item in Items)
{
if (string.Equals(item.Slug, slug, StringComparison.OrdinalIgnoreCase))
{
continue;
}
updatedItems.Add(item);
if (updatedItems.Count == MaxItems)
{
break;
}
}
Items = updatedItems;
await PersistAsync();
Changed?.Invoke();
}
private async Task PersistAsync()
{
var serialized = JsonSerializer.Serialize(Items, SerializerOptions);
await browserStorage.SetItemAsync(BrowserStorageKeys.RecentTables, serialized);
}
private static IReadOnlyList<RecentTableEntry> DeserializeItems(string? storedValue)
{
if (string.IsNullOrWhiteSpace(storedValue))
{
return [];
}
var items = JsonSerializer.Deserialize<List<RecentTableEntry>>(storedValue, SerializerOptions);
if (items is null || items.Count == 0)
{
return [];
}
return items
.Where(item => !string.IsNullOrWhiteSpace(item.Slug) && !string.IsNullOrWhiteSpace(item.Label))
.GroupBy(item => item.Slug, StringComparer.OrdinalIgnoreCase)
.Select(group => group
.OrderByDescending(item => item.ViewedAtUtc)
.First())
.OrderByDescending(item => item.ViewedAtUtc)
.Take(MaxItems)
.ToList();
}
}