namespace decePubClient.Services;
public enum ToastSeverity
{
Info,
Success,
Warning,
Error
}
public sealed record ToastMessage(Guid Id, ToastSeverity Severity, string Text);
///
/// The app's snackbar: components add messages (usually through the SUtility extensions AddResponseError,
/// AddResponseSuccess, AddWarning, AddException) and Components/ToastHost.razor, placed once in MainLayout, shows them.
/// A message already on screen isn't added twice, and at most MaxMessages stay, oldest dropped first.
///
public class ToastService
{
const int MaxMessages = 12;
readonly List messages = [];
public event Action Changed;
public IReadOnlyList Messages => messages;
/// Shows a message; with closeAfter it closes itself, otherwise it stays until the user closes it.
public void Add(ToastSeverity severity, string text, TimeSpan? closeAfter = null)
{
if (string.IsNullOrWhiteSpace(text) || messages.Any(m => m.Severity == severity && m.Text == text))
return;
var message = new ToastMessage(Guid.NewGuid(), severity, text);
messages.Add(message);
if (messages.Count > MaxMessages)
messages.RemoveAt(0);
Changed?.Invoke();
if (closeAfter is { } delay)
_ = CloseLater(message.Id, delay);
}
public void Close(Guid id)
{
if (messages.RemoveAll(m => m.Id == id) > 0)
Changed?.Invoke();
}
async Task CloseLater(Guid id, TimeSpan delay, CancellationToken cancellationToken = default)
{
await Task.Delay(delay, cancellationToken);
Close(id);
}
}