From fe419336d753ab161d9c45979d43361e4c051381 Mon Sep 17 00:00:00 2001 From: choco Date: Sun, 12 Oct 2025 00:04:10 +0200 Subject: [PATCH] math clamping sliders for notifcation settings, pair notifs disappear now when accepted with other methods --- LightlessSync/Plugin.cs | 3 +- LightlessSync/Services/Mediator/Messages.cs | 2 +- LightlessSync/Services/NotificationService.cs | 161 ++++++++++++------ LightlessSync/UI/LightlessNotificationUI.cs | 2 - LightlessSync/UI/SettingsUi.cs | 26 +-- 5 files changed, 122 insertions(+), 72 deletions(-) diff --git a/LightlessSync/Plugin.cs b/LightlessSync/Plugin.cs index bd13bd9..bac9e29 100644 --- a/LightlessSync/Plugin.cs +++ b/LightlessSync/Plugin.cs @@ -189,7 +189,8 @@ public sealed class Plugin : IDalamudPlugin s.GetRequiredService(), notificationManager, chatGui, - s.GetRequiredService())); + s.GetRequiredService(), + s.GetRequiredService())); collection.AddSingleton((s) => { var httpClient = new HttpClient(); diff --git a/LightlessSync/Services/Mediator/Messages.cs b/LightlessSync/Services/Mediator/Messages.cs index 4745d23..1524dbd 100644 --- a/LightlessSync/Services/Mediator/Messages.cs +++ b/LightlessSync/Services/Mediator/Messages.cs @@ -1,4 +1,4 @@ -using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Game.ClientState.Objects.Types; using LightlessSync.API.Data; using LightlessSync.API.Dto; using LightlessSync.API.Dto.CharaData; diff --git a/LightlessSync/Services/NotificationService.cs b/LightlessSync/Services/NotificationService.cs index 621b816..3f3fdfb 100644 --- a/LightlessSync/Services/NotificationService.cs +++ b/LightlessSync/Services/NotificationService.cs @@ -20,45 +20,54 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ private readonly DalamudUtilService _dalamudUtilService; private readonly INotificationManager _notificationManager; private readonly IChatGui _chatGui; + private readonly PairRequestService _pairRequestService; + private readonly HashSet _shownPairRequestNotifications = new(); + public NotificationService( ILogger logger, LightlessConfigService configService, DalamudUtilService dalamudUtilService, INotificationManager notificationManager, IChatGui chatGui, - LightlessMediator mediator) : base(logger, mediator) + LightlessMediator mediator, + PairRequestService pairRequestService) : base(logger, mediator) { _logger = logger; _configService = configService; _dalamudUtilService = dalamudUtilService; _notificationManager = notificationManager; _chatGui = chatGui; + _pairRequestService = pairRequestService; } + public Task StartAsync(CancellationToken cancellationToken) { Mediator.Subscribe(this, HandleNotificationMessage); + Mediator.Subscribe(this, HandlePairRequestsUpdated); return Task.CompletedTask; } + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; - public void ShowNotification(string title, string message, NotificationType type = NotificationType.Info, + + public void ShowNotification(string title, string message, NotificationType type = NotificationType.Info, TimeSpan? duration = null, List? actions = null, uint? soundEffectId = null) { var notification = CreateNotification(title, message, type, duration, actions, soundEffectId); - + if (_configService.Current.AutoDismissOnAction && notification.Actions.Any()) { WrapActionsWithAutoDismiss(notification); } - + if (notification.SoundEffectId.HasValue) { PlayNotificationSound(notification.SoundEffectId.Value); } - + Mediator.Publish(new LightlessNotificationMessage(notification)); } - - private LightlessNotification CreateNotification(string title, string message, NotificationType type, + + private LightlessNotification CreateNotification(string title, string message, NotificationType type, TimeSpan? duration, List? actions, uint? soundEffectId) { return new LightlessNotification @@ -73,7 +82,7 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ CreatedAt = DateTime.UtcNow }; } - + private void WrapActionsWithAutoDismiss(LightlessNotification notification) { foreach (var action in notification.Actions) @@ -89,16 +98,18 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ }; } } - + private void DismissNotification(LightlessNotification notification) { notification.IsDismissed = true; notification.IsAnimatingOut = true; } + public void ShowPairRequestNotification(string senderName, string senderId, Action onAccept, Action onDecline) { var notification = new LightlessNotification { + Id = $"pair_request_{senderId}", Title = "Pair Request Received", Message = $"{senderName} wants to directly pair with you.", Type = NotificationType.PairRequest, @@ -114,10 +125,10 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ Mediator.Publish(new LightlessNotificationMessage(notification)); } - + private uint? GetPairRequestSoundId() => !_configService.Current.DisablePairRequestSound ? _configService.Current.PairRequestSoundId : null; - + private List CreatePairRequestActions(Action onAccept, Action onDecline) { return new List @@ -152,6 +163,7 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ } }; } + public void ShowDownloadCompleteNotification(string fileName, int fileCount, Action? onOpenFolder = null) { var notification = new LightlessNotification @@ -171,12 +183,12 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ Mediator.Publish(new LightlessNotificationMessage(notification)); } - + private string FormatDownloadCompleteMessage(string fileName, int fileCount) => - fileCount > 1 - ? $"Downloaded {fileCount} files successfully." + fileCount > 1 + ? $"Downloaded {fileCount} files successfully." : $"Downloaded {fileName} successfully."; - + private List CreateDownloadCompleteActions(Action? onOpenFolder) { var actions = new List(); @@ -196,10 +208,12 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ } }); } - + return actions; } - public void ShowErrorNotification(string title, string message, Exception? exception = null, Action? onRetry = null, Action? onViewLog = null) + + public void ShowErrorNotification(string title, string message, Exception? exception = null, Action? onRetry = null, + Action? onViewLog = null) { var notification = new LightlessNotification { @@ -218,14 +232,14 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ Mediator.Publish(new LightlessNotificationMessage(notification)); } - + private string FormatErrorMessage(string message, Exception? exception) => exception != null ? $"{message}\n\nError: {exception.Message}" : message; - + private List CreateErrorActions(Action? onRetry, Action? onViewLog) { var actions = new List(); - + if (onRetry != null) { actions.Add(new LightlessNotificationAction @@ -241,7 +255,7 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ } }); } - + if (onViewLog != null) { actions.Add(new LightlessNotificationAction @@ -253,10 +267,12 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ OnClick = (n) => onViewLog() }); } - + return actions; } - public void ShowPairDownloadNotification(List<(string playerName, float progress, string status)> downloadStatus, int queueWaiting = 0) + + 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; @@ -272,63 +288,65 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ ShowProgress = true, Progress = totalProgress }; - + Mediator.Publish(new LightlessNotificationMessage(notification)); - + if (AreAllDownloadsCompleted(userDownloads)) { DismissPairDownloadNotification(); } } - - private string BuildPairDownloadMessage(List<(string playerName, float progress, string status)> userDownloads, int queueWaiting) + + private string BuildPairDownloadMessage(List<(string playerName, float progress, string status)> userDownloads, + int queueWaiting) { var messageParts = new List(); - + if (queueWaiting > 0) { messageParts.Add($"Queue: {queueWaiting} waiting"); } - + if (userDownloads.Count > 0) { var completedCount = userDownloads.Count(x => x.progress >= 1.0f); messageParts.Add($"Progress: {completedCount}/{userDownloads.Count} completed"); } - + var activeDownloadLines = BuildActiveDownloadLines(userDownloads); if (!string.IsNullOrEmpty(activeDownloadLines)) { messageParts.Add(activeDownloadLines); } - + return string.Join("\n", messageParts); } - + private string BuildActiveDownloadLines(List<(string playerName, float progress, string status)> userDownloads) { var activeDownloads = userDownloads .Where(x => x.progress < 1.0f) .Take(_configService.Current.MaxConcurrentPairApplications); - + if (!activeDownloads.Any()) return string.Empty; - + return string.Join("\n", activeDownloads.Select(x => $"• {x.playerName}: {FormatDownloadStatus(x)}")); } - - private string FormatDownloadStatus((string playerName, float progress, string status) download) => download.status switch - { - "downloading" => $"{download.progress:P0}", - "decompressing" => "decompressing", - "queued" => "queued", - "waiting" => "waiting for slot", - _ => download.status - }; - + + private string FormatDownloadStatus((string playerName, float progress, string status) download) => + download.status switch + { + "downloading" => $"{download.progress:P0}", + "decompressing" => "decompressing", + "queued" => "queued", + "waiting" => "waiting for slot", + _ => download.status + }; + private bool AreAllDownloadsCompleted(List<(string playerName, float progress, string status)> userDownloads) => userDownloads.Any() && userDownloads.All(x => x.progress >= 1.0f); - public void DismissPairDownloadNotification() => + public void DismissPairDownloadNotification() => Mediator.Publish(new LightlessNotificationDismissMessage("pair_download_progress")); private TimeSpan GetDefaultDurationForType(NotificationType type) => type switch @@ -347,7 +365,7 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ if (IsSoundDisabledForType(type)) return null; return GetConfiguredSoundForType(type); } - + private bool IsSoundDisabledForType(NotificationType type) => type switch { NotificationType.Info => _configService.Current.DisableInfoSound, @@ -356,7 +374,7 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ NotificationType.Download => _configService.Current.DisableDownloadSound, _ => false }; - + private uint GetConfiguredSoundForType(NotificationType type) => type switch { NotificationType.Info => _configService.Current.CustomInfoSoundId, @@ -387,12 +405,12 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ var location = GetNotificationLocation(msg.Type); ShowNotificationLocationBased(msg, location); } - + private NotificationLocation GetNotificationLocation(NotificationType type) => _configService.Current.UseLightlessNotifications ? GetLightlessNotificationLocation(type) : GetClassicNotificationLocation(type); - + private NotificationLocation GetLightlessNotificationLocation(NotificationType type) => type switch { NotificationType.Info => _configService.Current.LightlessInfoNotification, @@ -402,7 +420,7 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ NotificationType.Download => _configService.Current.LightlessDownloadNotification, _ => NotificationLocation.LightlessUi }; - + private NotificationLocation GetClassicNotificationLocation(NotificationType type) => type switch { NotificationType.Info => _configService.Current.InfoNotification, @@ -463,8 +481,9 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ InitialDuration = msg.TimeShownOnScreen ?? TimeSpan.FromSeconds(3) }); } - - private Dalamud.Interface.ImGuiNotification.NotificationType ConvertToDalamudNotificationType(NotificationType type) => type switch + + private Dalamud.Interface.ImGuiNotification.NotificationType + ConvertToDalamudNotificationType(NotificationType type) => type switch { NotificationType.Error => Dalamud.Interface.ImGuiNotification.NotificationType.Error, NotificationType.Warning => Dalamud.Interface.ImGuiNotification.NotificationType.Warning, @@ -497,14 +516,46 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ private void PrintInfoChat(string? message) { - SeStringBuilder se = new SeStringBuilder().AddText("[Lightless Sync] Info: ").AddItalics(message ?? string.Empty); + 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(); + SeStringBuilder se = new SeStringBuilder().AddText("[Lightless Sync] ") + .AddUiForeground("Warning: " + (message ?? string.Empty), 31).AddUiForegroundOff(); _chatGui.Print(se.BuiltString); } -} \ No newline at end of file + private void HandlePairRequestsUpdated(PairRequestsUpdatedMessage _) + { + var activeRequests = _pairRequestService.GetActiveRequests(); + var activeRequestIds = activeRequests.Select(r => r.HashedCid).ToHashSet(); + + // Dismiss notifications for requests that are no longer active + var notificationsToRemove = _shownPairRequestNotifications + .Where(hashedCid => !activeRequestIds.Contains(hashedCid)) + .ToList(); + + foreach (var hashedCid in notificationsToRemove) + { + var notificationId = $"pair_request_{hashedCid}"; + Mediator.Publish(new LightlessNotificationDismissMessage(notificationId)); + _shownPairRequestNotifications.Remove(hashedCid); + } + + // Show/update notifications for all active requests + foreach (var request in activeRequests) + { + _shownPairRequestNotifications.Add(request.HashedCid); + ShowPairRequestNotification( + request.DisplayName, + request.HashedCid, + () => _pairRequestService.AcceptPairRequest(request.HashedCid, request.DisplayName), + () => _pairRequestService.DeclinePairRequest(request.HashedCid) + ); + } + } +} + \ No newline at end of file diff --git a/LightlessSync/UI/LightlessNotificationUI.cs b/LightlessSync/UI/LightlessNotificationUI.cs index 139aa15..c0d5b01 100644 --- a/LightlessSync/UI/LightlessNotificationUI.cs +++ b/LightlessSync/UI/LightlessNotificationUI.cs @@ -42,8 +42,6 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase ImGuiWindowFlags.AlwaysAutoResize; PositionCondition = ImGuiCond.Always; - - Size = new Vector2(_configService.Current.NotificationWidth, 100); SizeCondition = ImGuiCond.FirstUseEver; IsOpen = false; RespectCloseHotkey = false; diff --git a/LightlessSync/UI/SettingsUi.cs b/LightlessSync/UI/SettingsUi.cs index 606d4b2..6c2fd6e 100644 --- a/LightlessSync/UI/SettingsUi.cs +++ b/LightlessSync/UI/SettingsUi.cs @@ -3674,7 +3674,7 @@ public class SettingsUi : WindowMediatorSubscriberBase int maxNotifications = _configService.Current.MaxSimultaneousNotifications; if (ImGui.SliderInt("Max Simultaneous Notifications", ref maxNotifications, 1, 10)) { - _configService.Current.MaxSimultaneousNotifications = maxNotifications; + _configService.Current.MaxSimultaneousNotifications = Math.Clamp(maxNotifications, 1, 10); _configService.Save(); } if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) @@ -3717,7 +3717,7 @@ public class SettingsUi : WindowMediatorSubscriberBase float opacity = _configService.Current.NotificationOpacity; if (ImGui.SliderFloat("Notification Opacity", ref opacity, 0.1f, 1.0f, "%.2f")) { - _configService.Current.NotificationOpacity = opacity; + _configService.Current.NotificationOpacity = Math.Clamp(opacity, 0.1f, 1.0f); _configService.Save(); } @@ -3738,7 +3738,7 @@ public class SettingsUi : WindowMediatorSubscriberBase float notifWidth = _configService.Current.NotificationWidth; if (ImGui.SliderFloat("Notification Width", ref notifWidth, 250f, 600f, "%.0f")) { - _configService.Current.NotificationWidth = notifWidth; + _configService.Current.NotificationWidth = Math.Clamp(notifWidth, 250f, 600f); _configService.Save(); } if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) @@ -3753,7 +3753,7 @@ public class SettingsUi : WindowMediatorSubscriberBase float notifSpacing = _configService.Current.NotificationSpacing; if (ImGui.SliderFloat("Notification Spacing", ref notifSpacing, 0f, 30f, "%.0f")) { - _configService.Current.NotificationSpacing = notifSpacing; + _configService.Current.NotificationSpacing = Math.Clamp(notifSpacing, 0f, 30f); _configService.Save(); } if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) @@ -3771,7 +3771,7 @@ public class SettingsUi : WindowMediatorSubscriberBase int offsetY = _configService.Current.NotificationOffsetY; if (ImGui.SliderInt("Vertical Offset", ref offsetY, 0, 500)) { - _configService.Current.NotificationOffsetY = offsetY; + _configService.Current.NotificationOffsetY = Math.Clamp(offsetY, 0, 500); _configService.Save(); } if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) @@ -3786,7 +3786,7 @@ public class SettingsUi : WindowMediatorSubscriberBase int offsetX = _configService.Current.NotificationOffsetX; if (ImGui.SliderInt("Horizontal Offset", ref offsetX, 0, 500)) { - _configService.Current.NotificationOffsetX = offsetX; + _configService.Current.NotificationOffsetX = Math.Clamp(offsetX, 0, 500); _configService.Save(); } if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) @@ -3804,7 +3804,7 @@ public class SettingsUi : WindowMediatorSubscriberBase float animSpeed = _configService.Current.NotificationAnimationSpeed; if (ImGui.SliderFloat("Animation Speed", ref animSpeed, 1f, 30f, "%.1f")) { - _configService.Current.NotificationAnimationSpeed = animSpeed; + _configService.Current.NotificationAnimationSpeed = Math.Clamp(animSpeed, 1f, 30f); _configService.Save(); } if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) @@ -3822,7 +3822,7 @@ public class SettingsUi : WindowMediatorSubscriberBase float accentWidth = _configService.Current.NotificationAccentBarWidth; if (ImGui.SliderFloat("Accent Bar Width", ref accentWidth, 0f, 10f, "%.1f")) { - _configService.Current.NotificationAccentBarWidth = accentWidth; + _configService.Current.NotificationAccentBarWidth = Math.Clamp(accentWidth, 0f, 10f); _configService.Save(); } if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) @@ -3848,7 +3848,7 @@ public class SettingsUi : WindowMediatorSubscriberBase int infoDuration = _configService.Current.InfoNotificationDurationSeconds; if (ImGui.SliderInt("Info Duration (seconds)", ref infoDuration, 3, 60)) { - _configService.Current.InfoNotificationDurationSeconds = infoDuration; + _configService.Current.InfoNotificationDurationSeconds = Math.Clamp(infoDuration, 3, 60); _configService.Save(); } if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) @@ -3862,7 +3862,7 @@ public class SettingsUi : WindowMediatorSubscriberBase int warningDuration = _configService.Current.WarningNotificationDurationSeconds; if (ImGui.SliderInt("Warning Duration (seconds)", ref warningDuration, 3, 60)) { - _configService.Current.WarningNotificationDurationSeconds = warningDuration; + _configService.Current.WarningNotificationDurationSeconds = Math.Clamp(warningDuration, 3, 60); _configService.Save(); } if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) @@ -3876,7 +3876,7 @@ public class SettingsUi : WindowMediatorSubscriberBase int errorDuration = _configService.Current.ErrorNotificationDurationSeconds; if (ImGui.SliderInt("Error Duration (seconds)", ref errorDuration, 3, 120)) { - _configService.Current.ErrorNotificationDurationSeconds = errorDuration; + _configService.Current.ErrorNotificationDurationSeconds = Math.Clamp(errorDuration, 3, 120); _configService.Save(); } if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) @@ -3890,7 +3890,7 @@ public class SettingsUi : WindowMediatorSubscriberBase int pairRequestDuration = _configService.Current.PairRequestDurationSeconds; if (ImGui.SliderInt("Pair Request Duration (seconds)", ref pairRequestDuration, 30, 600)) { - _configService.Current.PairRequestDurationSeconds = pairRequestDuration; + _configService.Current.PairRequestDurationSeconds = Math.Clamp(pairRequestDuration, 30, 600); _configService.Save(); } if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) @@ -3904,7 +3904,7 @@ public class SettingsUi : WindowMediatorSubscriberBase int downloadDuration = _configService.Current.DownloadNotificationDurationSeconds; if (ImGui.SliderInt("Download Duration (seconds)", ref downloadDuration, 60, 600)) { - _configService.Current.DownloadNotificationDurationSeconds = downloadDuration; + _configService.Current.DownloadNotificationDurationSeconds = Math.Clamp(downloadDuration, 60, 600); _configService.Save(); } if (ImGui.IsItemClicked(ImGuiMouseButton.Right))