Ajout du système de notifications : service, configuration et persistance des notifications introduits. Mise à jour de l'UI pour afficher et gérer les notifications Syncshell.
This commit is contained in:
@@ -2,29 +2,45 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MareSynchronos.Services.Mediator;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.MareConfiguration.Configurations;
|
||||
using MareSynchronos.MareConfiguration.Models;
|
||||
|
||||
namespace MareSynchronos.Services.Notifications;
|
||||
|
||||
public enum NotificationCategory
|
||||
{
|
||||
AutoDetect,
|
||||
Syncshell,
|
||||
}
|
||||
|
||||
public sealed record NotificationEntry(NotificationCategory Category, string Id, string Title, string? Description, DateTime CreatedAt)
|
||||
{
|
||||
public static NotificationEntry AutoDetect(string uid, string displayName)
|
||||
=> new(NotificationCategory.AutoDetect, uid, displayName, "Nouvelle demande d'appairage via AutoDetect.", DateTime.UtcNow);
|
||||
|
||||
public static NotificationEntry SyncshellPublic(string gid, string aliasOrGid)
|
||||
=> new(NotificationCategory.Syncshell, gid, $"Syncshell publique: {aliasOrGid}", "La Syncshell est désormais visible via AutoDetect.", DateTime.UtcNow);
|
||||
|
||||
public static NotificationEntry SyncshellNotPublic(string gid, string aliasOrGid)
|
||||
=> new(NotificationCategory.Syncshell, gid, $"Syncshell non publique: {aliasOrGid}", "La Syncshell n'est plus visible via AutoDetect.", DateTime.UtcNow);
|
||||
}
|
||||
|
||||
public sealed class NotificationTracker
|
||||
{
|
||||
private const int MaxStored = 100;
|
||||
|
||||
private readonly MareMediator _mediator;
|
||||
private readonly NotificationsConfigService _configService;
|
||||
private readonly Dictionary<(NotificationCategory Category, string Id), NotificationEntry> _entries = new();
|
||||
private readonly object _lock = new();
|
||||
|
||||
public NotificationTracker(MareMediator mediator)
|
||||
public NotificationTracker(MareMediator mediator, NotificationsConfigService configService)
|
||||
{
|
||||
_mediator = mediator;
|
||||
_configService = configService;
|
||||
LoadPersisted();
|
||||
PublishState();
|
||||
}
|
||||
|
||||
public void Upsert(NotificationEntry entry)
|
||||
@@ -32,6 +48,8 @@ public sealed class NotificationTracker
|
||||
lock (_lock)
|
||||
{
|
||||
_entries[(entry.Category, entry.Id)] = entry;
|
||||
TrimIfNecessary_NoLock();
|
||||
Persist_NoLock();
|
||||
}
|
||||
PublishState();
|
||||
}
|
||||
@@ -41,6 +59,7 @@ public sealed class NotificationTracker
|
||||
lock (_lock)
|
||||
{
|
||||
_entries.Remove((category, id));
|
||||
Persist_NoLock();
|
||||
}
|
||||
PublishState();
|
||||
}
|
||||
@@ -70,4 +89,56 @@ public sealed class NotificationTracker
|
||||
{
|
||||
_mediator.Publish(new NotificationStateChanged(Count));
|
||||
}
|
||||
|
||||
private void LoadPersisted()
|
||||
{
|
||||
try
|
||||
{
|
||||
var list = _configService.Current.Notifications ?? new List<StoredNotification>();
|
||||
foreach (var s in list)
|
||||
{
|
||||
if (!Enum.TryParse<NotificationCategory>(s.Category, out var cat)) continue;
|
||||
var entry = new NotificationEntry(cat, s.Id, s.Title, s.Description, s.CreatedAtUtc);
|
||||
_entries[(entry.Category, entry.Id)] = entry;
|
||||
}
|
||||
TrimIfNecessary_NoLock();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore load errors, start empty
|
||||
}
|
||||
}
|
||||
|
||||
private void Persist_NoLock()
|
||||
{
|
||||
try
|
||||
{
|
||||
var stored = _entries.Values
|
||||
.OrderBy(e => e.CreatedAt)
|
||||
.Select(e => new StoredNotification
|
||||
{
|
||||
Category = e.Category.ToString(),
|
||||
Id = e.Id,
|
||||
Title = e.Title,
|
||||
Description = e.Description,
|
||||
CreatedAtUtc = e.CreatedAt
|
||||
})
|
||||
.ToList();
|
||||
_configService.Current.Notifications = stored;
|
||||
_configService.Save();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore persistence errors
|
||||
}
|
||||
}
|
||||
|
||||
private void TrimIfNecessary_NoLock()
|
||||
{
|
||||
if (_entries.Count <= MaxStored) return;
|
||||
foreach (var kv in _entries.Values.OrderByDescending(v => v.CreatedAt).Skip(MaxStored).ToList())
|
||||
{
|
||||
_entries.Remove((kv.Category, kv.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Dalamud.Plugin.Services;
|
||||
@@ -17,22 +18,29 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
||||
private readonly INotificationManager _notificationManager;
|
||||
private readonly IChatGui _chatGui;
|
||||
private readonly MareConfigService _configurationService;
|
||||
private readonly Services.Notifications.NotificationTracker _notificationTracker;
|
||||
private readonly PlayerData.Pairs.PairManager _pairManager;
|
||||
|
||||
public NotificationService(ILogger<NotificationService> logger, MareMediator mediator,
|
||||
DalamudUtilService dalamudUtilService,
|
||||
INotificationManager notificationManager,
|
||||
IChatGui chatGui, MareConfigService configurationService) : base(logger, mediator)
|
||||
IChatGui chatGui, MareConfigService configurationService,
|
||||
Services.Notifications.NotificationTracker notificationTracker,
|
||||
PlayerData.Pairs.PairManager pairManager) : base(logger, mediator)
|
||||
{
|
||||
_dalamudUtilService = dalamudUtilService;
|
||||
_notificationManager = notificationManager;
|
||||
_chatGui = chatGui;
|
||||
_configurationService = configurationService;
|
||||
_notificationTracker = notificationTracker;
|
||||
_pairManager = pairManager;
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Mediator.Subscribe<NotificationMessage>(this, ShowNotification);
|
||||
Mediator.Subscribe<DualNotificationMessage>(this, ShowDualNotification);
|
||||
Mediator.Subscribe<Services.Mediator.SyncshellAutoDetectStateChanged>(this, OnSyncshellAutoDetectStateChanged);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -113,6 +121,31 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
||||
ShowChat(baseMsg);
|
||||
}
|
||||
|
||||
private void OnSyncshellAutoDetectStateChanged(SyncshellAutoDetectStateChanged msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (msg.Visible) return; // only handle transition to not visible
|
||||
|
||||
var gid = msg.Gid;
|
||||
// Try to resolve alias from PairManager snapshot; fallback to gid
|
||||
var alias = _pairManager.Groups.Values.FirstOrDefault(g => string.Equals(g.GID, gid, StringComparison.OrdinalIgnoreCase))?.GroupAliasOrGID ?? gid;
|
||||
|
||||
var title = $"Syncshell non publique: {alias}";
|
||||
var message = "La Syncshell n'est plus visible via AutoDetect.";
|
||||
|
||||
// Show toast + chat
|
||||
ShowDualNotification(new DualNotificationMessage(title, message, NotificationType.Info, TimeSpan.FromSeconds(4)));
|
||||
|
||||
// Persist into notification center
|
||||
_notificationTracker.Upsert(Services.Notifications.NotificationEntry.SyncshellNotPublic(gid, alias));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore failures
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ShouldForceChat(NotificationMessage msg, out bool appendInstruction)
|
||||
{
|
||||
appendInstruction = false;
|
||||
|
||||
@@ -3,6 +3,7 @@ using MareSynchronos.PlayerData.Pairs;
|
||||
using MareSynchronos.Services.AutoDetect;
|
||||
using MareSynchronos.Services.Mediator;
|
||||
using MareSynchronos.Services.ServerConfiguration;
|
||||
using MareSynchronos.Services.Notifications;
|
||||
using MareSynchronos.UI;
|
||||
using MareSynchronos.UI.Components.Popup;
|
||||
using MareSynchronos.WebAPI;
|
||||
@@ -21,10 +22,11 @@ public class UiFactory
|
||||
private readonly MareProfileManager _mareProfileManager;
|
||||
private readonly PerformanceCollectorService _performanceCollectorService;
|
||||
private readonly SyncshellDiscoveryService _syncshellDiscoveryService;
|
||||
private readonly NotificationTracker _notificationTracker;
|
||||
|
||||
public UiFactory(ILoggerFactory loggerFactory, MareMediator mareMediator, ApiController apiController,
|
||||
UiSharedService uiSharedService, PairManager pairManager, SyncshellDiscoveryService syncshellDiscoveryService, ServerConfigurationManager serverConfigManager,
|
||||
MareProfileManager mareProfileManager, PerformanceCollectorService performanceCollectorService)
|
||||
MareProfileManager mareProfileManager, PerformanceCollectorService performanceCollectorService, NotificationTracker notificationTracker)
|
||||
{
|
||||
_loggerFactory = loggerFactory;
|
||||
_mareMediator = mareMediator;
|
||||
@@ -35,12 +37,13 @@ public class UiFactory
|
||||
_serverConfigManager = serverConfigManager;
|
||||
_mareProfileManager = mareProfileManager;
|
||||
_performanceCollectorService = performanceCollectorService;
|
||||
_notificationTracker = notificationTracker;
|
||||
}
|
||||
|
||||
public SyncshellAdminUI CreateSyncshellAdminUi(GroupFullInfoDto dto)
|
||||
{
|
||||
return new SyncshellAdminUI(_loggerFactory.CreateLogger<SyncshellAdminUI>(), _mareMediator,
|
||||
_apiController, _uiSharedService, _pairManager, _syncshellDiscoveryService, dto, _performanceCollectorService);
|
||||
_apiController, _uiSharedService, _pairManager, _syncshellDiscoveryService, dto, _performanceCollectorService, _notificationTracker);
|
||||
}
|
||||
|
||||
public StandaloneProfileUi CreateStandaloneProfileUi(Pair pair)
|
||||
|
||||
Reference in New Issue
Block a user