diff --git a/LightlessSync/Plugin.cs b/LightlessSync/Plugin.cs index bac9e29..7bb93d6 100644 --- a/LightlessSync/Plugin.cs +++ b/LightlessSync/Plugin.cs @@ -190,7 +190,8 @@ public sealed class Plugin : IDalamudPlugin notificationManager, chatGui, s.GetRequiredService(), - s.GetRequiredService())); + s.GetRequiredService(), + s.GetRequiredService())); collection.AddSingleton((s) => { var httpClient = new HttpClient(); diff --git a/LightlessSync/Services/BroadcastService.cs b/LightlessSync/Services/BroadcastService.cs index 0cdd3c0..8cc8e5b 100644 --- a/LightlessSync/Services/BroadcastService.cs +++ b/LightlessSync/Services/BroadcastService.cs @@ -1,4 +1,4 @@ -using LightlessSync.API.Dto.Group; +using LightlessSync.API.Dto.Group; using LightlessSync.API.Dto.User; using LightlessSync.LightlessConfiguration; using LightlessSync.Services.Mediator; @@ -394,6 +394,10 @@ public class BroadcastService : IHostedService, IMediatorSubscriber if (!IsLightFinderAvailable) { _logger.LogWarning("ToggleBroadcast - Lightfinder is not available."); + _mediator.Publish(new NotificationMessage( + "Broadcast Unavailable", + "Lightfinder is not available on this server.", + LightlessConfiguration.Models.NotificationType.Error)); return; } @@ -403,6 +407,10 @@ public class BroadcastService : IHostedService, IMediatorSubscriber if (!_config.Current.BroadcastEnabled && cooldown is { } cd && cd > TimeSpan.Zero) { _logger.LogWarning("Cooldown active. Must wait {Remaining}s before re-enabling.", cd.TotalSeconds); + _mediator.Publish(new NotificationMessage( + "Broadcast Cooldown", + $"Please wait {cd.TotalSeconds:F0} seconds before re-enabling broadcast.", + LightlessConfiguration.Models.NotificationType.Warning)); return; } @@ -427,10 +435,19 @@ public class BroadcastService : IHostedService, IMediatorSubscriber _logger.LogDebug("Toggling broadcast. Server currently broadcasting: {ServerStatus}, setting to: {NewStatus}", isCurrentlyBroadcasting, newStatus); _mediator.Publish(new EnableBroadcastMessage(hashedCid, newStatus)); + + _mediator.Publish(new NotificationMessage( + newStatus ? "Broadcast Enabled" : "Broadcast Disabled", + newStatus ? "Your Lightfinder broadcast has been enabled." : "Your Lightfinder broadcast has been disabled.", + LightlessConfiguration.Models.NotificationType.Info)); } catch (Exception ex) { _logger.LogError(ex, "Failed to determine current broadcast status for toggle"); + _mediator.Publish(new NotificationMessage( + "Broadcast Failed", + $"Failed to toggle broadcast: {ex.Message}", + LightlessConfiguration.Models.NotificationType.Error)); } }).ConfigureAwait(false); } @@ -493,6 +510,7 @@ public class BroadcastService : IHostedService, IMediatorSubscriber { _logger.LogDebug("Broadcast TTL expired. Disabling broadcast locally."); ApplyBroadcastDisabled(forcePublish: true); + _mediator.Publish(new BroadcastExpiredMessage()); } } else diff --git a/LightlessSync/Services/Mediator/Messages.cs b/LightlessSync/Services/Mediator/Messages.cs index 8d45ed6..134b19c 100644 --- a/LightlessSync/Services/Mediator/Messages.cs +++ b/LightlessSync/Services/Mediator/Messages.cs @@ -108,6 +108,7 @@ public record EnableBroadcastMessage(string HashedCid, bool Enabled) : MessageBa public record BroadcastStatusChangedMessage(bool Enabled, TimeSpan? Ttl) : MessageBase; public record SyncshellBroadcastsUpdatedMessage : MessageBase; public record PairRequestsUpdatedMessage : MessageBase; +public record BroadcastExpiredMessage : MessageBase; public record VisibilityChange : MessageBase; #pragma warning restore S2094 #pragma warning restore MA0048 // File name must match type name \ No newline at end of file diff --git a/LightlessSync/Services/NotificationService.cs b/LightlessSync/Services/NotificationService.cs index 755e756..1dc65af 100644 --- a/LightlessSync/Services/NotificationService.cs +++ b/LightlessSync/Services/NotificationService.cs @@ -23,6 +23,7 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ private readonly INotificationManager _notificationManager; private readonly IChatGui _chatGui; private readonly PairRequestService _pairRequestService; + private readonly BroadcastService _broadcastService; private readonly HashSet _shownPairRequestNotifications = new(); public NotificationService( @@ -32,7 +33,8 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ INotificationManager notificationManager, IChatGui chatGui, LightlessMediator mediator, - PairRequestService pairRequestService) : base(logger, mediator) + PairRequestService pairRequestService, + BroadcastService broadcastService) : base(logger, mediator) { _logger = logger; _configService = configService; @@ -40,6 +42,7 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ _notificationManager = notificationManager; _chatGui = chatGui; _pairRequestService = pairRequestService; + _broadcastService = broadcastService; } public Task StartAsync(CancellationToken cancellationToken) @@ -47,6 +50,7 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ Mediator.Subscribe(this, HandleNotificationMessage); Mediator.Subscribe(this, HandlePairRequestsUpdated); Mediator.Subscribe(this, HandlePerformanceNotification); + Mediator.Subscribe(this, HandleBroadcastExpired); return Task.CompletedTask; } @@ -750,4 +754,73 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ } return $"{playerName} ({userData.UID})"; } + + private void HandleBroadcastExpired(BroadcastExpiredMessage _) + { + var location = GetNotificationLocation(NotificationType.Warning); + + if (location == NotificationLocation.Chat || location == NotificationLocation.ChatAndLightlessUi) + { + ShowChat(new NotificationMessage("Broadcast Expired", "Your Lightfinder broadcast has expired after 3 hours.", NotificationType.Warning)); + } + + if ((location == NotificationLocation.LightlessUi || location == NotificationLocation.ChatAndLightlessUi) + && _configService.Current.UseLightlessNotifications) + { + var notification = new LightlessNotification + { + Id = "broadcast_expired", + Title = "Broadcast Expired", + Message = "Your Lightfinder broadcast has expired after 3 hours. Would you like to re-enable it?", + Type = NotificationType.Warning, + Duration = TimeSpan.FromSeconds(_configService.Current.WarningNotificationDurationSeconds), + SoundEffectId = GetSoundEffectId(NotificationType.Warning, null), + Actions = CreateBroadcastExpiredActions() + }; + + if (notification.SoundEffectId.HasValue) + { + PlayNotificationSound(notification.SoundEffectId.Value); + } + + Mediator.Publish(new LightlessNotificationMessage(notification)); + } + else if (location != NotificationLocation.Nowhere && location != NotificationLocation.Chat) + { + HandleNotificationMessage(new NotificationMessage("Broadcast Expired", "Your Lightfinder broadcast has expired after 3 hours.", NotificationType.Warning)); + } + } + + private List CreateBroadcastExpiredActions() + { + return new List + { + new() + { + Id = "re_enable", + Label = "Re-enable Broadcast", + Icon = FontAwesomeIcon.Plus, + Color = UIColors.Get("LightlessGreen"), + IsPrimary = true, + OnClick = (n) => + { + _logger.LogInformation("Re-enabling broadcast from notification"); + _broadcastService.ToggleBroadcast(); + DismissNotification(n); + } + }, + new() + { + Id = "close", + Label = "Close", + Icon = FontAwesomeIcon.Times, + Color = UIColors.Get("DimRed"), + OnClick = (n) => + { + _logger.LogInformation("Broadcast expiration notification dismissed"); + DismissNotification(n); + } + } + }; + } } \ No newline at end of file