namespace Duempelkas.App.Services; public enum ToastKind { Info, Success, Error } public sealed record ToastMessage(Guid Id, string Message, ToastKind Kind, string? Details, string? ActionLabel, Func? Action); public sealed class ToastService : IDisposable { private readonly object sync = new(); private readonly Dictionary items = []; public event Action? Changed; public IReadOnlyList Messages { get { lock (sync) return items.Values.Select(x => x.Message).ToArray(); } } public Guid Show(string message, ToastKind kind = ToastKind.Info, string? details = null, string? actionLabel = null, Func? action = null, TimeSpan? duration = null, bool persistent = false) { var id = Guid.NewGuid(); var lifetime = persistent ? (TimeSpan?)null : duration ?? TimeSpan.FromSeconds(kind == ToastKind.Error ? 15 : 9); var toast = new ToastMessage(id, message, kind, details, actionLabel, action); lock (sync) { Timer? timer = lifetime.HasValue ? new Timer(_ => Dismiss(id), null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan) : null; items[id] = (toast, timer, lifetime, DateTimeOffset.UtcNow); if (lifetime.HasValue) timer!.Change(lifetime.Value, Timeout.InfiniteTimeSpan); } Changed?.Invoke(); return id; } public void Pause(Guid id) { lock (sync) { if (!items.TryGetValue(id, out var item) || item.Remaining == null || item.Started == default) return; item.Timer?.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); var left = item.Remaining.Value - (DateTimeOffset.UtcNow - item.Started); items[id] = (item.Message, item.Timer, left > TimeSpan.Zero ? left : TimeSpan.FromMilliseconds(1), default); } } public void Resume(Guid id) { lock (sync) { if (!items.TryGetValue(id, out var item) || item.Remaining == null || item.Started != default) return; items[id] = (item.Message, item.Timer, item.Remaining, DateTimeOffset.UtcNow); item.Timer?.Change(item.Remaining.Value, Timeout.InfiniteTimeSpan); } } public void Dismiss(Guid id) { lock (sync) { if (!items.Remove(id, out var item)) return; item.Timer?.Dispose(); } Changed?.Invoke(); } public void Dispose() { lock (sync) { foreach (var item in items.Values) item.Timer?.Dispose(); items.Clear(); } } }