Some checks failed
Tag and Release Lightless / tag-and-release (push) Failing after 32s
 Lightless 1.12.0 is HERE! In this major update, we are introducing something we've been working on and testing for the last couple of weeks. In this update we are introducing a new (**OPTIONAL**) feature called **LightFinder**! We took inspiration from FFXIV's very own Party Finder and decided to implement something that allows users to not only look for fellow Lightless users, but also put up their Syncshell for others looking to join a sync community! When you enable LightFinder, you will be visible to other LightFinder users for **3 hours** or when you want to disabled it. When the 3 hours are up, you can either leave it disabled or enable it again for another 3 hours. The tag shown above will show above your nameplate, and you will be able to receive pair requests in your UI from other users with LightFinder enabled without having to input their uid!  Are you at a Venue? In Limsa? Partying in the streets of Uldah? If you're looking for fellow Lightless users you can now enable LightFinder and you will be shown to others who also have LightFinder enabled! Looking for a Syncshell to join? Enable LightFinder to see what SyncShells are available to join! Want to advertise your Syncshell? Choose the syncshell you want to put up in LightFinder and enable LightFinder. **IMPORTANT: We want to stress the fact that, if you just want to sync with just your friends and no one else, this does not take that away. No one will know you use Lightless unless you choose to tell them, or use this **OPTIONAL** feature. Your privacy is still maintained if you don't want to use the feature.** # Major Changes ## LightFinder - **OPTIONAL FEATURE** - **DOES NOT AFFECT YOU IF YOU DON'T WANT TO USE IT**  * New **OPTIONAL** syncing feature where one can enable something like a Party Finder so that other Lightless users in the area can see you are looking for people to sync with. Enable it by clicking the compass button and then the `Enable LightFinder` button.  * [L] Send Pair Request has been added to player context menus. You should still be able to send a request without Lightfinder on BUT you will need to know the other player is using Lightless and have them send a pair request back.  * When in LightFinder mode, for X mins you will be visible to all Lightless Users WHO ALSO HAVE LIGHTFINDER ON and will receive notifications of people wanting to pair with you. If you are the person using LightFinder, you understand the risks of pairing with someone you don't know. If you are the person sending a request to someone with LightFinder on, you also understand the risks of pairing with someone you don't know. **AGAIN, THIS IS OPTIONAL.** * When in LightFinder mode, you can also put up a Syncshell you own on the Syncshell Finder so that others can easily find it and join. This has to be done prior to enabling LightFinder.  * Syncshell Finder allows you to join Syncshells that are indexed by LightFinder  # Minor Changes * Vanity addition to our supporters: On top of vanity ids you can find a fun addition under Your User Settings -> Edit Lightless Profile -> Vanity Settings to change the colour of your name in the lightless ui. This will be shown to all users.  * Pairing nameplate colour override can also now override FC tag (new option_ * Bunch of UI fixes, updates, and changes * kdb is now a whitelisted filetype that can upload Co-authored-by: CakeAndBanana <admin@cakeandbanana.nl> Co-authored-by: azyges <aaaaaa@aaa.aaa> Co-authored-by: choco <thijmenhogenkamp@gmail.com> Co-authored-by: choco <choco@noreply.git.lightless-sync.org> Co-authored-by: defnotken <itsdefnotken@gmail.com> Reviewed-on: #39
854 lines
35 KiB
C#
854 lines
35 KiB
C#
using Dalamud.Bindings.ImGui;
|
|
using Dalamud.Interface;
|
|
using Dalamud.Interface.Utility;
|
|
using Dalamud.Interface.Utility.Raii;
|
|
using Dalamud.Utility;
|
|
using LightlessSync.API.Data.Enum;
|
|
using LightlessSync.API.Data.Extensions;
|
|
using LightlessSync.LightlessConfiguration.Models;
|
|
using LightlessSync.PlayerData.Pairs;
|
|
using LightlessSync.Services;
|
|
using LightlessSync.Services.Mediator;
|
|
using LightlessSync.Utils;
|
|
using LightlessSync.WebAPI;
|
|
using Serilog;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Numerics;
|
|
using System.Reflection.Emit;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace LightlessSync.UI;
|
|
|
|
public class TopTabMenu
|
|
{
|
|
private readonly ApiController _apiController;
|
|
|
|
private readonly LightlessMediator _lightlessMediator;
|
|
|
|
private readonly PairManager _pairManager;
|
|
private readonly PairRequestService _pairRequestService;
|
|
private readonly DalamudUtilService _dalamudUtilService;
|
|
private readonly HashSet<string> _pendingPairRequestActions = new(StringComparer.Ordinal);
|
|
private bool _pairRequestsExpanded; // useless for now
|
|
private int _lastRequestCount;
|
|
private readonly UiSharedService _uiSharedService;
|
|
private string _filter = string.Empty;
|
|
private int _globalControlCountdown = 0;
|
|
private float _pairRequestsHeight = 150f;
|
|
private string _pairToAdd = string.Empty;
|
|
|
|
private SelectedTab _selectedTab = SelectedTab.None;
|
|
public TopTabMenu(LightlessMediator lightlessMediator, ApiController apiController, PairManager pairManager, UiSharedService uiSharedService, PairRequestService pairRequestService, DalamudUtilService dalamudUtilService)
|
|
{
|
|
_lightlessMediator = lightlessMediator;
|
|
_apiController = apiController;
|
|
_pairManager = pairManager;
|
|
_pairRequestService = pairRequestService;
|
|
_dalamudUtilService = dalamudUtilService;
|
|
_uiSharedService = uiSharedService;
|
|
}
|
|
|
|
private enum SelectedTab
|
|
{
|
|
None,
|
|
Individual,
|
|
Syncshell,
|
|
Lightfinder,
|
|
UserConfig,
|
|
Settings
|
|
}
|
|
|
|
public string Filter
|
|
{
|
|
get => _filter;
|
|
private set
|
|
{
|
|
if (!string.Equals(_filter, value, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
_lightlessMediator.Publish(new RefreshUiMessage());
|
|
}
|
|
|
|
_filter = value;
|
|
}
|
|
}
|
|
private SelectedTab TabSelection
|
|
{
|
|
get => _selectedTab; set
|
|
{
|
|
_selectedTab = value;
|
|
}
|
|
}
|
|
public void Draw()
|
|
{
|
|
var availableWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X;
|
|
var spacing = ImGui.GetStyle().ItemSpacing;
|
|
var buttonX = (availableWidth - (spacing.X * 4)) / 5f;
|
|
var buttonY = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Pause).Y;
|
|
var buttonSize = new Vector2(buttonX, buttonY);
|
|
var drawList = ImGui.GetWindowDrawList();
|
|
var underlineColor = ImGui.GetColorU32(UIColors.Get("LightlessPurpleActive")); // ImGui.GetColorU32(ImGuiCol.Separator);
|
|
var btncolor = ImRaii.PushColor(ImGuiCol.Button, ImGui.ColorConvertFloat4ToU32(new(0, 0, 0, 0)));
|
|
|
|
ImGuiHelpers.ScaledDummy(spacing.Y / 2f);
|
|
|
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
|
{
|
|
var x = ImGui.GetCursorScreenPos();
|
|
if (ImGui.Button(FontAwesomeIcon.User.ToIconString(), buttonSize))
|
|
{
|
|
TabSelection = TabSelection == SelectedTab.Individual ? SelectedTab.None : SelectedTab.Individual;
|
|
}
|
|
ImGui.SameLine();
|
|
var xAfter = ImGui.GetCursorScreenPos();
|
|
if (TabSelection == SelectedTab.Individual)
|
|
drawList.AddLine(x with { Y = x.Y + buttonSize.Y + spacing.Y },
|
|
xAfter with { Y = xAfter.Y + buttonSize.Y + spacing.Y, X = xAfter.X - spacing.X },
|
|
underlineColor, 2);
|
|
}
|
|
UiSharedService.AttachToolTip("Individual Pair Menu");
|
|
|
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
|
{
|
|
var x = ImGui.GetCursorScreenPos();
|
|
if (ImGui.Button(FontAwesomeIcon.Users.ToIconString(), buttonSize))
|
|
{
|
|
TabSelection = TabSelection == SelectedTab.Syncshell ? SelectedTab.None : SelectedTab.Syncshell;
|
|
}
|
|
ImGui.SameLine();
|
|
var xAfter = ImGui.GetCursorScreenPos();
|
|
if (TabSelection == SelectedTab.Syncshell)
|
|
drawList.AddLine(x with { Y = x.Y + buttonSize.Y + spacing.Y },
|
|
xAfter with { Y = xAfter.Y + buttonSize.Y + spacing.Y, X = xAfter.X - spacing.X },
|
|
underlineColor, 2);
|
|
}
|
|
UiSharedService.AttachToolTip("Syncshell Menu");
|
|
|
|
ImGui.SameLine();
|
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
|
{
|
|
var x = ImGui.GetCursorScreenPos();
|
|
if (ImGui.Button(FontAwesomeIcon.Compass.ToIconString(), buttonSize))
|
|
{
|
|
TabSelection = TabSelection == SelectedTab.Lightfinder ? SelectedTab.None : SelectedTab.Lightfinder;
|
|
}
|
|
|
|
ImGui.SameLine();
|
|
var xAfter = ImGui.GetCursorScreenPos();
|
|
if (TabSelection == SelectedTab.Lightfinder)
|
|
drawList.AddLine(x with { Y = x.Y + buttonSize.Y + spacing.Y },
|
|
xAfter with { Y = xAfter.Y + buttonSize.Y + spacing.Y, X = xAfter.X - spacing.X },
|
|
underlineColor, 2);
|
|
}
|
|
UiSharedService.AttachToolTip("Lightfinder");
|
|
|
|
ImGui.SameLine();
|
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
|
{
|
|
var x = ImGui.GetCursorScreenPos();
|
|
if (ImGui.Button(FontAwesomeIcon.UserCog.ToIconString(), buttonSize))
|
|
{
|
|
TabSelection = TabSelection == SelectedTab.UserConfig ? SelectedTab.None : SelectedTab.UserConfig;
|
|
}
|
|
|
|
ImGui.SameLine();
|
|
var xAfter = ImGui.GetCursorScreenPos();
|
|
if (TabSelection == SelectedTab.UserConfig)
|
|
drawList.AddLine(x with { Y = x.Y + buttonSize.Y + spacing.Y },
|
|
xAfter with { Y = xAfter.Y + buttonSize.Y + spacing.Y, X = xAfter.X - spacing.X },
|
|
underlineColor, 2);
|
|
}
|
|
UiSharedService.AttachToolTip("Your User Menu");
|
|
|
|
ImGui.SameLine();
|
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
|
{
|
|
var x = ImGui.GetCursorScreenPos();
|
|
if (ImGui.Button(FontAwesomeIcon.Cog.ToIconString(), buttonSize))
|
|
{
|
|
_lightlessMediator.Publish(new UiToggleMessage(typeof(SettingsUi)));
|
|
}
|
|
ImGui.SameLine();
|
|
}
|
|
UiSharedService.AttachToolTip("Open Lightless Settings");
|
|
|
|
ImGui.NewLine();
|
|
btncolor.Dispose();
|
|
|
|
ImGuiHelpers.ScaledDummy(spacing);
|
|
|
|
if (TabSelection == SelectedTab.Individual)
|
|
{
|
|
DrawAddPair(availableWidth, spacing.X);
|
|
DrawGlobalIndividualButtons(availableWidth, spacing.X);
|
|
}
|
|
else if (TabSelection == SelectedTab.Syncshell)
|
|
{
|
|
DrawSyncshellMenu(availableWidth, spacing.X);
|
|
DrawGlobalSyncshellButtons(availableWidth, spacing.X);
|
|
}
|
|
else if (TabSelection == SelectedTab.Lightfinder)
|
|
{
|
|
DrawLightfinderMenu(availableWidth, spacing.X);
|
|
}
|
|
else if (TabSelection == SelectedTab.UserConfig)
|
|
{
|
|
DrawUserConfig(availableWidth, spacing.X);
|
|
}
|
|
|
|
if (TabSelection != SelectedTab.None) ImGuiHelpers.ScaledDummy(3f);
|
|
|
|
#if DEBUG
|
|
if (ImGui.Button("Add Test Pair Request"))
|
|
{
|
|
var fakeCid = Guid.NewGuid().ToString("N");
|
|
var display = _pairRequestService.RegisterIncomingRequest(fakeCid, "Debug pair request");
|
|
_lightlessMediator.Publish(new NotificationMessage(
|
|
"Pair request received (debug)",
|
|
display.Message,
|
|
NotificationType.Info,
|
|
TimeSpan.FromSeconds(5)));
|
|
}
|
|
#endif
|
|
|
|
DrawIncomingPairRequests(availableWidth);
|
|
|
|
ImGui.Separator();
|
|
|
|
DrawFilter(availableWidth, spacing.X);
|
|
}
|
|
|
|
private void DrawAddPair(float availableXWidth, float spacingX)
|
|
{
|
|
var buttonSize = _uiSharedService.GetIconTextButtonSize(FontAwesomeIcon.UserPlus, "Add");
|
|
ImGui.SetNextItemWidth(availableXWidth - buttonSize - spacingX);
|
|
ImGui.InputTextWithHint("##otheruid", "Other players UID/Alias", ref _pairToAdd, 20);
|
|
ImGui.SameLine();
|
|
var alreadyExisting = _pairManager.DirectPairs.Exists(p => string.Equals(p.UserData.UID, _pairToAdd, StringComparison.Ordinal) || string.Equals(p.UserData.Alias, _pairToAdd, StringComparison.Ordinal));
|
|
using (ImRaii.Disabled(alreadyExisting || string.IsNullOrEmpty(_pairToAdd)))
|
|
{
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.UserPlus, "Add"))
|
|
{
|
|
_ = _apiController.UserAddPair(new(new(_pairToAdd)));
|
|
_pairToAdd = string.Empty;
|
|
}
|
|
}
|
|
UiSharedService.AttachToolTip("Pair with " + (_pairToAdd.IsNullOrEmpty() ? "other user" : _pairToAdd));
|
|
}
|
|
|
|
private void DrawIncomingPairRequests(float availableWidth)
|
|
{
|
|
var requests = _pairRequestService.GetActiveRequests();
|
|
var count = requests.Count;
|
|
if (count == 0)
|
|
{
|
|
_pairRequestsExpanded = false;
|
|
_lastRequestCount = 0;
|
|
return;
|
|
}
|
|
|
|
if (count > _lastRequestCount)
|
|
{
|
|
_pairRequestsExpanded = true;
|
|
}
|
|
_lastRequestCount = count;
|
|
|
|
var label = $"Incoming Pair Requests - {count}##IncomingPairRequestsHeader";
|
|
|
|
using (ImRaii.PushColor(ImGuiCol.Header, UIColors.Get("LightlessPurple")))
|
|
using (ImRaii.PushColor(ImGuiCol.HeaderHovered, UIColors.Get("LightlessPurpleActive")))
|
|
using (ImRaii.PushColor(ImGuiCol.HeaderActive, UIColors.Get("LightlessPurple")))
|
|
{
|
|
bool open = ImGui.CollapsingHeader(label, ImGuiTreeNodeFlags.DefaultOpen);
|
|
_pairRequestsExpanded = open;
|
|
|
|
if (ImGui.IsItemHovered())
|
|
UiSharedService.AttachToolTip("Expand to view incoming pair requests.");
|
|
|
|
if (open)
|
|
{
|
|
var lineHeight = ImGui.GetTextLineHeightWithSpacing();
|
|
//var desiredHeight = Math.Clamp(count * lineHeight * 2f, 130f * ImGuiHelpers.GlobalScale, 185f * ImGuiHelpers.GlobalScale); we use resize bar instead
|
|
|
|
ImGui.SetCursorPosX(ImGui.GetCursorPosX() - 2f);
|
|
|
|
using (ImRaii.PushColor(ImGuiCol.ChildBg, UIColors.Get("LightlessPurple")))
|
|
using (ImRaii.PushStyle(ImGuiStyleVar.ChildRounding, 6f))
|
|
{
|
|
if (ImGui.BeginChild("##IncomingPairRequestsOuter", new Vector2(availableWidth + 5f, _pairRequestsHeight), true))
|
|
{
|
|
var defaultChildBg = ImGui.GetStyle().Colors[(int)ImGuiCol.WindowBg];
|
|
using (ImRaii.PushColor(ImGuiCol.ChildBg, defaultChildBg))
|
|
using (ImRaii.PushStyle(ImGuiStyleVar.ChildRounding, 5f))
|
|
using (ImRaii.PushStyle(ImGuiStyleVar.WindowPadding, new Vector2(6, 6)))
|
|
{
|
|
if (ImGui.BeginChild("##IncomingPairRequestsInner", new Vector2(0, 0), true))
|
|
{
|
|
using (ImRaii.PushColor(ImGuiCol.TableBorderStrong, ImGui.GetStyle().Colors[(int)ImGuiCol.Border]))
|
|
using (ImRaii.PushColor(ImGuiCol.TableBorderLight, ImGui.GetStyle().Colors[(int)ImGuiCol.Border]))
|
|
{
|
|
DrawPairRequestList(requests);
|
|
}
|
|
}
|
|
ImGui.EndChild();
|
|
}
|
|
}
|
|
ImGui.EndChild();
|
|
|
|
ImGui.PushStyleColor(ImGuiCol.Button, UIColors.Get("ButtonDefault"));
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, UIColors.Get("LightlessPurple"));
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonActive, UIColors.Get("LightlessPurpleActive"));
|
|
ImGui.Button("##resizeHandle", new Vector2(availableWidth, 4f));
|
|
ImGui.PopStyleColor(3);
|
|
|
|
if (ImGui.IsItemActive())
|
|
{
|
|
_pairRequestsHeight += ImGui.GetIO().MouseDelta.Y;
|
|
_pairRequestsHeight = Math.Clamp(_pairRequestsHeight, 100f, 300f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawPairRequestList(IReadOnlyList<PairRequestService.PairRequestDisplay> requests)
|
|
{
|
|
float playerColWidth = 207f * ImGuiHelpers.GlobalScale;
|
|
float receivedColWidth = 73f * ImGuiHelpers.GlobalScale;
|
|
float actionsColWidth = 50f * ImGuiHelpers.GlobalScale;
|
|
|
|
ImGui.Separator();
|
|
ImGui.TextUnformatted("Player");
|
|
ImGui.SameLine(playerColWidth + 2f);
|
|
ImGui.TextUnformatted("Received");
|
|
ImGui.SameLine(playerColWidth + receivedColWidth + 12f);
|
|
ImGui.TextUnformatted("Actions");
|
|
ImGui.Separator();
|
|
|
|
foreach (var request in requests)
|
|
{
|
|
ImGui.BeginGroup();
|
|
|
|
var label = string.IsNullOrEmpty(request.DisplayName) ? request.HashedCid : request.DisplayName;
|
|
|
|
ImGui.TextUnformatted(label.Truncate(26));
|
|
if (ImGui.IsItemHovered())
|
|
{
|
|
ImGui.BeginTooltip();
|
|
ImGui.TextUnformatted(label);
|
|
ImGui.EndTooltip();
|
|
}
|
|
|
|
ImGui.SameLine(playerColWidth);
|
|
|
|
ImGui.TextUnformatted(GetRelativeTime(request.ReceivedAt));
|
|
ImGui.SameLine(playerColWidth + receivedColWidth);
|
|
|
|
DrawPairRequestActions(request);
|
|
|
|
ImGui.EndGroup();
|
|
}
|
|
}
|
|
|
|
private void DrawPairRequestActions(PairRequestService.PairRequestDisplay request)
|
|
{
|
|
using var id = ImRaii.PushId(request.HashedCid);
|
|
var label = string.IsNullOrEmpty(request.DisplayName) ? request.HashedCid : request.DisplayName;
|
|
var inFlight = _pendingPairRequestActions.Contains(request.HashedCid);
|
|
using (ImRaii.Disabled(inFlight))
|
|
{
|
|
if (_uiSharedService.IconButton(FontAwesomeIcon.Check))
|
|
{
|
|
_ = AcceptPairRequestAsync(request);
|
|
}
|
|
if (ImGui.IsItemHovered())
|
|
ImGui.SetTooltip("Accept request");
|
|
|
|
ImGui.SameLine();
|
|
|
|
if (_uiSharedService.IconButton(FontAwesomeIcon.Times))
|
|
{
|
|
RejectPairRequest(request.HashedCid, label);
|
|
}
|
|
if (ImGui.IsItemHovered())
|
|
ImGui.SetTooltip("Decline request");
|
|
}
|
|
}
|
|
|
|
private static string GetRelativeTime(DateTime receivedAt)
|
|
{
|
|
var delta = DateTime.UtcNow - receivedAt;
|
|
if (delta <= TimeSpan.FromSeconds(10))
|
|
{
|
|
return "Just now";
|
|
}
|
|
|
|
if (delta.TotalMinutes >= 1)
|
|
{
|
|
return $"{Math.Floor(delta.TotalMinutes)}m {delta.Seconds:D2}s ago";
|
|
}
|
|
|
|
return $"{delta.Seconds}s ago";
|
|
}
|
|
|
|
private async Task AcceptPairRequestAsync(PairRequestService.PairRequestDisplay request)
|
|
{
|
|
if (!_pendingPairRequestActions.Add(request.HashedCid))
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
var myCidHash = (await _dalamudUtilService.GetCIDAsync().ConfigureAwait(false)).ToString().GetHash256();
|
|
await _apiController.TryPairWithContentId(request.HashedCid, myCidHash).ConfigureAwait(false);
|
|
_pairRequestService.RemoveRequest(request.HashedCid);
|
|
|
|
var display = string.IsNullOrEmpty(request.DisplayName) ? request.HashedCid : request.DisplayName;
|
|
_lightlessMediator.Publish(new NotificationMessage(
|
|
"Pair request accepted",
|
|
$"Sent a pair request back to {display}.",
|
|
NotificationType.Info,
|
|
TimeSpan.FromSeconds(3)));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_lightlessMediator.Publish(new NotificationMessage(
|
|
"Failed to accept pair request",
|
|
ex.Message,
|
|
NotificationType.Error,
|
|
TimeSpan.FromSeconds(5)));
|
|
}
|
|
finally
|
|
{
|
|
_pendingPairRequestActions.Remove(request.HashedCid);
|
|
}
|
|
}
|
|
|
|
private void RejectPairRequest(string hashedCid, string playerName)
|
|
{
|
|
if (!_pairRequestService.RemoveRequest(hashedCid))
|
|
{
|
|
return;
|
|
}
|
|
|
|
_lightlessMediator.Publish(new NotificationMessage("Pair request declined", "Declined " + playerName + "'s pending pair request.",
|
|
NotificationType.Info,
|
|
TimeSpan.FromSeconds(3)));
|
|
}
|
|
|
|
private void DrawFilter(float availableWidth, float spacingX)
|
|
{
|
|
var buttonSize = _uiSharedService.GetIconTextButtonSize(FontAwesomeIcon.Ban, "Clear");
|
|
ImGui.SetNextItemWidth(availableWidth - buttonSize - spacingX);
|
|
string filter = Filter;
|
|
if (ImGui.InputTextWithHint("##filter", "Filter for UID/notes", ref filter, 255))
|
|
{
|
|
Filter = filter;
|
|
}
|
|
ImGui.SameLine();
|
|
using var disabled = ImRaii.Disabled(string.IsNullOrEmpty(Filter));
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Ban, "Clear"))
|
|
{
|
|
Filter = string.Empty;
|
|
}
|
|
}
|
|
|
|
private void DrawGlobalIndividualButtons(float availableXWidth, float spacingX)
|
|
{
|
|
var buttonX = (availableXWidth - (spacingX * 3)) / 4f;
|
|
var buttonY = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Pause).Y;
|
|
var buttonSize = new Vector2(buttonX, buttonY);
|
|
|
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
|
{
|
|
using var disabled = ImRaii.Disabled(_globalControlCountdown > 0);
|
|
|
|
if (ImGui.Button(FontAwesomeIcon.Pause.ToIconString(), buttonSize))
|
|
{
|
|
ImGui.OpenPopup("Individual Pause");
|
|
}
|
|
}
|
|
UiSharedService.AttachToolTip("Globally resume or pause all individual pairs." + UiSharedService.TooltipSeparator
|
|
+ (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty));
|
|
|
|
ImGui.SameLine();
|
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
|
{
|
|
using var disabled = ImRaii.Disabled(_globalControlCountdown > 0);
|
|
|
|
if (ImGui.Button(FontAwesomeIcon.VolumeUp.ToIconString(), buttonSize))
|
|
{
|
|
ImGui.OpenPopup("Individual Sounds");
|
|
}
|
|
}
|
|
UiSharedService.AttachToolTip("Globally enable or disable sound sync with all individual pairs."
|
|
+ (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty));
|
|
|
|
ImGui.SameLine();
|
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
|
{
|
|
using var disabled = ImRaii.Disabled(_globalControlCountdown > 0);
|
|
|
|
if (ImGui.Button(FontAwesomeIcon.Running.ToIconString(), buttonSize))
|
|
{
|
|
ImGui.OpenPopup("Individual Animations");
|
|
}
|
|
}
|
|
UiSharedService.AttachToolTip("Globally enable or disable animation sync with all individual pairs." + UiSharedService.TooltipSeparator
|
|
+ (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty));
|
|
|
|
ImGui.SameLine();
|
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
|
{
|
|
using var disabled = ImRaii.Disabled(_globalControlCountdown > 0);
|
|
|
|
if (ImGui.Button(FontAwesomeIcon.Sun.ToIconString(), buttonSize))
|
|
{
|
|
ImGui.OpenPopup("Individual VFX");
|
|
}
|
|
}
|
|
UiSharedService.AttachToolTip("Globally enable or disable VFX sync with all individual pairs." + UiSharedService.TooltipSeparator
|
|
+ (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty));
|
|
|
|
|
|
PopupIndividualSetting("Individual Pause", "Unpause all individuals", "Pause all individuals",
|
|
FontAwesomeIcon.Play, FontAwesomeIcon.Pause,
|
|
(perm) =>
|
|
{
|
|
perm.SetPaused(false);
|
|
return perm;
|
|
},
|
|
(perm) =>
|
|
{
|
|
perm.SetPaused(true);
|
|
return perm;
|
|
});
|
|
PopupIndividualSetting("Individual Sounds", "Enable sounds for all individuals", "Disable sounds for all individuals",
|
|
FontAwesomeIcon.VolumeUp, FontAwesomeIcon.VolumeMute,
|
|
(perm) =>
|
|
{
|
|
perm.SetDisableSounds(false);
|
|
return perm;
|
|
},
|
|
(perm) =>
|
|
{
|
|
perm.SetDisableSounds(true);
|
|
return perm;
|
|
});
|
|
PopupIndividualSetting("Individual Animations", "Enable animations for all individuals", "Disable animations for all individuals",
|
|
FontAwesomeIcon.Running, FontAwesomeIcon.Stop,
|
|
(perm) =>
|
|
{
|
|
perm.SetDisableAnimations(false);
|
|
return perm;
|
|
},
|
|
(perm) =>
|
|
{
|
|
perm.SetDisableAnimations(true);
|
|
return perm;
|
|
});
|
|
PopupIndividualSetting("Individual VFX", "Enable VFX for all individuals", "Disable VFX for all individuals",
|
|
FontAwesomeIcon.Sun, FontAwesomeIcon.Circle,
|
|
(perm) =>
|
|
{
|
|
perm.SetDisableVFX(false);
|
|
return perm;
|
|
},
|
|
(perm) =>
|
|
{
|
|
perm.SetDisableVFX(true);
|
|
return perm;
|
|
});
|
|
}
|
|
|
|
private void DrawGlobalSyncshellButtons(float availableXWidth, float spacingX)
|
|
{
|
|
var buttonX = (availableXWidth - (spacingX * 4)) / 5f;
|
|
var buttonY = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Pause).Y;
|
|
var buttonSize = new Vector2(buttonX, buttonY);
|
|
|
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
|
{
|
|
using var disabled = ImRaii.Disabled(_globalControlCountdown > 0);
|
|
|
|
if (ImGui.Button(FontAwesomeIcon.Pause.ToIconString(), buttonSize))
|
|
{
|
|
ImGui.OpenPopup("Syncshell Pause");
|
|
}
|
|
}
|
|
UiSharedService.AttachToolTip("Globally resume or pause all syncshells." + UiSharedService.TooltipSeparator
|
|
+ "Note: This will not affect users with preferred permissions in syncshells."
|
|
+ (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty));
|
|
|
|
ImGui.SameLine();
|
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
|
{
|
|
using var disabled = ImRaii.Disabled(_globalControlCountdown > 0);
|
|
|
|
if (ImGui.Button(FontAwesomeIcon.VolumeUp.ToIconString(), buttonSize))
|
|
{
|
|
ImGui.OpenPopup("Syncshell Sounds");
|
|
}
|
|
}
|
|
UiSharedService.AttachToolTip("Globally enable or disable sound sync with all syncshells." + UiSharedService.TooltipSeparator
|
|
+ "Note: This will not affect users with preferred permissions in syncshells."
|
|
+ (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty));
|
|
|
|
ImGui.SameLine();
|
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
|
{
|
|
using var disabled = ImRaii.Disabled(_globalControlCountdown > 0);
|
|
|
|
if (ImGui.Button(FontAwesomeIcon.Running.ToIconString(), buttonSize))
|
|
{
|
|
ImGui.OpenPopup("Syncshell Animations");
|
|
}
|
|
}
|
|
UiSharedService.AttachToolTip("Globally enable or disable animation sync with all syncshells." + UiSharedService.TooltipSeparator
|
|
+ "Note: This will not affect users with preferred permissions in syncshells."
|
|
+ (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty));
|
|
|
|
ImGui.SameLine();
|
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
|
{
|
|
using var disabled = ImRaii.Disabled(_globalControlCountdown > 0);
|
|
|
|
if (ImGui.Button(FontAwesomeIcon.Sun.ToIconString(), buttonSize))
|
|
{
|
|
ImGui.OpenPopup("Syncshell VFX");
|
|
}
|
|
}
|
|
UiSharedService.AttachToolTip("Globally enable or disable VFX sync with all syncshells." + UiSharedService.TooltipSeparator
|
|
+ "Note: This will not affect users with preferred permissions in syncshells."
|
|
+ (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty));
|
|
|
|
|
|
PopupSyncshellSetting("Syncshell Pause", "Unpause all syncshells", "Pause all syncshells",
|
|
FontAwesomeIcon.Play, FontAwesomeIcon.Pause,
|
|
(perm) =>
|
|
{
|
|
perm.SetPaused(false);
|
|
return perm;
|
|
},
|
|
(perm) =>
|
|
{
|
|
perm.SetPaused(true);
|
|
return perm;
|
|
});
|
|
PopupSyncshellSetting("Syncshell Sounds", "Enable sounds for all syncshells", "Disable sounds for all syncshells",
|
|
FontAwesomeIcon.VolumeUp, FontAwesomeIcon.VolumeMute,
|
|
(perm) =>
|
|
{
|
|
perm.SetDisableSounds(false);
|
|
return perm;
|
|
},
|
|
(perm) =>
|
|
{
|
|
perm.SetDisableSounds(true);
|
|
return perm;
|
|
});
|
|
PopupSyncshellSetting("Syncshell Animations", "Enable animations for all syncshells", "Disable animations for all syncshells",
|
|
FontAwesomeIcon.Running, FontAwesomeIcon.Stop,
|
|
(perm) =>
|
|
{
|
|
perm.SetDisableAnimations(false);
|
|
return perm;
|
|
},
|
|
(perm) =>
|
|
{
|
|
perm.SetDisableAnimations(true);
|
|
return perm;
|
|
});
|
|
PopupSyncshellSetting("Syncshell VFX", "Enable VFX for all syncshells", "Disable VFX for all syncshells",
|
|
FontAwesomeIcon.Sun, FontAwesomeIcon.Circle,
|
|
(perm) =>
|
|
{
|
|
perm.SetDisableVFX(false);
|
|
return perm;
|
|
},
|
|
(perm) =>
|
|
{
|
|
perm.SetDisableVFX(true);
|
|
return perm;
|
|
});
|
|
|
|
ImGui.SameLine();
|
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
|
{
|
|
using var disabled = ImRaii.Disabled(_globalControlCountdown > 0 || !UiSharedService.CtrlPressed());
|
|
|
|
if (ImGui.Button(FontAwesomeIcon.Check.ToIconString(), buttonSize))
|
|
{
|
|
_ = GlobalControlCountdown(10);
|
|
var bulkSyncshells = _pairManager.GroupPairs.Keys.OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase)
|
|
.ToDictionary(g => g.Group.GID, g =>
|
|
{
|
|
var perm = g.GroupUserPermissions;
|
|
perm.SetDisableSounds(g.GroupPermissions.IsPreferDisableSounds());
|
|
perm.SetDisableAnimations(g.GroupPermissions.IsPreferDisableAnimations());
|
|
perm.SetDisableVFX(g.GroupPermissions.IsPreferDisableVFX());
|
|
return perm;
|
|
}, StringComparer.Ordinal);
|
|
|
|
_ = _apiController.SetBulkPermissions(new(new(StringComparer.Ordinal), bulkSyncshells)).ConfigureAwait(false);
|
|
}
|
|
}
|
|
UiSharedService.AttachToolTip("Globally align syncshell permissions to suggested syncshell permissions." + UiSharedService.TooltipSeparator
|
|
+ "Note: This will not affect users with preferred permissions in syncshells." + Environment.NewLine
|
|
+ "Note: If multiple users share one syncshell the permissions to that user will be set to " + Environment.NewLine
|
|
+ "the ones of the last applied syncshell in alphabetical order." + UiSharedService.TooltipSeparator
|
|
+ "Hold CTRL to enable this button"
|
|
+ (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty));
|
|
}
|
|
|
|
private void DrawSyncshellMenu(float availableWidth, float spacingX)
|
|
{
|
|
var buttonX = (availableWidth - (spacingX)) / 2f;
|
|
|
|
using (ImRaii.Disabled(_pairManager.GroupPairs.Select(k => k.Key).Distinct()
|
|
.Count(g => string.Equals(g.OwnerUID, _apiController.UID, StringComparison.Ordinal)) >= _apiController.ServerInfo.MaxGroupsCreatedByUser))
|
|
{
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Create new Syncshell", buttonX))
|
|
{
|
|
_lightlessMediator.Publish(new UiToggleMessage(typeof(CreateSyncshellUI)));
|
|
}
|
|
ImGui.SameLine();
|
|
}
|
|
|
|
using (ImRaii.Disabled(_pairManager.GroupPairs.Select(k => k.Key).Distinct().Count() >= _apiController.ServerInfo.MaxGroupsJoinedByUser))
|
|
{
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Users, "Join existing Syncshell", buttonX))
|
|
{
|
|
_lightlessMediator.Publish(new UiToggleMessage(typeof(JoinSyncshellUI)));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawLightfinderMenu(float availableWidth, float spacingX)
|
|
{
|
|
var buttonX = (availableWidth - (spacingX)) / 2f;
|
|
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.PersonCirclePlus, "Lightfinder", buttonX, center: true))
|
|
{
|
|
_lightlessMediator.Publish(new UiToggleMessage(typeof(BroadcastUI)));
|
|
}
|
|
|
|
ImGui.SameLine();
|
|
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Globe, "Syncshell Finder", buttonX, center: true))
|
|
{
|
|
_lightlessMediator.Publish(new UiToggleMessage(typeof(SyncshellFinderUI)));
|
|
}
|
|
}
|
|
|
|
private void DrawUserConfig(float availableWidth, float spacingX)
|
|
{
|
|
var buttonX = (availableWidth - spacingX) / 2f;
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.UserCircle, "Edit Lightless Profile", buttonX))
|
|
{
|
|
_lightlessMediator.Publish(new UiToggleMessage(typeof(EditProfileUi)));
|
|
}
|
|
UiSharedService.AttachToolTip("Edit your Lightless Profile");
|
|
ImGui.SameLine();
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.PersonCircleQuestion, "Chara Data Analysis", buttonX))
|
|
{
|
|
_lightlessMediator.Publish(new UiToggleMessage(typeof(DataAnalysisUi)));
|
|
}
|
|
UiSharedService.AttachToolTip("View and analyze your generated character data");
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Running, "Character Data Hub", availableWidth))
|
|
{
|
|
_lightlessMediator.Publish(new UiToggleMessage(typeof(CharaDataHubUi)));
|
|
}
|
|
}
|
|
|
|
private async Task GlobalControlCountdown(int countdown)
|
|
{
|
|
#if DEBUG
|
|
return;
|
|
#endif
|
|
|
|
_globalControlCountdown = countdown;
|
|
while (_globalControlCountdown > 0)
|
|
{
|
|
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
|
|
_globalControlCountdown--;
|
|
}
|
|
}
|
|
|
|
private void PopupIndividualSetting(string popupTitle, string enableText, string disableText,
|
|
FontAwesomeIcon enableIcon, FontAwesomeIcon disableIcon,
|
|
Func<UserPermissions, UserPermissions> actEnable, Func<UserPermissions, UserPermissions> actDisable)
|
|
{
|
|
if (ImGui.BeginPopup(popupTitle))
|
|
{
|
|
if (_uiSharedService.IconTextButton(enableIcon, enableText, null, true))
|
|
{
|
|
_ = GlobalControlCountdown(10);
|
|
var bulkIndividualPairs = _pairManager.PairsWithGroups.Keys
|
|
.Where(g => g.IndividualPairStatus == IndividualPairStatus.Bidirectional)
|
|
.ToDictionary(g => g.UserPair.User.UID, g =>
|
|
{
|
|
return actEnable(g.UserPair.OwnPermissions);
|
|
}, StringComparer.Ordinal);
|
|
|
|
_ = _apiController.SetBulkPermissions(new(bulkIndividualPairs, new(StringComparer.Ordinal))).ConfigureAwait(false);
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
|
|
if (_uiSharedService.IconTextButton(disableIcon, disableText, null, true))
|
|
{
|
|
_ = GlobalControlCountdown(10);
|
|
var bulkIndividualPairs = _pairManager.PairsWithGroups.Keys
|
|
.Where(g => g.IndividualPairStatus == IndividualPairStatus.Bidirectional)
|
|
.ToDictionary(g => g.UserPair.User.UID, g =>
|
|
{
|
|
return actDisable(g.UserPair.OwnPermissions);
|
|
}, StringComparer.Ordinal);
|
|
|
|
_ = _apiController.SetBulkPermissions(new(bulkIndividualPairs, new(StringComparer.Ordinal))).ConfigureAwait(false);
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
ImGui.EndPopup();
|
|
}
|
|
}
|
|
private void PopupSyncshellSetting(string popupTitle, string enableText, string disableText,
|
|
FontAwesomeIcon enableIcon, FontAwesomeIcon disableIcon,
|
|
Func<GroupUserPreferredPermissions, GroupUserPreferredPermissions> actEnable,
|
|
Func<GroupUserPreferredPermissions, GroupUserPreferredPermissions> actDisable)
|
|
{
|
|
if (ImGui.BeginPopup(popupTitle))
|
|
{
|
|
|
|
if (_uiSharedService.IconTextButton(enableIcon, enableText, null, true))
|
|
{
|
|
_ = GlobalControlCountdown(10);
|
|
var bulkSyncshells = _pairManager.GroupPairs.Keys
|
|
.OrderBy(u => u.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase)
|
|
.ToDictionary(g => g.Group.GID, g =>
|
|
{
|
|
return actEnable(g.GroupUserPermissions);
|
|
}, StringComparer.Ordinal);
|
|
|
|
_ = _apiController.SetBulkPermissions(new(new(StringComparer.Ordinal), bulkSyncshells)).ConfigureAwait(false);
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
|
|
if (_uiSharedService.IconTextButton(disableIcon, disableText, null, true))
|
|
{
|
|
_ = GlobalControlCountdown(10);
|
|
var bulkSyncshells = _pairManager.GroupPairs.Keys
|
|
.OrderBy(u => u.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase)
|
|
.ToDictionary(g => g.Group.GID, g =>
|
|
{
|
|
return actDisable(g.GroupUserPermissions);
|
|
}, StringComparer.Ordinal);
|
|
|
|
_ = _apiController.SetBulkPermissions(new(new(StringComparer.Ordinal), bulkSyncshells)).ConfigureAwait(false);
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
ImGui.EndPopup();
|
|
}
|
|
}
|
|
}
|