Update UI & Syncshell Public & MCDF Share

This commit is contained in:
2025-11-01 19:55:49 +01:00
parent 513845b811
commit 8cc4f34c55
22 changed files with 1949 additions and 298 deletions

View File

@@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using MareSynchronos.API.Dto.Group;
using MareSynchronos.MareConfiguration;
using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.Services.Mediator;
@@ -29,11 +31,16 @@ public class AutoDetectUi : WindowMediatorSubscriberBase
private readonly PairManager _pairManager;
private List<Services.Mediator.NearbyEntry> _entries;
private readonly HashSet<string> _acceptInFlight = new(StringComparer.Ordinal);
private readonly SyncshellDiscoveryService _syncshellDiscoveryService;
private List<SyncshellDiscoveryEntryDto> _syncshellEntries = [];
private bool _syncshellInitialized;
private readonly HashSet<string> _syncshellJoinInFlight = new(StringComparer.OrdinalIgnoreCase);
private string? _syncshellLastError;
public AutoDetectUi(ILogger<AutoDetectUi> logger, MareMediator mediator,
MareConfigService configService, DalamudUtilService dalamudUtilService,
AutoDetectRequestService requestService, NearbyPendingService pendingService, PairManager pairManager,
NearbyDiscoveryService discoveryService,
NearbyDiscoveryService discoveryService, SyncshellDiscoveryService syncshellDiscoveryService,
PerformanceCollectorService performanceCollectorService)
: base(logger, mediator, "AutoDetect", performanceCollectorService)
{
@@ -43,7 +50,9 @@ public class AutoDetectUi : WindowMediatorSubscriberBase
_pendingService = pendingService;
_pairManager = pairManager;
_discoveryService = discoveryService;
_syncshellDiscoveryService = syncshellDiscoveryService;
Mediator.Subscribe<Services.Mediator.DiscoveryListUpdated>(this, OnDiscoveryUpdated);
Mediator.Subscribe<SyncshellDiscoveryUpdated>(this, OnSyncshellDiscoveryUpdated);
_entries = _discoveryService.SnapshotEntries();
Flags |= ImGuiWindowFlags.NoScrollbar;
@@ -81,14 +90,7 @@ public class AutoDetectUi : WindowMediatorSubscriberBase
});
DrawStyledTab("Proximité", accent, inactiveTab, hoverTab, DrawNearbyTab);
using (ImRaii.Disabled(true))
{
DrawStyledTab("Syncshell", accent, inactiveTab, hoverTab, () =>
{
UiSharedService.ColorTextWrapped("Disponible prochainement.", ImGuiColors.DalamudGrey3);
}, true);
}
DrawStyledTab("Syncshell", accent, inactiveTab, hoverTab, DrawSyncshellTab);
}
public void DrawInline()
@@ -221,6 +223,7 @@ public class AutoDetectUi : WindowMediatorSubscriberBase
var sourceEntries = _entries.Count > 0 ? _entries : _discoveryService.SnapshotEntries();
var orderedEntries = sourceEntries
.Where(e => e.IsMatch)
.OrderBy(e => float.IsNaN(e.Distance) ? float.MaxValue : e.Distance)
.ToList();
@@ -245,10 +248,9 @@ public class AutoDetectUi : WindowMediatorSubscriberBase
for (int i = 0; i < orderedEntries.Count; i++)
{
var entry = orderedEntries[i];
bool isMatch = entry.IsMatch;
bool alreadyPaired = IsAlreadyPairedByUidOrAlias(entry);
bool overDistance = !float.IsNaN(entry.Distance) && entry.Distance > maxDist;
bool canRequest = isMatch && entry.AcceptPairRequests && !string.IsNullOrEmpty(entry.Token) && !alreadyPaired;
bool canRequest = entry.AcceptPairRequests && !string.IsNullOrEmpty(entry.Token) && !alreadyPaired;
string displayName = entry.DisplayName ?? entry.Name;
string worldName = entry.WorldId == 0
@@ -260,13 +262,11 @@ public class AutoDetectUi : WindowMediatorSubscriberBase
? "Déjà appairé"
: overDistance
? $"Hors portée (> {maxDist} m)"
: !isMatch
? "Umbra non activé"
: !entry.AcceptPairRequests
? "Invitations refusées"
: string.IsNullOrEmpty(entry.Token)
? "Indisponible"
: "Disponible";
: !entry.AcceptPairRequests
? "Invitations refusées"
: string.IsNullOrEmpty(entry.Token)
? "Indisponible"
: "Disponible";
ImGui.TableNextColumn();
ImGui.TextUnformatted(displayName);
@@ -297,13 +297,11 @@ public class AutoDetectUi : WindowMediatorSubscriberBase
? "Vous êtes déjà appairé avec ce joueur."
: overDistance
? $"Ce joueur est au-delà de la distance maximale configurée ({maxDist} m)."
: !isMatch
? "Ce joueur n'utilise pas UmbraSync ou ne s'est pas rendu détectable."
: !entry.AcceptPairRequests
? "Ce joueur a désactivé la réception automatique des invitations."
: string.IsNullOrEmpty(entry.Token)
? "Impossible d'obtenir un jeton d'invitation pour ce joueur."
: string.Empty;
: !entry.AcceptPairRequests
? "Ce joueur a désactivé la réception automatique des invitations."
: string.IsNullOrEmpty(entry.Token)
? "Impossible d'obtenir un jeton d'invitation pour ce joueur."
: string.Empty;
ImGui.TextDisabled(status);
if (!string.IsNullOrEmpty(reason))
@@ -317,11 +315,147 @@ public class AutoDetectUi : WindowMediatorSubscriberBase
ImGui.EndTable();
}
private async Task JoinSyncshellAsync(SyncshellDiscoveryEntryDto entry)
{
if (!_syncshellJoinInFlight.Add(entry.GID))
{
return;
}
try
{
var joined = await _syncshellDiscoveryService.JoinAsync(entry.GID, CancellationToken.None).ConfigureAwait(false);
if (joined)
{
Mediator.Publish(new NotificationMessage("AutoDetect Syncshell", $"Rejoint {entry.Alias ?? entry.GID}.", NotificationType.Info, TimeSpan.FromSeconds(5)));
await _syncshellDiscoveryService.RefreshAsync(CancellationToken.None).ConfigureAwait(false);
}
else
{
_syncshellLastError = $"Impossible de rejoindre {entry.Alias ?? entry.GID}.";
Mediator.Publish(new NotificationMessage("AutoDetect Syncshell", _syncshellLastError, NotificationType.Warning, TimeSpan.FromSeconds(5)));
}
}
catch (Exception ex)
{
_syncshellLastError = $"Erreur lors de l'adhésion : {ex.Message}";
Mediator.Publish(new NotificationMessage("AutoDetect Syncshell", _syncshellLastError, NotificationType.Error, TimeSpan.FromSeconds(5)));
}
finally
{
_syncshellJoinInFlight.Remove(entry.GID);
}
}
private void DrawSyncshellTab()
{
if (!_syncshellInitialized)
{
_syncshellInitialized = true;
_ = _syncshellDiscoveryService.RefreshAsync(CancellationToken.None);
}
bool isRefreshing = _syncshellDiscoveryService.IsRefreshing;
var serviceError = _syncshellDiscoveryService.LastError;
if (ImGui.Button("Actualiser la liste"))
{
_ = _syncshellDiscoveryService.RefreshAsync(CancellationToken.None);
}
UiSharedService.AttachToolTip("Met à jour la liste des Syncshells ayant activé l'AutoDetect.");
if (isRefreshing)
{
ImGui.SameLine();
ImGui.TextDisabled("Actualisation...");
}
ImGuiHelpers.ScaledDummy(4);
UiSharedService.TextWrapped("Les Syncshells affichées ont temporairement désactivé leur mot de passe pour permettre un accès direct via AutoDetect. Rejoignez-les uniquement si vous faites confiance aux administrateurs.");
if (!string.IsNullOrEmpty(serviceError))
{
UiSharedService.ColorTextWrapped(serviceError, ImGuiColors.DalamudRed);
}
else if (!string.IsNullOrEmpty(_syncshellLastError))
{
UiSharedService.ColorTextWrapped(_syncshellLastError!, ImGuiColors.DalamudOrange);
}
var entries = _syncshellEntries.Count > 0 ? _syncshellEntries : _syncshellDiscoveryService.Entries.ToList();
if (entries.Count == 0)
{
ImGuiHelpers.ScaledDummy(4);
UiSharedService.ColorTextWrapped("Aucune Syncshell n'est actuellement visible dans AutoDetect.", ImGuiColors.DalamudGrey3);
return;
}
if (!ImGui.BeginTable("autodetect-syncshells", 5, ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.RowBg))
{
return;
}
ImGui.TableSetupColumn("Nom");
ImGui.TableSetupColumn("Propriétaire");
ImGui.TableSetupColumn("Membres");
ImGui.TableSetupColumn("Invitations");
ImGui.TableSetupColumn("Action");
ImGui.TableHeadersRow();
foreach (var entry in entries.OrderBy(e => e.Alias ?? e.GID, StringComparer.OrdinalIgnoreCase))
{
bool alreadyMember = _pairManager.Groups.Keys.Any(g => string.Equals(g.GID, entry.GID, StringComparison.OrdinalIgnoreCase));
bool joining = _syncshellJoinInFlight.Contains(entry.GID);
ImGui.TableNextColumn();
ImGui.TextUnformatted(string.IsNullOrEmpty(entry.Alias) ? entry.GID : $"{entry.Alias} ({entry.GID})");
ImGui.TableNextColumn();
ImGui.TextUnformatted(string.IsNullOrEmpty(entry.OwnerAlias) ? entry.OwnerUID : $"{entry.OwnerAlias} ({entry.OwnerUID})");
ImGui.TableNextColumn();
ImGui.TextUnformatted(entry.MemberCount.ToString(CultureInfo.InvariantCulture));
ImGui.TableNextColumn();
string inviteMode = entry.AutoAcceptPairs ? "Auto" : "Manuel";
ImGui.TextUnformatted(inviteMode);
if (!entry.AutoAcceptPairs)
{
UiSharedService.AttachToolTip("L'administrateur doit approuver manuellement les nouveaux membres.");
}
ImGui.TableNextColumn();
using (ImRaii.Disabled(alreadyMember || joining))
{
if (alreadyMember)
{
ImGui.TextDisabled("Déjà membre");
}
else if (joining)
{
ImGui.TextDisabled("Connexion...");
}
else if (ImGui.Button("Rejoindre"))
{
_syncshellLastError = null;
_ = JoinSyncshellAsync(entry);
}
}
}
ImGui.EndTable();
}
private void OnDiscoveryUpdated(Services.Mediator.DiscoveryListUpdated msg)
{
_entries = msg.Entries;
}
private void OnSyncshellDiscoveryUpdated(SyncshellDiscoveryUpdated msg)
{
_syncshellEntries = msg.Entries;
}
private bool IsAlreadyPairedByUidOrAlias(Services.Mediator.NearbyEntry e)
{
try