math clamping sliders for notifcation settings, pair notifs disappear now when accepted with other methods

This commit is contained in:
choco
2025-10-12 00:04:10 +02:00
parent a7475a7007
commit fe419336d7
5 changed files with 122 additions and 72 deletions

View File

@@ -189,7 +189,8 @@ public sealed class Plugin : IDalamudPlugin
s.GetRequiredService<DalamudUtilService>(),
notificationManager,
chatGui,
s.GetRequiredService<LightlessMediator>()));
s.GetRequiredService<LightlessMediator>(),
s.GetRequiredService<PairRequestService>()));
collection.AddSingleton((s) =>
{
var httpClient = new HttpClient();

View File

@@ -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;

View File

@@ -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<string> _shownPairRequestNotifications = new();
public NotificationService(
ILogger<NotificationService> 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<NotificationMessage>(this, HandleNotificationMessage);
Mediator.Subscribe<PairRequestsUpdatedMessage>(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<LightlessNotificationAction>? 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<LightlessNotificationAction>? 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<LightlessNotificationAction> CreatePairRequestActions(Action onAccept, Action onDecline)
{
return new List<LightlessNotificationAction>
@@ -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<LightlessNotificationAction> CreateDownloadCompleteActions(Action? onOpenFolder)
{
var actions = new List<LightlessNotificationAction>();
@@ -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<LightlessNotificationAction> CreateErrorActions(Action? onRetry, Action? onViewLog)
{
var actions = new List<LightlessNotificationAction>();
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<string>();
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);
}
}
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)
);
}
}
}

View File

@@ -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;

View File

@@ -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))