2.0.0 (#92)
All checks were successful
Tag and Release Lightless / tag-and-release (push) Successful in 2m27s
All checks were successful
Tag and Release Lightless / tag-and-release (push) Successful in 2m27s
2.0.0 Changes: - Reworked shell finder UI with compact or list view with profile tags showing with the listing, allowing moderators to broadcast the syncshell as well to have it be used more. - Reworked user list in syncshell admin screen to have filter visible and moved away from table to its own thing, allowing to copy uid/note/alias when clicking on the name. - Reworked download bars and download box to make it look more modern, removed the jitter around, so it shouldn't vibrate around much. - Chat has been added to the top menu, working in Zone or in Syncshells to be used there. - Paired system has been revamped to make pausing and unpausing faster, and loading people should be faster as well. - Moved to the internal object table to have faster load times for users; people should load in faster - Compactor is running on a multi-threaded level instead of single-threaded; this should increase the speed of compacting files - Nameplate Service has been reworked so it wouldn't use the nameplate handler anymore. - Files can be resized when downloading to reduce load on users if they aren't compressed. (can be toggled to resize all). - Penumbra Collections are now only made when people are visible, reducing the load on boot-up when having many syncshells in your list. - Lightfinder plates have been moved away from using Nameplates, but will use an overlay. - Main UI has been changed a bit with a gradient, and on hover will glow up now. - Reworked Profile UI for Syncshell and Users to be more user-facing with more customizable items. - Reworked Settings UI to look more modern. - Performance should be better due to new systems that would dispose of the collections and better caching of items. Co-authored-by: defnotken <itsdefnotken@gmail.com> Co-authored-by: azyges <aaaaaa@aaa.aaa> Co-authored-by: choco <choco@patat.nl> Co-authored-by: cake <admin@cakeandbanana.nl> Co-authored-by: Minmoose <KennethBohr@outlook.com> Reviewed-on: #92
This commit was merged in pull request #92.
This commit is contained in:
@@ -2,8 +2,6 @@ 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.API.Dto.Group;
|
||||
using LightlessSync.Interop.Ipc;
|
||||
@@ -11,24 +9,26 @@ using LightlessSync.LightlessConfiguration;
|
||||
using LightlessSync.PlayerData.Handlers;
|
||||
using LightlessSync.PlayerData.Pairs;
|
||||
using LightlessSync.Services;
|
||||
using LightlessSync.Services.LightFinder;
|
||||
using LightlessSync.Services.Mediator;
|
||||
using LightlessSync.Services.ServerConfiguration;
|
||||
using LightlessSync.UI.Components;
|
||||
using LightlessSync.UI.Handlers;
|
||||
using LightlessSync.UI.Models;
|
||||
using LightlessSync.UI.Services;
|
||||
using LightlessSync.UI.Style;
|
||||
using LightlessSync.Utils;
|
||||
using LightlessSync.WebAPI;
|
||||
using LightlessSync.WebAPI.Files;
|
||||
using LightlessSync.WebAPI.Files.Models;
|
||||
using LightlessSync.WebAPI.SignalR.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LightlessSync.UI;
|
||||
|
||||
@@ -38,11 +38,12 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
private readonly ApiController _apiController;
|
||||
private readonly LightlessConfigService _configService;
|
||||
private readonly LightlessMediator _lightlessMediator;
|
||||
private readonly PairLedger _pairLedger;
|
||||
private readonly ConcurrentDictionary<GameObjectHandler, Dictionary<string, FileDownloadStatus>> _currentDownloads = new();
|
||||
private readonly DrawEntityFactory _drawEntityFactory;
|
||||
private readonly FileUploadManager _fileTransferManager;
|
||||
private readonly PlayerPerformanceConfigService _playerPerformanceConfig;
|
||||
private readonly PairManager _pairManager;
|
||||
private readonly PairUiService _pairUiService;
|
||||
private readonly SelectTagForPairUi _selectTagForPairUi;
|
||||
private readonly SelectTagForSyncshellUi _selectTagForSyncshellUi;
|
||||
private readonly SelectSyncshellForTagUi _selectSyncshellForTagUi;
|
||||
@@ -54,7 +55,8 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
private readonly TopTabMenu _tabMenu;
|
||||
private readonly TagHandler _tagHandler;
|
||||
private readonly UiSharedService _uiSharedService;
|
||||
private readonly BroadcastService _broadcastService;
|
||||
private readonly LightFinderService _broadcastService;
|
||||
private readonly DalamudUtilService _dalamudUtilService;
|
||||
|
||||
private List<IDrawFolder> _drawFolders;
|
||||
private Pair? _lastAddedUser;
|
||||
@@ -65,13 +67,18 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
private float _transferPartHeight;
|
||||
private bool _wasOpen;
|
||||
private float _windowContentWidth;
|
||||
private readonly SeluneBrush _seluneBrush = new();
|
||||
private const float _connectButtonHighlightThickness = 14f;
|
||||
private Pair? _focusedPair;
|
||||
private Pair? _pendingFocusPair;
|
||||
private int _pendingFocusFrame = -1;
|
||||
|
||||
public CompactUi(
|
||||
ILogger<CompactUi> logger,
|
||||
UiSharedService uiShared,
|
||||
LightlessConfigService configService,
|
||||
ApiController apiController,
|
||||
PairManager pairManager,
|
||||
PairUiService pairUiService,
|
||||
ServerConfigurationManager serverManager,
|
||||
LightlessMediator mediator,
|
||||
FileUploadManager fileTransferManager,
|
||||
@@ -85,14 +92,14 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
RenameSyncshellTagUi renameSyncshellTagUi,
|
||||
PerformanceCollectorService performanceCollectorService,
|
||||
IpcManager ipcManager,
|
||||
BroadcastService broadcastService,
|
||||
LightFinderService broadcastService,
|
||||
CharacterAnalyzer characterAnalyzer,
|
||||
PlayerPerformanceConfigService playerPerformanceConfig, PairRequestService pairRequestService, DalamudUtilService dalamudUtilService, NotificationService lightlessNotificationService) : base(logger, mediator, "###LightlessSyncMainUI", performanceCollectorService)
|
||||
PlayerPerformanceConfigService playerPerformanceConfig, PairRequestService pairRequestService, DalamudUtilService dalamudUtilService, NotificationService lightlessNotificationService, PairLedger pairLedger, LightFinderScannerService lightFinderScannerService) : base(logger, mediator, "###LightlessSyncMainUI", performanceCollectorService)
|
||||
{
|
||||
_uiSharedService = uiShared;
|
||||
_configService = configService;
|
||||
_apiController = apiController;
|
||||
_pairManager = pairManager;
|
||||
_pairUiService = pairUiService;
|
||||
_serverManager = serverManager;
|
||||
_fileTransferManager = fileTransferManager;
|
||||
_tagHandler = tagHandler;
|
||||
@@ -105,43 +112,19 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
_renamePairTagUi = renameTagUi;
|
||||
_ipcManager = ipcManager;
|
||||
_broadcastService = broadcastService;
|
||||
_tabMenu = new TopTabMenu(Mediator, _apiController, _pairManager, _uiSharedService, pairRequestService, dalamudUtilService, lightlessNotificationService);
|
||||
_pairLedger = pairLedger;
|
||||
_dalamudUtilService = dalamudUtilService;
|
||||
_tabMenu = new TopTabMenu(Mediator, _apiController, _uiSharedService, pairRequestService, dalamudUtilService, lightlessNotificationService, broadcastService, lightFinderScannerService);
|
||||
Mediator.Subscribe<PairFocusCharacterMessage>(this, msg => RegisterFocusCharacter(msg.Pair));
|
||||
|
||||
AllowPinning = true;
|
||||
AllowClickthrough = false;
|
||||
TitleBarButtons = new()
|
||||
{
|
||||
new TitleBarButton()
|
||||
{
|
||||
Icon = FontAwesomeIcon.Cog,
|
||||
Click = (msg) =>
|
||||
{
|
||||
Mediator.Publish(new UiToggleMessage(typeof(SettingsUi)));
|
||||
},
|
||||
IconOffset = new(2,1),
|
||||
ShowTooltip = () =>
|
||||
{
|
||||
ImGui.BeginTooltip();
|
||||
ImGui.Text("Open Lightless Settings");
|
||||
ImGui.EndTooltip();
|
||||
}
|
||||
},
|
||||
new TitleBarButton()
|
||||
{
|
||||
Icon = FontAwesomeIcon.Book,
|
||||
Click = (msg) =>
|
||||
{
|
||||
Mediator.Publish(new UiToggleMessage(typeof(EventViewerUI)));
|
||||
},
|
||||
IconOffset = new(2,1),
|
||||
ShowTooltip = () =>
|
||||
{
|
||||
ImGui.BeginTooltip();
|
||||
ImGui.Text("Open Lightless Event Viewer");
|
||||
ImGui.EndTooltip();
|
||||
}
|
||||
},
|
||||
};
|
||||
WindowBuilder.For(this)
|
||||
.AllowPinning(true)
|
||||
.AllowClickthrough(false)
|
||||
.SetSizeConstraints(new Vector2(375, 400), new Vector2(375, 2000))
|
||||
.AddFlags(ImGuiWindowFlags.NoDocking)
|
||||
.AddTitleBarButton(FontAwesomeIcon.Cog, "Open Lightless Settings", () => Mediator.Publish(new UiToggleMessage(typeof(SettingsUi))))
|
||||
.AddTitleBarButton(FontAwesomeIcon.Book, "Open Lightless Event Viewer", () => Mediator.Publish(new UiToggleMessage(typeof(EventViewerUI))))
|
||||
.Apply();
|
||||
|
||||
_drawFolders = [.. DrawFolders];
|
||||
|
||||
@@ -162,20 +145,24 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
Mediator.Subscribe<DownloadFinishedMessage>(this, (msg) => _currentDownloads.TryRemove(msg.DownloadId, out _));
|
||||
Mediator.Subscribe<RefreshUiMessage>(this, (msg) => _drawFolders = DrawFolders.ToList());
|
||||
|
||||
Flags |= ImGuiWindowFlags.NoDocking;
|
||||
|
||||
SizeConstraints = new WindowSizeConstraints()
|
||||
{
|
||||
MinimumSize = new Vector2(375, 400),
|
||||
MaximumSize = new Vector2(375, 2000),
|
||||
};
|
||||
_characterAnalyzer = characterAnalyzer;
|
||||
_playerPerformanceConfig = playerPerformanceConfig;
|
||||
_lightlessMediator = mediator;
|
||||
}
|
||||
|
||||
public override void OnClose()
|
||||
{
|
||||
ForceReleaseFocus();
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
protected override void DrawInternal()
|
||||
{
|
||||
var drawList = ImGui.GetWindowDrawList();
|
||||
var windowPos = ImGui.GetWindowPos();
|
||||
var windowSize = ImGui.GetWindowSize();
|
||||
using var selune = Selune.Begin(_seluneBrush, drawList, windowPos, windowSize);
|
||||
|
||||
_windowContentWidth = UiSharedService.GetWindowContentRegionWidth();
|
||||
if (!_apiController.IsCurrentVersion)
|
||||
{
|
||||
@@ -223,29 +210,49 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
|
||||
using (ImRaii.PushId("header")) DrawUIDHeader();
|
||||
_uiSharedService.RoundedSeparator(UIColors.Get("LightlessPurple"), 2.5f, 1f, 12f);
|
||||
using (ImRaii.PushId("serverstatus")) DrawServerStatus();
|
||||
using (ImRaii.PushId("serverstatus"))
|
||||
{
|
||||
DrawServerStatus();
|
||||
}
|
||||
selune.DrawHighlightOnly(ImGui.GetIO().DeltaTime);
|
||||
var style = ImGui.GetStyle();
|
||||
var contentMinY = windowPos.Y + ImGui.GetWindowContentRegionMin().Y;
|
||||
var gradientInset = 4f * ImGuiHelpers.GlobalScale;
|
||||
var gradientTop = MathF.Max(contentMinY, ImGui.GetCursorScreenPos().Y - style.ItemSpacing.Y + gradientInset);
|
||||
ImGui.Separator();
|
||||
|
||||
if (_apiController.ServerState is ServerState.Connected)
|
||||
{
|
||||
using (ImRaii.PushId("global-topmenu")) _tabMenu.Draw();
|
||||
var pairSnapshot = _pairUiService.GetSnapshot();
|
||||
|
||||
using (ImRaii.PushId("global-topmenu")) _tabMenu.Draw(pairSnapshot);
|
||||
using (ImRaii.PushId("pairlist")) DrawPairs();
|
||||
ImGui.Separator();
|
||||
var transfersTop = ImGui.GetCursorScreenPos().Y;
|
||||
var gradientBottom = MathF.Max(gradientTop, transfersTop - style.ItemSpacing.Y - gradientInset);
|
||||
selune.DrawGradient(gradientTop, gradientBottom, ImGui.GetIO().DeltaTime);
|
||||
float pairlistEnd = ImGui.GetCursorPosY();
|
||||
using (ImRaii.PushId("transfers")) DrawTransfers();
|
||||
_transferPartHeight = ImGui.GetCursorPosY() - pairlistEnd - ImGui.GetTextLineHeight();
|
||||
using (ImRaii.PushId("group-pair-popup")) _selectPairsForGroupUi.Draw(_pairManager.DirectPairs);
|
||||
using (ImRaii.PushId("group-syncshell-popup")) _selectSyncshellForTagUi.Draw([.. _pairManager.Groups.Values]);
|
||||
using (ImRaii.PushId("group-pair-popup")) _selectPairsForGroupUi.Draw(pairSnapshot.DirectPairs);
|
||||
using (ImRaii.PushId("group-syncshell-popup")) _selectSyncshellForTagUi.Draw(pairSnapshot.Groups);
|
||||
using (ImRaii.PushId("group-pair-edit")) _renamePairTagUi.Draw();
|
||||
using (ImRaii.PushId("group-syncshell-edit")) _renameSyncshellTagUi.Draw();
|
||||
using (ImRaii.PushId("grouping-pair-popup")) _selectTagForPairUi.Draw();
|
||||
using (ImRaii.PushId("grouping-syncshell-popup")) _selectTagForSyncshellUi.Draw();
|
||||
}
|
||||
|
||||
if (_configService.Current.OpenPopupOnAdd && _pairManager.LastAddedUser != null)
|
||||
else
|
||||
{
|
||||
_lastAddedUser = _pairManager.LastAddedUser;
|
||||
_pairManager.LastAddedUser = null;
|
||||
selune.Animate(ImGui.GetIO().DeltaTime);
|
||||
}
|
||||
|
||||
ProcessFocusTracker();
|
||||
|
||||
var lastAddedPair = _pairUiService.GetLastAddedPair();
|
||||
if (_configService.Current.OpenPopupOnAdd && lastAddedPair is not null)
|
||||
{
|
||||
_lastAddedUser = lastAddedPair;
|
||||
_pairUiService.ClearLastAddedPair();
|
||||
ImGui.OpenPopup("Set Notes for New User");
|
||||
_showModalForUserAddition = true;
|
||||
_lastAddedUserComment = string.Empty;
|
||||
@@ -285,20 +292,22 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
|
||||
private void DrawPairs()
|
||||
{
|
||||
var ySize = _transferPartHeight == 0
|
||||
float ySize = Math.Abs(_transferPartHeight) < 0.0001f
|
||||
? 1
|
||||
: (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y
|
||||
+ ImGui.GetTextLineHeight() - ImGui.GetStyle().WindowPadding.Y - ImGui.GetStyle().WindowBorderSize) - _transferPartHeight - ImGui.GetCursorPosY();
|
||||
|
||||
ImGui.BeginChild("list", new Vector2(_windowContentWidth, ySize), border: false);
|
||||
|
||||
foreach (var item in _drawFolders)
|
||||
if (ImGui.BeginChild("list", new Vector2(_windowContentWidth, ySize), border: false))
|
||||
{
|
||||
item.Draw();
|
||||
foreach (var item in _drawFolders)
|
||||
{
|
||||
item.Draw();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
private void DrawServerStatus()
|
||||
{
|
||||
var buttonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Link);
|
||||
@@ -371,6 +380,19 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenBlockedByActiveItem) || ImGui.IsItemActive())
|
||||
{
|
||||
Selune.RegisterHighlight(
|
||||
ImGui.GetItemRectMin(),
|
||||
ImGui.GetItemRectMax(),
|
||||
SeluneHighlightMode.Both,
|
||||
borderOnly: true,
|
||||
borderThicknessOverride: _connectButtonHighlightThickness,
|
||||
exactSize: true,
|
||||
clipToElement: true,
|
||||
roundingOverride: ImGui.GetStyle().FrameRounding);
|
||||
}
|
||||
|
||||
UiSharedService.AttachToolTip(isConnectingOrConnected ? "Disconnect from " + _serverManager.CurrentServer.ServerName : "Connect to " + _serverManager.CurrentServer.ServerName);
|
||||
}
|
||||
}
|
||||
@@ -470,6 +492,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
return new DownloadSummary(totalFiles, transferredFiles, transferredBytes, totalBytes);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
private readonly record struct DownloadSummary(int TotalFiles, int TransferredFiles, long TransferredBytes, long TotalBytes)
|
||||
{
|
||||
public bool HasDownloads => TotalFiles > 0 || TotalBytes > 0;
|
||||
@@ -477,7 +500,8 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
|
||||
private void DrawUIDHeader()
|
||||
{
|
||||
var uidText = GetUidText();
|
||||
var uidText = _apiController.ServerState.GetUidText(_apiController.DisplayName);
|
||||
var uidColor = _apiController.ServerState.GetUidColor();
|
||||
|
||||
Vector4? vanityTextColor = null;
|
||||
Vector4? vanityGlowColor = null;
|
||||
@@ -527,6 +551,17 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
var padding = new Vector2(10f * ImGuiHelpers.GlobalScale);
|
||||
Selune.RegisterHighlight(
|
||||
ImGui.GetItemRectMin() - padding,
|
||||
ImGui.GetItemRectMax() + padding,
|
||||
SeluneHighlightMode.Point,
|
||||
exactSize: true,
|
||||
clipToElement: true,
|
||||
clipPadding: padding,
|
||||
highlightColorOverride: UIColors.Get("LightlessGreen"),
|
||||
highlightAlphaOverride: 0.2f);
|
||||
|
||||
ImGui.BeginTooltip();
|
||||
ImGui.PushTextWrapPos(ImGui.GetFontSize() * 32f);
|
||||
|
||||
@@ -539,7 +574,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
ImGui.PopStyleColor();
|
||||
|
||||
ImGuiHelpers.ScaledDummy(0.2f);
|
||||
_uiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 2f);
|
||||
UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 2f);
|
||||
|
||||
if (_configService.Current.BroadcastEnabled)
|
||||
{
|
||||
@@ -581,7 +616,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
|
||||
if (ImGui.IsItemClicked())
|
||||
_lightlessMediator.Publish(new UiToggleMessage(typeof(BroadcastUI)));
|
||||
_lightlessMediator.Publish(new UiToggleMessage(typeof(LightFinderUI)));
|
||||
}
|
||||
|
||||
ImGui.SetCursorPosY(cursorY);
|
||||
@@ -594,15 +629,30 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
{
|
||||
var seString = SeStringUtils.BuildFormattedPlayerName(uidText, vanityTextColor, vanityGlowColor);
|
||||
var cursorPos = ImGui.GetCursorScreenPos();
|
||||
var fontPtr = ImGui.GetFont();
|
||||
SeStringUtils.RenderSeStringWithHitbox(seString, cursorPos, fontPtr, "uid-header");
|
||||
var targetFontSize = ImGui.GetFontSize();
|
||||
var font = ImGui.GetFont();
|
||||
SeStringUtils.RenderSeStringWithHitbox(seString, cursorPos, targetFontSize ,font , "uid-header");
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextColored(GetUidColor(), uidText);
|
||||
ImGui.TextColored(uidColor, uidText);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
var padding = new Vector2(35f * ImGuiHelpers.GlobalScale);
|
||||
Selune.RegisterHighlight(
|
||||
ImGui.GetItemRectMin() - padding,
|
||||
ImGui.GetItemRectMax() + padding,
|
||||
SeluneHighlightMode.Point,
|
||||
exactSize: true,
|
||||
clipToElement: true,
|
||||
clipPadding: padding,
|
||||
highlightColorOverride: vanityGlowColor,
|
||||
highlightAlphaOverride: 0.05f);
|
||||
}
|
||||
|
||||
headerItemClicked = ImGui.IsItemClicked();
|
||||
|
||||
if (headerItemClicked)
|
||||
@@ -667,12 +717,27 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
{
|
||||
var seString = SeStringUtils.BuildFormattedPlayerName(_apiController.UID, vanityTextColor, vanityGlowColor);
|
||||
var cursorPos = ImGui.GetCursorScreenPos();
|
||||
var fontPtr = ImGui.GetFont();
|
||||
SeStringUtils.RenderSeStringWithHitbox(seString, cursorPos, fontPtr, "uid-footer");
|
||||
var targetFontSize = ImGui.GetFontSize();
|
||||
var font = ImGui.GetFont();
|
||||
SeStringUtils.RenderSeStringWithHitbox(seString, cursorPos, targetFontSize, font, "uid-footer");
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextColored(GetUidColor(), _apiController.UID);
|
||||
ImGui.TextColored(uidColor, _apiController.UID);
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
var padding = new Vector2(30f * ImGuiHelpers.GlobalScale);
|
||||
Selune.RegisterHighlight(
|
||||
ImGui.GetItemRectMin() - padding,
|
||||
ImGui.GetItemRectMax() + padding,
|
||||
SeluneHighlightMode.Point,
|
||||
exactSize: true,
|
||||
clipToElement: true,
|
||||
clipPadding: padding,
|
||||
highlightColorOverride: vanityGlowColor,
|
||||
highlightAlphaOverride: 0.05f);
|
||||
}
|
||||
|
||||
bool uidFooterClicked = ImGui.IsItemClicked();
|
||||
@@ -685,7 +750,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
else
|
||||
{
|
||||
UiSharedService.ColorTextWrapped(GetServerError(), GetUidColor());
|
||||
UiSharedService.ColorTextWrapped(_apiController.ServerState.GetServerError(_apiController.AuthFailureMessage), uidColor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -696,28 +761,45 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
var drawFolders = new List<IDrawFolder>();
|
||||
var filter = _tabMenu.Filter;
|
||||
|
||||
var allPairs = _pairManager.PairsWithGroups.ToDictionary(k => k.Key, k => k.Value);
|
||||
var filteredPairs = allPairs.Where(p => PassesFilter(p.Key, filter)).ToDictionary(k => k.Key, k => k.Value);
|
||||
var allEntries = _drawEntityFactory.GetAllEntries().ToList();
|
||||
var filteredEntries = string.IsNullOrEmpty(filter)
|
||||
? allEntries
|
||||
: allEntries.Where(e => PassesFilter(e, filter)).ToList();
|
||||
|
||||
var syncshells = _pairLedger.GetAllSyncshells();
|
||||
var groupInfos = syncshells.Values
|
||||
.Select(s => s.GroupFullInfo)
|
||||
.OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
var entryLookup = allEntries.ToDictionary(e => e.DisplayEntry.Ident.UserId, StringComparer.Ordinal);
|
||||
var filteredEntryLookup = filteredEntries.ToDictionary(e => e.DisplayEntry.Ident.UserId, StringComparer.Ordinal);
|
||||
|
||||
//Filter of online/visible pairs
|
||||
if (_configService.Current.ShowVisibleUsersSeparately)
|
||||
{
|
||||
var allVisiblePairs = ImmutablePairList(allPairs.Where(p => FilterVisibleUsers(p.Key)));
|
||||
var filteredVisiblePairs = BasicSortedDictionary(filteredPairs.Where(p => FilterVisibleUsers(p.Key)));
|
||||
|
||||
drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomVisibleTag, filteredVisiblePairs, allVisiblePairs));
|
||||
var allVisiblePairs = SortVisibleEntries(allEntries.Where(FilterVisibleUsers));
|
||||
if (allVisiblePairs.Count > 0)
|
||||
{
|
||||
var filteredVisiblePairs = SortVisibleEntries(filteredEntries.Where(FilterVisibleUsers));
|
||||
drawFolders.Add(_drawEntityFactory.CreateTagFolder(TagHandler.CustomVisibleTag, filteredVisiblePairs, allVisiblePairs));
|
||||
}
|
||||
}
|
||||
|
||||
//Filter of not foldered syncshells
|
||||
var groupFolders = new List<GroupFolder>();
|
||||
foreach (var group in _pairManager.GroupPairs.Select(g => g.Key).OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase))
|
||||
foreach (var group in groupInfos)
|
||||
{
|
||||
GetGroups(allPairs, filteredPairs, group, out ImmutableList<Pair> allGroupPairs, out Dictionary<Pair, List<GroupFullInfoDto>> filteredGroupPairs);
|
||||
|
||||
if (FilterNotTaggedSyncshells(group))
|
||||
if (!FilterNotTaggedSyncshells(group))
|
||||
{
|
||||
groupFolders.Add(new GroupFolder(group, _drawEntityFactory.CreateDrawGroupFolder(group, filteredGroupPairs, allGroupPairs)));
|
||||
continue;
|
||||
}
|
||||
|
||||
var allGroupEntries = ResolveGroupEntries(entryLookup, syncshells, group, applyFilters: false);
|
||||
var filteredGroupEntries = ResolveGroupEntries(filteredEntryLookup, syncshells, group, applyFilters: true);
|
||||
// Always create the folder so empty syncshells remain visible in the UI.
|
||||
var drawGroupFolder = _drawEntityFactory.CreateGroupFolder(group.Group.GID, group, filteredGroupEntries, allGroupEntries);
|
||||
groupFolders.Add(new GroupFolder(group, drawGroupFolder));
|
||||
}
|
||||
|
||||
//Filter of grouped up syncshells (All Syncshells Folder)
|
||||
@@ -730,190 +812,224 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
//Filter of grouped/foldered pairs
|
||||
foreach (var tag in _tagHandler.GetAllPairTagsSorted())
|
||||
{
|
||||
var allTagPairs = ImmutablePairList(allPairs.Where(p => FilterTagUsers(p.Key, tag)));
|
||||
var filteredTagPairs = BasicSortedDictionary(filteredPairs.Where(p => FilterTagUsers(p.Key, tag) && FilterOnlineOrPausedSelf(p.Key)));
|
||||
|
||||
drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(tag, filteredTagPairs, allTagPairs));
|
||||
var allTagPairs = SortEntries(allEntries.Where(e => FilterTagUsers(e, tag)));
|
||||
if (allTagPairs.Count > 0)
|
||||
{
|
||||
var filteredTagPairs = SortEntries(filteredEntries.Where(e => FilterTagUsers(e, tag) && FilterOnlineOrPausedSelf(e)));
|
||||
drawFolders.Add(_drawEntityFactory.CreateTagFolder(tag, filteredTagPairs, allTagPairs));
|
||||
}
|
||||
}
|
||||
|
||||
//Filter of grouped/foldered syncshells
|
||||
foreach (var syncshellTag in _tagHandler.GetAllSyncshellTagsSorted())
|
||||
{
|
||||
var syncshellFolderTags = new List<GroupFolder>();
|
||||
foreach (var group in _pairManager.GroupPairs.Select(g => g.Key).OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase))
|
||||
foreach (var group in groupInfos)
|
||||
{
|
||||
if (_tagHandler.HasSyncshellTag(group.GID, syncshellTag))
|
||||
if (!_tagHandler.HasSyncshellTag(group.Group.GID, syncshellTag))
|
||||
{
|
||||
GetGroups(allPairs, filteredPairs, group,
|
||||
out ImmutableList<Pair> allGroupPairs,
|
||||
out Dictionary<Pair, List<GroupFullInfoDto>> filteredGroupPairs);
|
||||
|
||||
syncshellFolderTags.Add(new GroupFolder(group, _drawEntityFactory.CreateDrawGroupFolder($"tag_{group.GID}", group, filteredGroupPairs, allGroupPairs)));
|
||||
continue;
|
||||
}
|
||||
|
||||
var allGroupEntries = ResolveGroupEntries(entryLookup, syncshells, group, applyFilters: false);
|
||||
var filteredGroupEntries = ResolveGroupEntries(filteredEntryLookup, syncshells, group, applyFilters: true);
|
||||
// Keep tagged syncshells rendered regardless of whether membership info has loaded.
|
||||
var taggedGroupFolder = _drawEntityFactory.CreateGroupFolder($"tag_{group.Group.GID}", group, filteredGroupEntries, allGroupEntries);
|
||||
syncshellFolderTags.Add(new GroupFolder(group, taggedGroupFolder));
|
||||
}
|
||||
|
||||
drawFolders.Add(new DrawGroupedGroupFolder(syncshellFolderTags, _tagHandler, _apiController, _uiSharedService, _selectSyncshellForTagUi, _renameSyncshellTagUi, syncshellTag));
|
||||
}
|
||||
|
||||
//Filter of not grouped/foldered and offline pairs
|
||||
var allOnlineNotTaggedPairs = ImmutablePairList(allPairs.Where(p => FilterNotTaggedUsers(p.Key)));
|
||||
var onlineNotTaggedPairs = BasicSortedDictionary(filteredPairs.Where(p => FilterNotTaggedUsers(p.Key) && FilterOnlineOrPausedSelf(p.Key)));
|
||||
|
||||
drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder((_configService.Current.ShowOfflineUsersSeparately ? TagHandler.CustomOnlineTag : TagHandler.CustomAllTag), onlineNotTaggedPairs, allOnlineNotTaggedPairs));
|
||||
var allOnlineNotTaggedPairs = SortEntries(allEntries.Where(FilterNotTaggedUsers));
|
||||
if (allOnlineNotTaggedPairs.Count > 0 && _configService.Current.ShowOfflineUsersSeparately) {
|
||||
var filteredOnlineEntries = SortOnlineEntries(filteredEntries.Where(e => FilterNotTaggedUsers(e) && FilterOnlineOrPausedSelf(e)));
|
||||
drawFolders.Add(_drawEntityFactory.CreateTagFolder(
|
||||
TagHandler.CustomOnlineTag,
|
||||
filteredOnlineEntries,
|
||||
allOnlineNotTaggedPairs));
|
||||
} else if (allOnlineNotTaggedPairs.Count > 0 && !_configService.Current.ShowOfflineUsersSeparately) {
|
||||
var onlineNotTaggedPairs = SortEntries(filteredEntries.Where(FilterNotTaggedUsers));
|
||||
drawFolders.Add(_drawEntityFactory.CreateTagFolder(
|
||||
TagHandler.CustomAllTag,
|
||||
onlineNotTaggedPairs,
|
||||
allOnlineNotTaggedPairs));
|
||||
}
|
||||
|
||||
if (_configService.Current.ShowOfflineUsersSeparately)
|
||||
{
|
||||
var allOfflinePairs = ImmutablePairList(allPairs.Where(p => FilterOfflineUsers(p.Key, p.Value)));
|
||||
var filteredOfflinePairs = BasicSortedDictionary(filteredPairs.Where(p => FilterOfflineUsers(p.Key, p.Value)));
|
||||
|
||||
drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineTag, filteredOfflinePairs, allOfflinePairs));
|
||||
var allOfflinePairs = SortEntries(allEntries.Where(FilterOfflineUsers));
|
||||
if (allOfflinePairs.Count > 0)
|
||||
{
|
||||
var filteredOfflinePairs = SortEntries(filteredEntries.Where(FilterOfflineUsers));
|
||||
drawFolders.Add(_drawEntityFactory.CreateTagFolder(TagHandler.CustomOfflineTag, filteredOfflinePairs, allOfflinePairs));
|
||||
}
|
||||
|
||||
if (_configService.Current.ShowSyncshellOfflineUsersSeparately)
|
||||
{
|
||||
var allOfflineSyncshellUsers = ImmutablePairList(allPairs.Where(p => FilterOfflineSyncshellUsers(p.Key)));
|
||||
var filteredOfflineSyncshellUsers = BasicSortedDictionary(filteredPairs.Where(p => FilterOfflineSyncshellUsers(p.Key)));
|
||||
|
||||
drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineSyncshellTag, filteredOfflineSyncshellUsers, allOfflineSyncshellUsers));
|
||||
var allOfflineSyncshellUsers = SortEntries(allEntries.Where(FilterOfflineSyncshellUsers));
|
||||
if (allOfflineSyncshellUsers.Count > 0)
|
||||
{
|
||||
var filteredOfflineSyncshellUsers = SortEntries(filteredEntries.Where(FilterOfflineSyncshellUsers));
|
||||
drawFolders.Add(_drawEntityFactory.CreateTagFolder(TagHandler.CustomOfflineSyncshellTag, filteredOfflineSyncshellUsers, allOfflineSyncshellUsers));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Unpaired
|
||||
drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomUnpairedTag,
|
||||
BasicSortedDictionary(filteredPairs.Where(p => p.Key.IsOneSidedPair)),
|
||||
ImmutablePairList(allPairs.Where(p => p.Key.IsOneSidedPair))));
|
||||
//Unpaired
|
||||
var unpairedAllEntries = SortEntries(allEntries.Where(e => e.IsOneSided));
|
||||
if (unpairedAllEntries.Count > 0)
|
||||
{
|
||||
var unpairedFiltered = SortEntries(filteredEntries.Where(e => e.IsOneSided));
|
||||
drawFolders.Add(_drawEntityFactory.CreateTagFolder(TagHandler.CustomUnpairedTag, unpairedFiltered, unpairedAllEntries));
|
||||
}
|
||||
|
||||
return drawFolders;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool PassesFilter(Pair pair, string filter)
|
||||
private static bool PassesFilter(PairUiEntry entry, string filter)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filter)) return true;
|
||||
|
||||
return pair.UserData.AliasOrUID.Contains(filter, StringComparison.OrdinalIgnoreCase) || (pair.GetNote()?.Contains(filter, StringComparison.OrdinalIgnoreCase) ?? false) || (pair.PlayerName?.Contains(filter, StringComparison.OrdinalIgnoreCase) ?? false);
|
||||
return entry.AliasOrUid.Contains(filter, StringComparison.OrdinalIgnoreCase)
|
||||
|| (!string.IsNullOrEmpty(entry.Note) && entry.Note.Contains(filter, StringComparison.OrdinalIgnoreCase))
|
||||
|| (!string.IsNullOrEmpty(entry.DisplayName) && entry.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private string AlphabeticalSortKey(Pair pair)
|
||||
private string AlphabeticalSortKey(PairUiEntry entry)
|
||||
{
|
||||
if (_configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(pair.PlayerName))
|
||||
if (_configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(entry.DisplayName))
|
||||
{
|
||||
return _configService.Current.PreferNotesOverNamesForVisible ? (pair.GetNote() ?? string.Empty) : pair.PlayerName;
|
||||
return _configService.Current.PreferNotesOverNamesForVisible ? (entry.Note ?? string.Empty) : entry.DisplayName;
|
||||
}
|
||||
|
||||
return pair.GetNote() ?? pair.UserData.AliasOrUID;
|
||||
return !string.IsNullOrEmpty(entry.Note) ? entry.Note : entry.AliasOrUid;
|
||||
}
|
||||
|
||||
private bool FilterOnlineOrPausedSelf(Pair pair) => pair.IsOnline || (!pair.IsOnline && !_configService.Current.ShowOfflineUsersSeparately) || pair.UserPair.OwnPermissions.IsPaused();
|
||||
private bool FilterOnlineOrPausedSelf(PairUiEntry entry) => entry.IsOnline || (!entry.IsOnline && !_configService.Current.ShowOfflineUsersSeparately) || entry.SelfPermissions.IsPaused();
|
||||
|
||||
private bool FilterVisibleUsers(Pair pair) => pair.IsVisible && (_configService.Current.ShowSyncshellUsersInVisible || pair.IsDirectlyPaired);
|
||||
private bool FilterVisibleUsers(PairUiEntry entry) => entry.IsVisible && entry.IsOnline && (_configService.Current.ShowSyncshellUsersInVisible || entry.IsDirectlyPaired);
|
||||
|
||||
private bool FilterTagUsers(Pair pair, string tag) => pair.IsDirectlyPaired && !pair.IsOneSidedPair && _tagHandler.HasPairTag(pair.UserData.UID, tag);
|
||||
private bool FilterTagUsers(PairUiEntry entry, string tag) => entry.IsDirectlyPaired && !entry.IsOneSided && _tagHandler.HasPairTag(entry.DisplayEntry.Ident.UserId, tag);
|
||||
|
||||
private static bool FilterGroupUsers(List<GroupFullInfoDto> groups, GroupFullInfoDto group) => groups.Exists(g => string.Equals(g.GID, group.GID, StringComparison.Ordinal));
|
||||
|
||||
private bool FilterNotTaggedUsers(Pair pair) => pair.IsDirectlyPaired && !pair.IsOneSidedPair && !_tagHandler.HasAnyPairTag(pair.UserData.UID);
|
||||
private bool FilterNotTaggedUsers(PairUiEntry entry) => entry.IsDirectlyPaired && !entry.IsOneSided && !_tagHandler.HasAnyPairTag(entry.DisplayEntry.Ident.UserId);
|
||||
|
||||
private bool FilterNotTaggedSyncshells(GroupFullInfoDto group) => !_tagHandler.HasAnySyncshellTag(group.GID) || _configService.Current.ShowGroupedSyncshellsInAll;
|
||||
|
||||
private bool FilterOfflineUsers(Pair pair, List<GroupFullInfoDto> groups) => ((pair.IsDirectlyPaired && _configService.Current.ShowSyncshellOfflineUsersSeparately) || !_configService.Current.ShowSyncshellOfflineUsersSeparately) && (!pair.IsOneSidedPair || groups.Count != 0) && !pair.IsOnline && !pair.UserPair.OwnPermissions.IsPaused();
|
||||
|
||||
private static bool FilterOfflineSyncshellUsers(Pair pair) => !pair.IsDirectlyPaired && !pair.IsOnline && !pair.UserPair.OwnPermissions.IsPaused();
|
||||
|
||||
private Dictionary<Pair, List<GroupFullInfoDto>> BasicSortedDictionary(IEnumerable<KeyValuePair<Pair, List<GroupFullInfoDto>>> pairs) => pairs.OrderByDescending(u => u.Key.IsVisible).ThenByDescending(u => u.Key.IsOnline).ThenBy(u => AlphabeticalSortKey(u.Key), StringComparer.OrdinalIgnoreCase).ToDictionary(u => u.Key, u => u.Value);
|
||||
|
||||
private static ImmutableList<Pair> ImmutablePairList(IEnumerable<KeyValuePair<Pair, List<GroupFullInfoDto>>> pairs) => [.. pairs.Select(k => k.Key)];
|
||||
|
||||
private void GetGroups(Dictionary<Pair, List<GroupFullInfoDto>> allPairs,
|
||||
Dictionary<Pair, List<GroupFullInfoDto>> filteredPairs,
|
||||
GroupFullInfoDto group,
|
||||
out ImmutableList<Pair> allGroupPairs,
|
||||
out Dictionary<Pair, List<GroupFullInfoDto>> filteredGroupPairs)
|
||||
private bool FilterOfflineUsers(PairUiEntry entry)
|
||||
{
|
||||
allGroupPairs = ImmutablePairList(allPairs
|
||||
.Where(u => FilterGroupUsers(u.Value, group)));
|
||||
|
||||
filteredGroupPairs = filteredPairs
|
||||
.Where(u => FilterGroupUsers(u.Value, group) && FilterOnlineOrPausedSelf(u.Key))
|
||||
.OrderByDescending(u => u.Key.IsOnline)
|
||||
.ThenBy(u =>
|
||||
{
|
||||
if (string.Equals(u.Key.UserData.UID, group.OwnerUID, StringComparison.Ordinal)) return 0;
|
||||
if (group.GroupPairUserInfos.TryGetValue(u.Key.UserData.UID, out var info))
|
||||
{
|
||||
if (info.IsModerator()) return 1;
|
||||
if (info.IsPinned()) return 2;
|
||||
}
|
||||
return u.Key.IsVisible ? 3 : 4;
|
||||
})
|
||||
.ThenBy(u => AlphabeticalSortKey(u.Key), StringComparer.OrdinalIgnoreCase)
|
||||
.ToDictionary(k => k.Key, k => k.Value);
|
||||
var groups = entry.DisplayEntry.Groups;
|
||||
var includeDirect = _configService.Current.ShowSyncshellOfflineUsersSeparately ? entry.IsDirectlyPaired : true;
|
||||
var includeGroup = !entry.IsOneSided || groups.Count != 0;
|
||||
return includeDirect && includeGroup && !entry.IsOnline && !entry.SelfPermissions.IsPaused();
|
||||
}
|
||||
|
||||
private string GetServerError()
|
||||
private static bool FilterOfflineSyncshellUsers(PairUiEntry entry) => !entry.IsDirectlyPaired && !entry.IsOnline && !entry.SelfPermissions.IsPaused();
|
||||
|
||||
private ImmutableList<PairUiEntry> SortEntries(IEnumerable<PairUiEntry> entries)
|
||||
{
|
||||
return _apiController.ServerState switch
|
||||
return [.. entries
|
||||
.OrderByDescending(e => e.IsVisible)
|
||||
.ThenByDescending(e => e.IsOnline)
|
||||
.ThenBy(e => AlphabeticalSortKey(e), StringComparer.OrdinalIgnoreCase)];
|
||||
}
|
||||
|
||||
private ImmutableList<PairUiEntry> SortVisibleEntries(IEnumerable<PairUiEntry> entries)
|
||||
{
|
||||
var entryList = entries.ToList();
|
||||
return _configService.Current.VisiblePairSortMode switch
|
||||
{
|
||||
ServerState.Connecting => "Attempting to connect to the server.",
|
||||
ServerState.Reconnecting => "Connection to server interrupted, attempting to reconnect to the server.",
|
||||
ServerState.Disconnected => "You are currently disconnected from the Lightless Sync server.",
|
||||
ServerState.Disconnecting => "Disconnecting from the server",
|
||||
ServerState.Unauthorized => "Server Response: " + _apiController.AuthFailureMessage,
|
||||
ServerState.Offline => "Your selected Lightless Sync server is currently offline.",
|
||||
ServerState.VersionMisMatch =>
|
||||
"Your plugin or the server you are connecting to is out of date. Please update your plugin now. If you already did so, contact the server provider to update their server to the latest version.",
|
||||
ServerState.RateLimited => "You are rate limited for (re)connecting too often. Disconnect, wait 10 minutes and try again.",
|
||||
ServerState.Connected => string.Empty,
|
||||
ServerState.NoSecretKey => "You have no secret key set for this current character. Open Settings -> Service Settings and set a secret key for the current character. You can reuse the same secret key for multiple characters.",
|
||||
ServerState.MultiChara => "Your Character Configuration has multiple characters configured with same name and world. You will not be able to connect until you fix this issue. Remove the duplicates from the configuration in Settings -> Service Settings -> Character Management and reconnect manually after.",
|
||||
ServerState.OAuthMisconfigured => "OAuth2 is enabled but not fully configured, verify in the Settings -> Service Settings that you have OAuth2 connected and, importantly, a UID assigned to your current character.",
|
||||
ServerState.OAuthLoginTokenStale => "Your OAuth2 login token is stale and cannot be used to renew. Go to the Settings -> Service Settings and unlink then relink your OAuth2 configuration.",
|
||||
ServerState.NoAutoLogon => "This character has automatic login into Lightless disabled. Press the connect button to connect to Lightless.",
|
||||
_ => string.Empty
|
||||
VisiblePairSortMode.VramUsage => SortVisibleByMetric(entryList, e => e.LastAppliedApproximateVramBytes),
|
||||
VisiblePairSortMode.EffectiveVramUsage => SortVisibleByMetric(entryList, e => e.LastAppliedApproximateEffectiveVramBytes),
|
||||
VisiblePairSortMode.TriangleCount => SortVisibleByMetric(entryList, e => e.LastAppliedDataTris),
|
||||
VisiblePairSortMode.Alphabetical => [.. entryList.OrderBy(e => AlphabeticalSortKey(e), StringComparer.OrdinalIgnoreCase)],
|
||||
VisiblePairSortMode.PreferredDirectPairs => SortVisibleByPreferred(entryList),
|
||||
_ => SortEntries(entryList),
|
||||
};
|
||||
}
|
||||
|
||||
private Vector4 GetUidColor()
|
||||
private ImmutableList<PairUiEntry> SortOnlineEntries(IEnumerable<PairUiEntry> entries)
|
||||
{
|
||||
return _apiController.ServerState switch
|
||||
var entryList = entries.ToList();
|
||||
return _configService.Current.OnlinePairSortMode switch
|
||||
{
|
||||
ServerState.Connecting => UIColors.Get("LightlessYellow"),
|
||||
ServerState.Reconnecting => UIColors.Get("DimRed"),
|
||||
ServerState.Connected => UIColors.Get("LightlessPurple"),
|
||||
ServerState.Disconnected => UIColors.Get("LightlessYellow"),
|
||||
ServerState.Disconnecting => UIColors.Get("LightlessYellow"),
|
||||
ServerState.Unauthorized => UIColors.Get("DimRed"),
|
||||
ServerState.VersionMisMatch => UIColors.Get("DimRed"),
|
||||
ServerState.Offline => UIColors.Get("DimRed"),
|
||||
ServerState.RateLimited => UIColors.Get("LightlessYellow"),
|
||||
ServerState.NoSecretKey => UIColors.Get("LightlessYellow"),
|
||||
ServerState.MultiChara => UIColors.Get("LightlessYellow"),
|
||||
ServerState.OAuthMisconfigured => UIColors.Get("DimRed"),
|
||||
ServerState.OAuthLoginTokenStale => UIColors.Get("DimRed"),
|
||||
ServerState.NoAutoLogon => UIColors.Get("LightlessYellow"),
|
||||
_ => UIColors.Get("DimRed")
|
||||
OnlinePairSortMode.Alphabetical => [.. entryList.OrderBy(e => AlphabeticalSortKey(e), StringComparer.OrdinalIgnoreCase)],
|
||||
OnlinePairSortMode.PreferredDirectPairs => SortVisibleByPreferred(entryList),
|
||||
_ => SortEntries(entryList),
|
||||
};
|
||||
}
|
||||
|
||||
private string GetUidText()
|
||||
private ImmutableList<PairUiEntry> SortVisibleByMetric(IEnumerable<PairUiEntry> entries, Func<PairUiEntry, long> selector)
|
||||
{
|
||||
return _apiController.ServerState switch
|
||||
return [.. entries
|
||||
.OrderByDescending(entry => selector(entry) >= 0)
|
||||
.ThenByDescending(selector)
|
||||
.ThenByDescending(entry => entry.IsOnline)
|
||||
.ThenBy(entry => AlphabeticalSortKey(entry), StringComparer.OrdinalIgnoreCase)];
|
||||
}
|
||||
|
||||
private ImmutableList<PairUiEntry> SortVisibleByPreferred(IEnumerable<PairUiEntry> entries)
|
||||
{
|
||||
return [.. entries
|
||||
.OrderByDescending(entry => entry.IsDirectlyPaired && entry.SelfPermissions.IsSticky())
|
||||
.ThenByDescending(entry => entry.IsDirectlyPaired)
|
||||
.ThenByDescending(entry => entry.IsOnline)
|
||||
.ThenBy(entry => AlphabeticalSortKey(entry), StringComparer.OrdinalIgnoreCase)];
|
||||
}
|
||||
|
||||
private ImmutableList<PairUiEntry> SortGroupEntries(IEnumerable<PairUiEntry> entries, GroupFullInfoDto group)
|
||||
{
|
||||
return [.. entries
|
||||
.OrderByDescending(e => e.IsOnline)
|
||||
.ThenBy(e => GroupSortWeight(e, group))
|
||||
.ThenBy(e => AlphabeticalSortKey(e), StringComparer.OrdinalIgnoreCase)];
|
||||
}
|
||||
|
||||
private int GroupSortWeight(PairUiEntry entry, GroupFullInfoDto group)
|
||||
{
|
||||
if (string.Equals(entry.DisplayEntry.Ident.UserId, group.OwnerUID, StringComparison.Ordinal))
|
||||
{
|
||||
ServerState.Reconnecting => "Reconnecting",
|
||||
ServerState.Connecting => "Connecting",
|
||||
ServerState.Disconnected => "Disconnected",
|
||||
ServerState.Disconnecting => "Disconnecting",
|
||||
ServerState.Unauthorized => "Unauthorized",
|
||||
ServerState.VersionMisMatch => "Version mismatch",
|
||||
ServerState.Offline => "Unavailable",
|
||||
ServerState.RateLimited => "Rate Limited",
|
||||
ServerState.NoSecretKey => "No Secret Key",
|
||||
ServerState.MultiChara => "Duplicate Characters",
|
||||
ServerState.OAuthMisconfigured => "Misconfigured OAuth2",
|
||||
ServerState.OAuthLoginTokenStale => "Stale OAuth2",
|
||||
ServerState.NoAutoLogon => "Auto Login disabled",
|
||||
ServerState.Connected => _apiController.DisplayName,
|
||||
_ => string.Empty
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (group.GroupPairUserInfos.TryGetValue(entry.DisplayEntry.Ident.UserId, out var info))
|
||||
{
|
||||
if (info.IsModerator()) return 1;
|
||||
if (info.IsPinned()) return 2;
|
||||
}
|
||||
|
||||
return entry.IsVisible ? 3 : 4;
|
||||
}
|
||||
|
||||
private ImmutableList<PairUiEntry> ResolveGroupEntries(
|
||||
IReadOnlyDictionary<string, PairUiEntry> entryLookup,
|
||||
IReadOnlyDictionary<string, Syncshell> syncshells,
|
||||
GroupFullInfoDto group,
|
||||
bool applyFilters)
|
||||
{
|
||||
if (!syncshells.TryGetValue(group.Group.GID, out var shell))
|
||||
{
|
||||
return ImmutableList<PairUiEntry>.Empty;
|
||||
}
|
||||
|
||||
var entries = shell.Users.Keys
|
||||
.Select(id => entryLookup.TryGetValue(id, out var entry) ? entry : null)
|
||||
.Where(entry => entry is not null)
|
||||
.Cast<PairUiEntry>();
|
||||
|
||||
if (applyFilters && _configService.Current.ShowOfflineUsersSeparately)
|
||||
{
|
||||
entries = entries.Where(entry => !FilterOfflineUsers(entry));
|
||||
}
|
||||
|
||||
if (applyFilters && _configService.Current.ShowSyncshellOfflineUsersSeparately)
|
||||
{
|
||||
entries = entries.Where(entry => !FilterOfflineSyncshellUsers(entry));
|
||||
}
|
||||
|
||||
return SortGroupEntries(entries, group);
|
||||
}
|
||||
|
||||
private void UiSharedService_GposeEnd()
|
||||
@@ -926,4 +1042,50 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
_wasOpen = IsOpen;
|
||||
IsOpen = false;
|
||||
}
|
||||
|
||||
private void RegisterFocusCharacter(Pair pair)
|
||||
{
|
||||
_pendingFocusPair = pair;
|
||||
_pendingFocusFrame = ImGui.GetFrameCount();
|
||||
}
|
||||
|
||||
private void ProcessFocusTracker()
|
||||
{
|
||||
var frame = ImGui.GetFrameCount();
|
||||
Pair? character = _pendingFocusFrame == frame ? _pendingFocusPair : null;
|
||||
if (!ReferenceEquals(character, _focusedPair))
|
||||
{
|
||||
if (character is null)
|
||||
{
|
||||
_dalamudUtilService.ReleaseVisiblePairFocus();
|
||||
}
|
||||
else
|
||||
{
|
||||
_dalamudUtilService.FocusVisiblePair(character);
|
||||
}
|
||||
|
||||
_focusedPair = character;
|
||||
}
|
||||
|
||||
if (_pendingFocusFrame == frame)
|
||||
{
|
||||
_pendingFocusPair = null;
|
||||
_pendingFocusFrame = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private void ForceReleaseFocus()
|
||||
{
|
||||
if (_focusedPair is null)
|
||||
{
|
||||
_pendingFocusPair = null;
|
||||
_pendingFocusFrame = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
_dalamudUtilService.ReleaseVisiblePairFocus();
|
||||
_focusedPair = null;
|
||||
_pendingFocusPair = null;
|
||||
_pendingFocusFrame = -1;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user