339 lines
13 KiB
C#
339 lines
13 KiB
C#
using Dalamud.Bindings.ImGui;
|
|
using Dalamud.Interface;
|
|
using Dalamud.Interface.Colors;
|
|
using Dalamud.Interface.Utility;
|
|
using Dalamud.Interface.Utility.Raii;
|
|
using LightlessSync.API.Data.Enum;
|
|
using LightlessSync.API.Data.Extensions;
|
|
using LightlessSync.API.Dto;
|
|
using LightlessSync.API.Dto.Group;
|
|
using LightlessSync.PlayerData.Pairs;
|
|
using LightlessSync.Services;
|
|
using LightlessSync.Services.Mediator;
|
|
using LightlessSync.Utils;
|
|
using LightlessSync.WebAPI;
|
|
using Microsoft.Extensions.Logging;
|
|
using System.Numerics;
|
|
|
|
namespace LightlessSync.UI;
|
|
|
|
public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
|
{
|
|
private readonly ApiController _apiController;
|
|
private readonly BroadcastService _broadcastService;
|
|
private readonly UiSharedService _uiSharedService;
|
|
private readonly BroadcastScannerService _broadcastScannerService;
|
|
private readonly PairManager _pairManager;
|
|
private readonly DalamudUtilService _dalamudUtilService;
|
|
|
|
private readonly List<GroupJoinDto> _nearbySyncshells = [];
|
|
private List<GroupFullInfoDto> _currentSyncshells = [];
|
|
private int _selectedNearbyIndex = -1;
|
|
private readonly HashSet<string> _recentlyJoined = new(StringComparer.Ordinal);
|
|
|
|
private GroupJoinDto? _joinDto;
|
|
private GroupJoinInfoDto? _joinInfo;
|
|
private DefaultPermissionsDto _ownPermissions = null!;
|
|
|
|
public SyncshellFinderUI(
|
|
ILogger<SyncshellFinderUI> logger,
|
|
LightlessMediator mediator,
|
|
PerformanceCollectorService performanceCollectorService,
|
|
BroadcastService broadcastService,
|
|
UiSharedService uiShared,
|
|
ApiController apiController,
|
|
BroadcastScannerService broadcastScannerService,
|
|
PairManager pairManager,
|
|
DalamudUtilService dalamudUtilService) : base(logger, mediator, "Shellfinder###LightlessSyncshellFinderUI", performanceCollectorService)
|
|
{
|
|
_broadcastService = broadcastService;
|
|
_uiSharedService = uiShared;
|
|
_apiController = apiController;
|
|
_broadcastScannerService = broadcastScannerService;
|
|
_pairManager = pairManager;
|
|
_dalamudUtilService = dalamudUtilService;
|
|
|
|
IsOpen = false;
|
|
SizeConstraints = new()
|
|
{
|
|
MinimumSize = new(600, 400),
|
|
MaximumSize = new(600, 550)
|
|
};
|
|
|
|
Mediator.Subscribe<SyncshellBroadcastsUpdatedMessage>(this, async _ => await RefreshSyncshellsAsync().ConfigureAwait(false));
|
|
Mediator.Subscribe<BroadcastStatusChangedMessage>(this, async _ => await RefreshSyncshellsAsync().ConfigureAwait(false));
|
|
}
|
|
|
|
public override async void OnOpen()
|
|
{
|
|
_ownPermissions = _apiController.DefaultPermissions.DeepClone()!;
|
|
await RefreshSyncshellsAsync().ConfigureAwait(false);
|
|
}
|
|
|
|
protected override void DrawInternal()
|
|
{
|
|
_uiSharedService.MediumText("Nearby Syncshells", UIColors.Get("PairBlue"));
|
|
_uiSharedService.ColoredSeparator(UIColors.Get("PairBlue"));
|
|
|
|
if (_nearbySyncshells.Count == 0)
|
|
{
|
|
ImGui.TextColored(ImGuiColors.DalamudGrey, "No nearby syncshells are being broadcasted.");
|
|
|
|
if (!_broadcastService.IsBroadcasting)
|
|
{
|
|
|
|
_uiSharedService.ColoredSeparator(UIColors.Get("LightlessYellow"));
|
|
|
|
ImGui.TextColored(UIColors.Get("LightlessYellow"), "Lightfinder is currently disabled, to locate nearby syncshells, Lightfinder must be active.");
|
|
ImGuiHelpers.ScaledDummy(0.5f);
|
|
|
|
ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 10.0f);
|
|
ImGui.PushStyleColor(ImGuiCol.Button, UIColors.Get("LightlessYellow2"));
|
|
|
|
if (ImGui.Button("Open Lightfinder", new Vector2(200 * ImGuiHelpers.GlobalScale, 0)))
|
|
{
|
|
Mediator.Publish(new UiToggleMessage(typeof(BroadcastUI)));
|
|
}
|
|
|
|
ImGui.PopStyleColor();
|
|
ImGui.PopStyleVar();
|
|
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
DrawSyncshellTable();
|
|
|
|
if (_joinDto != null && _joinInfo != null && _joinInfo.Success)
|
|
DrawConfirmation();
|
|
}
|
|
|
|
private void DrawSyncshellTable()
|
|
{
|
|
if (ImGui.BeginTable("##NearbySyncshellsTable", 3, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg))
|
|
{
|
|
ImGui.TableSetupColumn("Syncshell", ImGuiTableColumnFlags.WidthStretch);
|
|
ImGui.TableSetupColumn("Broadcaster", ImGuiTableColumnFlags.WidthStretch);
|
|
ImGui.TableSetupColumn("Join", ImGuiTableColumnFlags.WidthFixed, 80f * ImGuiHelpers.GlobalScale);
|
|
ImGui.TableHeadersRow();
|
|
|
|
foreach (var shell in _nearbySyncshells)
|
|
{
|
|
// Check if there is an active broadcast for this syncshell, if not, skipping this syncshell
|
|
var broadcast = _broadcastScannerService.GetActiveSyncshellBroadcasts()
|
|
.FirstOrDefault(b => string.Equals(b.GID, shell.Group.GID, StringComparison.Ordinal));
|
|
|
|
if (broadcast == null)
|
|
continue; // no active broadcasts
|
|
|
|
var (Name, Address) = _dalamudUtilService.FindPlayerByNameHash(broadcast.HashedCID);
|
|
if (string.IsNullOrEmpty(Name))
|
|
continue; // broadcaster not found in area, skipping
|
|
|
|
ImGui.TableNextRow();
|
|
ImGui.TableNextColumn();
|
|
|
|
var displayName = !string.IsNullOrEmpty(shell.Group.Alias) ? shell.Group.Alias : shell.Group.GID;
|
|
ImGui.TextUnformatted(displayName);
|
|
|
|
ImGui.TableNextColumn();
|
|
var worldName = _dalamudUtilService.GetWorldNameFromPlayerAddress(Address);
|
|
var broadcasterName = !string.IsNullOrEmpty(worldName) ? $"{Name} ({worldName})" : Name;
|
|
ImGui.TextUnformatted(broadcasterName);
|
|
|
|
ImGui.TableNextColumn();
|
|
|
|
var label = $"Join##{shell.Group.GID}";
|
|
ImGui.PushStyleColor(ImGuiCol.Button, UIColors.Get("LightlessGreen"));
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, UIColors.Get("LightlessGreen").WithAlpha(0.85f));
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonActive, UIColors.Get("LightlessGreen").WithAlpha(0.75f));
|
|
|
|
var isAlreadyMember = _currentSyncshells.Exists(g => string.Equals(g.GID, shell.GID, StringComparison.Ordinal));
|
|
var isRecentlyJoined = _recentlyJoined.Contains(shell.GID);
|
|
|
|
if (!isAlreadyMember && !isRecentlyJoined)
|
|
{
|
|
if (ImGui.Button(label))
|
|
{
|
|
_logger.LogInformation($"Join requested for Syncshell {shell.Group.GID} ({shell.Group.Alias})");
|
|
|
|
_ = Task.Run(async () =>
|
|
{
|
|
try
|
|
{
|
|
var info = await _apiController.GroupJoinHashed(new GroupJoinHashedDto(
|
|
shell.Group,
|
|
shell.Password,
|
|
shell.GroupUserPreferredPermissions
|
|
)).ConfigureAwait(false);
|
|
|
|
if (info != null && info.Success)
|
|
{
|
|
_joinDto = new GroupJoinDto(shell.Group, shell.Password, shell.GroupUserPreferredPermissions);
|
|
_joinInfo = info;
|
|
_ownPermissions = _apiController.DefaultPermissions.DeepClone()!;
|
|
|
|
_logger.LogInformation($"Fetched join info for {shell.Group.GID}");
|
|
}
|
|
else
|
|
{
|
|
_logger.LogWarning($"Failed to join {shell.Group.GID}: info was null or unsuccessful");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"Join failed for {shell.Group.GID}");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
using (ImRaii.Disabled())
|
|
{
|
|
ImGui.Button(label);
|
|
}
|
|
UiSharedService.AttachToolTip("Already a member or owner of this Syncshell.");
|
|
}
|
|
ImGui.PopStyleColor(3);
|
|
}
|
|
|
|
ImGui.EndTable();
|
|
}
|
|
}
|
|
|
|
private void DrawConfirmation()
|
|
{
|
|
if (_joinDto != null && _joinInfo != null)
|
|
{
|
|
ImGui.Separator();
|
|
ImGui.TextUnformatted($"Join Syncshell: {_joinDto.Group.AliasOrGID} by {_joinInfo.OwnerAliasOrUID}");
|
|
ImGuiHelpers.ScaledDummy(2f);
|
|
ImGui.TextUnformatted("Suggested Syncshell Permissions:");
|
|
|
|
DrawPermissionRow("Sounds", _joinInfo.GroupPermissions.IsPreferDisableSounds(), _ownPermissions.DisableGroupSounds, v => _ownPermissions.DisableGroupSounds = v);
|
|
DrawPermissionRow("Animations", _joinInfo.GroupPermissions.IsPreferDisableAnimations(), _ownPermissions.DisableGroupAnimations, v => _ownPermissions.DisableGroupAnimations = v);
|
|
DrawPermissionRow("VFX", _joinInfo.GroupPermissions.IsPreferDisableVFX(), _ownPermissions.DisableGroupVFX, v => _ownPermissions.DisableGroupVFX = v);
|
|
|
|
ImGui.NewLine();
|
|
ImGui.NewLine();
|
|
|
|
if (_uiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.Plus, $"Finalize and join {_joinDto.Group.AliasOrGID}"))
|
|
{
|
|
var finalPermissions = GroupUserPreferredPermissions.NoneSet;
|
|
finalPermissions.SetDisableSounds(_ownPermissions.DisableGroupSounds);
|
|
finalPermissions.SetDisableAnimations(_ownPermissions.DisableGroupAnimations);
|
|
finalPermissions.SetDisableVFX(_ownPermissions.DisableGroupVFX);
|
|
|
|
_ = _apiController.GroupJoinFinalize(new GroupJoinDto(_joinDto.Group, _joinDto.Password, finalPermissions));
|
|
|
|
_recentlyJoined.Add(_joinDto.Group.GID);
|
|
|
|
_joinDto = null;
|
|
_joinInfo = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawPermissionRow(string label, bool suggested, bool current, Action<bool> apply)
|
|
{
|
|
ImGui.AlignTextToFramePadding();
|
|
ImGui.TextUnformatted($"- {label}");
|
|
|
|
ImGui.SameLine(150 * ImGuiHelpers.GlobalScale);
|
|
ImGui.TextUnformatted("Current:");
|
|
ImGui.SameLine();
|
|
_uiSharedService.BooleanToColoredIcon(!current);
|
|
|
|
ImGui.SameLine(300 * ImGuiHelpers.GlobalScale);
|
|
ImGui.TextUnformatted("Suggested:");
|
|
ImGui.SameLine();
|
|
_uiSharedService.BooleanToColoredIcon(!suggested);
|
|
|
|
ImGui.SameLine(450 * ImGuiHelpers.GlobalScale);
|
|
using var id = ImRaii.PushId(label);
|
|
if (current != suggested)
|
|
{
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowRight, "Apply"))
|
|
apply(suggested);
|
|
}
|
|
|
|
ImGui.NewLine();
|
|
}
|
|
|
|
private async Task RefreshSyncshellsAsync()
|
|
{
|
|
var syncshellBroadcasts = _broadcastScannerService.GetActiveSyncshellBroadcasts();
|
|
_currentSyncshells = [.. _pairManager.GroupPairs.Select(g => g.Key)];
|
|
|
|
_recentlyJoined.RemoveWhere(gid => _currentSyncshells.Any(s => string.Equals(s.GID, gid, StringComparison.Ordinal)));
|
|
|
|
if (syncshellBroadcasts.Count == 0)
|
|
{
|
|
ClearSyncshells();
|
|
return;
|
|
}
|
|
|
|
List<GroupJoinDto>? updatedList = [];
|
|
try
|
|
{
|
|
var groups = await _apiController.GetBroadcastedGroups(syncshellBroadcasts).ConfigureAwait(false);
|
|
updatedList = groups?.ToList();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Failed to refresh broadcasted syncshells.");
|
|
return;
|
|
}
|
|
|
|
var currentGids = _nearbySyncshells.Select(s => s.Group.GID).ToHashSet(StringComparer.Ordinal);
|
|
|
|
if (updatedList != null)
|
|
{
|
|
var previousGid = GetSelectedGid();
|
|
|
|
_nearbySyncshells.Clear();
|
|
_nearbySyncshells.AddRange(updatedList);
|
|
|
|
if (previousGid != null)
|
|
{
|
|
var newIndex = _nearbySyncshells.FindIndex(s => string.Equals(s.Group.GID, previousGid, StringComparison.Ordinal));
|
|
if (newIndex >= 0)
|
|
{
|
|
_selectedNearbyIndex = newIndex;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
ClearSelection();
|
|
}
|
|
|
|
private void ClearSyncshells()
|
|
{
|
|
if (_nearbySyncshells.Count == 0)
|
|
return;
|
|
|
|
_nearbySyncshells.Clear();
|
|
ClearSelection();
|
|
}
|
|
|
|
private void ClearSelection()
|
|
{
|
|
_selectedNearbyIndex = -1;
|
|
_joinDto = null;
|
|
_joinInfo = null;
|
|
}
|
|
|
|
private string? GetSelectedGid()
|
|
{
|
|
if (_selectedNearbyIndex < 0 || _selectedNearbyIndex >= _nearbySyncshells.Count)
|
|
return null;
|
|
|
|
return _nearbySyncshells[_selectedNearbyIndex].Group.GID;
|
|
}
|
|
|
|
}
|