using Dalamud.Interface; using LightlessSync.LightlessConfiguration; using LightlessSync.LightlessConfiguration.Models; using LightlessSync.Services.Mediator; using LightlessSync.UI; using LightlessSync.UI.Models; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System.Numerics; namespace LightlessSync.Services; public class LightlessNotificationService : DisposableMediatorSubscriberBase, IHostedService { private readonly ILogger _logger; private readonly LightlessConfigService _configService; private readonly DalamudUtilService _dalamudUtilService; private LightlessNotificationUI? _notificationUI; public LightlessNotificationService( ILogger logger, LightlessConfigService configService, DalamudUtilService dalamudUtilService, LightlessMediator mediator) : base(logger, mediator) { _logger = logger; _configService = configService; _dalamudUtilService = dalamudUtilService; } public Task StartAsync(CancellationToken cancellationToken) { return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) { return Task.CompletedTask; } public void SetNotificationUI(LightlessNotificationUI notificationUI) { _notificationUI = notificationUI; } public void ShowNotification(string title, string message, NotificationType type = NotificationType.Info, TimeSpan? duration = null, List? actions = null) { var notification = new LightlessNotification { Title = title, Message = message, Type = type, Duration = duration ?? TimeSpan.FromSeconds(10), Actions = actions ?? new List() }; Mediator.Publish(new LightlessNotificationMessage(notification)); } public void ShowPairRequestNotification(string senderName, string senderId, Action onAccept, Action onDecline) { var notification = new LightlessNotification { Title = "Pair Request Received", Message = $"{senderName} wants to pair with you.", Type = NotificationType.Info, Duration = TimeSpan.FromSeconds(60), Actions = new List { new() { Id = "accept", Label = "Accept", Icon = FontAwesomeIcon.Check, Color = UIColors.Get("LightlessGreen"), IsPrimary = true, OnClick = (n) => { _logger.LogInformation("Pair request accepted"); onAccept(); n.IsDismissed = true; n.IsAnimatingOut = true; } }, new() { Id = "decline", Label = "Decline", Icon = FontAwesomeIcon.Times, Color = UIColors.Get("DimRed"), IsDestructive = true, OnClick = (n) => { _logger.LogInformation("Pair request declined"); onDecline(); n.IsDismissed = true; n.IsAnimatingOut = true; } } } }; Mediator.Publish(new LightlessNotificationMessage(notification)); } public void ShowDownloadCompleteNotification(string fileName, int fileCount, Action? onOpenFolder = null) { var actions = new List(); if (onOpenFolder != null) { actions.Add(new LightlessNotificationAction { Id = "open_folder", Label = "Open Folder", Icon = FontAwesomeIcon.FolderOpen, Color = UIColors.Get("LightlessBlue"), OnClick = (n) => { onOpenFolder(); n.IsDismissed = true; n.IsAnimatingOut = true; } }); } var notification = new LightlessNotification { Title = "Download Complete", Message = fileCount > 1 ? $"Downloaded {fileCount} files successfully." : $"Downloaded {fileName} successfully.", Type = NotificationType.Info, Duration = TimeSpan.FromSeconds(8), Actions = actions }; Mediator.Publish(new LightlessNotificationMessage(notification)); } public void ShowErrorNotification(string title, string message, Exception? exception = null, Action? onRetry = null, Action? onViewLog = null) { var actions = new List(); if (onRetry != null) { actions.Add(new LightlessNotificationAction { Id = "retry", Label = "Retry", Icon = FontAwesomeIcon.Redo, Color = UIColors.Get("LightlessBlue"), OnClick = (n) => { onRetry(); n.IsDismissed = true; n.IsAnimatingOut = true; } }); } if (onViewLog != null) { actions.Add(new LightlessNotificationAction { Id = "view_log", Label = "View Log", Icon = FontAwesomeIcon.FileAlt, Color = UIColors.Get("LightlessYellow"), OnClick = (n) => onViewLog() }); } var notification = new LightlessNotification { Title = title, Message = exception != null ? $"{message}\n\nError: {exception.Message}" : message, Type = NotificationType.Error, Duration = TimeSpan.FromSeconds(15), Actions = actions }; Mediator.Publish(new LightlessNotificationMessage(notification)); } public void ShowPairDownloadNotification(List<(string playerName, float progress, string status)> downloadStatus) { var totalProgress = downloadStatus.Count > 0 ? downloadStatus.Average(x => x.progress) : 0f; var completedCount = downloadStatus.Count(x => x.progress >= 1.0f); var totalCount = downloadStatus.Count; var message = $"Progress: {completedCount}/{totalCount} completed"; if (downloadStatus.Any(x => x.progress < 1.0f)) { var activeDownloads = downloadStatus.Where(x => x.progress < 1.0f).Take(3); message += "\n" + string.Join("\n", activeDownloads.Select(x => { var statusText = x.status switch { "downloading" => $"{x.progress:P0}", "decompressing" => "decompressing", "queued" => "queued", "waiting" => "waiting for slot", _ => x.status }; return $"• {x.playerName}: {statusText}"; })); if (downloadStatus.Count(x => x.progress < 1.0f) > 3) { message += $"\n• ... and {downloadStatus.Count(x => x.progress < 1.0f) - 3} more"; } } var notification = new LightlessNotification { Id = "pair_download_progress", Title = "Downloading Pair Data", Message = message, Type = NotificationType.Info, Duration = TimeSpan.FromMinutes(5), ShowProgress = true, Progress = totalProgress }; Mediator.Publish(new LightlessNotificationMessage(notification)); } public void DismissPairDownloadNotification() { Mediator.Publish(new LightlessNotificationDismissMessage("pair_download_progress")); } }