Compare commits

...

2 Commits

6 changed files with 114 additions and 18 deletions

View File

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

View File

@@ -1,4 +1,4 @@
using LightlessSync.API.Dto.Group; using LightlessSync.API.Dto.Group;
using LightlessSync.API.Dto.User; using LightlessSync.API.Dto.User;
using LightlessSync.LightlessConfiguration; using LightlessSync.LightlessConfiguration;
using LightlessSync.Services.Mediator; using LightlessSync.Services.Mediator;
@@ -394,6 +394,10 @@ public class BroadcastService : IHostedService, IMediatorSubscriber
if (!IsLightFinderAvailable) if (!IsLightFinderAvailable)
{ {
_logger.LogWarning("ToggleBroadcast - Lightfinder is not available."); _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; return;
} }
@@ -403,6 +407,10 @@ public class BroadcastService : IHostedService, IMediatorSubscriber
if (!_config.Current.BroadcastEnabled && cooldown is { } cd && cd > TimeSpan.Zero) if (!_config.Current.BroadcastEnabled && cooldown is { } cd && cd > TimeSpan.Zero)
{ {
_logger.LogWarning("Cooldown active. Must wait {Remaining}s before re-enabling.", cd.TotalSeconds); _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; return;
} }
@@ -427,10 +435,19 @@ public class BroadcastService : IHostedService, IMediatorSubscriber
_logger.LogDebug("Toggling broadcast. Server currently broadcasting: {ServerStatus}, setting to: {NewStatus}", isCurrentlyBroadcasting, newStatus); _logger.LogDebug("Toggling broadcast. Server currently broadcasting: {ServerStatus}, setting to: {NewStatus}", isCurrentlyBroadcasting, newStatus);
_mediator.Publish(new EnableBroadcastMessage(hashedCid, 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) catch (Exception ex)
{ {
_logger.LogError(ex, "Failed to determine current broadcast status for toggle"); _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); }).ConfigureAwait(false);
} }
@@ -493,6 +510,7 @@ public class BroadcastService : IHostedService, IMediatorSubscriber
{ {
_logger.LogDebug("Broadcast TTL expired. Disabling broadcast locally."); _logger.LogDebug("Broadcast TTL expired. Disabling broadcast locally.");
ApplyBroadcastDisabled(forcePublish: true); ApplyBroadcastDisabled(forcePublish: true);
_mediator.Publish(new BroadcastExpiredMessage());
} }
} }
else else

View File

@@ -108,6 +108,8 @@ public record EnableBroadcastMessage(string HashedCid, bool Enabled) : MessageBa
public record BroadcastStatusChangedMessage(bool Enabled, TimeSpan? Ttl) : MessageBase; public record BroadcastStatusChangedMessage(bool Enabled, TimeSpan? Ttl) : MessageBase;
public record SyncshellBroadcastsUpdatedMessage : MessageBase; public record SyncshellBroadcastsUpdatedMessage : MessageBase;
public record PairRequestsUpdatedMessage : MessageBase; public record PairRequestsUpdatedMessage : MessageBase;
public record PairRequestReceivedMessage(string SenderName, string SenderId) : MessageBase;
public record BroadcastExpiredMessage : MessageBase;
public record VisibilityChange : MessageBase; public record VisibilityChange : MessageBase;
#pragma warning restore S2094 #pragma warning restore S2094
#pragma warning restore MA0048 // File name must match type name #pragma warning restore MA0048 // File name must match type name

View File

@@ -23,6 +23,7 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
private readonly INotificationManager _notificationManager; private readonly INotificationManager _notificationManager;
private readonly IChatGui _chatGui; private readonly IChatGui _chatGui;
private readonly PairRequestService _pairRequestService; private readonly PairRequestService _pairRequestService;
private readonly BroadcastService _broadcastService;
private readonly HashSet<string> _shownPairRequestNotifications = new(); private readonly HashSet<string> _shownPairRequestNotifications = new();
public NotificationService( public NotificationService(
@@ -32,7 +33,8 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
INotificationManager notificationManager, INotificationManager notificationManager,
IChatGui chatGui, IChatGui chatGui,
LightlessMediator mediator, LightlessMediator mediator,
PairRequestService pairRequestService) : base(logger, mediator) PairRequestService pairRequestService,
BroadcastService broadcastService) : base(logger, mediator)
{ {
_logger = logger; _logger = logger;
_configService = configService; _configService = configService;
@@ -40,13 +42,16 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
_notificationManager = notificationManager; _notificationManager = notificationManager;
_chatGui = chatGui; _chatGui = chatGui;
_pairRequestService = pairRequestService; _pairRequestService = pairRequestService;
_broadcastService = broadcastService;
} }
public Task StartAsync(CancellationToken cancellationToken) public Task StartAsync(CancellationToken cancellationToken)
{ {
Mediator.Subscribe<NotificationMessage>(this, HandleNotificationMessage); Mediator.Subscribe<NotificationMessage>(this, HandleNotificationMessage);
Mediator.Subscribe<PairRequestsUpdatedMessage>(this, HandlePairRequestsUpdated); Mediator.Subscribe<PairRequestsUpdatedMessage>(this, HandlePairRequestsUpdated);
Mediator.Subscribe<PairRequestReceivedMessage>(this, HandlePairRequestReceived);
Mediator.Subscribe<PerformanceNotificationMessage>(this, HandlePerformanceNotification); Mediator.Subscribe<PerformanceNotificationMessage>(this, HandlePerformanceNotification);
Mediator.Subscribe<BroadcastExpiredMessage>(this, HandleBroadcastExpired);
return Task.CompletedTask; return Task.CompletedTask;
} }
@@ -581,6 +586,16 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
_chatGui.Print(se.BuiltString); _chatGui.Print(se.BuiltString);
} }
private void HandlePairRequestReceived(PairRequestReceivedMessage msg)
{
ShowPairRequestNotification(
msg.SenderName,
msg.SenderId,
() => _pairRequestService.AcceptPairRequest(msg.SenderId, msg.SenderName),
() => _pairRequestService.DeclinePairRequest(msg.SenderId)
);
}
private void HandlePairRequestsUpdated(PairRequestsUpdatedMessage _) private void HandlePairRequestsUpdated(PairRequestsUpdatedMessage _)
{ {
var activeRequests = _pairRequestService.GetActiveRequests(); var activeRequests = _pairRequestService.GetActiveRequests();
@@ -598,16 +613,10 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
_shownPairRequestNotifications.Remove(hashedCid); _shownPairRequestNotifications.Remove(hashedCid);
} }
// Show/update notifications for all active requests // Track active requests
foreach (var request in activeRequests) foreach (var request in activeRequests)
{ {
_shownPairRequestNotifications.Add(request.HashedCid); _shownPairRequestNotifications.Add(request.HashedCid);
ShowPairRequestNotification(
request.DisplayName,
request.HashedCid,
() => _pairRequestService.AcceptPairRequest(request.HashedCid, request.DisplayName),
() => _pairRequestService.DeclinePairRequest(request.HashedCid)
);
} }
} }
@@ -750,4 +759,73 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
} }
return $"{playerName} ({userData.UID})"; 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<LightlessNotificationAction> CreateBroadcastExpiredActions()
{
return new List<LightlessNotificationAction>
{
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);
}
}
};
}
} }

View File

@@ -70,6 +70,10 @@ public sealed class PairRequestService : DisposableMediatorSubscriberBase
: _dalamudUtil.RunOnFrameworkThread(() => ToDisplay(entry)).GetAwaiter().GetResult(); : _dalamudUtil.RunOnFrameworkThread(() => ToDisplay(entry)).GetAwaiter().GetResult();
Mediator.Publish(new PairRequestsUpdatedMessage()); Mediator.Publish(new PairRequestsUpdatedMessage());
var senderName = string.IsNullOrEmpty(display.DisplayName) ? "Unknown User" : display.DisplayName;
Mediator.Publish(new PairRequestReceivedMessage(senderName, display.HashedCid));
return display; return display;
} }

View File

@@ -110,14 +110,7 @@ public partial class ApiController
if (dto == null) if (dto == null)
return Task.CompletedTask; return Task.CompletedTask;
var request = _pairRequestService.RegisterIncomingRequest(dto.myHashedCid, dto.message ?? string.Empty); _pairRequestService.RegisterIncomingRequest(dto.myHashedCid, dto.message ?? string.Empty);
var senderName = string.IsNullOrEmpty(request.DisplayName) ? "Unknown User" : request.DisplayName;
_lightlessNotificationService.ShowPairRequestNotification(
senderName,
request.HashedCid,
onAccept: () => _pairRequestService.AcceptPairRequest(request.HashedCid, senderName),
onDecline: () => _pairRequestService.DeclinePairRequest(request.HashedCid));
return Task.CompletedTask; return Task.CompletedTask;
} }