Merge pull request 'pair-notifs-ui' (#58) from pair-notifs-ui into 1.12.2
Reviewed-on: #58
This commit was merged in pull request #58.
This commit is contained in:
@@ -115,11 +115,11 @@ public class LightlessConfig : ILightlessConfiguration
|
||||
public uint CustomErrorSoundId { get; set; } = 16; // Se15
|
||||
public uint PairRequestSoundId { get; set; } = 5; // Se5
|
||||
public uint DownloadSoundId { get; set; } = 15; // Se14
|
||||
public bool DisableInfoSound { get; set; } = false;
|
||||
public bool DisableWarningSound { get; set; } = false;
|
||||
public bool DisableErrorSound { get; set; } = false;
|
||||
public bool DisablePairRequestSound { get; set; } = false;
|
||||
public bool DisableDownloadSound { get; set; } = true; // Disabled by default (annoying)
|
||||
public bool DisableInfoSound { get; set; } = true;
|
||||
public bool DisableWarningSound { get; set; } = true;
|
||||
public bool DisableErrorSound { get; set; } = true;
|
||||
public bool DisablePairRequestSound { get; set; } = true;
|
||||
public bool DisableDownloadSound { get; set; } = true;
|
||||
public bool UseFocusTarget { get; set; } = false;
|
||||
public bool overrideFriendColor { get; set; } = false;
|
||||
public bool overridePartyColor { get; set; } = false;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -20,26 +20,35 @@ 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,
|
||||
TimeSpan? duration = null, List<LightlessNotificationAction>? actions = null, uint? soundEffectId = null)
|
||||
{
|
||||
@@ -95,10 +104,12 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
||||
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,
|
||||
@@ -152,6 +163,7 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void ShowDownloadCompleteNotification(string fileName, int fileCount, Action? onOpenFolder = null)
|
||||
{
|
||||
var notification = new LightlessNotification
|
||||
@@ -199,7 +211,9 @@ 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
|
||||
{
|
||||
@@ -256,7 +270,9 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
||||
|
||||
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;
|
||||
@@ -281,7 +297,8 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
||||
}
|
||||
}
|
||||
|
||||
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>();
|
||||
|
||||
@@ -316,14 +333,15 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
||||
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);
|
||||
@@ -464,7 +482,8 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
||||
});
|
||||
}
|
||||
|
||||
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)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ public sealed class PairRequestService : DisposableMediatorSubscriberBase
|
||||
return _requests.RemoveAll(r => now - r.ReceivedAt > Expiration) > 0;
|
||||
}
|
||||
|
||||
public void AcceptPairRequest(string hashedCid)
|
||||
public void AcceptPairRequest(string hashedCid, string displayName)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
@@ -196,8 +196,7 @@ public sealed class PairRequestService : DisposableMediatorSubscriberBase
|
||||
await _apiController.Value.TryPairWithContentId(hashedCid).ConfigureAwait(false);
|
||||
RemoveRequest(hashedCid);
|
||||
|
||||
var display = ResolveDisplayName(hashedCid);
|
||||
var displayText = string.IsNullOrEmpty(display) ? hashedCid : display;
|
||||
var displayText = string.IsNullOrEmpty(displayName) ? hashedCid : displayName;
|
||||
Mediator.Publish(new NotificationMessage(
|
||||
"Pair request accepted",
|
||||
$"Sent a pair request back to {displayText}.",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -2333,20 +2333,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
ImGui.Dummy(new Vector2(10));
|
||||
_uiShared.BigText("Notifications");
|
||||
|
||||
|
||||
if (_uiShared.MediumTreeNode("Display", UIColors.Get("LightlessPurple")))
|
||||
{
|
||||
_uiShared.DrawHelpText(
|
||||
"Notification settings have been moved to the 'Enhanced Notifications' tab for better organization. You can configure where all notifications appear from there.");
|
||||
|
||||
_uiShared.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f);
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
}
|
||||
|
||||
@@ -3688,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))
|
||||
@@ -3731,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();
|
||||
}
|
||||
|
||||
@@ -3752,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))
|
||||
@@ -3767,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))
|
||||
@@ -3785,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))
|
||||
@@ -3800,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))
|
||||
@@ -3818,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))
|
||||
@@ -3836,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))
|
||||
@@ -3862,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))
|
||||
@@ -3876,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))
|
||||
@@ -3890,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))
|
||||
@@ -3904,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))
|
||||
@@ -3918,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))
|
||||
@@ -3996,18 +3982,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
// Pairing Request Notifications Section
|
||||
if (_uiShared.MediumTreeNode("Pairing Request Notifications", UIColors.Get("LightlessBlue")))
|
||||
{
|
||||
UiSharedService.ColorTextWrapped(
|
||||
"Pairing requests always show as interactive notifications with Accept/Decline buttons. Configure the sound in the Sound Settings table above.",
|
||||
ImGuiColors.DalamudGrey);
|
||||
ImGuiHelpers.ScaledDummy(3);
|
||||
|
||||
_uiShared.ColoredSeparator(UIColors.Get("LightlessBlue"), 1.5f);
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
if (_uiShared.MediumTreeNode("System Notifications", UIColors.Get("LightlessYellow")))
|
||||
{
|
||||
var disableOptionalPluginWarnings = _configService.Current.DisableOptionalPluginWarnings;
|
||||
|
||||
@@ -116,7 +116,7 @@ public partial class ApiController
|
||||
_lightlessNotificationService.ShowPairRequestNotification(
|
||||
senderName,
|
||||
request.HashedCid,
|
||||
onAccept: () => _pairRequestService.AcceptPairRequest(request.HashedCid),
|
||||
onAccept: () => _pairRequestService.AcceptPairRequest(request.HashedCid, senderName),
|
||||
onDecline: () => _pairRequestService.DeclinePairRequest(request.HashedCid));
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
||||
Reference in New Issue
Block a user