more to notification system with new settings tab

This commit is contained in:
choco
2025-10-08 23:20:58 +02:00
parent 17f4ddad89
commit 27e7fb7ed9
8 changed files with 963 additions and 150 deletions

View File

@@ -70,6 +70,31 @@ public class LightlessConfig : ILightlessConfiguration
public bool AutoPopulateEmptyNotesFromCharaName { get; set; } = false;
public int Version { get; set; } = 1;
public NotificationLocation WarningNotification { get; set; } = NotificationLocation.Both;
// Lightless Notification Configuration
// TODO: clean these
public bool UseLightlessNotifications { get; set; } = true;
public bool EnableNotificationSounds { get; set; } = true;
public int DefaultNotificationDurationSeconds { get; set; } = 10;
public bool ShowNotificationProgress { get; set; } = true;
public NotificationLocation LightlessInfoNotification { get; set; } = NotificationLocation.LightlessUI;
public NotificationLocation LightlessWarningNotification { get; set; } = NotificationLocation.LightlessUI;
public NotificationLocation LightlessErrorNotification { get; set; } = NotificationLocation.ChatAndLightlessUI;
public float NotificationOpacity { get; set; } = 0.95f;
public bool EnableNotificationAnimations { get; set; } = true;
public int MaxSimultaneousNotifications { get; set; } = 5;
public bool AutoDismissOnAction { get; set; } = true;
public bool ShowNotificationTimestamp { get; set; } = false;
public bool EnableNotificationHistory { get; set; } = true;
public int NotificationHistorySize { get; set; } = 50;
public uint CustomInfoSoundId { get; set; } = 2; // Se2
public uint CustomWarningSoundId { get; set; } = 15; // Se15
public uint CustomErrorSoundId { get; set; } = 16; // Se16
public bool UseCustomSounds { get; set; } = false;
public float NotificationSoundVolume { get; set; } = 1.0f;
// till here c:
public bool UseFocusTarget { get; set; } = false;
public bool overrideFriendColor { get; set; } = false;
public bool overridePartyColor { get; set; } = false;

View File

@@ -1,11 +1,13 @@
namespace LightlessSync.LightlessConfiguration.Models;
namespace LightlessSync.LightlessConfiguration.Models;
public enum NotificationLocation
{
Nowhere,
Chat,
Toast,
Both
Both,
LightlessUI,
ChatAndLightlessUI,
}
public enum NotificationType

View File

@@ -179,6 +179,8 @@ public sealed class Plugin : IDalamudPlugin
s.GetRequiredService<ILogger<LightlessNotificationService>>(),
s.GetRequiredService<LightlessConfigService>(),
s.GetRequiredService<DalamudUtilService>(),
notificationManager,
chatGui,
s.GetRequiredService<LightlessMediator>()));
collection.AddSingleton((s) =>
{
@@ -244,7 +246,8 @@ public sealed class Plugin : IDalamudPlugin
new LightlessNotificationUI(
s.GetRequiredService<ILogger<LightlessNotificationUI>>(),
s.GetRequiredService<LightlessMediator>(),
s.GetRequiredService<PerformanceCollectorService>()));
s.GetRequiredService<PerformanceCollectorService>(),
s.GetRequiredService<LightlessConfigService>()));
collection.AddScoped<IPopupHandler, CensusPopupHandler>();
collection.AddScoped<CacheCreationService>();
collection.AddScoped<PlayerDataFactory>();

View File

@@ -1,4 +1,6 @@
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Interface;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Plugin.Services;
using LightlessSync.LightlessConfiguration;
using LightlessSync.LightlessConfiguration.Models;
@@ -8,6 +10,7 @@ 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
@@ -15,19 +18,26 @@ public class LightlessNotificationService : DisposableMediatorSubscriberBase, IH
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)
@@ -46,12 +56,31 @@ public class LightlessNotificationService : DisposableMediatorSubscriberBase, IH
Title = title,
Message = message,
Type = type,
Duration = duration ?? TimeSpan.FromSeconds(10),
Duration = duration ?? TimeSpan.FromSeconds(_configService.Current.DefaultNotificationDurationSeconds),
Actions = actions ?? new List<LightlessNotificationAction>(),
SoundEffectId = soundEffectId ?? NotificationSounds.GetDefaultSound(type)
SoundEffectId = GetSoundEffectId(type, soundEffectId),
ShowProgress = _configService.Current.ShowNotificationProgress,
CreatedAt = DateTime.UtcNow
};
if (notification.SoundEffectId.HasValue)
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);
}
@@ -260,6 +289,28 @@ public class LightlessNotificationService : DisposableMediatorSubscriberBase, IH
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
@@ -279,4 +330,172 @@ public class LightlessNotificationService : DisposableMediatorSubscriberBase, IH
_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
};
}
}

View File

@@ -3,11 +3,13 @@ using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using LightlessSync.LightlessConfiguration;
using LightlessSync.LightlessConfiguration.Models;
using LightlessSync.Services;
using LightlessSync.Services.Mediator;
using LightlessSync.UI.Models;
using Microsoft.Extensions.Logging;
using System.Numerics;
using Dalamud.Bindings.ImGui;
@@ -17,18 +19,22 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
{
private readonly List<LightlessNotification> _notifications = new();
private readonly object _notificationLock = new();
private readonly LightlessConfigService _configService;
private const float NotificationWidth = 350f;
private const float NotificationMinHeight = 60f;
private const float NotificationMaxHeight = 200f;
private const float NotificationSpacing = 8f;
private const float AnimationSpeed = 10f;
private const float EdgeXMargin = 0;
private const float EdgeYMargin = 30f;
private const float SlideDistance = 100f;
public LightlessNotificationUI(ILogger<LightlessNotificationUI> logger, LightlessMediator mediator, PerformanceCollectorService performanceCollector)
public LightlessNotificationUI(ILogger<LightlessNotificationUI> logger, LightlessMediator mediator, PerformanceCollectorService performanceCollector, LightlessConfigService configService)
: base(logger, mediator, "Lightless Notifications##LightlessNotifications", performanceCollector)
{
_configService = configService;
Flags = ImGuiWindowFlags.NoDecoration |
ImGuiWindowFlags.NoMove |
ImGuiWindowFlags.NoResize |
@@ -141,6 +147,19 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
{
var deltaTime = ImGui.GetIO().DeltaTime;
var maxNotifications = _configService.Current.MaxSimultaneousNotifications;
while (_notifications.Count(n => !n.IsAnimatingOut) > maxNotifications)
{
var oldestNotification = _notifications
.Where(n => !n.IsAnimatingOut)
.OrderBy(n => n.CreatedAt)
.FirstOrDefault();
if (oldestNotification != null)
{
oldestNotification.IsAnimatingOut = true;
oldestNotification.IsAnimatingIn = false;
}
}
for (int i = _notifications.Count - 1; i >= 0; i--)
{
var notification = _notifications[i];
@@ -174,9 +193,16 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
private void DrawNotification(LightlessNotification notification, int index)
{
var alpha = notification.AnimationProgress;
if (alpha <= 0f) return;
var slideOffset = (1f - alpha) * SlideDistance;
if (_configService.Current.EnableNotificationAnimations && alpha <= 0f)
return;
var slideOffset = 0f;
if (_configService.Current.EnableNotificationAnimations)
{
slideOffset = (1f - alpha) * SlideDistance;
}
var originalCursorPos = ImGui.GetCursorPos();
ImGui.SetCursorPosX(originalCursorPos.X + slideOffset);
@@ -198,13 +224,18 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
var windowPos = ImGui.GetWindowPos();
var windowSize = ImGui.GetWindowSize();
var bgColor = new Vector4(30f/255f, 30f/255f, 30f/255f, 0.95f * alpha);
var baseOpacity = _configService.Current.NotificationOpacity;
var finalOpacity = _configService.Current.EnableNotificationAnimations ? baseOpacity * alpha : baseOpacity;
var bgColor = new Vector4(30f/255f, 30f/255f, 30f/255f, finalOpacity);
var accentColor = GetNotificationAccentColor(notification.Type);
var progressBarColor = UIColors.Get("LightlessBlue");
accentColor.W *= alpha;
var finalAccentAlpha = _configService.Current.EnableNotificationAnimations ? alpha : 1f;
accentColor.W *= finalAccentAlpha;
var shadowOffset = new Vector2(1f, 1f);
var shadowColor = new Vector4(0f, 0f, 0f, 0.4f * alpha);
var shadowAlpha = _configService.Current.EnableNotificationAnimations ? 0.4f * alpha : 0.4f;
var shadowColor = new Vector4(0f, 0f, 0f, shadowAlpha);
drawList.AddRectFilled(
windowPos + shadowOffset,
windowPos + windowSize + shadowOffset,
@@ -278,14 +309,31 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
ImGui.SetCursorPos(contentPos);
float titleHeight = 0f;
using (ImRaii.PushColor(ImGuiCol.Text, new Vector4(1f, 1f, 1f, alpha)))
{
ImGui.Text(notification.Title);
// Set text wrap position to prevent title overflow
ImGui.PushTextWrapPos(ImGui.GetCursorPosX() + contentSize.X);
var titleStartY = ImGui.GetCursorPosY();
if (_configService.Current.ShowNotificationTimestamp)
{
var timestamp = notification.CreatedAt.ToLocalTime().ToString("HH:mm:ss");
ImGui.TextWrapped($"[{timestamp}] {notification.Title}");
}
else
{
ImGui.TextWrapped(notification.Title);
}
titleHeight = ImGui.GetCursorPosY() - titleStartY;
ImGui.PopTextWrapPos();
}
if (!string.IsNullOrEmpty(notification.Message))
{
ImGui.SetCursorPos(contentPos + new Vector2(0f, 18f));
ImGui.SetCursorPos(contentPos + new Vector2(0f, titleHeight + 4f));
ImGui.PushTextWrapPos(ImGui.GetCursorPosX() + contentSize.X);
using (ImRaii.PushColor(ImGuiCol.Text, new Vector4(0.9f, 0.9f, 0.9f, alpha)))
{
@@ -436,24 +484,40 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
private float CalculateNotificationHeight(LightlessNotification notification)
{
var height = 40f;
if (!string.IsNullOrEmpty(notification.Message))
var contentWidth = NotificationWidth - 35f; // Account for padding and accent bar
var height = 20f; // Base height for padding
var titleText = notification.Title;
if (_configService.Current.ShowNotificationTimestamp)
{
var textSize = ImGui.CalcTextSize(notification.Message, true, NotificationWidth - 35f);
height += textSize.Y + 4f;
var timestamp = notification.CreatedAt.ToLocalTime().ToString("HH:mm:ss");
titleText = $"[{timestamp}] {titleText}";
}
var titleSize = ImGui.CalcTextSize(titleText, true, contentWidth);
height += titleSize.Y + 4f; // Title height + spacing
// Calculate message height
if (!string.IsNullOrEmpty(notification.Message))
{
var messageSize = ImGui.CalcTextSize(notification.Message, true, contentWidth);
height += messageSize.Y + 4f; // Message height + spacing
}
// Add height for progress bar
if (notification.ShowProgress)
{
height += 12f;
}
// Add height for action buttons
if (notification.Actions.Count > 0)
{
height += 28f;
}
return Math.Max(height, NotificationMinHeight);
// Allow notifications to grow taller but cap at maximum height
return Math.Clamp(height, NotificationMinHeight, NotificationMaxHeight);
}
private Vector4 GetNotificationAccentColor(NotificationType type)

View File

@@ -1,4 +1,4 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Bindings.ImGui;
using Dalamud.Game.Text;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
@@ -61,6 +61,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
private readonly IProgress<(int, int, FileCacheEntity)> _validationProgress;
private readonly NameplateService _nameplateService;
private readonly NameplateHandler _nameplateHandler;
private readonly LightlessNotificationService _lightlessNotificationService;
private (int, int, FileCacheEntity) _currentProgress;
private bool _deleteAccountPopupModalShown = false;
private bool _deleteFilesPopupModalShown = false;
@@ -105,7 +106,8 @@ public class SettingsUi : WindowMediatorSubscriberBase
IpcManager ipcManager, CacheMonitor cacheMonitor,
DalamudUtilService dalamudUtilService, HttpClient httpClient,
NameplateService nameplateService,
NameplateHandler nameplateHandler) : base(logger, mediator, "Lightless Sync Settings", performanceCollector)
NameplateHandler nameplateHandler,
LightlessNotificationService lightlessNotificationService) : base(logger, mediator, "Lightless Sync Settings", performanceCollector)
{
_configService = configService;
_pairManager = pairManager;
@@ -125,6 +127,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
_uiShared = uiShared;
_nameplateService = nameplateService;
_nameplateHandler = nameplateHandler;
_lightlessNotificationService = lightlessNotificationService;
AllowClickthrough = false;
AllowPinning = true;
_validationProgress = new Progress<(int, int, FileCacheEntity)>(v => _currentProgress = v);
@@ -351,13 +354,8 @@ public class SettingsUi : WindowMediatorSubscriberBase
_uiShared.UnderlinedBigText("Transfer UI", UIColors.Get("LightlessBlue"));
ImGuiHelpers.ScaledDummy(5);
bool useNotificationsForDownloads = _configService.Current.UseNotificationsForDownloads;
if (ImGui.Checkbox("Use notifications for download progress", ref useNotificationsForDownloads))
{
_configService.Current.UseNotificationsForDownloads = useNotificationsForDownloads;
_configService.Save();
}
_uiShared.DrawHelpText("Show download progress as clean notifications instead of overlay text. Notifications update in real-time.");
_uiShared.DrawHelpText("Download progress notification settings have been moved to the 'Enhanced Notifications' tab for better organization.");
ImGuiHelpers.ScaledDummy(5);
bool showTransferWindow = _configService.Current.ShowTransferWindow;
if (ImGui.Checkbox("Show separate transfer window", ref showTransferWindow))
@@ -1645,48 +1643,10 @@ public class SettingsUi : WindowMediatorSubscriberBase
ImGui.Dummy(new Vector2(10));
_uiShared.BigText("Notifications");
var disableOptionalPluginWarnings = _configService.Current.DisableOptionalPluginWarnings;
var onlineNotifs = _configService.Current.ShowOnlineNotifications;
var onlineNotifsPairsOnly = _configService.Current.ShowOnlineNotificationsOnlyForIndividualPairs;
var onlineNotifsNamedOnly = _configService.Current.ShowOnlineNotificationsOnlyForNamedPairs;
if (_uiShared.MediumTreeNode("Display", UIColors.Get("LightlessPurple")))
{
_uiShared.DrawCombo("Info Notification Display##settingsUi", (NotificationLocation[])Enum.GetValues(typeof(NotificationLocation)), (i) => i.ToString(),
(i) =>
{
_configService.Current.InfoNotification = i;
_configService.Save();
}, _configService.Current.InfoNotification);
_uiShared.DrawHelpText("The location where \"Info\" notifications will display."
+ Environment.NewLine + "'Nowhere' will not show any Info notifications"
+ Environment.NewLine + "'Chat' will print Info notifications in chat"
+ Environment.NewLine + "'Toast' will show Warning toast notifications in the bottom right corner"
+ Environment.NewLine + "'Both' will show chat as well as the toast notification");
_uiShared.DrawCombo("Warning Notification Display##settingsUi", (NotificationLocation[])Enum.GetValues(typeof(NotificationLocation)), (i) => i.ToString(),
(i) =>
{
_configService.Current.WarningNotification = i;
_configService.Save();
}, _configService.Current.WarningNotification);
_uiShared.DrawHelpText("The location where \"Warning\" notifications will display."
+ Environment.NewLine + "'Nowhere' will not show any Warning notifications"
+ Environment.NewLine + "'Chat' will print Warning notifications in chat"
+ Environment.NewLine + "'Toast' will show Warning toast notifications in the bottom right corner"
+ Environment.NewLine + "'Both' will show chat as well as the toast notification");
_uiShared.DrawCombo("Error Notification Display##settingsUi", (NotificationLocation[])Enum.GetValues(typeof(NotificationLocation)), (i) => i.ToString(),
(i) =>
{
_configService.Current.ErrorNotification = i;
_configService.Save();
}, _configService.Current.ErrorNotification);
_uiShared.DrawHelpText("The location where \"Error\" notifications will display."
+ Environment.NewLine + "'Nowhere' will not show any Error notifications"
+ Environment.NewLine + "'Chat' will print Error notifications in chat"
+ Environment.NewLine + "'Toast' will show Error toast notifications in the bottom right corner"
+ Environment.NewLine + "'Both' will show chat as well as the toast notification");
_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();
@@ -1694,38 +1654,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
ImGui.Separator();
if (_uiShared.MediumTreeNode("Toggles", UIColors.Get("LightlessPurple")))
{
if (ImGui.Checkbox("Disable optional plugin warnings", ref disableOptionalPluginWarnings))
{
_configService.Current.DisableOptionalPluginWarnings = disableOptionalPluginWarnings;
_configService.Save();
}
_uiShared.DrawHelpText("Enabling this will not show any \"Warning\" labeled messages for missing optional plugins.");
if (ImGui.Checkbox("Enable online notifications", ref onlineNotifs))
{
_configService.Current.ShowOnlineNotifications = onlineNotifs;
_configService.Save();
}
_uiShared.DrawHelpText("Enabling this will show a small notification (type: Info) in the bottom right corner when pairs go online.");
using var disabled = ImRaii.Disabled(!onlineNotifs);
if (ImGui.Checkbox("Notify only for individual pairs", ref onlineNotifsPairsOnly))
{
_configService.Current.ShowOnlineNotificationsOnlyForIndividualPairs = onlineNotifsPairsOnly;
_configService.Save();
}
_uiShared.DrawHelpText("Enabling this will only show online notifications (type: Info) for individual pairs.");
if (ImGui.Checkbox("Notify only for named pairs", ref onlineNotifsNamedOnly))
{
_configService.Current.ShowOnlineNotificationsOnlyForNamedPairs = onlineNotifsNamedOnly;
_configService.Save();
}
_uiShared.DrawHelpText("Enabling this will only show online notifications (type: Info) for pairs where you have set an individual note.");
_uiShared.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f);
ImGui.TreePop();
}
}
private void DrawPerformance()
@@ -2661,6 +2589,12 @@ public class SettingsUi : WindowMediatorSubscriberBase
ImGui.EndTabItem();
}
if (ImGui.BeginTabItem("Notifications"))
{
DrawNotificationSettings();
ImGui.EndTabItem();
}
if (ImGui.BeginTabItem("Debug"))
{
DrawDebug();
@@ -2681,4 +2615,568 @@ public class SettingsUi : WindowMediatorSubscriberBase
_wasOpen = IsOpen;
IsOpen = false;
}
private void DrawNotificationSettings()
{
_lastTab = "Notifications";
_uiShared.UnderlinedBigText("Notification System", UIColors.Get("LightlessBlue"));
ImGuiHelpers.ScaledDummy(5);
bool useLightlessNotifications = _configService.Current.UseLightlessNotifications;
if (ImGui.Checkbox("Use Enhanced Lightless Notifications", ref useLightlessNotifications))
{
_configService.Current.UseLightlessNotifications = useLightlessNotifications;
_configService.Save();
}
_uiShared.DrawHelpText("Enable the new enhanced notification system with interactive buttons, animations, and better visual design.");
ImGui.Separator();
_uiShared.UnderlinedBigText("Notification Locations", UIColors.Get("LightlessBlue"));
ImGuiHelpers.ScaledDummy(5);
UiSharedService.ColorTextWrapped("Configure where different types of notifications appear. Enhanced notifications provide modern interactive notifications with backwards compatibility support for classic toast/chat notifications.", ImGuiColors.DalamudGrey);
ImGuiHelpers.ScaledDummy(5);
if (useLightlessNotifications)
{
// Enhanced notification locations (primary)
_uiShared.BigText("Enhanced Notification Locations");
ImGuiHelpers.ScaledDummy(3);
var lightlessLocations = GetLightlessNotificationLocations();
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Info Notifications:");
ImGui.SameLine();
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
_uiShared.DrawCombo("###enhanced_info", lightlessLocations, GetNotificationLocationLabel, (location) =>
{
_configService.Current.LightlessInfoNotification = location;
_configService.Save();
}, _configService.Current.LightlessInfoNotification);
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Warning Notifications:");
ImGui.SameLine();
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
_uiShared.DrawCombo("###enhanced_warning", lightlessLocations, GetNotificationLocationLabel, (location) =>
{
_configService.Current.LightlessWarningNotification = location;
_configService.Save();
}, _configService.Current.LightlessWarningNotification);
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Error Notifications:");
ImGui.SameLine();
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
_uiShared.DrawCombo("###enhanced_error", lightlessLocations, GetNotificationLocationLabel, (location) =>
{
_configService.Current.LightlessErrorNotification = location;
_configService.Save();
}, _configService.Current.LightlessErrorNotification);
ImGuiHelpers.ScaledDummy(5);
// Classic notification locations (backwards compatibility)
if (ImGui.CollapsingHeader("Classic Notification Settings (Backwards Compatibility)"))
{
_uiShared.DrawHelpText("These settings provide backwards compatibility. They will also be used if they're different from the enhanced settings above and don't conflict.");
ImGuiHelpers.ScaledDummy(3);
var classicLocations = GetClassicNotificationLocations();
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Info Fallback:");
ImGui.SameLine();
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
_uiShared.DrawCombo("###classic_info", classicLocations, GetNotificationLocationLabel, (location) =>
{
_configService.Current.InfoNotification = location;
_configService.Save();
}, _configService.Current.InfoNotification);
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Warning Fallback:");
ImGui.SameLine();
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
_uiShared.DrawCombo("###classic_warning", classicLocations, GetNotificationLocationLabel, (location) =>
{
_configService.Current.WarningNotification = location;
_configService.Save();
}, _configService.Current.WarningNotification);
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Error Fallback:");
ImGui.SameLine();
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
_uiShared.DrawCombo("###classic_error", classicLocations, GetNotificationLocationLabel, (location) =>
{
_configService.Current.ErrorNotification = location;
_configService.Save();
}, _configService.Current.ErrorNotification);
}
}
else
{
// Only classic notifications when enhanced is disabled
var classicLocations = GetClassicNotificationLocations();
_uiShared.BigText("Classic Notification Locations");
ImGuiHelpers.ScaledDummy(3);
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Info Notifications:");
ImGui.SameLine();
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
_uiShared.DrawCombo("###info", classicLocations, GetNotificationLocationLabel, (location) =>
{
_configService.Current.InfoNotification = location;
_configService.Save();
}, _configService.Current.InfoNotification);
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Warning Notifications:");
ImGui.SameLine();
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
_uiShared.DrawCombo("###warning", classicLocations, GetNotificationLocationLabel, (location) =>
{
_configService.Current.WarningNotification = location;
_configService.Save();
}, _configService.Current.WarningNotification);
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Error Notifications:");
ImGui.SameLine();
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
_uiShared.DrawCombo("###error", classicLocations, GetNotificationLocationLabel, (location) =>
{
_configService.Current.ErrorNotification = location;
_configService.Save();
}, _configService.Current.ErrorNotification);
}
ImGui.Separator();
if (useLightlessNotifications)
{
UiSharedService.ColorTextWrapped("• Lightless Notifications: Modern animated notifications with interactive buttons", ImGuiColors.DalamudGrey);
UiSharedService.ColorTextWrapped("• Chat: Traditional chat messages with colored text", ImGuiColors.DalamudGrey);
UiSharedService.ColorTextWrapped("• Combined options: Show in multiple locations simultaneously", ImGuiColors.DalamudGrey);
}
else
{
UiSharedService.ColorTextWrapped("• Toast: Dalamud's built-in notification toasts", ImGuiColors.DalamudGrey);
UiSharedService.ColorTextWrapped("• Chat: Traditional chat messages with colored text", ImGuiColors.DalamudGrey);
UiSharedService.ColorTextWrapped("• Both: Show in both toast and chat", ImGuiColors.DalamudGrey);
}
ImGui.Separator();
if (useLightlessNotifications)
{
ImGui.Indent();
// Test notification buttons
if (_uiShared.IconTextButton(FontAwesomeIcon.Bell, "Test Info"))
{
Mediator.Publish(new NotificationMessage("Test Info", "This is a test info notification with the current settings!", NotificationType.Info));
}
ImGui.SameLine();
if (_uiShared.IconTextButton(FontAwesomeIcon.ExclamationTriangle, "Test Warning"))
{
Mediator.Publish(new NotificationMessage("Test Warning", "This is a test warning notification!", NotificationType.Warning));
}
ImGui.SameLine();
if (_uiShared.IconTextButton(FontAwesomeIcon.ExclamationCircle, "Test Error"))
{
Mediator.Publish(new NotificationMessage("Test Error", "This is a test error notification!", NotificationType.Error));
}
_uiShared.DrawHelpText("Click to preview different notification types with your current settings.");
ImGui.Unindent();
ImGui.Separator();
_uiShared.UnderlinedBigText("Basic Settings", UIColors.Get("LightlessBlue"));
ImGuiHelpers.ScaledDummy(5);
int defaultDuration = _configService.Current.DefaultNotificationDurationSeconds;
if (ImGui.SliderInt("Default Duration (seconds)", ref defaultDuration, 3, 60))
{
_configService.Current.DefaultNotificationDurationSeconds = defaultDuration;
_configService.Save();
}
_uiShared.DrawHelpText("How long notifications stay visible by default.");
bool showProgress = _configService.Current.ShowNotificationProgress;
if (ImGui.Checkbox("Show Progress Bars", ref showProgress))
{
_configService.Current.ShowNotificationProgress = showProgress;
_configService.Save();
}
_uiShared.DrawHelpText("Display progress bars for download and other progress-based notifications.");
bool showTimestamp = _configService.Current.ShowNotificationTimestamp;
if (ImGui.Checkbox("Show Timestamps", ref showTimestamp))
{
_configService.Current.ShowNotificationTimestamp = showTimestamp;
_configService.Save();
}
_uiShared.DrawHelpText("Display the time when each notification was created.");
bool autoDismiss = _configService.Current.AutoDismissOnAction;
if (ImGui.Checkbox("Auto-dismiss on Action", ref autoDismiss))
{
_configService.Current.AutoDismissOnAction = autoDismiss;
_configService.Save();
}
_uiShared.DrawHelpText("Automatically close notifications when you click an action button.");
if (useLightlessNotifications)
{
ImGui.Separator();
_uiShared.UnderlinedBigText("Appearance & Animation", UIColors.Get("LightlessBlue"));
ImGuiHelpers.ScaledDummy(5);
float opacity = _configService.Current.NotificationOpacity;
if (ImGui.SliderFloat("Notification Opacity", ref opacity, 0.1f, 1.0f, "%.2f"))
{
_configService.Current.NotificationOpacity = opacity;
_configService.Save();
}
_uiShared.DrawHelpText("Transparency level of notification windows.");
bool enableAnimations = _configService.Current.EnableNotificationAnimations;
if (ImGui.Checkbox("Enable Animations", ref enableAnimations))
{
_configService.Current.EnableNotificationAnimations = enableAnimations;
_configService.Save();
}
_uiShared.DrawHelpText("Enable slide-in/out animations for notifications.");
int maxNotifications = _configService.Current.MaxSimultaneousNotifications;
if (ImGui.SliderInt("Max Simultaneous Notifications", ref maxNotifications, 1, 10))
{
_configService.Current.MaxSimultaneousNotifications = maxNotifications;
_configService.Save();
}
_uiShared.DrawHelpText("Maximum number of notifications that can be shown at once.");
bool enableHistory = _configService.Current.EnableNotificationHistory;
if (ImGui.Checkbox("Enable Notification History", ref enableHistory))
{
_configService.Current.EnableNotificationHistory = enableHistory;
_configService.Save();
}
_uiShared.DrawHelpText("Keep a history of recent notifications that you can review.");
if (enableHistory)
{
ImGui.Indent();
int historySize = _configService.Current.NotificationHistorySize;
if (ImGui.SliderInt("History Size", ref historySize, 10, 200))
{
_configService.Current.NotificationHistorySize = historySize;
_configService.Save();
}
_uiShared.DrawHelpText("Number of notifications to keep in history.");
ImGui.Unindent();
}
}
ImGui.Separator();
_uiShared.UnderlinedBigText("Sound Settings", UIColors.Get("LightlessBlue"));
ImGuiHelpers.ScaledDummy(5);
bool enableSounds = _configService.Current.EnableNotificationSounds;
if (ImGui.Checkbox("Enable Notification Sounds", ref enableSounds))
{
_configService.Current.EnableNotificationSounds = enableSounds;
_configService.Save();
}
_uiShared.DrawHelpText("Play FFXIV sound effects when notifications appear.");
if (enableSounds)
{
ImGui.Indent();
// float soundVolume = _configService.Current.NotificationSoundVolume;
// if (ImGui.SliderFloat("Sound Volume", ref soundVolume, 0.0f, 1.0f, "%.2f"))
// {
// _configService.Current.NotificationSoundVolume = soundVolume;
// _configService.Save();
// }
// _uiShared.DrawHelpText("Volume level for notification sounds (Note: FFXIV doesn't support volume control for SE sounds).");
// bool useCustomSounds = _configService.Current.UseCustomSounds;
// if (ImGui.Checkbox("Use Custom Sound Effects", ref useCustomSounds))
// {
// _configService.Current.UseCustomSounds = useCustomSounds;
// _configService.Save();
// }
// _uiShared.DrawHelpText("Override default sounds with custom FFXIV sound effects.");
DrawSoundCustomization();
// if (useCustomSounds)
// {
// ImGui.Indent();
// DrawSoundCustomization();
// ImGui.Unindent();
// }
ImGui.Unindent();
}
ImGui.Separator();
_uiShared.UnderlinedBigText("Specific Notification Types", UIColors.Get("LightlessBlue"));
ImGuiHelpers.ScaledDummy(5);
UiSharedService.ColorTextWrapped("Configure specific types of notifications and their behavior.", ImGuiColors.DalamudGrey);
ImGuiHelpers.ScaledDummy(3);
// Online Notifications Section
if (_uiShared.MediumTreeNode("Online Status Notifications", UIColors.Get("LightlessGreen")))
{
var onlineNotifs = _configService.Current.ShowOnlineNotifications;
var onlineNotifsPairsOnly = _configService.Current.ShowOnlineNotificationsOnlyForIndividualPairs;
var onlineNotifsNamedOnly = _configService.Current.ShowOnlineNotificationsOnlyForNamedPairs;
if (ImGui.Checkbox("Enable online notifications", ref onlineNotifs))
{
_configService.Current.ShowOnlineNotifications = onlineNotifs;
_configService.Save();
}
_uiShared.DrawHelpText("Show notifications when pairs come online. These will use the Info notification location settings above.");
using var disabled = ImRaii.Disabled(!onlineNotifs);
ImGui.Indent();
if (ImGui.Checkbox("Notify only for individual pairs", ref onlineNotifsPairsOnly))
{
_configService.Current.ShowOnlineNotificationsOnlyForIndividualPairs = onlineNotifsPairsOnly;
_configService.Save();
}
_uiShared.DrawHelpText("Only show online notifications for individual pairs (not syncshell members).");
if (ImGui.Checkbox("Notify only for named pairs", ref onlineNotifsNamedOnly))
{
_configService.Current.ShowOnlineNotificationsOnlyForNamedPairs = onlineNotifsNamedOnly;
_configService.Save();
}
_uiShared.DrawHelpText("Only show online notifications for pairs where you have set an individual note.");
ImGui.Unindent();
_uiShared.ColoredSeparator(UIColors.Get("LightlessGreen"), 1.5f);
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. These settings control additional behavior.", ImGuiColors.DalamudGrey);
ImGuiHelpers.ScaledDummy(3);
// Note: Pairing requests are always shown as interactive notifications
// This section can be expanded later with additional pairing notification settings
_uiShared.ColoredSeparator(UIColors.Get("LightlessBlue"), 1.5f);
ImGui.TreePop();
}
// Download Progress Notifications Section
if (_uiShared.MediumTreeNode("Download Progress Notifications", UIColors.Get("LightlessPurple")))
{
var useNotificationsForDownloads = _configService.Current.UseNotificationsForDownloads;
if (ImGui.Checkbox("Show download progress as notifications", ref useNotificationsForDownloads))
{
_configService.Current.UseNotificationsForDownloads = useNotificationsForDownloads;
_configService.Save();
}
_uiShared.DrawHelpText("Show download progress as clean notifications instead of overlay text. Notifications update in real-time and use the Info notification location settings above.");
_uiShared.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f);
ImGui.TreePop();
}
// System Notifications Section
if (_uiShared.MediumTreeNode("System Notifications", UIColors.Get("LightlessYellow")))
{
var disableOptionalPluginWarnings = _configService.Current.DisableOptionalPluginWarnings;
if (ImGui.Checkbox("Disable optional plugin warnings", ref disableOptionalPluginWarnings))
{
_configService.Current.DisableOptionalPluginWarnings = disableOptionalPluginWarnings;
_configService.Save();
}
_uiShared.DrawHelpText("Disable warning notifications for missing optional plugins.");
_uiShared.ColoredSeparator(UIColors.Get("LightlessYellow"), 1.5f);
ImGui.TreePop();
}
ImGui.Separator();
_uiShared.UnderlinedBigText("Location Descriptions", UIColors.Get("LightlessBlue"));
ImGuiHelpers.ScaledDummy(5);
}
}
private NotificationLocation[] GetLightlessNotificationLocations()
{
return new[]
{
NotificationLocation.LightlessUI,
NotificationLocation.ChatAndLightlessUI,
NotificationLocation.Nowhere
};
}
private NotificationLocation[] GetClassicNotificationLocations()
{
return new[]
{
NotificationLocation.Toast,
NotificationLocation.Chat,
NotificationLocation.Both,
NotificationLocation.Nowhere
};
}
private string GetNotificationLocationLabel(NotificationLocation location)
{
return location switch
{
NotificationLocation.Nowhere => "Nowhere",
NotificationLocation.Chat => "Chat",
NotificationLocation.Toast => "Toast",
NotificationLocation.Both => "Toast + Chat",
NotificationLocation.LightlessUI => "Lightless Notifications",
NotificationLocation.ChatAndLightlessUI => "Chat + Lightless Notifications",
_ => location.ToString()
};
}
private void DrawSoundCustomization()
{
var soundEffects = new[]
{
(1u, "Se1 - Soft chime"),
(2u, "Se2 - Higher chime"),
(3u, "Se3 - Bell tone"),
(4u, "Se4 - Harp tone"),
(5u, "Se5 - Drum/percussion"),
(6u, "Se6 - Mechanical click"),
(7u, "Se7 - Metallic chime"),
(8u, "Se8 - Wooden tone"),
(9u, "Se9 - Wind/flute tone"),
(10u, "Se10 - Magical sparkle"),
(11u, "Se11 - Metallic ring"),
(12u, "Se12 - Deep thud"),
(13u, "Se13 - Tell received ping"),
(14u, "Se14 - Success fanfare"),
(15u, "Se15 - System warning"),
(16u, "Se16 - Error/failure")
};
// Info Sound
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Info Sound:");
ImGui.SameLine();
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
var currentInfoSound = _configService.Current.CustomInfoSoundId;
var currentInfoIndex = Array.FindIndex(soundEffects, s => s.Item1 == currentInfoSound);
if (currentInfoIndex == -1) currentInfoIndex = 1; // Default to Se2
if (ImGui.Combo("###info_sound", ref currentInfoIndex, soundEffects.Select(s => s.Item2).ToArray(), soundEffects.Length))
{
_configService.Current.CustomInfoSoundId = soundEffects[currentInfoIndex].Item1;
_configService.Save();
}
ImGui.SameLine();
ImGui.PushID("test_info");
if (_uiShared.IconButton(FontAwesomeIcon.Play))
{
try
{
FFXIVClientStructs.FFXIV.Client.UI.UIGlobals.PlayChatSoundEffect(_configService.Current.CustomInfoSoundId);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to play test sound");
}
}
ImGui.PopID();
UiSharedService.AttachToolTip("Test this sound");
// Warning Sound
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Warning Sound:");
ImGui.SameLine();
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
var currentWarningSound = _configService.Current.CustomWarningSoundId;
var currentWarningIndex = Array.FindIndex(soundEffects, s => s.Item1 == currentWarningSound);
if (currentWarningIndex == -1) currentWarningIndex = 14; // Default to Se15
if (ImGui.Combo("###warning_sound", ref currentWarningIndex, soundEffects.Select(s => s.Item2).ToArray(), soundEffects.Length))
{
_configService.Current.CustomWarningSoundId = soundEffects[currentWarningIndex].Item1;
_configService.Save();
}
ImGui.SameLine();
ImGui.PushID("test_warning");
if (_uiShared.IconButton(FontAwesomeIcon.Play))
{
try
{
FFXIVClientStructs.FFXIV.Client.UI.UIGlobals.PlayChatSoundEffect(_configService.Current.CustomWarningSoundId);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to play test sound");
}
}
ImGui.PopID();
UiSharedService.AttachToolTip("Test this sound");
// Error Sound
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Error Sound:");
ImGui.SameLine();
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
var currentErrorSound = _configService.Current.CustomErrorSoundId;
var currentErrorIndex = Array.FindIndex(soundEffects, s => s.Item1 == currentErrorSound);
if (currentErrorIndex == -1) currentErrorIndex = 15; // Default to Se16
if (ImGui.Combo("###error_sound", ref currentErrorIndex, soundEffects.Select(s => s.Item2).ToArray(), soundEffects.Length))
{
_configService.Current.CustomErrorSoundId = soundEffects[currentErrorIndex].Item1;
_configService.Save();
}
ImGui.SameLine();
ImGui.PushID("test_error");
if (_uiShared.IconButton(FontAwesomeIcon.Play))
{
try
{
FFXIVClientStructs.FFXIV.Client.UI.UIGlobals.PlayChatSoundEffect(_configService.Current.CustomErrorSoundId);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to play test sound");
}
}
ImGui.PopID();
UiSharedService.AttachToolTip("Test this sound");
ImGuiHelpers.ScaledDummy(5);
if (_uiShared.IconTextButton(FontAwesomeIcon.VolumeUp, "Test All Sounds"))
{
Task.Run(async () =>
{
try
{
FFXIVClientStructs.FFXIV.Client.UI.UIGlobals.PlayChatSoundEffect(_configService.Current.CustomInfoSoundId);
await Task.Delay(800);
FFXIVClientStructs.FFXIV.Client.UI.UIGlobals.PlayChatSoundEffect(_configService.Current.CustomWarningSoundId);
await Task.Delay(800);
FFXIVClientStructs.FFXIV.Client.UI.UIGlobals.PlayChatSoundEffect(_configService.Current.CustomErrorSoundId);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to play test sounds");
}
});
}
_uiShared.DrawHelpText("Play all custom sounds in sequence: Info → Warning → Error");
}
}

View File

@@ -1,4 +1,4 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
@@ -204,19 +204,19 @@ public class TopTabMenu
"debug-user-id",
onAccept: () =>
{
_lightlessNotificationService.ShowNotification(
_lightlessMediator.Publish(new NotificationMessage(
"Pair Accepted",
"Debug pair request was accepted!",
NotificationType.Info,
TimeSpan.FromSeconds(3));
TimeSpan.FromSeconds(3)));
},
onDecline: () =>
{
_lightlessNotificationService.ShowNotification(
_lightlessMediator.Publish(new NotificationMessage(
"Pair Declined",
"Debug pair request was declined.",
NotificationType.Warning,
TimeSpan.FromSeconds(3));
TimeSpan.FromSeconds(3)));
}
);
}
@@ -224,31 +224,31 @@ public class TopTabMenu
ImGui.SameLine();
if (ImGui.Button("Test Info"))
{
_lightlessNotificationService.ShowNotification(
_lightlessMediator.Publish(new NotificationMessage(
"Information",
"This is a test ifno notification with some longer text to see how it wraps. This is a test ifno notification with some longer text to see how it wraps. This is a test ifno notification with some longer text to see how it wraps. This is a test ifno notification with some longer text to see how it wraps.",
NotificationType.Info,
TimeSpan.FromSeconds(5));
TimeSpan.FromSeconds(5)));
}
ImGui.SameLine();
if (ImGui.Button("Test Warning"))
{
_lightlessNotificationService.ShowNotification(
_lightlessMediator.Publish(new NotificationMessage(
"Warning",
"This is a test warning notification.",
NotificationType.Warning,
TimeSpan.FromSeconds(7));
TimeSpan.FromSeconds(7)));
}
ImGui.SameLine();
if (ImGui.Button("Test Error"))
{
_lightlessNotificationService.ShowNotification(
_lightlessMediator.Publish(new NotificationMessage(
"Error",
"This is a test error notification erp police",
NotificationType.Error,
TimeSpan.FromSeconds(10));
TimeSpan.FromSeconds(10)));
}
if (ImGui.Button("Test Download Progress"))

View File

@@ -1,4 +1,4 @@
using LightlessSync.API.Data;
using LightlessSync.API.Data;
using LightlessSync.API.Data.Enum;
using LightlessSync.API.Dto;
using LightlessSync.API.Dto.CharaData;
@@ -122,36 +122,38 @@ public partial class ApiController
// Fire and forget async operation
_ = Task.Run(async () =>
{
try
{
var myCidHash = (await _dalamudUtil.GetCIDAsync().ConfigureAwait(false)).ToString().GetHash256();
try
{
await TryPairWithContentId(request.HashedCid, myCidHash).ConfigureAwait(false);
_pairRequestService.RemoveRequest(request.HashedCid);
_lightlessNotificationService.ShowNotification(
Mediator.Publish(new NotificationMessage(
"Pair Request Accepted",
$"Sent a pair request back to {senderName}.",
NotificationType.Info,
TimeSpan.FromSeconds(3));
TimeSpan.FromSeconds(3)));
}
catch (Exception ex)
{
_lightlessNotificationService.ShowNotification(
Mediator.Publish(new NotificationMessage(
"Failed to Accept Pair Request",
ex.Message,
NotificationType.Error,
TimeSpan.FromSeconds(5));
TimeSpan.FromSeconds(5)));
}
}
});
},
onDecline: () =>
{
_pairRequestService.RemoveRequest(request.HashedCid);
_lightlessNotificationService.ShowNotification(
Mediator.Publish(new NotificationMessage(
"Pair Request Declined",
$"Declined {senderName}'s pair request.",
NotificationType.Info,
TimeSpan.FromSeconds(3));
TimeSpan.FromSeconds(3)));
});
return Task.CompletedTask;