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:
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using MareSynchronos.MareConfiguration.Models;
|
||||||
|
|
||||||
|
namespace MareSynchronos.MareConfiguration.Configurations;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class NotificationsConfig : IMareConfiguration
|
||||||
|
{
|
||||||
|
public List<StoredNotification> Notifications { get; set; } = new();
|
||||||
|
public int Version { get; set; } = 1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MareSynchronos.MareConfiguration.Models;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class StoredNotification
|
||||||
|
{
|
||||||
|
public string Category { get; set; } = string.Empty; // name of enum NotificationCategory
|
||||||
|
public string Id { get; set; } = string.Empty;
|
||||||
|
public string Title { get; set; } = string.Empty;
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public DateTime CreatedAtUtc { get; set; } = DateTime.UtcNow;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using MareSynchronos.MareConfiguration.Configurations;
|
||||||
|
|
||||||
|
namespace MareSynchronos.MareConfiguration;
|
||||||
|
|
||||||
|
public class NotificationsConfigService : ConfigurationServiceBase<NotificationsConfig>
|
||||||
|
{
|
||||||
|
public const string ConfigName = "notifications.json";
|
||||||
|
|
||||||
|
public NotificationsConfigService(string configDir) : base(configDir)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ConfigurationName => ConfigName;
|
||||||
|
}
|
||||||
@@ -167,6 +167,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddSingleton((s) => new ServerBlockConfigService(pluginInterface.ConfigDirectory.FullName));
|
collection.AddSingleton((s) => new ServerBlockConfigService(pluginInterface.ConfigDirectory.FullName));
|
||||||
collection.AddSingleton((s) => new CharaDataConfigService(pluginInterface.ConfigDirectory.FullName));
|
collection.AddSingleton((s) => new CharaDataConfigService(pluginInterface.ConfigDirectory.FullName));
|
||||||
collection.AddSingleton((s) => new RemoteConfigCacheService(pluginInterface.ConfigDirectory.FullName));
|
collection.AddSingleton((s) => new RemoteConfigCacheService(pluginInterface.ConfigDirectory.FullName));
|
||||||
|
collection.AddSingleton((s) => new NotificationsConfigService(pluginInterface.ConfigDirectory.FullName));
|
||||||
collection.AddSingleton<IConfigService<IMareConfiguration>>(s => s.GetRequiredService<MareConfigService>());
|
collection.AddSingleton<IConfigService<IMareConfiguration>>(s => s.GetRequiredService<MareConfigService>());
|
||||||
collection.AddSingleton<IConfigService<IMareConfiguration>>(s => s.GetRequiredService<ServerConfigService>());
|
collection.AddSingleton<IConfigService<IMareConfiguration>>(s => s.GetRequiredService<ServerConfigService>());
|
||||||
collection.AddSingleton<IConfigService<IMareConfiguration>>(s => s.GetRequiredService<NotesConfigService>());
|
collection.AddSingleton<IConfigService<IMareConfiguration>>(s => s.GetRequiredService<NotesConfigService>());
|
||||||
@@ -178,6 +179,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddSingleton<IConfigService<IMareConfiguration>>(s => s.GetRequiredService<ServerBlockConfigService>());
|
collection.AddSingleton<IConfigService<IMareConfiguration>>(s => s.GetRequiredService<ServerBlockConfigService>());
|
||||||
collection.AddSingleton<IConfigService<IMareConfiguration>>(s => s.GetRequiredService<CharaDataConfigService>());
|
collection.AddSingleton<IConfigService<IMareConfiguration>>(s => s.GetRequiredService<CharaDataConfigService>());
|
||||||
collection.AddSingleton<IConfigService<IMareConfiguration>>(s => s.GetRequiredService<RemoteConfigCacheService>());
|
collection.AddSingleton<IConfigService<IMareConfiguration>>(s => s.GetRequiredService<RemoteConfigCacheService>());
|
||||||
|
collection.AddSingleton<IConfigService<IMareConfiguration>>(s => s.GetRequiredService<NotificationsConfigService>());
|
||||||
collection.AddSingleton<ConfigurationMigrator>();
|
collection.AddSingleton<ConfigurationMigrator>();
|
||||||
collection.AddSingleton<ConfigurationSaveService>();
|
collection.AddSingleton<ConfigurationSaveService>();
|
||||||
|
|
||||||
|
|||||||
@@ -2,29 +2,45 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
|
using MareSynchronos.MareConfiguration;
|
||||||
|
using MareSynchronos.MareConfiguration.Configurations;
|
||||||
|
using MareSynchronos.MareConfiguration.Models;
|
||||||
|
|
||||||
namespace MareSynchronos.Services.Notifications;
|
namespace MareSynchronos.Services.Notifications;
|
||||||
|
|
||||||
public enum NotificationCategory
|
public enum NotificationCategory
|
||||||
{
|
{
|
||||||
AutoDetect,
|
AutoDetect,
|
||||||
|
Syncshell,
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed record NotificationEntry(NotificationCategory Category, string Id, string Title, string? Description, DateTime CreatedAt)
|
public sealed record NotificationEntry(NotificationCategory Category, string Id, string Title, string? Description, DateTime CreatedAt)
|
||||||
{
|
{
|
||||||
public static NotificationEntry AutoDetect(string uid, string displayName)
|
public static NotificationEntry AutoDetect(string uid, string displayName)
|
||||||
=> new(NotificationCategory.AutoDetect, uid, displayName, "Nouvelle demande d'appairage via AutoDetect.", DateTime.UtcNow);
|
=> 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
|
public sealed class NotificationTracker
|
||||||
{
|
{
|
||||||
|
private const int MaxStored = 100;
|
||||||
|
|
||||||
private readonly MareMediator _mediator;
|
private readonly MareMediator _mediator;
|
||||||
|
private readonly NotificationsConfigService _configService;
|
||||||
private readonly Dictionary<(NotificationCategory Category, string Id), NotificationEntry> _entries = new();
|
private readonly Dictionary<(NotificationCategory Category, string Id), NotificationEntry> _entries = new();
|
||||||
private readonly object _lock = new();
|
private readonly object _lock = new();
|
||||||
|
|
||||||
public NotificationTracker(MareMediator mediator)
|
public NotificationTracker(MareMediator mediator, NotificationsConfigService configService)
|
||||||
{
|
{
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
|
_configService = configService;
|
||||||
|
LoadPersisted();
|
||||||
|
PublishState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Upsert(NotificationEntry entry)
|
public void Upsert(NotificationEntry entry)
|
||||||
@@ -32,6 +48,8 @@ public sealed class NotificationTracker
|
|||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_entries[(entry.Category, entry.Id)] = entry;
|
_entries[(entry.Category, entry.Id)] = entry;
|
||||||
|
TrimIfNecessary_NoLock();
|
||||||
|
Persist_NoLock();
|
||||||
}
|
}
|
||||||
PublishState();
|
PublishState();
|
||||||
}
|
}
|
||||||
@@ -41,6 +59,7 @@ public sealed class NotificationTracker
|
|||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_entries.Remove((category, id));
|
_entries.Remove((category, id));
|
||||||
|
Persist_NoLock();
|
||||||
}
|
}
|
||||||
PublishState();
|
PublishState();
|
||||||
}
|
}
|
||||||
@@ -70,4 +89,56 @@ public sealed class NotificationTracker
|
|||||||
{
|
{
|
||||||
_mediator.Publish(new NotificationStateChanged(Count));
|
_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;
|
||||||
|
using System.Linq;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Interface.ImGuiNotification;
|
using Dalamud.Interface.ImGuiNotification;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
@@ -17,22 +18,29 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
|||||||
private readonly INotificationManager _notificationManager;
|
private readonly INotificationManager _notificationManager;
|
||||||
private readonly IChatGui _chatGui;
|
private readonly IChatGui _chatGui;
|
||||||
private readonly MareConfigService _configurationService;
|
private readonly MareConfigService _configurationService;
|
||||||
|
private readonly Services.Notifications.NotificationTracker _notificationTracker;
|
||||||
|
private readonly PlayerData.Pairs.PairManager _pairManager;
|
||||||
|
|
||||||
public NotificationService(ILogger<NotificationService> logger, MareMediator mediator,
|
public NotificationService(ILogger<NotificationService> logger, MareMediator mediator,
|
||||||
DalamudUtilService dalamudUtilService,
|
DalamudUtilService dalamudUtilService,
|
||||||
INotificationManager notificationManager,
|
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;
|
_dalamudUtilService = dalamudUtilService;
|
||||||
_notificationManager = notificationManager;
|
_notificationManager = notificationManager;
|
||||||
_chatGui = chatGui;
|
_chatGui = chatGui;
|
||||||
_configurationService = configurationService;
|
_configurationService = configurationService;
|
||||||
|
_notificationTracker = notificationTracker;
|
||||||
|
_pairManager = pairManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken)
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
Mediator.Subscribe<NotificationMessage>(this, ShowNotification);
|
Mediator.Subscribe<NotificationMessage>(this, ShowNotification);
|
||||||
Mediator.Subscribe<DualNotificationMessage>(this, ShowDualNotification);
|
Mediator.Subscribe<DualNotificationMessage>(this, ShowDualNotification);
|
||||||
|
Mediator.Subscribe<Services.Mediator.SyncshellAutoDetectStateChanged>(this, OnSyncshellAutoDetectStateChanged);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +121,31 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
|||||||
ShowChat(baseMsg);
|
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)
|
private static bool ShouldForceChat(NotificationMessage msg, out bool appendInstruction)
|
||||||
{
|
{
|
||||||
appendInstruction = false;
|
appendInstruction = false;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using MareSynchronos.PlayerData.Pairs;
|
|||||||
using MareSynchronos.Services.AutoDetect;
|
using MareSynchronos.Services.AutoDetect;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.Services.ServerConfiguration;
|
using MareSynchronos.Services.ServerConfiguration;
|
||||||
|
using MareSynchronos.Services.Notifications;
|
||||||
using MareSynchronos.UI;
|
using MareSynchronos.UI;
|
||||||
using MareSynchronos.UI.Components.Popup;
|
using MareSynchronos.UI.Components.Popup;
|
||||||
using MareSynchronos.WebAPI;
|
using MareSynchronos.WebAPI;
|
||||||
@@ -21,10 +22,11 @@ public class UiFactory
|
|||||||
private readonly MareProfileManager _mareProfileManager;
|
private readonly MareProfileManager _mareProfileManager;
|
||||||
private readonly PerformanceCollectorService _performanceCollectorService;
|
private readonly PerformanceCollectorService _performanceCollectorService;
|
||||||
private readonly SyncshellDiscoveryService _syncshellDiscoveryService;
|
private readonly SyncshellDiscoveryService _syncshellDiscoveryService;
|
||||||
|
private readonly NotificationTracker _notificationTracker;
|
||||||
|
|
||||||
public UiFactory(ILoggerFactory loggerFactory, MareMediator mareMediator, ApiController apiController,
|
public UiFactory(ILoggerFactory loggerFactory, MareMediator mareMediator, ApiController apiController,
|
||||||
UiSharedService uiSharedService, PairManager pairManager, SyncshellDiscoveryService syncshellDiscoveryService, ServerConfigurationManager serverConfigManager,
|
UiSharedService uiSharedService, PairManager pairManager, SyncshellDiscoveryService syncshellDiscoveryService, ServerConfigurationManager serverConfigManager,
|
||||||
MareProfileManager mareProfileManager, PerformanceCollectorService performanceCollectorService)
|
MareProfileManager mareProfileManager, PerformanceCollectorService performanceCollectorService, NotificationTracker notificationTracker)
|
||||||
{
|
{
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
_mareMediator = mareMediator;
|
_mareMediator = mareMediator;
|
||||||
@@ -35,12 +37,13 @@ public class UiFactory
|
|||||||
_serverConfigManager = serverConfigManager;
|
_serverConfigManager = serverConfigManager;
|
||||||
_mareProfileManager = mareProfileManager;
|
_mareProfileManager = mareProfileManager;
|
||||||
_performanceCollectorService = performanceCollectorService;
|
_performanceCollectorService = performanceCollectorService;
|
||||||
|
_notificationTracker = notificationTracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SyncshellAdminUI CreateSyncshellAdminUi(GroupFullInfoDto dto)
|
public SyncshellAdminUI CreateSyncshellAdminUi(GroupFullInfoDto dto)
|
||||||
{
|
{
|
||||||
return new SyncshellAdminUI(_loggerFactory.CreateLogger<SyncshellAdminUI>(), _mareMediator,
|
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)
|
public StandaloneProfileUi CreateStandaloneProfileUi(Pair pair)
|
||||||
|
|||||||
@@ -860,6 +860,14 @@ if (showNearby && pendingInvites > 0)
|
|||||||
ImGuiHelpers.ScaledDummy(4f);
|
ImGuiHelpers.ScaledDummy(4f);
|
||||||
var indent = 18f * ImGuiHelpers.GlobalScale;
|
var indent = 18f * ImGuiHelpers.GlobalScale;
|
||||||
ImGui.Indent(indent);
|
ImGui.Indent(indent);
|
||||||
|
|
||||||
|
// Use a table to guarantee right-aligned action within the card content area
|
||||||
|
var actionButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.UserPlus);
|
||||||
|
if (ImGui.BeginTable("nearby-table", 2, ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.RowBg | ImGuiTableFlags.PadOuterX | ImGuiTableFlags.BordersInnerV))
|
||||||
|
{
|
||||||
|
ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthStretch, 1f);
|
||||||
|
ImGui.TableSetupColumn("Action", ImGuiTableColumnFlags.WidthFixed, actionButtonSize.X);
|
||||||
|
|
||||||
foreach (var e in nearbyEntries)
|
foreach (var e in nearbyEntries)
|
||||||
{
|
{
|
||||||
if (!e.AcceptPairRequests || string.IsNullOrEmpty(e.Token))
|
if (!e.AcceptPairRequests || string.IsNullOrEmpty(e.Token))
|
||||||
@@ -867,21 +875,19 @@ if (showNearby && pendingInvites > 0)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui.TableNextRow();
|
||||||
|
|
||||||
|
ImGui.TableSetColumnIndex(0);
|
||||||
var name = e.DisplayName ?? e.Name;
|
var name = e.DisplayName ?? e.Name;
|
||||||
ImGui.AlignTextToFramePadding();
|
ImGui.AlignTextToFramePadding();
|
||||||
ImGui.TextUnformatted(name);
|
ImGui.TextUnformatted(name);
|
||||||
var right = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth();
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
var statusButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.UserPlus);
|
// Right column: action button, aligned to the right within the column
|
||||||
ImGui.SetCursorPosX(right - statusButtonSize.X);
|
ImGui.TableSetColumnIndex(1);
|
||||||
if (!e.AcceptPairRequests)
|
var curX = ImGui.GetCursorPosX();
|
||||||
{
|
var availX = ImGui.GetContentRegionAvail().X; // width of the action column
|
||||||
_uiSharedService.IconText(FontAwesomeIcon.Ban, ImGuiColors.DalamudGrey3);
|
ImGui.SetCursorPosX(curX + MathF.Max(0, availX - actionButtonSize.X));
|
||||||
UiSharedService.AttachToolTip("Les demandes sont désactivées pour ce joueur");
|
|
||||||
}
|
|
||||||
else if (!string.IsNullOrEmpty(e.Token))
|
|
||||||
{
|
|
||||||
using (ImRaii.PushId(e.Token ?? e.Uid ?? e.Name ?? string.Empty))
|
using (ImRaii.PushId(e.Token ?? e.Uid ?? e.Name ?? string.Empty))
|
||||||
{
|
{
|
||||||
if (_uiSharedService.IconButton(FontAwesomeIcon.UserPlus))
|
if (_uiSharedService.IconButton(FontAwesomeIcon.UserPlus))
|
||||||
@@ -891,12 +897,9 @@ if (showNearby && pendingInvites > 0)
|
|||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Envoyer une invitation d'apparaige");
|
UiSharedService.AttachToolTip("Envoyer une invitation d'apparaige");
|
||||||
}
|
}
|
||||||
else
|
ImGui.EndTable();
|
||||||
{
|
|
||||||
_uiSharedService.IconText(FontAwesomeIcon.QuestionCircle, ImGuiColors.DalamudGrey3);
|
|
||||||
UiSharedService.AttachToolTip("Impossible d'inviter ce joueur");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Unindent(indent);
|
ImGui.Unindent(indent);
|
||||||
}, stretchWidth: true);
|
}, stretchWidth: true);
|
||||||
}
|
}
|
||||||
@@ -1173,6 +1176,9 @@ if (showNearby && pendingInvites > 0)
|
|||||||
case NotificationCategory.AutoDetect:
|
case NotificationCategory.AutoDetect:
|
||||||
DrawAutoDetectNotification(notification);
|
DrawAutoDetectNotification(notification);
|
||||||
break;
|
break;
|
||||||
|
case NotificationCategory.Syncshell:
|
||||||
|
DrawSyncshellNotification(notification);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
UiSharedService.DrawCard($"notification-{notification.Category}-{notification.Id}", () =>
|
UiSharedService.DrawCard($"notification-{notification.Category}-{notification.Id}", () =>
|
||||||
{
|
{
|
||||||
@@ -1237,6 +1243,30 @@ if (showNearby && pendingInvites > 0)
|
|||||||
}, stretchWidth: true);
|
}, stretchWidth: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawSyncshellNotification(NotificationEntry notification)
|
||||||
|
{
|
||||||
|
UiSharedService.DrawCard($"notification-syncshell-{notification.Id}", () =>
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted(notification.Title);
|
||||||
|
if (!string.IsNullOrEmpty(notification.Description))
|
||||||
|
{
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey3);
|
||||||
|
ImGui.TextWrapped(notification.Description);
|
||||||
|
ImGui.PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiHelpers.ScaledDummy(3f);
|
||||||
|
|
||||||
|
using (ImRaii.PushId($"syncshell-{notification.Id}"))
|
||||||
|
{
|
||||||
|
if (ImGui.Button("Effacer"))
|
||||||
|
{
|
||||||
|
_notificationTracker.Remove(NotificationCategory.Syncshell, notification.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, stretchWidth: true);
|
||||||
|
}
|
||||||
|
|
||||||
private void TriggerAcceptAutoDetectNotification(string uid)
|
private void TriggerAcceptAutoDetectNotification(string uid)
|
||||||
{
|
{
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ using MareSynchronos.PlayerData.Pairs;
|
|||||||
using MareSynchronos.Services;
|
using MareSynchronos.Services;
|
||||||
using MareSynchronos.Services.AutoDetect;
|
using MareSynchronos.Services.AutoDetect;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
|
using MareSynchronos.Services.Notifications;
|
||||||
using MareSynchronos.WebAPI;
|
using MareSynchronos.WebAPI;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MareSynchronos.MareConfiguration.Models;
|
||||||
|
|
||||||
namespace MareSynchronos.UI.Components.Popup;
|
namespace MareSynchronos.UI.Components.Popup;
|
||||||
|
|
||||||
@@ -28,6 +30,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
private readonly PairManager _pairManager;
|
private readonly PairManager _pairManager;
|
||||||
private readonly UiSharedService _uiSharedService;
|
private readonly UiSharedService _uiSharedService;
|
||||||
private readonly SyncshellDiscoveryService _syncshellDiscoveryService;
|
private readonly SyncshellDiscoveryService _syncshellDiscoveryService;
|
||||||
|
private readonly NotificationTracker _notificationTracker;
|
||||||
private List<BannedGroupUserDto> _bannedUsers = [];
|
private List<BannedGroupUserDto> _bannedUsers = [];
|
||||||
private int _multiInvites;
|
private int _multiInvites;
|
||||||
private string _newPassword;
|
private string _newPassword;
|
||||||
@@ -54,7 +57,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
public SyncshellAdminUI(ILogger<SyncshellAdminUI> logger, MareMediator mediator, ApiController apiController,
|
public SyncshellAdminUI(ILogger<SyncshellAdminUI> logger, MareMediator mediator, ApiController apiController,
|
||||||
UiSharedService uiSharedService, PairManager pairManager, SyncshellDiscoveryService syncshellDiscoveryService,
|
UiSharedService uiSharedService, PairManager pairManager, SyncshellDiscoveryService syncshellDiscoveryService,
|
||||||
GroupFullInfoDto groupFullInfo, PerformanceCollectorService performanceCollectorService)
|
GroupFullInfoDto groupFullInfo, PerformanceCollectorService performanceCollectorService, NotificationTracker notificationTracker)
|
||||||
: base(logger, mediator, "Syncshell Admin Panel (" + groupFullInfo.GroupAliasOrGID + ")", performanceCollectorService)
|
: base(logger, mediator, "Syncshell Admin Panel (" + groupFullInfo.GroupAliasOrGID + ")", performanceCollectorService)
|
||||||
{
|
{
|
||||||
GroupFullInfo = groupFullInfo;
|
GroupFullInfo = groupFullInfo;
|
||||||
@@ -62,6 +65,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
_uiSharedService = uiSharedService;
|
_uiSharedService = uiSharedService;
|
||||||
_pairManager = pairManager;
|
_pairManager = pairManager;
|
||||||
_syncshellDiscoveryService = syncshellDiscoveryService;
|
_syncshellDiscoveryService = syncshellDiscoveryService;
|
||||||
|
_notificationTracker = notificationTracker;
|
||||||
_isOwner = string.Equals(GroupFullInfo.OwnerUID, _apiController.UID, StringComparison.Ordinal);
|
_isOwner = string.Equals(GroupFullInfo.OwnerUID, _apiController.UID, StringComparison.Ordinal);
|
||||||
_isModerator = GroupFullInfo.GroupUserInfo.IsModerator();
|
_isModerator = GroupFullInfo.GroupUserInfo.IsModerator();
|
||||||
_newPassword = string.Empty;
|
_newPassword = string.Empty;
|
||||||
@@ -650,6 +654,11 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
_autoDetectMessage = desiredVisibility
|
_autoDetectMessage = desiredVisibility
|
||||||
? "La Syncshell est désormais visible dans AutoDetect."
|
? "La Syncshell est désormais visible dans AutoDetect."
|
||||||
: "La Syncshell n'est plus visible dans AutoDetect.";
|
: "La Syncshell n'est plus visible dans AutoDetect.";
|
||||||
|
|
||||||
|
if (desiredVisibility)
|
||||||
|
{
|
||||||
|
PublishSyncshellPublicNotification();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -711,6 +720,11 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
_autoDetectMessage = _autoDetectDesiredVisibility
|
_autoDetectMessage = _autoDetectDesiredVisibility
|
||||||
? "Paramètres AutoDetect envoyés. La Syncshell sera visible selon le planning défini."
|
? "Paramètres AutoDetect envoyés. La Syncshell sera visible selon le planning défini."
|
||||||
: "La Syncshell n'est plus visible dans AutoDetect.";
|
: "La Syncshell n'est plus visible dans AutoDetect.";
|
||||||
|
|
||||||
|
if (_autoDetectDesiredVisibility)
|
||||||
|
{
|
||||||
|
PublishSyncshellPublicNotification();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -744,4 +758,19 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
Mediator.Publish(new RemoveWindowMessage(this));
|
Mediator.Publish(new RemoveWindowMessage(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void PublishSyncshellPublicNotification()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var title = $"Syncshell publique: {GroupFullInfo.GroupAliasOrGID}";
|
||||||
|
var message = "La Syncshell est désormais visible via AutoDetect.";
|
||||||
|
Mediator.Publish(new DualNotificationMessage(title, message, NotificationType.Info, TimeSpan.FromSeconds(4)));
|
||||||
|
_notificationTracker.Upsert(NotificationEntry.SyncshellPublic(GroupFullInfo.GID, GroupFullInfo.GroupAliasOrGID));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// swallow any notification errors to not break UI flow
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,9 +174,10 @@ public class HubFactory : MediatorSubscriberBase
|
|||||||
})
|
})
|
||||||
.AddMessagePackProtocol(opt =>
|
.AddMessagePackProtocol(opt =>
|
||||||
{
|
{
|
||||||
var resolver = CompositeResolver.Create(StandardResolverAllowPrivate.Instance,
|
var resolver = CompositeResolver.Create(
|
||||||
BuiltinResolver.Instance,
|
|
||||||
AttributeFormatterResolver.Instance,
|
AttributeFormatterResolver.Instance,
|
||||||
|
StandardResolverAllowPrivate.Instance,
|
||||||
|
BuiltinResolver.Instance,
|
||||||
// replace enum resolver
|
// replace enum resolver
|
||||||
DynamicEnumAsStringResolver.Instance,
|
DynamicEnumAsStringResolver.Instance,
|
||||||
DynamicGenericResolver.Instance,
|
DynamicGenericResolver.Instance,
|
||||||
|
|||||||
Reference in New Issue
Block a user