379 lines
16 KiB
C#
379 lines
16 KiB
C#
using Dalamud.Bindings.ImGui;
|
||
using Dalamud.Interface.Utility;
|
||
using Dalamud.Utility;
|
||
using LightlessSync.API.Dto.Group;
|
||
using LightlessSync.LightlessConfiguration;
|
||
using LightlessSync.Services;
|
||
using LightlessSync.Services.Mediator;
|
||
using LightlessSync.WebAPI;
|
||
using Microsoft.Extensions.Logging;
|
||
using System.Numerics;
|
||
|
||
namespace LightlessSync.UI
|
||
{
|
||
public class BroadcastUI : WindowMediatorSubscriberBase
|
||
{
|
||
private readonly ApiController _apiController;
|
||
private readonly LightlessConfigService _configService;
|
||
private readonly BroadcastService _broadcastService;
|
||
private readonly UiSharedService _uiSharedService;
|
||
private readonly BroadcastScannerService _broadcastScannerService;
|
||
|
||
private IReadOnlyList<GroupFullInfoDto> _allSyncshells;
|
||
private string _userUid = string.Empty;
|
||
|
||
private readonly List<(string Label, string? GID, bool IsAvailable)> _syncshellOptions = new();
|
||
|
||
public BroadcastUI(
|
||
ILogger<BroadcastUI> logger,
|
||
LightlessMediator mediator,
|
||
PerformanceCollectorService performanceCollectorService,
|
||
BroadcastService broadcastService,
|
||
LightlessConfigService configService,
|
||
UiSharedService uiShared,
|
||
ApiController apiController,
|
||
BroadcastScannerService broadcastScannerService
|
||
) : base(logger, mediator, "Lightfinder###LightlessLightfinderUI", performanceCollectorService)
|
||
{
|
||
_broadcastService = broadcastService;
|
||
_uiSharedService = uiShared;
|
||
_configService = configService;
|
||
_apiController = apiController;
|
||
_broadcastScannerService = broadcastScannerService;
|
||
|
||
IsOpen = false;
|
||
this.SizeConstraints = new()
|
||
{
|
||
MinimumSize = new(600, 340),
|
||
MaximumSize = new(750, 400)
|
||
};
|
||
|
||
mediator.Subscribe<RefreshUiMessage>(this, async _ => await RefreshSyncshells().ConfigureAwait(false));
|
||
}
|
||
|
||
private void RebuildSyncshellDropdownOptions()
|
||
{
|
||
var selectedGid = _configService.Current.SelectedFinderSyncshell;
|
||
var allSyncshells = _allSyncshells ?? Array.Empty<GroupFullInfoDto>();
|
||
var ownedSyncshells = allSyncshells
|
||
.Where(g => string.Equals(g.OwnerUID, _userUid, StringComparison.Ordinal))
|
||
.ToList();
|
||
|
||
_syncshellOptions.Clear();
|
||
_syncshellOptions.Add(("None", null, true));
|
||
|
||
var addedGids = new HashSet<string>(StringComparer.Ordinal);
|
||
|
||
foreach (var shell in ownedSyncshells)
|
||
{
|
||
var label = shell.GroupAliasOrGID ?? shell.GID;
|
||
_syncshellOptions.Add((label, shell.GID, true));
|
||
addedGids.Add(shell.GID);
|
||
}
|
||
|
||
if (!string.IsNullOrEmpty(selectedGid) && !addedGids.Contains(selectedGid))
|
||
{
|
||
var matching = allSyncshells.FirstOrDefault(g => string.Equals(g.GID, selectedGid, StringComparison.Ordinal));
|
||
if (matching != null)
|
||
{
|
||
var label = matching.GroupAliasOrGID ?? matching.GID;
|
||
_syncshellOptions.Add((label, matching.GID, true));
|
||
addedGids.Add(matching.GID);
|
||
}
|
||
}
|
||
|
||
if (!string.IsNullOrEmpty(selectedGid) && !addedGids.Contains(selectedGid))
|
||
{
|
||
_syncshellOptions.Add(($"[Unavailable] {selectedGid}", selectedGid, false));
|
||
}
|
||
}
|
||
|
||
public Task RefreshSyncshells()
|
||
{
|
||
return RefreshSyncshellsInternal();
|
||
}
|
||
|
||
private async Task RefreshSyncshellsInternal()
|
||
{
|
||
if (!_apiController.IsConnected)
|
||
{
|
||
_allSyncshells = [];
|
||
RebuildSyncshellDropdownOptions();
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
_allSyncshells = await _apiController.GroupsGetAll().ConfigureAwait(false);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "Failed to fetch Syncshells.");
|
||
_allSyncshells = [];
|
||
}
|
||
|
||
RebuildSyncshellDropdownOptions();
|
||
}
|
||
|
||
|
||
public override void OnOpen()
|
||
{
|
||
_userUid = _apiController.UID;
|
||
_ = RefreshSyncshellsInternal();
|
||
}
|
||
|
||
protected override void DrawInternal()
|
||
{
|
||
if (!_broadcastService.IsLightFinderAvailable)
|
||
{
|
||
_uiSharedService.MediumText("This server doesn't support Lightfinder.", UIColors.Get("LightlessYellow"));
|
||
|
||
ImGuiHelpers.ScaledDummy(0.25f);
|
||
}
|
||
|
||
if (ImGui.BeginTabBar("##BroadcastTabs"))
|
||
{
|
||
if (ImGui.BeginTabItem("Lightfinder"))
|
||
{
|
||
_uiSharedService.MediumText("Lightfinder", UIColors.Get("PairBlue"));
|
||
|
||
ImGui.PushTextWrapPos();
|
||
ImGui.Text("This lets other Lightless users know you use Lightless.");
|
||
ImGui.Text("By enabling this, the server will allow other people to see that you are using Lightless.");
|
||
ImGui.Text("When disabled, pairing is still possible but both parties need to mutually send each other requests, receiving party will not be notified about the request unless the pairing is complete.");
|
||
ImGui.Text("At no point ever, even when Lightfinder is active that any Lightless data is getting sent to other people (including ID's), the server keeps this to itself.");
|
||
ImGui.Text("You can request to pair by right-clicking any (not yourself) character and using 'Send Pair Request'.");
|
||
ImGui.PopTextWrapPos();
|
||
|
||
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("DimRed"));
|
||
ImGui.Text("Use it only when you want to be visible.");
|
||
ImGui.PopStyleColor();
|
||
|
||
ImGuiHelpers.ScaledDummy(0.2f);
|
||
_uiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 2f);
|
||
|
||
if (_configService.Current.BroadcastEnabled)
|
||
{
|
||
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("LightlessGreen"));
|
||
ImGui.Text("The Lightfinder calls, and somewhere, a soul may answer."); // cringe..
|
||
ImGui.PopStyleColor();
|
||
|
||
var ttl = _broadcastService.RemainingTtl;
|
||
if (ttl is { } remaining && remaining > TimeSpan.Zero)
|
||
{
|
||
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("LightlessYellow"));
|
||
ImGui.Text($"Still shining, for {remaining:hh\\:mm\\:ss}");
|
||
ImGui.PopStyleColor();
|
||
}
|
||
else
|
||
{
|
||
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("DimRed"));
|
||
ImGui.Text("The Lightfinder’s light wanes, but not in vain."); // cringe..
|
||
ImGui.PopStyleColor();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("DimRed"));
|
||
ImGui.Text("The Lightfinder rests, waiting to shine again."); // cringe..
|
||
ImGui.PopStyleColor();
|
||
}
|
||
|
||
var cooldown = _broadcastService.RemainingCooldown;
|
||
if (cooldown is { } cd)
|
||
{
|
||
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("DimRed"));
|
||
ImGui.Text($"The Lightfinder gathers its strength... ({Math.Ceiling(cd.TotalSeconds)}s)");
|
||
ImGui.PopStyleColor();
|
||
}
|
||
|
||
ImGuiHelpers.ScaledDummy(0.5f);
|
||
|
||
bool isBroadcasting = _broadcastService.IsBroadcasting;
|
||
bool isOnCooldown = cooldown.HasValue && cooldown.Value.TotalSeconds > 0;
|
||
|
||
ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 10.0f);
|
||
|
||
if (isOnCooldown)
|
||
ImGui.PushStyleColor(ImGuiCol.Button, UIColors.Get("DimRed"));
|
||
else if (isBroadcasting)
|
||
ImGui.PushStyleColor(ImGuiCol.Button, UIColors.Get("LightlessGreen"));
|
||
else
|
||
ImGui.PushStyleColor(ImGuiCol.Button, UIColors.Get("PairBlue"));
|
||
|
||
if (isOnCooldown || !_broadcastService.IsLightFinderAvailable)
|
||
ImGui.BeginDisabled();
|
||
|
||
string buttonText = isBroadcasting ? "Disable Lightfinder" : "Enable Lightfinder";
|
||
|
||
if (ImGui.Button(buttonText, new Vector2(200 * ImGuiHelpers.GlobalScale, 0)))
|
||
{
|
||
_broadcastService.ToggleBroadcast();
|
||
}
|
||
|
||
if (isOnCooldown || !_broadcastService.IsLightFinderAvailable)
|
||
ImGui.EndDisabled();
|
||
|
||
ImGui.PopStyleColor();
|
||
ImGui.PopStyleVar();
|
||
|
||
ImGui.EndTabItem();
|
||
}
|
||
|
||
if (ImGui.BeginTabItem("Syncshell Finder"))
|
||
{
|
||
if (_allSyncshells == null)
|
||
{
|
||
ImGui.Text("Loading Syncshells...");
|
||
return;
|
||
}
|
||
|
||
_uiSharedService.MediumText("Syncshell Finder", UIColors.Get("PairBlue"));
|
||
|
||
_uiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 2f);
|
||
|
||
ImGui.PushTextWrapPos();
|
||
ImGui.Text("Allow your owned Syncshell to be indexed by the Nearby Syncshell Finder.");
|
||
ImGui.Text("To enable this, select one of your owned Syncshells from the dropdown menu below and ensure that \"Toggle Syncshell Finder\" is enabled. Your Syncshell will be visible in the Nearby Syncshell Finder as long as Lightfinder is active.");
|
||
ImGui.PopTextWrapPos();
|
||
|
||
ImGuiHelpers.ScaledDummy(0.2f);
|
||
_uiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 2f);
|
||
|
||
bool ShellFinderEnabled = _configService.Current.SyncshellFinderEnabled;
|
||
bool isBroadcasting = _broadcastService.IsBroadcasting;
|
||
|
||
if (isBroadcasting)
|
||
ImGui.BeginDisabled();
|
||
|
||
if (ImGui.Checkbox("Toggle Syncshell Finder", ref ShellFinderEnabled))
|
||
{
|
||
_configService.Current.SyncshellFinderEnabled = ShellFinderEnabled;
|
||
_configService.Save();
|
||
}
|
||
|
||
if (ImGui.IsItemHovered())
|
||
{
|
||
ImGui.BeginTooltip();
|
||
ImGui.Text("Toggle to broadcast specified Syncshell.");
|
||
ImGui.EndTooltip();
|
||
}
|
||
|
||
var selectedGid = _configService.Current.SelectedFinderSyncshell;
|
||
var currentOption = _syncshellOptions.FirstOrDefault(o => string.Equals(o.GID, selectedGid, StringComparison.Ordinal));
|
||
var preview = currentOption.Label ?? "Select a Syncshell...";
|
||
|
||
if (ImGui.BeginCombo("##SyncshellDropdown", preview))
|
||
{
|
||
foreach (var (label, gid, available) in _syncshellOptions)
|
||
{
|
||
bool isSelected = string.Equals(gid, selectedGid, StringComparison.Ordinal);
|
||
|
||
if (!available)
|
||
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("DimRed"));
|
||
|
||
if (ImGui.Selectable(label, isSelected))
|
||
{
|
||
_configService.Current.SelectedFinderSyncshell = gid;
|
||
_configService.Save();
|
||
}
|
||
|
||
if (!available && ImGui.IsItemHovered())
|
||
{
|
||
ImGui.BeginTooltip();
|
||
ImGui.Text("This Syncshell is not available on the current service.");
|
||
ImGui.EndTooltip();
|
||
}
|
||
|
||
if (!available)
|
||
ImGui.PopStyleColor();
|
||
|
||
if (isSelected)
|
||
ImGui.SetItemDefaultFocus();
|
||
}
|
||
|
||
ImGui.EndCombo();
|
||
}
|
||
|
||
|
||
if (ImGui.IsItemHovered())
|
||
{
|
||
ImGui.BeginTooltip();
|
||
ImGui.Text("Choose one of the available options.");
|
||
ImGui.EndTooltip();
|
||
}
|
||
|
||
|
||
if (isBroadcasting)
|
||
ImGui.EndDisabled();
|
||
|
||
ImGui.EndTabItem();
|
||
}
|
||
|
||
#if DEBUG
|
||
if (ImGui.BeginTabItem("Debug"))
|
||
{
|
||
ImGui.Text("Broadcast Cache");
|
||
|
||
if (ImGui.BeginTable("##BroadcastCacheTable", 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders | ImGuiTableFlags.ScrollY, new Vector2(-1, 225f)))
|
||
{
|
||
ImGui.TableSetupColumn("CID", ImGuiTableColumnFlags.WidthStretch);
|
||
ImGui.TableSetupColumn("IsBroadcasting", ImGuiTableColumnFlags.WidthStretch);
|
||
ImGui.TableSetupColumn("Expires In", ImGuiTableColumnFlags.WidthStretch);
|
||
ImGui.TableSetupColumn("Syncshell GID", ImGuiTableColumnFlags.WidthStretch);
|
||
ImGui.TableHeadersRow();
|
||
|
||
var now = DateTime.UtcNow;
|
||
|
||
foreach (var (cid, entry) in _broadcastScannerService.BroadcastCache)
|
||
{
|
||
ImGui.TableNextRow();
|
||
|
||
ImGui.TableNextColumn();
|
||
ImGui.TextUnformatted(cid.Truncate(12));
|
||
if (ImGui.IsItemHovered())
|
||
{
|
||
ImGui.BeginTooltip();
|
||
ImGui.TextUnformatted(cid);
|
||
ImGui.EndTooltip();
|
||
}
|
||
|
||
ImGui.TableNextColumn();
|
||
var colorBroadcast = entry.IsBroadcasting
|
||
? UIColors.Get("LightlessGreen")
|
||
: UIColors.Get("DimRed");
|
||
|
||
ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, ImGui.GetColorU32(colorBroadcast));
|
||
ImGui.TextUnformatted(entry.IsBroadcasting.ToString());
|
||
|
||
ImGui.TableNextColumn();
|
||
var remaining = entry.ExpiryTime - now;
|
||
var colorTtl =
|
||
remaining <= TimeSpan.Zero ? UIColors.Get("DimRed") :
|
||
remaining < TimeSpan.FromSeconds(10) ? UIColors.Get("LightlessYellow") :
|
||
(Vector4?)null;
|
||
|
||
if (colorTtl != null)
|
||
ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, ImGui.GetColorU32(colorTtl.Value));
|
||
|
||
ImGui.TextUnformatted(remaining > TimeSpan.Zero
|
||
? remaining.ToString("hh\\:mm\\:ss")
|
||
: "Expired");
|
||
|
||
ImGui.TableNextColumn();
|
||
ImGui.TextUnformatted(entry.GID ?? "-");
|
||
}
|
||
|
||
ImGui.EndTable();
|
||
}
|
||
|
||
ImGui.EndTabItem();
|
||
}
|
||
#endif
|
||
|
||
ImGui.EndTabBar();
|
||
}
|
||
}
|
||
}
|
||
}
|