notifications refactor with duplication bugfix
This commit is contained in:
@@ -25,7 +25,7 @@ public sealed class PairManager : DisposableMediatorSubscriberBase
|
|||||||
private readonly LightlessConfigService _configurationService;
|
private readonly LightlessConfigService _configurationService;
|
||||||
private readonly IContextMenu _dalamudContextMenu;
|
private readonly IContextMenu _dalamudContextMenu;
|
||||||
private readonly PairFactory _pairFactory;
|
private readonly PairFactory _pairFactory;
|
||||||
private readonly LightlessNotificationService _lightlessNotificationService;
|
private readonly NotificationService _lightlessNotificationService;
|
||||||
private Lazy<List<Pair>> _directPairsInternal;
|
private Lazy<List<Pair>> _directPairsInternal;
|
||||||
private Lazy<Dictionary<GroupFullInfoDto, List<Pair>>> _groupPairsInternal;
|
private Lazy<Dictionary<GroupFullInfoDto, List<Pair>>> _groupPairsInternal;
|
||||||
private Lazy<Dictionary<Pair, List<GroupFullInfoDto>>> _pairsWithGroupsInternal;
|
private Lazy<Dictionary<Pair, List<GroupFullInfoDto>>> _pairsWithGroupsInternal;
|
||||||
@@ -37,7 +37,7 @@ public sealed class PairManager : DisposableMediatorSubscriberBase
|
|||||||
public PairManager(ILogger<PairManager> logger, PairFactory pairFactory,
|
public PairManager(ILogger<PairManager> logger, PairFactory pairFactory,
|
||||||
LightlessConfigService configurationService, LightlessMediator mediator,
|
LightlessConfigService configurationService, LightlessMediator mediator,
|
||||||
IContextMenu dalamudContextMenu, PairProcessingLimiter pairProcessingLimiter,
|
IContextMenu dalamudContextMenu, PairProcessingLimiter pairProcessingLimiter,
|
||||||
LightlessNotificationService lightlessNotificationService) : base(logger, mediator)
|
NotificationService lightlessNotificationService) : base(logger, mediator)
|
||||||
{
|
{
|
||||||
_pairFactory = pairFactory;
|
_pairFactory = pairFactory;
|
||||||
_configurationService = configurationService;
|
_configurationService = configurationService;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
using Dalamud.Game.ClientState.Objects;
|
using Dalamud.Game.ClientState.Objects;
|
||||||
using Dalamud.Interface.ImGuiFileDialog;
|
using Dalamud.Interface.ImGuiFileDialog;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
@@ -145,7 +145,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddSingleton((s) => new DtrEntry(s.GetRequiredService<ILogger<DtrEntry>>(), dtrBar, s.GetRequiredService<LightlessConfigService>(),
|
collection.AddSingleton((s) => new DtrEntry(s.GetRequiredService<ILogger<DtrEntry>>(), dtrBar, s.GetRequiredService<LightlessConfigService>(),
|
||||||
s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<PairManager>(), s.GetRequiredService<ApiController>(), s.GetRequiredService<ServerConfigurationManager>()));
|
s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<PairManager>(), s.GetRequiredService<ApiController>(), s.GetRequiredService<ServerConfigurationManager>()));
|
||||||
collection.AddSingleton(s => new PairManager(s.GetRequiredService<ILogger<PairManager>>(), s.GetRequiredService<PairFactory>(),
|
collection.AddSingleton(s => new PairManager(s.GetRequiredService<ILogger<PairManager>>(), s.GetRequiredService<PairFactory>(),
|
||||||
s.GetRequiredService<LightlessConfigService>(), s.GetRequiredService<LightlessMediator>(), contextMenu, s.GetRequiredService<PairProcessingLimiter>(), s.GetRequiredService<LightlessNotificationService>()));
|
s.GetRequiredService<LightlessConfigService>(), s.GetRequiredService<LightlessMediator>(), contextMenu, s.GetRequiredService<PairProcessingLimiter>(), s.GetRequiredService<NotificationService>()));
|
||||||
collection.AddSingleton<RedrawManager>();
|
collection.AddSingleton<RedrawManager>();
|
||||||
collection.AddSingleton<BroadcastService>();
|
collection.AddSingleton<BroadcastService>();
|
||||||
collection.AddSingleton(addonLifecycle);
|
collection.AddSingleton(addonLifecycle);
|
||||||
@@ -172,16 +172,14 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<IpcCallerPenumbra>(), s.GetRequiredService<IpcCallerGlamourer>(),
|
s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<IpcCallerPenumbra>(), s.GetRequiredService<IpcCallerGlamourer>(),
|
||||||
s.GetRequiredService<IpcCallerCustomize>(), s.GetRequiredService<IpcCallerHeels>(), s.GetRequiredService<IpcCallerHonorific>(),
|
s.GetRequiredService<IpcCallerCustomize>(), s.GetRequiredService<IpcCallerHeels>(), s.GetRequiredService<IpcCallerHonorific>(),
|
||||||
s.GetRequiredService<IpcCallerMoodles>(), s.GetRequiredService<IpcCallerPetNames>(), s.GetRequiredService<IpcCallerBrio>()));
|
s.GetRequiredService<IpcCallerMoodles>(), s.GetRequiredService<IpcCallerPetNames>(), s.GetRequiredService<IpcCallerBrio>()));
|
||||||
collection.AddSingleton((s) => new NotificationService(s.GetRequiredService<ILogger<NotificationService>>(),
|
collection.AddSingleton((s) => new NotificationService(
|
||||||
s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<DalamudUtilService>(),
|
s.GetRequiredService<ILogger<NotificationService>>(),
|
||||||
notificationManager, chatGui, s.GetRequiredService<LightlessConfigService>()));
|
|
||||||
collection.AddSingleton((s) => new LightlessNotificationService(
|
|
||||||
s.GetRequiredService<ILogger<LightlessNotificationService>>(),
|
|
||||||
s.GetRequiredService<LightlessConfigService>(),
|
s.GetRequiredService<LightlessConfigService>(),
|
||||||
s.GetRequiredService<DalamudUtilService>(),
|
s.GetRequiredService<DalamudUtilService>(),
|
||||||
notificationManager,
|
notificationManager,
|
||||||
chatGui,
|
chatGui,
|
||||||
s.GetRequiredService<LightlessMediator>()));
|
s.GetRequiredService<LightlessMediator>(),
|
||||||
|
s.GetServices<WindowMediatorSubscriberBase>()));
|
||||||
collection.AddSingleton((s) =>
|
collection.AddSingleton((s) =>
|
||||||
{
|
{
|
||||||
var httpClient = new HttpClient();
|
var httpClient = new HttpClient();
|
||||||
@@ -257,7 +255,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
s.GetRequiredService<UiFactory>(),
|
s.GetRequiredService<UiFactory>(),
|
||||||
s.GetRequiredService<FileDialogManager>(),
|
s.GetRequiredService<FileDialogManager>(),
|
||||||
s.GetRequiredService<LightlessMediator>(),
|
s.GetRequiredService<LightlessMediator>(),
|
||||||
s.GetRequiredService<LightlessNotificationService>()));
|
s.GetRequiredService<NotificationService>()));
|
||||||
collection.AddScoped((s) => new CommandManagerService(commandManager, s.GetRequiredService<PerformanceCollectorService>(),
|
collection.AddScoped((s) => new CommandManagerService(commandManager, s.GetRequiredService<PerformanceCollectorService>(),
|
||||||
s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<CacheMonitor>(), s.GetRequiredService<ApiController>(),
|
s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<CacheMonitor>(), s.GetRequiredService<ApiController>(),
|
||||||
s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<LightlessConfigService>()));
|
s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<LightlessConfigService>()));
|
||||||
@@ -276,7 +274,6 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddHostedService(p => p.GetRequiredService<FileCacheManager>());
|
collection.AddHostedService(p => p.GetRequiredService<FileCacheManager>());
|
||||||
collection.AddHostedService(p => p.GetRequiredService<ConfigurationMigrator>());
|
collection.AddHostedService(p => p.GetRequiredService<ConfigurationMigrator>());
|
||||||
collection.AddHostedService(p => p.GetRequiredService<DalamudUtilService>());
|
collection.AddHostedService(p => p.GetRequiredService<DalamudUtilService>());
|
||||||
collection.AddHostedService(p => p.GetRequiredService<LightlessNotificationService>());
|
|
||||||
collection.AddHostedService(p => p.GetRequiredService<PerformanceCollectorService>());
|
collection.AddHostedService(p => p.GetRequiredService<PerformanceCollectorService>());
|
||||||
collection.AddHostedService(p => p.GetRequiredService<DtrEntry>());
|
collection.AddHostedService(p => p.GetRequiredService<DtrEntry>());
|
||||||
collection.AddHostedService(p => p.GetRequiredService<EventAggregator>());
|
collection.AddHostedService(p => p.GetRequiredService<EventAggregator>());
|
||||||
|
|||||||
@@ -1,501 +0,0 @@
|
|||||||
using Dalamud.Game.Text.SeStringHandling;
|
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.ImGuiNotification;
|
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
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 FFXIVClientStructs.FFXIV.Client.UI;
|
|
||||||
using NotificationType = LightlessSync.LightlessConfiguration.Models.NotificationType;
|
|
||||||
|
|
||||||
namespace LightlessSync.Services;
|
|
||||||
public class LightlessNotificationService : DisposableMediatorSubscriberBase, IHostedService
|
|
||||||
{
|
|
||||||
private readonly ILogger<LightlessNotificationService> _logger;
|
|
||||||
private readonly LightlessConfigService _configService;
|
|
||||||
private readonly DalamudUtilService _dalamudUtilService;
|
|
||||||
private readonly INotificationManager _notificationManager;
|
|
||||||
private readonly IChatGui _chatGui;
|
|
||||||
private LightlessNotificationUI? _notificationUI;
|
|
||||||
public LightlessNotificationService(
|
|
||||||
ILogger<LightlessNotificationService> logger,
|
|
||||||
LightlessConfigService configService,
|
|
||||||
DalamudUtilService dalamudUtilService,
|
|
||||||
INotificationManager notificationManager,
|
|
||||||
IChatGui chatGui,
|
|
||||||
LightlessMediator mediator) : base(logger, mediator)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_configService = configService;
|
|
||||||
_dalamudUtilService = dalamudUtilService;
|
|
||||||
_notificationManager = notificationManager;
|
|
||||||
_chatGui = chatGui;
|
|
||||||
}
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Mediator.Subscribe<NotificationMessage>(this, HandleNotificationMessage);
|
|
||||||
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(_configService.Current.DefaultNotificationDurationSeconds),
|
|
||||||
Actions = actions ?? new List<LightlessNotificationAction>(),
|
|
||||||
SoundEffectId = GetSoundEffectId(type, soundEffectId),
|
|
||||||
ShowProgress = _configService.Current.ShowNotificationProgress,
|
|
||||||
CreatedAt = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
|
|
||||||
if (_configService.Current.AutoDismissOnAction && notification.Actions.Any())
|
|
||||||
{
|
|
||||||
foreach (var action in notification.Actions)
|
|
||||||
{
|
|
||||||
var originalOnClick = action.OnClick;
|
|
||||||
action.OnClick = (n) =>
|
|
||||||
{
|
|
||||||
originalOnClick(n);
|
|
||||||
if (_configService.Current.AutoDismissOnAction)
|
|
||||||
{
|
|
||||||
n.IsDismissed = true;
|
|
||||||
n.IsAnimatingOut = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (notification.SoundEffectId.HasValue && _configService.Current.EnableNotificationSounds)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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 = "";
|
|
||||||
|
|
||||||
if (queueWaiting > 0)
|
|
||||||
{
|
|
||||||
message = $"Queue: {queueWaiting} waiting";
|
|
||||||
}
|
|
||||||
|
|
||||||
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}";
|
|
||||||
}
|
|
||||||
|
|
||||||
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 uint? GetSoundEffectId(NotificationType type, uint? overrideSoundId)
|
|
||||||
{
|
|
||||||
if (!_configService.Current.EnableNotificationSounds)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (overrideSoundId.HasValue)
|
|
||||||
return overrideSoundId;
|
|
||||||
|
|
||||||
if (_configService.Current.UseCustomSounds)
|
|
||||||
{
|
|
||||||
return type switch
|
|
||||||
{
|
|
||||||
NotificationType.Info => _configService.Current.CustomInfoSoundId,
|
|
||||||
NotificationType.Warning => _configService.Current.CustomWarningSoundId,
|
|
||||||
NotificationType.Error => _configService.Current.CustomErrorSoundId,
|
|
||||||
_ => NotificationSounds.GetDefaultSound(type)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return NotificationSounds.GetDefaultSound(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PlayNotificationSound(uint soundEffectId)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
UIGlobals.PlayChatSoundEffect(soundEffectId);
|
|
||||||
_logger.LogDebug("Played notification sound effect {SoundId} via ChatGui", soundEffectId);
|
|
||||||
}
|
|
||||||
catch (Exception chatEx)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(chatEx, "Failed to play sound via ChatGui for ID {SoundId}", soundEffectId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "Failed to play notification sound effect {SoundId}", soundEffectId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleNotificationMessage(NotificationMessage msg)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("{msg}", msg.ToString());
|
|
||||||
|
|
||||||
if (!_dalamudUtilService.IsLoggedIn) return;
|
|
||||||
|
|
||||||
// Get both old and new notification locations
|
|
||||||
var oldLocation = msg.Type switch
|
|
||||||
{
|
|
||||||
NotificationType.Info => _configService.Current.InfoNotification,
|
|
||||||
NotificationType.Warning => _configService.Current.WarningNotification,
|
|
||||||
NotificationType.Error => _configService.Current.ErrorNotification,
|
|
||||||
_ => NotificationLocation.Nowhere
|
|
||||||
};
|
|
||||||
|
|
||||||
var newLocation = msg.Type switch
|
|
||||||
{
|
|
||||||
NotificationType.Info => _configService.Current.LightlessInfoNotification,
|
|
||||||
NotificationType.Warning => _configService.Current.LightlessWarningNotification,
|
|
||||||
NotificationType.Error => _configService.Current.LightlessErrorNotification,
|
|
||||||
_ => NotificationLocation.LightlessUI
|
|
||||||
};
|
|
||||||
|
|
||||||
// Show notifications based on system selection with backwards compatibility
|
|
||||||
if (!_configService.Current.UseLightlessNotifications)
|
|
||||||
{
|
|
||||||
// Only use old system when new system is disabled
|
|
||||||
ShowNotificationLocationBased(msg, oldLocation);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Use new enhanced system as primary
|
|
||||||
ShowNotificationLocationBased(msg, newLocation);
|
|
||||||
|
|
||||||
// Also use old system as fallback for backwards compatibility
|
|
||||||
// Only if it's different from the new location and not "Nowhere"
|
|
||||||
if (oldLocation != NotificationLocation.Nowhere &&
|
|
||||||
oldLocation != newLocation &&
|
|
||||||
!IsLightlessLocation(oldLocation))
|
|
||||||
{
|
|
||||||
ShowNotificationLocationBased(msg, oldLocation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ShowNotificationLocationBased(NotificationMessage msg, NotificationLocation location)
|
|
||||||
{
|
|
||||||
switch (location)
|
|
||||||
{
|
|
||||||
case NotificationLocation.Toast:
|
|
||||||
ShowToast(msg);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NotificationLocation.Chat:
|
|
||||||
ShowChat(msg);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NotificationLocation.Both:
|
|
||||||
ShowToast(msg);
|
|
||||||
ShowChat(msg);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NotificationLocation.LightlessUI:
|
|
||||||
ShowLightlessNotification(msg);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NotificationLocation.ChatAndLightlessUI:
|
|
||||||
ShowChat(msg);
|
|
||||||
ShowLightlessNotification(msg);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NotificationLocation.Nowhere:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ShowLightlessNotification(NotificationMessage msg)
|
|
||||||
{
|
|
||||||
var duration = msg.TimeShownOnScreen ?? TimeSpan.FromSeconds(_configService.Current.DefaultNotificationDurationSeconds);
|
|
||||||
uint? soundId = null;
|
|
||||||
|
|
||||||
if (_configService.Current.EnableNotificationSounds)
|
|
||||||
{
|
|
||||||
if (_configService.Current.UseCustomSounds)
|
|
||||||
{
|
|
||||||
soundId = msg.Type switch
|
|
||||||
{
|
|
||||||
NotificationType.Info => _configService.Current.CustomInfoSoundId,
|
|
||||||
NotificationType.Warning => _configService.Current.CustomWarningSoundId,
|
|
||||||
NotificationType.Error => _configService.Current.CustomErrorSoundId,
|
|
||||||
_ => NotificationSounds.GetDefaultSound(msg.Type)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
soundId = NotificationSounds.GetDefaultSound(msg.Type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShowNotification(msg.Title ?? "Lightless Sync", msg.Message ?? string.Empty, msg.Type, duration, null, soundId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ShowToast(NotificationMessage msg)
|
|
||||||
{
|
|
||||||
Dalamud.Interface.ImGuiNotification.NotificationType dalamudType = msg.Type switch
|
|
||||||
{
|
|
||||||
NotificationType.Error => Dalamud.Interface.ImGuiNotification.NotificationType.Error,
|
|
||||||
NotificationType.Warning => Dalamud.Interface.ImGuiNotification.NotificationType.Warning,
|
|
||||||
NotificationType.Info => Dalamud.Interface.ImGuiNotification.NotificationType.Info,
|
|
||||||
_ => Dalamud.Interface.ImGuiNotification.NotificationType.Info
|
|
||||||
};
|
|
||||||
|
|
||||||
_notificationManager.AddNotification(new Notification()
|
|
||||||
{
|
|
||||||
Content = msg.Message ?? string.Empty,
|
|
||||||
Title = msg.Title,
|
|
||||||
Type = dalamudType,
|
|
||||||
Minimized = false,
|
|
||||||
InitialDuration = msg.TimeShownOnScreen ?? TimeSpan.FromSeconds(3)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ShowChat(NotificationMessage msg)
|
|
||||||
{
|
|
||||||
switch (msg.Type)
|
|
||||||
{
|
|
||||||
case NotificationType.Info:
|
|
||||||
PrintInfoChat(msg.Message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NotificationType.Warning:
|
|
||||||
PrintWarnChat(msg.Message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NotificationType.Error:
|
|
||||||
PrintErrorChat(msg.Message);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PrintErrorChat(string? message)
|
|
||||||
{
|
|
||||||
SeStringBuilder se = new SeStringBuilder().AddText("[Lightless Sync] Error: " + message);
|
|
||||||
_chatGui.PrintError(se.BuiltString);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PrintInfoChat(string? message)
|
|
||||||
{
|
|
||||||
SeStringBuilder se = new SeStringBuilder().AddText("[Lightless Sync] Info: ").AddItalics(message ?? string.Empty);
|
|
||||||
_chatGui.Print(se.BuiltString);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PrintWarnChat(string? message)
|
|
||||||
{
|
|
||||||
SeStringBuilder se = new SeStringBuilder().AddText("[Lightless Sync] ").AddUiForeground("Warning: " + (message ?? string.Empty), 31).AddUiForegroundOff();
|
|
||||||
_chatGui.Print(se.BuiltString);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsLightlessLocation(NotificationLocation location)
|
|
||||||
{
|
|
||||||
return location switch
|
|
||||||
{
|
|
||||||
NotificationLocation.LightlessUI => true,
|
|
||||||
NotificationLocation.ChatAndLightlessUI => true,
|
|
||||||
_ => false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +1,377 @@
|
|||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.ImGuiNotification;
|
using Dalamud.Interface.ImGuiNotification;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using LightlessSync.LightlessConfiguration;
|
using LightlessSync.LightlessConfiguration;
|
||||||
using LightlessSync.LightlessConfiguration.Models;
|
using LightlessSync.LightlessConfiguration.Models;
|
||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
|
using LightlessSync.UI;
|
||||||
|
using LightlessSync.UI.Models;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
using NotificationType = LightlessSync.LightlessConfiguration.Models.NotificationType;
|
using NotificationType = LightlessSync.LightlessConfiguration.Models.NotificationType;
|
||||||
|
|
||||||
namespace LightlessSync.Services;
|
namespace LightlessSync.Services;
|
||||||
|
|
||||||
public class NotificationService : DisposableMediatorSubscriberBase, IHostedService
|
public class NotificationService : DisposableMediatorSubscriberBase, IHostedService
|
||||||
{
|
{
|
||||||
|
private readonly ILogger<NotificationService> _logger;
|
||||||
|
private readonly LightlessConfigService _configService;
|
||||||
private readonly DalamudUtilService _dalamudUtilService;
|
private readonly DalamudUtilService _dalamudUtilService;
|
||||||
private readonly INotificationManager _notificationManager;
|
private readonly INotificationManager _notificationManager;
|
||||||
private readonly IChatGui _chatGui;
|
private readonly IChatGui _chatGui;
|
||||||
private readonly LightlessConfigService _configurationService;
|
private readonly LightlessNotificationUI? _notificationUI;
|
||||||
|
|
||||||
public NotificationService(ILogger<NotificationService> logger, LightlessMediator mediator,
|
public NotificationService(
|
||||||
|
ILogger<NotificationService> logger,
|
||||||
|
LightlessConfigService configService,
|
||||||
DalamudUtilService dalamudUtilService,
|
DalamudUtilService dalamudUtilService,
|
||||||
INotificationManager notificationManager,
|
INotificationManager notificationManager,
|
||||||
IChatGui chatGui, LightlessConfigService configurationService) : base(logger, mediator)
|
IChatGui chatGui,
|
||||||
|
LightlessMediator mediator,
|
||||||
|
IEnumerable<WindowMediatorSubscriberBase> windows) : base(logger, mediator)
|
||||||
{
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_configService = configService;
|
||||||
_dalamudUtilService = dalamudUtilService;
|
_dalamudUtilService = dalamudUtilService;
|
||||||
_notificationManager = notificationManager;
|
_notificationManager = notificationManager;
|
||||||
_chatGui = chatGui;
|
_chatGui = chatGui;
|
||||||
_configurationService = configurationService;
|
_notificationUI = windows.OfType<LightlessNotificationUI>().FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken)
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
Mediator.Subscribe<NotificationMessage>(this, ShowNotification);
|
Mediator.Subscribe<NotificationMessage>(this, HandleNotificationMessage);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken)
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
public void ShowNotification(string title, string message, NotificationType type = NotificationType.Info,
|
||||||
private void PrintErrorChat(string? message)
|
TimeSpan? duration = null, List<LightlessNotificationAction>? actions = null, uint? soundEffectId = null)
|
||||||
{
|
{
|
||||||
SeStringBuilder se = new SeStringBuilder().AddText("[Lightless Sync] Error: " + message);
|
var notification = new LightlessNotification
|
||||||
_chatGui.PrintError(se.BuiltString);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PrintInfoChat(string? message)
|
|
||||||
{
|
|
||||||
SeStringBuilder se = new SeStringBuilder().AddText("[Lightless Sync] Info: ").AddItalics(message ?? string.Empty);
|
|
||||||
_chatGui.Print(se.BuiltString);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PrintWarnChat(string? message)
|
|
||||||
{
|
|
||||||
SeStringBuilder se = new SeStringBuilder().AddText("[Lightless Sync] ").AddUiForeground("Warning: " + (message ?? string.Empty), 31).AddUiForegroundOff();
|
|
||||||
_chatGui.Print(se.BuiltString);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ShowChat(NotificationMessage msg)
|
|
||||||
{
|
|
||||||
switch (msg.Type)
|
|
||||||
{
|
{
|
||||||
case NotificationType.Info:
|
Title = title,
|
||||||
PrintInfoChat(msg.Message);
|
Message = message,
|
||||||
break;
|
Type = type,
|
||||||
|
Duration = duration ?? TimeSpan.FromSeconds(_configService.Current.DefaultNotificationDurationSeconds),
|
||||||
|
Actions = actions ?? new List<LightlessNotificationAction>(),
|
||||||
|
SoundEffectId = GetSoundEffectId(type, soundEffectId),
|
||||||
|
ShowProgress = _configService.Current.ShowNotificationProgress,
|
||||||
|
CreatedAt = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_configService.Current.AutoDismissOnAction && notification.Actions.Any())
|
||||||
|
{
|
||||||
|
foreach (var action in notification.Actions)
|
||||||
|
{
|
||||||
|
var originalOnClick = action.OnClick;
|
||||||
|
action.OnClick = (n) =>
|
||||||
|
{
|
||||||
|
originalOnClick(n);
|
||||||
|
if (_configService.Current.AutoDismissOnAction)
|
||||||
|
{
|
||||||
|
n.IsDismissed = true;
|
||||||
|
n.IsAnimatingOut = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notification.SoundEffectId.HasValue && _configService.Current.EnableNotificationSounds)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
case NotificationType.Warning:
|
if (notification.SoundEffectId.HasValue)
|
||||||
PrintWarnChat(msg.Message);
|
{
|
||||||
break;
|
PlayNotificationSound(notification.SoundEffectId.Value);
|
||||||
|
}
|
||||||
|
|
||||||
case NotificationType.Error:
|
Mediator.Publish(new LightlessNotificationMessage(notification));
|
||||||
PrintErrorChat(msg.Message);
|
}
|
||||||
break;
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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 = "";
|
||||||
|
|
||||||
|
if (queueWaiting > 0)
|
||||||
|
{
|
||||||
|
message = $"Queue: {queueWaiting} waiting";
|
||||||
|
}
|
||||||
|
|
||||||
|
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}";
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShowNotification(NotificationMessage msg)
|
public void DismissPairDownloadNotification()
|
||||||
{
|
{
|
||||||
Logger.LogInformation("{msg}", msg.ToString());
|
Mediator.Publish(new LightlessNotificationDismissMessage("pair_download_progress"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private uint? GetSoundEffectId(NotificationType type, uint? overrideSoundId)
|
||||||
|
{
|
||||||
|
if (!_configService.Current.EnableNotificationSounds)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (overrideSoundId.HasValue)
|
||||||
|
return overrideSoundId;
|
||||||
|
|
||||||
|
if (_configService.Current.UseCustomSounds)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
NotificationType.Info => _configService.Current.CustomInfoSoundId,
|
||||||
|
NotificationType.Warning => _configService.Current.CustomWarningSoundId,
|
||||||
|
NotificationType.Error => _configService.Current.CustomErrorSoundId,
|
||||||
|
_ => NotificationSounds.GetDefaultSound(type)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return NotificationSounds.GetDefaultSound(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PlayNotificationSound(uint soundEffectId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UIGlobals.PlayChatSoundEffect(soundEffectId);
|
||||||
|
_logger.LogDebug("Played notification sound effect {SoundId} via ChatGui", soundEffectId);
|
||||||
|
}
|
||||||
|
catch (Exception chatEx)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(chatEx, "Failed to play sound via ChatGui for ID {SoundId}", soundEffectId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Failed to play notification sound effect {SoundId}", soundEffectId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleNotificationMessage(NotificationMessage msg)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("{msg}", msg.ToString());
|
||||||
|
|
||||||
if (!_dalamudUtilService.IsLoggedIn) return;
|
if (!_dalamudUtilService.IsLoggedIn) return;
|
||||||
|
|
||||||
switch (msg.Type)
|
// Get both old and new notification locations
|
||||||
|
var oldLocation = msg.Type switch
|
||||||
{
|
{
|
||||||
case NotificationType.Info:
|
NotificationType.Info => _configService.Current.InfoNotification,
|
||||||
ShowNotificationLocationBased(msg, _configurationService.Current.InfoNotification);
|
NotificationType.Warning => _configService.Current.WarningNotification,
|
||||||
break;
|
NotificationType.Error => _configService.Current.ErrorNotification,
|
||||||
|
_ => NotificationLocation.Nowhere
|
||||||
|
};
|
||||||
|
|
||||||
case NotificationType.Warning:
|
var newLocation = msg.Type switch
|
||||||
ShowNotificationLocationBased(msg, _configurationService.Current.WarningNotification);
|
{
|
||||||
break;
|
NotificationType.Info => _configService.Current.LightlessInfoNotification,
|
||||||
|
NotificationType.Warning => _configService.Current.LightlessWarningNotification,
|
||||||
|
NotificationType.Error => _configService.Current.LightlessErrorNotification,
|
||||||
|
_ => NotificationLocation.LightlessUI
|
||||||
|
};
|
||||||
|
|
||||||
case NotificationType.Error:
|
// Show notifications based on system selection with backwards compatibility
|
||||||
ShowNotificationLocationBased(msg, _configurationService.Current.ErrorNotification);
|
if (!_configService.Current.UseLightlessNotifications)
|
||||||
break;
|
{
|
||||||
|
// Only use old system when new system is disabled
|
||||||
|
ShowNotificationLocationBased(msg, oldLocation);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Use new system as primary
|
||||||
|
ShowNotificationLocationBased(msg, newLocation);
|
||||||
|
|
||||||
|
// Also use old system as fallback for backwards compatibility
|
||||||
|
// Only if it's different from the new location and not "Nowhere"
|
||||||
|
if (oldLocation != NotificationLocation.Nowhere &&
|
||||||
|
oldLocation != newLocation &&
|
||||||
|
!IsLightlessLocation(oldLocation))
|
||||||
|
{
|
||||||
|
ShowNotificationLocationBased(msg, oldLocation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,11 +392,46 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
|||||||
ShowChat(msg);
|
ShowChat(msg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NotificationLocation.LightlessUI:
|
||||||
|
ShowLightlessNotification(msg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NotificationLocation.ChatAndLightlessUI:
|
||||||
|
ShowChat(msg);
|
||||||
|
ShowLightlessNotification(msg);
|
||||||
|
break;
|
||||||
|
|
||||||
case NotificationLocation.Nowhere:
|
case NotificationLocation.Nowhere:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ShowLightlessNotification(NotificationMessage msg)
|
||||||
|
{
|
||||||
|
var duration = msg.TimeShownOnScreen ?? TimeSpan.FromSeconds(_configService.Current.DefaultNotificationDurationSeconds);
|
||||||
|
uint? soundId = null;
|
||||||
|
|
||||||
|
if (_configService.Current.EnableNotificationSounds)
|
||||||
|
{
|
||||||
|
if (_configService.Current.UseCustomSounds)
|
||||||
|
{
|
||||||
|
soundId = msg.Type switch
|
||||||
|
{
|
||||||
|
NotificationType.Info => _configService.Current.CustomInfoSoundId,
|
||||||
|
NotificationType.Warning => _configService.Current.CustomWarningSoundId,
|
||||||
|
NotificationType.Error => _configService.Current.CustomErrorSoundId,
|
||||||
|
_ => NotificationSounds.GetDefaultSound(msg.Type)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
soundId = NotificationSounds.GetDefaultSound(msg.Type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowNotification(msg.Title ?? "Lightless Sync", msg.Message ?? string.Empty, msg.Type, duration, null, soundId);
|
||||||
|
}
|
||||||
|
|
||||||
private void ShowToast(NotificationMessage msg)
|
private void ShowToast(NotificationMessage msg)
|
||||||
{
|
{
|
||||||
Dalamud.Interface.ImGuiNotification.NotificationType dalamudType = msg.Type switch
|
Dalamud.Interface.ImGuiNotification.NotificationType dalamudType = msg.Type switch
|
||||||
@@ -138,4 +451,50 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
|||||||
InitialDuration = msg.TimeShownOnScreen ?? TimeSpan.FromSeconds(3)
|
InitialDuration = msg.TimeShownOnScreen ?? TimeSpan.FromSeconds(3)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ShowChat(NotificationMessage msg)
|
||||||
|
{
|
||||||
|
switch (msg.Type)
|
||||||
|
{
|
||||||
|
case NotificationType.Info:
|
||||||
|
PrintInfoChat(msg.Message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NotificationType.Warning:
|
||||||
|
PrintWarnChat(msg.Message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NotificationType.Error:
|
||||||
|
PrintErrorChat(msg.Message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintErrorChat(string? message)
|
||||||
|
{
|
||||||
|
SeStringBuilder se = new SeStringBuilder().AddText("[Lightless Sync] Error: " + message);
|
||||||
|
_chatGui.PrintError(se.BuiltString);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintInfoChat(string? message)
|
||||||
|
{
|
||||||
|
SeStringBuilder se = new SeStringBuilder().AddText("[Lightless Sync] Info: ").AddItalics(message ?? string.Empty);
|
||||||
|
_chatGui.Print(se.BuiltString);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintWarnChat(string? message)
|
||||||
|
{
|
||||||
|
SeStringBuilder se = new SeStringBuilder().AddText("[Lightless Sync] ").AddUiForeground("Warning: " + (message ?? string.Empty), 31).AddUiForegroundOff();
|
||||||
|
_chatGui.Print(se.BuiltString);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsLightlessLocation(NotificationLocation location)
|
||||||
|
{
|
||||||
|
return location switch
|
||||||
|
{
|
||||||
|
NotificationLocation.LightlessUI => true,
|
||||||
|
NotificationLocation.ChatAndLightlessUI => true,
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.ImGuiFileDialog;
|
using Dalamud.Interface.ImGuiFileDialog;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using LightlessSync.LightlessConfiguration;
|
using LightlessSync.LightlessConfiguration;
|
||||||
@@ -23,7 +23,7 @@ public sealed class UiService : DisposableMediatorSubscriberBase
|
|||||||
IEnumerable<WindowMediatorSubscriberBase> windows,
|
IEnumerable<WindowMediatorSubscriberBase> windows,
|
||||||
UiFactory uiFactory, FileDialogManager fileDialogManager,
|
UiFactory uiFactory, FileDialogManager fileDialogManager,
|
||||||
LightlessMediator lightlessMediator,
|
LightlessMediator lightlessMediator,
|
||||||
LightlessNotificationService lightlessNotificationService) : base(logger, lightlessMediator)
|
NotificationService notificationService) : base(logger, lightlessMediator)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_logger.LogTrace("Creating {type}", GetType().Name);
|
_logger.LogTrace("Creating {type}", GetType().Name);
|
||||||
@@ -41,12 +41,6 @@ public sealed class UiService : DisposableMediatorSubscriberBase
|
|||||||
foreach (var window in windows)
|
foreach (var window in windows)
|
||||||
{
|
{
|
||||||
_windowSystem.AddWindow(window);
|
_windowSystem.AddWindow(window);
|
||||||
|
|
||||||
// Connect the notification service to the notification UI
|
|
||||||
if (window is LightlessNotificationUI notificationUI)
|
|
||||||
{
|
|
||||||
lightlessNotificationService.SetNotificationUI(notificationUI);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Mediator.Subscribe<ProfileOpenStandaloneMessage>(this, (msg) =>
|
Mediator.Subscribe<ProfileOpenStandaloneMessage>(this, (msg) =>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
@@ -87,7 +87,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
IpcManager ipcManager,
|
IpcManager ipcManager,
|
||||||
BroadcastService broadcastService,
|
BroadcastService broadcastService,
|
||||||
CharacterAnalyzer characterAnalyzer,
|
CharacterAnalyzer characterAnalyzer,
|
||||||
PlayerPerformanceConfigService playerPerformanceConfig, PairRequestService pairRequestService, DalamudUtilService dalamudUtilService, LightlessNotificationService lightlessNotificationService) : base(logger, mediator, "###LightlessSyncMainUI", performanceCollectorService)
|
PlayerPerformanceConfigService playerPerformanceConfig, PairRequestService pairRequestService, DalamudUtilService dalamudUtilService, NotificationService lightlessNotificationService) : base(logger, mediator, "###LightlessSyncMainUI", performanceCollectorService)
|
||||||
{
|
{
|
||||||
_uiSharedService = uiShared;
|
_uiSharedService = uiShared;
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using LightlessSync.LightlessConfiguration;
|
using LightlessSync.LightlessConfiguration;
|
||||||
using LightlessSync.PlayerData.Handlers;
|
using LightlessSync.PlayerData.Handlers;
|
||||||
@@ -21,12 +21,12 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
private readonly UiSharedService _uiShared;
|
private readonly UiSharedService _uiShared;
|
||||||
private readonly PairProcessingLimiter _pairProcessingLimiter;
|
private readonly PairProcessingLimiter _pairProcessingLimiter;
|
||||||
private readonly ConcurrentDictionary<GameObjectHandler, bool> _uploadingPlayers = new();
|
private readonly ConcurrentDictionary<GameObjectHandler, bool> _uploadingPlayers = new();
|
||||||
private readonly LightlessNotificationService _notificationService;
|
private readonly NotificationService _notificationService;
|
||||||
private bool _notificationDismissed = true;
|
private bool _notificationDismissed = true;
|
||||||
|
|
||||||
public DownloadUi(ILogger<DownloadUi> logger, DalamudUtilService dalamudUtilService, LightlessConfigService configService,
|
public DownloadUi(ILogger<DownloadUi> logger, DalamudUtilService dalamudUtilService, LightlessConfigService configService,
|
||||||
PairProcessingLimiter pairProcessingLimiter, FileUploadManager fileTransferManager, LightlessMediator mediator, UiSharedService uiShared,
|
PairProcessingLimiter pairProcessingLimiter, FileUploadManager fileTransferManager, LightlessMediator mediator, UiSharedService uiShared,
|
||||||
PerformanceCollectorService performanceCollectorService, LightlessNotificationService notificationService)
|
PerformanceCollectorService performanceCollectorService, NotificationService notificationService)
|
||||||
: base(logger, mediator, "Lightless Sync Downloads", performanceCollectorService)
|
: base(logger, mediator, "Lightless Sync Downloads", performanceCollectorService)
|
||||||
{
|
{
|
||||||
_dalamudUtilService = dalamudUtilService;
|
_dalamudUtilService = dalamudUtilService;
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
private readonly IProgress<(int, int, FileCacheEntity)> _validationProgress;
|
private readonly IProgress<(int, int, FileCacheEntity)> _validationProgress;
|
||||||
private readonly NameplateService _nameplateService;
|
private readonly NameplateService _nameplateService;
|
||||||
private readonly NameplateHandler _nameplateHandler;
|
private readonly NameplateHandler _nameplateHandler;
|
||||||
private readonly LightlessNotificationService _lightlessNotificationService;
|
private readonly NotificationService _lightlessNotificationService;
|
||||||
private (int, int, FileCacheEntity) _currentProgress;
|
private (int, int, FileCacheEntity) _currentProgress;
|
||||||
private bool _deleteAccountPopupModalShown = false;
|
private bool _deleteAccountPopupModalShown = false;
|
||||||
private bool _deleteFilesPopupModalShown = false;
|
private bool _deleteFilesPopupModalShown = false;
|
||||||
@@ -107,7 +107,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
DalamudUtilService dalamudUtilService, HttpClient httpClient,
|
DalamudUtilService dalamudUtilService, HttpClient httpClient,
|
||||||
NameplateService nameplateService,
|
NameplateService nameplateService,
|
||||||
NameplateHandler nameplateHandler,
|
NameplateHandler nameplateHandler,
|
||||||
LightlessNotificationService lightlessNotificationService) : base(logger, mediator, "Lightless Sync Settings", performanceCollector)
|
NotificationService lightlessNotificationService) : base(logger, mediator, "Lightless Sync Settings", performanceCollector)
|
||||||
{
|
{
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
_pairManager = pairManager;
|
_pairManager = pairManager;
|
||||||
|
|||||||
@@ -29,14 +29,14 @@ public class TopTabMenu
|
|||||||
private bool _pairRequestsExpanded; // useless for now
|
private bool _pairRequestsExpanded; // useless for now
|
||||||
private int _lastRequestCount;
|
private int _lastRequestCount;
|
||||||
private readonly UiSharedService _uiSharedService;
|
private readonly UiSharedService _uiSharedService;
|
||||||
private readonly LightlessNotificationService _lightlessNotificationService;
|
private readonly NotificationService _lightlessNotificationService;
|
||||||
private string _filter = string.Empty;
|
private string _filter = string.Empty;
|
||||||
private int _globalControlCountdown = 0;
|
private int _globalControlCountdown = 0;
|
||||||
private float _pairRequestsHeight = 150f;
|
private float _pairRequestsHeight = 150f;
|
||||||
private string _pairToAdd = string.Empty;
|
private string _pairToAdd = string.Empty;
|
||||||
|
|
||||||
private SelectedTab _selectedTab = SelectedTab.None;
|
private SelectedTab _selectedTab = SelectedTab.None;
|
||||||
public TopTabMenu(LightlessMediator lightlessMediator, ApiController apiController, PairManager pairManager, UiSharedService uiSharedService, PairRequestService pairRequestService, DalamudUtilService dalamudUtilService, LightlessNotificationService lightlessNotificationService)
|
public TopTabMenu(LightlessMediator lightlessMediator, ApiController apiController, PairManager pairManager, UiSharedService uiSharedService, PairRequestService pairRequestService, DalamudUtilService dalamudUtilService, NotificationService lightlessNotificationService)
|
||||||
{
|
{
|
||||||
_lightlessMediator = lightlessMediator;
|
_lightlessMediator = lightlessMediator;
|
||||||
_apiController = apiController;
|
_apiController = apiController;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using LightlessSync.API.Data;
|
using LightlessSync.API.Data;
|
||||||
using LightlessSync.API.Data.Extensions;
|
using LightlessSync.API.Data.Extensions;
|
||||||
using LightlessSync.API.Dto;
|
using LightlessSync.API.Dto;
|
||||||
@@ -32,7 +32,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IL
|
|||||||
private readonly ServerConfigurationManager _serverManager;
|
private readonly ServerConfigurationManager _serverManager;
|
||||||
private readonly TokenProvider _tokenProvider;
|
private readonly TokenProvider _tokenProvider;
|
||||||
private readonly LightlessConfigService _lightlessConfigService;
|
private readonly LightlessConfigService _lightlessConfigService;
|
||||||
private readonly LightlessNotificationService _lightlessNotificationService;
|
private readonly NotificationService _lightlessNotificationService;
|
||||||
private CancellationTokenSource _connectionCancellationTokenSource;
|
private CancellationTokenSource _connectionCancellationTokenSource;
|
||||||
private ConnectionDto? _connectionDto;
|
private ConnectionDto? _connectionDto;
|
||||||
private bool _doNotNotifyOnNextInfo = false;
|
private bool _doNotNotifyOnNextInfo = false;
|
||||||
@@ -45,7 +45,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IL
|
|||||||
|
|
||||||
public ApiController(ILogger<ApiController> logger, HubFactory hubFactory, DalamudUtilService dalamudUtil,
|
public ApiController(ILogger<ApiController> logger, HubFactory hubFactory, DalamudUtilService dalamudUtil,
|
||||||
PairManager pairManager, PairRequestService pairRequestService, ServerConfigurationManager serverManager, LightlessMediator mediator,
|
PairManager pairManager, PairRequestService pairRequestService, ServerConfigurationManager serverManager, LightlessMediator mediator,
|
||||||
TokenProvider tokenProvider, LightlessConfigService lightlessConfigService, LightlessNotificationService lightlessNotificationService) : base(logger, mediator)
|
TokenProvider tokenProvider, LightlessConfigService lightlessConfigService, NotificationService lightlessNotificationService) : base(logger, mediator)
|
||||||
{
|
{
|
||||||
_hubFactory = hubFactory;
|
_hubFactory = hubFactory;
|
||||||
_dalamudUtil = dalamudUtil;
|
_dalamudUtil = dalamudUtil;
|
||||||
|
|||||||
Reference in New Issue
Block a user