286 lines
11 KiB
C#
286 lines
11 KiB
C#
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;
|
|
namespace LightlessSync.Services;
|
|
public class LightlessNotificationService : DisposableMediatorSubscriberBase, IHostedService
|
|
{
|
|
private readonly ILogger<LightlessNotificationService> _logger;
|
|
private readonly LightlessConfigService _configService;
|
|
private readonly DalamudUtilService _dalamudUtilService;
|
|
private LightlessNotificationUI? _notificationUI;
|
|
public LightlessNotificationService(
|
|
ILogger<LightlessNotificationService> 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<LightlessNotificationAction>? actions = null, uint? soundEffectId = null)
|
|
{
|
|
var notification = new LightlessNotification
|
|
{
|
|
Title = title,
|
|
Message = message,
|
|
Type = type,
|
|
Duration = duration ?? TimeSpan.FromSeconds(10),
|
|
Actions = actions ?? new List<LightlessNotificationAction>(),
|
|
SoundEffectId = soundEffectId ?? NotificationSounds.GetDefaultSound(type)
|
|
};
|
|
|
|
// Play sound effect if specified
|
|
if (notification.SoundEffectId.HasValue)
|
|
{
|
|
PlayNotificationSound(notification.SoundEffectId.Value);
|
|
}
|
|
|
|
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),
|
|
SoundEffectId = NotificationSounds.PairRequest,
|
|
Actions = new List<LightlessNotificationAction>
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Play sound effect
|
|
if (notification.SoundEffectId.HasValue)
|
|
{
|
|
PlayNotificationSound(notification.SoundEffectId.Value);
|
|
}
|
|
|
|
Mediator.Publish(new LightlessNotificationMessage(notification));
|
|
}
|
|
public void ShowDownloadCompleteNotification(string fileName, int fileCount, Action? onOpenFolder = null)
|
|
{
|
|
var actions = new List<LightlessNotificationAction>();
|
|
|
|
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,
|
|
SoundEffectId = NotificationSounds.DownloadComplete
|
|
};
|
|
|
|
// Play sound effect
|
|
if (notification.SoundEffectId.HasValue)
|
|
{
|
|
PlayNotificationSound(notification.SoundEffectId.Value);
|
|
}
|
|
|
|
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<LightlessNotificationAction>();
|
|
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,
|
|
SoundEffectId = NotificationSounds.Error
|
|
};
|
|
|
|
// Play sound effect
|
|
if (notification.SoundEffectId.HasValue)
|
|
{
|
|
PlayNotificationSound(notification.SoundEffectId.Value);
|
|
}
|
|
|
|
Mediator.Publish(new LightlessNotificationMessage(notification));
|
|
}
|
|
public void ShowPairDownloadNotification(List<(string playerName, float progress, string status)> downloadStatus, int queueWaiting = 0)
|
|
{
|
|
// Filter out queue status from user downloads
|
|
var userDownloads = downloadStatus.Where(x => x.playerName != "Pair Queue").ToList();
|
|
|
|
var totalProgress = userDownloads.Count > 0 ? userDownloads.Average(x => x.progress) : 0f;
|
|
var completedCount = userDownloads.Count(x => x.progress >= 1.0f);
|
|
var totalCount = userDownloads.Count;
|
|
|
|
var message = "";
|
|
|
|
// Add queue status at the top if there are waiting items
|
|
if (queueWaiting > 0)
|
|
{
|
|
message = $"Queue: {queueWaiting} waiting";
|
|
}
|
|
|
|
// Add download progress if there are downloads
|
|
if (totalCount > 0)
|
|
{
|
|
var progressMessage = $"Progress: {completedCount}/{totalCount} completed";
|
|
message = string.IsNullOrEmpty(message) ? progressMessage : $"{message}\n{progressMessage}";
|
|
}
|
|
|
|
if (userDownloads.Any(x => x.progress < 1.0f))
|
|
{
|
|
var maxNamesToShow = _configService.Current.MaxConcurrentPairApplications;
|
|
var activeDownloads = userDownloads.Where(x => x.progress < 1.0f).Take(maxNamesToShow);
|
|
var downloadLines = 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}";
|
|
}));
|
|
|
|
message += string.IsNullOrEmpty(message) ? downloadLines : $"\n{downloadLines}";
|
|
}
|
|
|
|
// Check if all downloads are completed
|
|
var allDownloadsCompleted = userDownloads.All(x => x.progress >= 1.0f) && userDownloads.Any();
|
|
|
|
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));
|
|
if (allDownloadsCompleted)
|
|
{
|
|
DismissPairDownloadNotification();
|
|
}
|
|
}
|
|
public void DismissPairDownloadNotification()
|
|
{
|
|
Mediator.Publish(new LightlessNotificationDismissMessage("pair_download_progress"));
|
|
}
|
|
|
|
private void PlayNotificationSound(uint soundEffectId)
|
|
{
|
|
try
|
|
{
|
|
// TODO: Implement proper sound playback
|
|
// The ChatGui.PlaySoundEffect method doesn't exist in the current Dalamud API
|
|
// For now, just log what sound would be played
|
|
_logger.LogDebug("Would play notification sound effect {SoundId}", soundEffectId);
|
|
|
|
// Future implementation options:
|
|
// 1. Use UIModule->PlaySound() with proper unsafe interop
|
|
// 2. Use game's sound system through SigScanner
|
|
// 3. Wait for official Dalamud sound API
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning(ex, "Failed to play notification sound effect {SoundId}", soundEffectId);
|
|
}
|
|
}
|
|
} |