From f6a471f457d772c277e03cca701411a489315cce Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Sat, 18 Mar 2023 21:39:17 +0100 Subject: [PATCH] rework for ui shit --- MareSynchronos/.editorconfig | 6 +- MareSynchronos/FileCache/FileCacheManager.cs | 9 +- .../FileCache/PeriodicFileScanner.cs | 6 +- .../ConfigurationMigrator.cs | 3 +- .../Configurations/Obsolete/ServerConfigV0.cs | 4 +- .../PlayerData/Pairs/PairManager.cs | 5 +- MareSynchronos/Plugin.cs | 2 + .../Services/CommandManagerService.cs | 6 + MareSynchronos/UI/CompactUI.cs | 233 +---------- MareSynchronos/UI/Components/DrawGroupPair.cs | 299 ++++++++++++++ MareSynchronos/UI/Components/DrawPairBase.cs | 43 ++ MareSynchronos/UI/Components/DrawUserPair.cs | 213 ++++++++++ MareSynchronos/UI/Components/GroupPanel.cs | 376 +----------------- MareSynchronos/UI/Components/PairGroupsUi.cs | 104 ++--- .../UI/Components/SelectGroupForPairUi.cs | 32 +- .../UI/Components/SelectPairForGroupUi.cs | 26 +- MareSynchronos/UI/GposeUi.cs | 2 +- MareSynchronos/UI/Handlers/TagHandler.cs | 16 +- .../UI/Handlers/UidDisplayHandler.cs | 117 ++++++ 19 files changed, 821 insertions(+), 681 deletions(-) create mode 100644 MareSynchronos/UI/Components/DrawGroupPair.cs create mode 100644 MareSynchronos/UI/Components/DrawPairBase.cs create mode 100644 MareSynchronos/UI/Components/DrawUserPair.cs create mode 100644 MareSynchronos/UI/Handlers/UidDisplayHandler.cs diff --git a/MareSynchronos/.editorconfig b/MareSynchronos/.editorconfig index d852067..a21012b 100644 --- a/MareSynchronos/.editorconfig +++ b/MareSynchronos/.editorconfig @@ -94,5 +94,9 @@ csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion csharp_style_prefer_tuple_swap = true:suggestion csharp_style_prefer_utf8_string_literals = true:suggestion dotnet_diagnostic.S1075.severity = silent +dotnet_diagnostic.SS3358.severity = true:suggestion dotnet_diagnostic.MA0007.severity = silent -dotnet_diagnostic.MA0075.severity = silent \ No newline at end of file +dotnet_diagnostic.MA0075.severity = silent + +# S3358: Ternary operators should not be nested +dotnet_diagnostic.S3358.severity = suggestion diff --git a/MareSynchronos/FileCache/FileCacheManager.cs b/MareSynchronos/FileCache/FileCacheManager.cs index 475c834..22fe596 100644 --- a/MareSynchronos/FileCache/FileCacheManager.cs +++ b/MareSynchronos/FileCache/FileCacheManager.cs @@ -122,10 +122,13 @@ public sealed class FileCacheManager : IDisposable return validatedCacheEntry; } - public void RemoveHash(FileCacheEntity entity) + public void RemoveHash(FileCacheEntity? entity) { - _logger.LogTrace("Removing {path}", entity.ResolvedFilepath); - _fileCaches.Remove(entity.PrefixedFilePath, out _); + if (entity != null) + { + _logger.LogTrace("Removing {path}", entity.ResolvedFilepath); + _fileCaches.Remove(entity.PrefixedFilePath, out _); + } } public string ResolveFileReplacement(string gamePath) diff --git a/MareSynchronos/FileCache/PeriodicFileScanner.cs b/MareSynchronos/FileCache/PeriodicFileScanner.cs index 474c545..2d539a6 100644 --- a/MareSynchronos/FileCache/PeriodicFileScanner.cs +++ b/MareSynchronos/FileCache/PeriodicFileScanner.cs @@ -15,7 +15,7 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase private readonly PerformanceCollectorService _performanceCollector; private long _currentFileProgress = 0; private bool _fileScanWasRunning = false; - private CancellationTokenSource? _scanCancellationTokenSource; + private CancellationTokenSource _scanCancellationTokenSource = new(); private TimeSpan _timeUntilNextScan = TimeSpan.Zero; public PeriodicFileScanner(ILogger logger, IpcManager ipcManager, MareConfigService configService, @@ -246,7 +246,7 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase Logger.LogWarning(ex, "Error during enumerating FileCaches"); } - Task.WaitAll(dbTasks); + Task.WaitAll(dbTasks, _scanCancellationTokenSource.Token); if (!_ipcManager.CheckPenumbraApi()) { @@ -308,7 +308,7 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase if (ct.IsCancellationRequested) return; } - Task.WaitAll(dbTasks); + Task.WaitAll(dbTasks, _scanCancellationTokenSource.Token); Logger.LogTrace("Scanner added new files to db"); diff --git a/MareSynchronos/MareConfiguration/ConfigurationMigrator.cs b/MareSynchronos/MareConfiguration/ConfigurationMigrator.cs index 2e9dff1..2d43757 100644 --- a/MareSynchronos/MareConfiguration/ConfigurationMigrator.cs +++ b/MareSynchronos/MareConfiguration/ConfigurationMigrator.cs @@ -8,6 +8,7 @@ using Newtonsoft.Json; namespace MareSynchronos.MareConfiguration; #pragma warning disable CS0618 // ignore Obsolete tag, the point of this migrator is to migrate obsolete configs to new ones +#pragma warning disable CS0612 // ignore Obsolete tag, the point of this migrator is to migrate obsolete configs to new ones public class ConfigurationMigrator : IHostedService { @@ -92,7 +93,6 @@ public class ConfigurationMigrator : IHostedService MareConfig mareConfigV1 = mareConfigV0.ToV1(); - int i = 0; var serverConfig = new ServerConfig() { ServerStorage = mareConfigV0.ServerStorage.Select(p => p.Value.ToV1()).ToList() @@ -152,4 +152,5 @@ public class ConfigurationMigrator : IHostedService } } +#pragma warning restore CS0612 // ignore Obsolete tag, the point of this migrator is to migrate obsolete configs to new ones #pragma warning restore CS0618 // ignore Obsolete tag, the point of this migrator is to migrate obsolete configs to new ones \ No newline at end of file diff --git a/MareSynchronos/MareConfiguration/Configurations/Obsolete/ServerConfigV0.cs b/MareSynchronos/MareConfiguration/Configurations/Obsolete/ServerConfigV0.cs index 5cd6f04..a46ac69 100644 --- a/MareSynchronos/MareConfiguration/Configurations/Obsolete/ServerConfigV0.cs +++ b/MareSynchronos/MareConfiguration/Configurations/Obsolete/ServerConfigV0.cs @@ -4,7 +4,7 @@ using MareSynchronos.WebAPI; namespace MareSynchronos.MareConfiguration.Configurations.Obsolete; [Serializable] -[Obsolete] +[Obsolete("Replaced with ServerConfig")] public class ServerConfigV0 : IMareConfiguration { public string CurrentServer { get; set; } = string.Empty; @@ -15,4 +15,4 @@ public class ServerConfigV0 : IMareConfiguration }; public int Version { get; set; } = 0; -} +} \ No newline at end of file diff --git a/MareSynchronos/PlayerData/Pairs/PairManager.cs b/MareSynchronos/PlayerData/Pairs/PairManager.cs index e8628ba..4163d87 100644 --- a/MareSynchronos/PlayerData/Pairs/PairManager.cs +++ b/MareSynchronos/PlayerData/Pairs/PairManager.cs @@ -326,13 +326,14 @@ public sealed class PairManager : DisposableMediatorSubscriberBase private void DisposePairs(bool recreate = false) { Logger.LogDebug("Disposing all Pairs"); - foreach (var item in _allClientPairs) + Parallel.ForEach(_allClientPairs, item => { if (recreate) item.Value.RecreateCachedPlayer(); else item.Value.MarkOffline(); - } + }); + RecreateLazy(); } diff --git a/MareSynchronos/Plugin.cs b/MareSynchronos/Plugin.cs index 4e26274..4dd1d20 100644 --- a/MareSynchronos/Plugin.cs +++ b/MareSynchronos/Plugin.cs @@ -22,6 +22,7 @@ using MareSynchronos.Services; using MareSynchronos.Services.Mediator; using MareSynchronos.Services.ServerConfiguration; using MareSynchronos.UI; +using MareSynchronos.UI.Handlers; using MareSynchronos.WebAPI; using MareSynchronos.WebAPI.Files; using MareSynchronos.WebAPI.SignalR; @@ -65,6 +66,7 @@ public sealed class Plugin : IDalamudPlugin collection.AddSingleton(); collection.AddSingleton(); collection.AddSingleton(); + collection.AddSingleton(); collection.AddSingleton((s) => new DalamudUtilService(s.GetRequiredService>(), clientState, objectTable, framework, gameGui, condition, gameData, s.GetRequiredService(), s.GetRequiredService())); diff --git a/MareSynchronos/Services/CommandManagerService.cs b/MareSynchronos/Services/CommandManagerService.cs index a6ff39b..05a43c1 100644 --- a/MareSynchronos/Services/CommandManagerService.cs +++ b/MareSynchronos/Services/CommandManagerService.cs @@ -55,6 +55,12 @@ public sealed class CommandManagerService : IDisposable if (string.Equals(splitArgs[0], "toggle", StringComparison.OrdinalIgnoreCase)) { + if (_apiController.ServerState == WebAPI.SignalR.Utils.ServerState.Disconnecting) + { + _mediator.Publish(new NotificationMessage("Mare disconnecting", "Cannot use /toggle while Mare Synchronos is still disconnecting", + Dalamud.Interface.Internal.Notifications.NotificationType.Error)); + } + if (_serverConfigurationManager.CurrentServer == null) return; var fullPause = splitArgs.Length > 1 ? splitArgs[1] switch { diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index da16d43..7527c41 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -27,9 +27,6 @@ namespace MareSynchronos.UI; public class CompactUi : WindowMediatorSubscriberBase { - public readonly Dictionary ShowUidForEntry = new(StringComparer.Ordinal); - public string EditNickEntry = string.Empty; - public string EditUserComment = string.Empty; public float TransferPartHeight; public float WindowContentWidth; private readonly ApiController _apiController; @@ -43,6 +40,7 @@ public class CompactUi : WindowMediatorSubscriberBase private readonly SelectPairForGroupUi _selectPairsForGroupUi; private readonly ServerConfigurationManager _serverManager; private readonly Stopwatch _timeout = new(); + private readonly UidDisplayHandler _uidDisplayHandler; private readonly UiSharedService _uiShared; private bool _buttonState; private string _characterOrCommentFilter = string.Empty; @@ -55,7 +53,7 @@ public class CompactUi : WindowMediatorSubscriberBase private bool _wasOpen; public CompactUi(ILogger logger, UiSharedService uiShared, MareConfigService configService, ApiController apiController, PairManager pairManager, - ServerConfigurationManager serverManager, MareMediator mediator, FileUploadManager fileTransferManager) : base(logger, mediator, "###MareSynchronosMainUI") + ServerConfigurationManager serverManager, MareMediator mediator, FileUploadManager fileTransferManager, UidDisplayHandler uidDisplayHandler) : base(logger, mediator, "###MareSynchronosMainUI") { _uiShared = uiShared; _configService = configService; @@ -63,12 +61,13 @@ public class CompactUi : WindowMediatorSubscriberBase _pairManager = pairManager; _serverManager = serverManager; _fileTransferManager = fileTransferManager; + _uidDisplayHandler = uidDisplayHandler; var tagHandler = new TagHandler(_serverManager); - _groupPanel = new(this, uiShared, _pairManager, _serverManager, _configService); - _selectGroupForPairUi = new(tagHandler); - _selectPairsForGroupUi = new(tagHandler); - _pairGroupsUi = new(configService, tagHandler, DrawPairedClient, apiController, _selectPairsForGroupUi); + _groupPanel = new(this, uiShared, _pairManager, uidDisplayHandler, _serverManager); + _selectGroupForPairUi = new(tagHandler, uidDisplayHandler); + _selectPairsForGroupUi = new(tagHandler, uidDisplayHandler); + _pairGroupsUi = new(configService, tagHandler, apiController, _selectPairsForGroupUi); #if DEBUG string dev = "Dev Build"; @@ -164,8 +163,8 @@ public class CompactUi : WindowMediatorSubscriberBase ImGui.Separator(); UiSharedService.DrawWithID("transfers", DrawTransfers); TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight; - UiSharedService.DrawWithID("group-user-popup", () => _selectPairsForGroupUi.Draw(_pairManager.DirectPairs, ShowUidForEntry)); - UiSharedService.DrawWithID("grouping-popup", () => _selectGroupForPairUi.Draw(ShowUidForEntry)); + UiSharedService.DrawWithID("group-user-popup", () => _selectPairsForGroupUi.Draw(_pairManager.DirectPairs)); + UiSharedService.DrawWithID("grouping-popup", () => _selectGroupForPairUi.Draw()); } if (_configService.Current.OpenPopupOnAdd && _pairManager.LastAddedUser != null) @@ -202,8 +201,7 @@ public class CompactUi : WindowMediatorSubscriberBase public override void OnClose() { - EditNickEntry = string.Empty; - EditUserComment = string.Empty; + _uidDisplayHandler.Clear(); base.OnClose(); } @@ -213,8 +211,6 @@ public class CompactUi : WindowMediatorSubscriberBase var keys = _serverManager.CurrentServer!.SecretKeys; if (keys.TryGetValue(_secretKeyIdx, out var secretKey)) { - var friendlyName = secretKey.FriendlyName; - if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Add current character with secret key")) { _serverManager.CurrentServer!.Authentications.Add(new MareConfiguration.Models.Authentication() @@ -350,210 +346,6 @@ public class CompactUi : WindowMediatorSubscriberBase } } - private void DrawPairedClient(Pair entry) - { - if (entry.UserPair == null) return; - - var pauseIcon = entry.UserPair!.OwnPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; - var pauseIconSize = UiSharedService.GetIconButtonSize(pauseIcon); - var barButtonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars); - var entryUID = entry.UserData.AliasOrUID; - var textSize = ImGui.CalcTextSize(entryUID); - var originalY = ImGui.GetCursorPosY(); - var buttonSizes = pauseIconSize.Y + barButtonSize.Y; - var spacingX = ImGui.GetStyle().ItemSpacing.X; - var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth(); - - var textPos = originalY + pauseIconSize.Y / 2 - textSize.Y / 2; - ImGui.SetCursorPosY(textPos); - FontAwesomeIcon connectionIcon; - string connectionText = string.Empty; - Vector4 connectionColor; - if (!(entry.UserPair!.OwnPermissions.IsPaired() && entry.UserPair!.OtherPermissions.IsPaired())) - { - connectionIcon = FontAwesomeIcon.ArrowUp; - connectionText = entryUID + " has not added you back"; - connectionColor = ImGuiColors.DalamudRed; - } - else if (entry.UserPair!.OwnPermissions.IsPaused() || entry.UserPair!.OtherPermissions.IsPaused()) - { - connectionIcon = FontAwesomeIcon.PauseCircle; - connectionText = "Pairing status with " + entryUID + " is paused"; - connectionColor = ImGuiColors.DalamudYellow; - } - else - { - connectionIcon = FontAwesomeIcon.Check; - connectionText = "You are paired with " + entryUID; - connectionColor = ImGuiColors.ParsedGreen; - } - - ImGui.PushFont(UiBuilder.IconFont); - UiSharedService.ColorText(connectionIcon.ToIconString(), connectionColor); - ImGui.PopFont(); - UiSharedService.AttachToolTip(connectionText); - ImGui.SameLine(); - ImGui.SetCursorPosY(textPos); - if (entry is { IsOnline: true, IsVisible: true }) - { - ImGui.PushFont(UiBuilder.IconFont); - UiSharedService.ColorText(FontAwesomeIcon.Eye.ToIconString(), ImGuiColors.ParsedGreen); - ImGui.PopFont(); - UiSharedService.AttachToolTip(entryUID + " is visible: " + entry.PlayerName!); - } - - var textIsUid = true; - ShowUidForEntry.TryGetValue(entry.UserPair!.User.UID, out var showUidInsteadOfName); - string? playerText = _serverManager.GetNoteForUid(entry.UserPair!.User.UID); - if (!showUidInsteadOfName && playerText != null) - { - if (string.IsNullOrEmpty(playerText)) - { - playerText = entryUID; - } - else - { - textIsUid = false; - } - } - else - { - playerText = entryUID; - } - - if (_configService.Current.ShowCharacterNameInsteadOfNotesForVisible && entry.IsVisible && !showUidInsteadOfName) - { - playerText = entry.PlayerName; - textIsUid = false; - } - - ImGui.SameLine(); - if (!string.Equals(EditNickEntry, entry.UserData.UID, StringComparison.Ordinal)) - { - ImGui.SetCursorPosY(textPos); - if (textIsUid) ImGui.PushFont(UiBuilder.MonoFont); - ImGui.TextUnformatted(playerText); - if (textIsUid) ImGui.PopFont(); - UiSharedService.AttachToolTip("Left click to switch between UID display and nick" + Environment.NewLine + - "Right click to change nick for " + entryUID); - if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) - { - var prevState = textIsUid; - if (ShowUidForEntry.ContainsKey(entry.UserPair!.User.UID)) - { - prevState = ShowUidForEntry[entry.UserPair!.User.UID]; - } - - ShowUidForEntry[entry.UserPair!.User.UID] = !prevState; - } - - if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) - { - var pair = _pairManager.DirectPairs.Find(p => string.Equals(p.UserData.UID, EditNickEntry, StringComparison.Ordinal)); - if (pair != null) - { - pair.SetNote(EditUserComment); - _configService.Save(); - } - EditUserComment = entry.GetNote() ?? string.Empty; - EditNickEntry = entry.UserPair!.User.UID; - } - } - else - { - ImGui.SetCursorPosY(originalY); - - ImGui.SetNextItemWidth(UiSharedService.GetWindowContentRegionWidth() - ImGui.GetCursorPosX() - buttonSizes - ImGui.GetStyle().ItemSpacing.X * 2); - if (ImGui.InputTextWithHint("", "Nick/Notes", ref EditUserComment, 255, ImGuiInputTextFlags.EnterReturnsTrue)) - { - _serverManager.SetNoteForUid(entry.UserPair!.User.UID, EditUserComment); - EditNickEntry = string.Empty; - } - - if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) - { - EditNickEntry = string.Empty; - } - UiSharedService.AttachToolTip("Hit ENTER to save\nRight click to cancel"); - } - - // Pause Button && sound warnings - if (entry.UserPair!.OwnPermissions.IsPaired() && entry.UserPair!.OtherPermissions.IsPaired()) - { - var individualSoundsDisabled = (entry.UserPair?.OwnPermissions.IsDisableSounds() ?? false) || (entry.UserPair?.OtherPermissions.IsDisableSounds() ?? false); - var individualAnimDisabled = (entry.UserPair?.OwnPermissions.IsDisableAnimations() ?? false) || (entry.UserPair?.OtherPermissions.IsDisableAnimations() ?? false); - - if (individualAnimDisabled || individualSoundsDisabled) - { - var infoIconPosDist = windowEndX - barButtonSize.X - spacingX - pauseIconSize.X - spacingX; - var icon = FontAwesomeIcon.ExclamationTriangle; - var iconwidth = UiSharedService.GetIconSize(icon); - - ImGui.SameLine(infoIconPosDist - iconwidth.X); - - ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); - UiSharedService.FontText(icon.ToIconString(), UiBuilder.IconFont); - ImGui.PopStyleColor(); - if (ImGui.IsItemHovered()) - { - ImGui.BeginTooltip(); - - ImGui.Text("Individual User permissions"); - - if (individualSoundsDisabled) - { - var userSoundsText = "Sound sync disabled with " + entry.UserData.AliasOrUID; - UiSharedService.FontText(FontAwesomeIcon.VolumeOff.ToIconString(), UiBuilder.IconFont); - ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); - ImGui.Text(userSoundsText); - ImGui.NewLine(); - ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); - ImGui.Text("You: " + (entry.UserPair!.OwnPermissions.IsDisableSounds() ? "Disabled" : "Enabled") + ", They: " + (entry.UserPair!.OtherPermissions.IsDisableSounds() ? "Disabled" : "Enabled")); - } - - if (individualAnimDisabled) - { - var userAnimText = "Animation sync disabled with " + entry.UserData.AliasOrUID; - UiSharedService.FontText(FontAwesomeIcon.Stop.ToIconString(), UiBuilder.IconFont); - ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); - ImGui.Text(userAnimText); - ImGui.NewLine(); - ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); - ImGui.Text("You: " + (entry.UserPair!.OwnPermissions.IsDisableAnimations() ? "Disabled" : "Enabled") + ", They: " + (entry.UserPair!.OtherPermissions.IsDisableAnimations() ? "Disabled" : "Enabled")); - } - - ImGui.EndTooltip(); - } - } - - ImGui.SameLine(windowEndX - barButtonSize.X - spacingX - pauseIconSize.X); - ImGui.SetCursorPosY(originalY); - if (ImGuiComponents.IconButton(pauseIcon)) - { - var perm = entry.UserPair!.OwnPermissions; - perm.SetPaused(!perm.IsPaused()); - _ = _apiController.UserSetPairPermissions(new(entry.UserData, perm)); - } - UiSharedService.AttachToolTip(!entry.UserPair!.OwnPermissions.IsPaused() - ? "Pause pairing with " + entryUID - : "Resume pairing with " + entryUID); - } - - // Flyout Menu - ImGui.SameLine(windowEndX - barButtonSize.X); - ImGui.SetCursorPosY(originalY); - - if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars)) - { - ImGui.OpenPopup("User Flyout Menu"); - } - if (ImGui.BeginPopup("User Flyout Menu")) - { - UiSharedService.DrawWithID($"buttons-{entry.UserPair!.User.UID}", () => DrawPairedClientMenu(entry)); - ImGui.EndPopup(); - } - } - private void DrawPairedClientMenu(Pair entry) { if (entry.IsVisible) @@ -617,14 +409,15 @@ public class CompactUi : WindowMediatorSubscriberBase .OrderBy( u => _configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(u.PlayerName) ? u.PlayerName - : (u.GetNote() ?? u.UserData.AliasOrUID), StringComparer.OrdinalIgnoreCase).ToList(); + : (u.GetNote() ?? u.UserData.AliasOrUID), StringComparer.OrdinalIgnoreCase) + .Select(c => new DrawUserPair(c, _uidDisplayHandler, _apiController, _selectGroupForPairUi)).ToList(); if (_configService.Current.ReverseUserSort) { users.Reverse(); } - var onlineUsers = users.Where(u => u.IsOnline || u.UserPair.OwnPermissions.IsPaused()).ToList(); + var onlineUsers = users.Where(u => u.IsOnline || u.UserPair!.OwnPermissions.IsPaused()).ToList(); var visibleUsers = onlineUsers.Where(u => u.IsVisible).ToList(); var offlineUsers = users.Except(onlineUsers).ToList(); diff --git a/MareSynchronos/UI/Components/DrawGroupPair.cs b/MareSynchronos/UI/Components/DrawGroupPair.cs new file mode 100644 index 0000000..27f619a --- /dev/null +++ b/MareSynchronos/UI/Components/DrawGroupPair.cs @@ -0,0 +1,299 @@ +using Dalamud.Interface.Colors; +using Dalamud.Interface.Components; +using Dalamud.Interface; +using ImGuiNET; +using MareSynchronos.PlayerData.Pairs; +using MareSynchronos.API.Data.Extensions; +using MareSynchronos.WebAPI; +using MareSynchronos.API.Dto.User; +using MareSynchronos.UI.Handlers; +using MareSynchronos.API.Dto.Group; +using MareSynchronos.API.Data.Enum; + +namespace MareSynchronos.UI.Components; + +public class DrawGroupPair : DrawPairBase +{ + private readonly ApiController _apiController; + private readonly GroupPairFullInfoDto _fullInfoDto; + private readonly GroupFullInfoDto _group; + private string _banReason = string.Empty; + private bool _banUserPopupOpen; + private bool _showModalBanUser; + + public DrawGroupPair(Pair entry, ApiController apiController, GroupFullInfoDto group, GroupPairFullInfoDto fullInfoDto, UidDisplayHandler handler) : base(entry, handler) + { + _apiController = apiController; + _group = group; + _fullInfoDto = fullInfoDto; + } + + protected override void DrawLeftSide(float textPosY, float originalY) + { + var entryUID = _pair.UserData.AliasOrUID; + var entryIsMod = _fullInfoDto.GroupPairStatusInfo.IsModerator(); + var entryIsOwner = string.Equals(_pair.UserData.UID, _group.OwnerUID, StringComparison.Ordinal); + var entryIsPinned = _fullInfoDto.GroupPairStatusInfo.IsPinned(); + var presenceIcon = _pair.IsVisible ? FontAwesomeIcon.Eye : (_pair.IsOnline ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink); + var presenceColor = (_pair.IsOnline || _pair.IsVisible) ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; + var presenceText = entryUID + " is offline"; + + ImGui.SetCursorPosY(textPosY); + if (_pair.IsPaused) + { + presenceIcon = FontAwesomeIcon.Question; + presenceColor = ImGuiColors.DalamudGrey; + presenceText = entryUID + " online status is unknown (paused)"; + + ImGui.PushFont(UiBuilder.IconFont); + UiSharedService.ColorText(FontAwesomeIcon.PauseCircle.ToIconString(), ImGuiColors.DalamudYellow); + ImGui.PopFont(); + + UiSharedService.AttachToolTip("Pairing status with " + entryUID + " is paused"); + } + else + { + ImGui.PushFont(UiBuilder.IconFont); + UiSharedService.ColorText(FontAwesomeIcon.Check.ToIconString(), ImGuiColors.ParsedGreen); + ImGui.PopFont(); + + UiSharedService.AttachToolTip("You are paired with " + entryUID); + } + + if (_pair.IsOnline && !_pair.IsVisible) presenceText = entryUID + " is online"; + else if (_pair.IsOnline && _pair.IsVisible) presenceText = entryUID + " is visible: " + _pair.PlayerName; + + ImGui.SameLine(); + ImGui.SetCursorPosY(textPosY); + ImGui.PushFont(UiBuilder.IconFont); + UiSharedService.ColorText(presenceIcon.ToIconString(), presenceColor); + ImGui.PopFont(); + UiSharedService.AttachToolTip(presenceText); + + if (entryIsOwner) + { + ImGui.SameLine(); + ImGui.SetCursorPosY(textPosY); + ImGui.PushFont(UiBuilder.IconFont); + ImGui.TextUnformatted(FontAwesomeIcon.Crown.ToIconString()); + ImGui.PopFont(); + UiSharedService.AttachToolTip("User is owner of this Syncshell"); + } + else if (entryIsMod) + { + ImGui.SameLine(); + ImGui.SetCursorPosY(textPosY); + ImGui.PushFont(UiBuilder.IconFont); + ImGui.TextUnformatted(FontAwesomeIcon.UserShield.ToIconString()); + ImGui.PopFont(); + UiSharedService.AttachToolTip("User is moderator of this Syncshell"); + } + else if (entryIsPinned) + { + ImGui.SameLine(); + ImGui.SetCursorPosY(textPosY); + ImGui.PushFont(UiBuilder.IconFont); + ImGui.TextUnformatted(FontAwesomeIcon.Thumbtack.ToIconString()); + ImGui.PopFont(); + UiSharedService.AttachToolTip("User is pinned in this Syncshell"); + } + } + + protected override float DrawRightSide(float textPosY, float originalY) + { + var entryUID = _fullInfoDto.UserAliasOrUID; + var entryIsMod = _fullInfoDto.GroupPairStatusInfo.IsModerator(); + var entryIsOwner = string.Equals(_pair.UserData.UID, _group.OwnerUID, StringComparison.Ordinal); + var entryIsPinned = _fullInfoDto.GroupPairStatusInfo.IsPinned(); + var userIsOwner = string.Equals(_group.OwnerUID, _apiController.UID, StringComparison.OrdinalIgnoreCase); + var userIsModerator = _group.GroupUserInfo.IsModerator(); + + var soundsDisabled = _fullInfoDto.GroupUserPermissions.IsDisableSounds(); + var animDisabled = _fullInfoDto.GroupUserPermissions.IsDisableAnimations(); + var individualSoundsDisabled = (_pair.UserPair?.OwnPermissions.IsDisableSounds() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableSounds() ?? false); + var individualAnimDisabled = (_pair.UserPair?.OwnPermissions.IsDisableAnimations() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableAnimations() ?? false); + + bool showInfo = (individualAnimDisabled || individualSoundsDisabled || animDisabled || soundsDisabled); + bool showPlus = _pair.UserPair == null; + bool showBars = userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner); + + var spacing = ImGui.GetStyle().ItemSpacing.X; + var permIcon = (individualAnimDisabled || individualSoundsDisabled) ? FontAwesomeIcon.ExclamationTriangle + : ((soundsDisabled || animDisabled) ? FontAwesomeIcon.InfoCircle : FontAwesomeIcon.None); + var infoIconWidth = UiSharedService.GetIconSize(permIcon).X; + var plusButtonWidth = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Plus).X; + var barButtonWidth = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars).X; + + var pos = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() + spacing + - (showInfo ? (infoIconWidth + spacing) : 0) + - (showPlus ? (plusButtonWidth + spacing) : 0) + - (showBars ? (barButtonWidth + spacing) : 0); + + ImGui.SameLine(pos); + if (individualAnimDisabled || individualSoundsDisabled) + { + ImGui.SetCursorPosY(textPosY); + ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); + UiSharedService.FontText(permIcon.ToIconString(), UiBuilder.IconFont); + ImGui.PopStyleColor(); + if (ImGui.IsItemHovered()) + { + ImGui.BeginTooltip(); + + ImGui.Text("Individual User permissions"); + + if (individualSoundsDisabled) + { + var userSoundsText = "Sound sync disabled with " + _pair.UserData.AliasOrUID; + UiSharedService.FontText(FontAwesomeIcon.VolumeOff.ToIconString(), UiBuilder.IconFont); + ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.Text(userSoundsText); + ImGui.NewLine(); + ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.Text("You: " + (_pair.UserPair!.OwnPermissions.IsDisableSounds() ? "Disabled" : "Enabled") + ", They: " + (_pair.UserPair!.OtherPermissions.IsDisableSounds() ? "Disabled" : "Enabled")); + } + + if (individualAnimDisabled) + { + var userAnimText = "Animation sync disabled with " + _pair.UserData.AliasOrUID; + UiSharedService.FontText(FontAwesomeIcon.Stop.ToIconString(), UiBuilder.IconFont); + ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.Text(userAnimText); + ImGui.NewLine(); + ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.Text("You: " + (_pair.UserPair!.OwnPermissions.IsDisableAnimations() ? "Disabled" : "Enabled") + ", They: " + (_pair.UserPair!.OtherPermissions.IsDisableAnimations() ? "Disabled" : "Enabled")); + } + + ImGui.EndTooltip(); + } + ImGui.SameLine(); + } + else if ((animDisabled || soundsDisabled)) + { + ImGui.SetCursorPosY(textPosY); + UiSharedService.FontText(permIcon.ToIconString(), UiBuilder.IconFont); + if (ImGui.IsItemHovered()) + { + ImGui.BeginTooltip(); + + ImGui.Text("Sycnshell User permissions"); + + if (soundsDisabled) + { + var userSoundsText = "Sound sync disabled by " + _pair.UserData.AliasOrUID; + UiSharedService.FontText(FontAwesomeIcon.VolumeOff.ToIconString(), UiBuilder.IconFont); + ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.Text(userSoundsText); + } + + if (animDisabled) + { + var userAnimText = "Animation sync disabled by " + _pair.UserData.AliasOrUID; + UiSharedService.FontText(FontAwesomeIcon.Stop.ToIconString(), UiBuilder.IconFont); + ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.Text(userAnimText); + } + + ImGui.EndTooltip(); + } + ImGui.SameLine(); + } + + if (showPlus) + { + ImGui.SetCursorPosY(originalY); + + if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus)) + { + _ = _apiController.UserAddPair(new UserDto(new(_pair.UserData.UID))); + } + UiSharedService.AttachToolTip("Pair with " + entryUID + " individually"); + ImGui.SameLine(); + } + + if (showBars) + { + ImGui.SetCursorPosY(originalY); + + if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars)) + { + ImGui.OpenPopup("Popup"); + } + } + + if (ImGui.BeginPopup("Popup")) + { + if ((userIsModerator || userIsOwner) && !(entryIsMod || entryIsOwner)) + { + var pinText = entryIsPinned ? "Unpin user" : "Pin user"; + if (UiSharedService.IconTextButton(FontAwesomeIcon.Thumbtack, pinText)) + { + ImGui.CloseCurrentPopup(); + var userInfo = _fullInfoDto.GroupPairStatusInfo ^ GroupUserInfo.IsPinned; + _ = _apiController.GroupSetUserInfo(new GroupPairUserInfoDto(_fullInfoDto.Group, _fullInfoDto.User, userInfo)); + } + UiSharedService.AttachToolTip("Pin this user to the Syncshell. Pinned users will not be deleted in case of a manually initiated Syncshell clean"); + + if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Remove user") && UiSharedService.CtrlPressed()) + { + ImGui.CloseCurrentPopup(); + _ = _apiController.GroupRemoveUser(_fullInfoDto); + } + + UiSharedService.AttachToolTip("Hold CTRL and click to remove user " + (_pair.UserData.AliasOrUID) + " from Syncshell"); + if (UiSharedService.IconTextButton(FontAwesomeIcon.UserSlash, "Ban User")) + { + _showModalBanUser = true; + ImGui.CloseCurrentPopup(); + } + UiSharedService.AttachToolTip("Ban user from this Syncshell"); + } + + if (userIsOwner) + { + string modText = entryIsMod ? "Demod user" : "Mod user"; + if (UiSharedService.IconTextButton(FontAwesomeIcon.UserShield, modText) && UiSharedService.CtrlPressed()) + { + ImGui.CloseCurrentPopup(); + var userInfo = _fullInfoDto.GroupPairStatusInfo ^ GroupUserInfo.IsModerator; + _ = _apiController.GroupSetUserInfo(new GroupPairUserInfoDto(_fullInfoDto.Group, _fullInfoDto.User, userInfo)); + } + UiSharedService.AttachToolTip("Hold CTRL to change the moderator status for " + (_fullInfoDto.UserAliasOrUID) + Environment.NewLine + + "Moderators can kick, ban/unban, pin/unpin users and clear the Syncshell."); + if (UiSharedService.IconTextButton(FontAwesomeIcon.Crown, "Transfer Ownership") && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed()) + { + ImGui.CloseCurrentPopup(); + _ = _apiController.GroupChangeOwnership(_fullInfoDto); + } + UiSharedService.AttachToolTip("Hold CTRL and SHIFT and click to transfer ownership of this Syncshell to " + (_fullInfoDto.UserAliasOrUID) + Environment.NewLine + "WARNING: This action is irreversible."); + } + ImGui.EndPopup(); + } + + if (_showModalBanUser && !_banUserPopupOpen) + { + ImGui.OpenPopup("Ban User"); + _banUserPopupOpen = true; + } + + if (!_showModalBanUser) _banUserPopupOpen = false; + + if (ImGui.BeginPopupModal("Ban User", ref _showModalBanUser, UiSharedService.PopupWindowFlags)) + { + UiSharedService.TextWrapped("User " + (_fullInfoDto.UserAliasOrUID) + " will be banned and removed from this Syncshell."); + ImGui.InputTextWithHint("##banreason", "Ban Reason", ref _banReason, 255); + if (ImGui.Button("Ban User")) + { + ImGui.CloseCurrentPopup(); + var reason = _banReason; + _ = _apiController.GroupBanUser(new GroupPairDto(_group.Group, _fullInfoDto.User), reason); + _banReason = string.Empty; + } + UiSharedService.TextWrapped("The reason will be displayed in the banlist. The current server-side alias if present (Vanity ID) will automatically be attached to the reason."); + UiSharedService.SetScaledWindowSize(300); + ImGui.EndPopup(); + } + + return pos - spacing; + } +} \ No newline at end of file diff --git a/MareSynchronos/UI/Components/DrawPairBase.cs b/MareSynchronos/UI/Components/DrawPairBase.cs new file mode 100644 index 0000000..6d2124d --- /dev/null +++ b/MareSynchronos/UI/Components/DrawPairBase.cs @@ -0,0 +1,43 @@ +using Dalamud.Interface; +using ImGuiNET; +using MareSynchronos.PlayerData.Pairs; +using MareSynchronos.UI.Handlers; + +namespace MareSynchronos.UI.Components; + +public abstract class DrawPairBase +{ + protected Pair _pair; + private readonly UidDisplayHandler _displayHandler; + + protected DrawPairBase(Pair entry, UidDisplayHandler uIDDisplayHandler) + { + _pair = entry; + _displayHandler = uIDDisplayHandler; + } + + public string UID => _pair.UserData.UID; + + public void DrawPairedClient() + { + var originalY = ImGui.GetCursorPosY(); + var pauseIconSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Play); + var textSize = ImGui.CalcTextSize(_pair.UserData.AliasOrUID); + + var textPosY = originalY + pauseIconSize.Y / 2 - textSize.Y / 2; + DrawLeftSide(textPosY, originalY); + ImGui.SameLine(); + var posX = ImGui.GetCursorPosX(); + var rightSide = DrawRightSide(textPosY, originalY); + DrawName(originalY, posX, rightSide); + } + + protected abstract void DrawLeftSide(float textPosY, float originalY); + + protected abstract float DrawRightSide(float textPosY, float originalY); + + private void DrawName(float originalY, float leftSide, float rightSide) + { + _displayHandler.DrawPairText(_pair, leftSide, originalY, () => rightSide - leftSide); + } +} \ No newline at end of file diff --git a/MareSynchronos/UI/Components/DrawUserPair.cs b/MareSynchronos/UI/Components/DrawUserPair.cs new file mode 100644 index 0000000..0731337 --- /dev/null +++ b/MareSynchronos/UI/Components/DrawUserPair.cs @@ -0,0 +1,213 @@ +using Dalamud.Interface.Colors; +using Dalamud.Interface.Components; +using Dalamud.Interface; +using ImGuiNET; +using MareSynchronos.PlayerData.Pairs; +using System.Numerics; +using MareSynchronos.API.Data.Extensions; +using MareSynchronos.WebAPI; +using MareSynchronos.API.Dto.User; +using MareSynchronos.UI.Handlers; + +namespace MareSynchronos.UI.Components; + +public class DrawUserPair : DrawPairBase +{ + private readonly ApiController _apiController; + private readonly SelectGroupForPairUi _selectGroupForPairUi; + + public DrawUserPair(Pair entry, UidDisplayHandler displayHandler, ApiController apiController, SelectGroupForPairUi selectGroupForPairUi) : base(entry, displayHandler) + { + if (_pair.UserPair == null) throw new ArgumentException("Pair must be UserPair", nameof(entry)); + _pair = entry; + _apiController = apiController; + _selectGroupForPairUi = selectGroupForPairUi; + } + + public bool IsOnline => _pair.IsOnline; + public bool IsVisible => _pair.IsVisible; + public UserPairDto UserPair => _pair.UserPair!; + + protected override void DrawLeftSide(float textPos, float originalY) + { + FontAwesomeIcon connectionIcon; + Vector4 connectionColor; + string connectionText; + if (!(_pair.UserPair!.OwnPermissions.IsPaired() && _pair.UserPair!.OtherPermissions.IsPaired())) + { + connectionIcon = FontAwesomeIcon.ArrowUp; + connectionText = _pair.UserData.AliasOrUID + " has not added you back"; + connectionColor = ImGuiColors.DalamudRed; + } + else if (_pair.UserPair!.OwnPermissions.IsPaused() || _pair.UserPair!.OtherPermissions.IsPaused()) + { + connectionIcon = FontAwesomeIcon.PauseCircle; + connectionText = "Pairing status with " + _pair.UserData.AliasOrUID + " is paused"; + connectionColor = ImGuiColors.DalamudYellow; + } + else + { + connectionIcon = FontAwesomeIcon.Check; + connectionText = "You are paired with " + _pair.UserData.AliasOrUID; + connectionColor = ImGuiColors.ParsedGreen; + } + + ImGui.SetCursorPosY(textPos); + ImGui.PushFont(UiBuilder.IconFont); + UiSharedService.ColorText(connectionIcon.ToIconString(), connectionColor); + ImGui.PopFont(); + UiSharedService.AttachToolTip(connectionText); + if (_pair is { IsOnline: true, IsVisible: true }) + { + ImGui.SameLine(); + ImGui.SetCursorPosY(textPos); + ImGui.PushFont(UiBuilder.IconFont); + UiSharedService.ColorText(FontAwesomeIcon.Eye.ToIconString(), ImGuiColors.ParsedGreen); + ImGui.PopFont(); + UiSharedService.AttachToolTip(_pair.UserData.AliasOrUID + " is visible: " + _pair.PlayerName!); + } + } + + protected override float DrawRightSide(float textPosY, float originalY) + { + var pauseIcon = _pair.UserPair!.OwnPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; + var pauseIconSize = UiSharedService.GetIconButtonSize(pauseIcon); + var barButtonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars); + var entryUID = _pair.UserData.AliasOrUID; + var spacingX = ImGui.GetStyle().ItemSpacing.X; + var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth(); + var rightSideStart = 0f; + + if (_pair.UserPair!.OwnPermissions.IsPaired() && _pair.UserPair!.OtherPermissions.IsPaired()) + { + var individualSoundsDisabled = (_pair.UserPair?.OwnPermissions.IsDisableSounds() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableSounds() ?? false); + var individualAnimDisabled = (_pair.UserPair?.OwnPermissions.IsDisableAnimations() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableAnimations() ?? false); + + if (individualAnimDisabled || individualSoundsDisabled) + { + var infoIconPosDist = windowEndX - barButtonSize.X - spacingX - pauseIconSize.X - spacingX; + var icon = FontAwesomeIcon.ExclamationTriangle; + var iconwidth = UiSharedService.GetIconSize(icon); + + rightSideStart = infoIconPosDist - iconwidth.X; + ImGui.SameLine(infoIconPosDist - iconwidth.X); + + ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); + UiSharedService.FontText(icon.ToIconString(), UiBuilder.IconFont); + ImGui.PopStyleColor(); + if (ImGui.IsItemHovered()) + { + ImGui.BeginTooltip(); + + ImGui.Text("Individual User permissions"); + + if (individualSoundsDisabled) + { + var userSoundsText = "Sound sync disabled with " + _pair.UserData.AliasOrUID; + UiSharedService.FontText(FontAwesomeIcon.VolumeOff.ToIconString(), UiBuilder.IconFont); + ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.Text(userSoundsText); + ImGui.NewLine(); + ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.Text("You: " + (_pair.UserPair!.OwnPermissions.IsDisableSounds() ? "Disabled" : "Enabled") + ", They: " + (_pair.UserPair!.OtherPermissions.IsDisableSounds() ? "Disabled" : "Enabled")); + } + + if (individualAnimDisabled) + { + var userAnimText = "Animation sync disabled with " + _pair.UserData.AliasOrUID; + UiSharedService.FontText(FontAwesomeIcon.Stop.ToIconString(), UiBuilder.IconFont); + ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.Text(userAnimText); + ImGui.NewLine(); + ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.Text("You: " + (_pair.UserPair!.OwnPermissions.IsDisableAnimations() ? "Disabled" : "Enabled") + ", They: " + (_pair.UserPair!.OtherPermissions.IsDisableAnimations() ? "Disabled" : "Enabled")); + } + + ImGui.EndTooltip(); + } + } + + if (rightSideStart == 0f) + { + rightSideStart = windowEndX - barButtonSize.X - spacingX * 2 - pauseIconSize.X; + } + ImGui.SameLine(windowEndX - barButtonSize.X - spacingX - pauseIconSize.X); + ImGui.SetCursorPosY(originalY); + if (ImGuiComponents.IconButton(pauseIcon)) + { + var perm = _pair.UserPair!.OwnPermissions; + perm.SetPaused(!perm.IsPaused()); + _ = _apiController.UserSetPairPermissions(new(_pair.UserData, perm)); + } + UiSharedService.AttachToolTip(!_pair.UserPair!.OwnPermissions.IsPaused() + ? "Pause pairing with " + entryUID + : "Resume pairing with " + entryUID); + } + + // Flyout Menu + if (rightSideStart == 0f) + { + rightSideStart = windowEndX - barButtonSize.X; + } + ImGui.SameLine(windowEndX - barButtonSize.X); + ImGui.SetCursorPosY(originalY); + + if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars)) + { + ImGui.OpenPopup("User Flyout Menu"); + } + if (ImGui.BeginPopup("User Flyout Menu")) + { + UiSharedService.DrawWithID($"buttons-{_pair.UserData.UID}", () => DrawPairedClientMenu(_pair)); + ImGui.EndPopup(); + } + + return rightSideStart; + } + + private void DrawPairedClientMenu(Pair entry) + { + if (entry.IsVisible) + { + if (UiSharedService.IconTextButton(FontAwesomeIcon.Sync, "Reload last data")) + { + entry.ApplyLastReceivedData(forced: true); + ImGui.CloseCurrentPopup(); + } + UiSharedService.AttachToolTip("This reapplies the last received character data to this character"); + } + + var entryUID = entry.UserData.AliasOrUID; + if (UiSharedService.IconTextButton(FontAwesomeIcon.Folder, "Pair Groups")) + { + _selectGroupForPairUi.Open(entry); + } + UiSharedService.AttachToolTip("Choose pair groups for " + entryUID); + + var isDisableSounds = entry.UserPair!.OwnPermissions.IsDisableSounds(); + string disableSoundsText = isDisableSounds ? "Enable sound sync" : "Disable sound sync"; + var disableSoundsIcon = isDisableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeMute; + if (UiSharedService.IconTextButton(disableSoundsIcon, disableSoundsText)) + { + var permissions = entry.UserPair.OwnPermissions; + permissions.SetDisableSounds(!isDisableSounds); + _ = _apiController.UserSetPairPermissions(new UserPermissionsDto(entry.UserData, permissions)); + } + + var isDisableAnims = entry.UserPair!.OwnPermissions.IsDisableAnimations(); + string disableAnimsText = isDisableAnims ? "Enable animation sync" : "Disable animation sync"; + var disableAnimsIcon = isDisableAnims ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop; + if (UiSharedService.IconTextButton(disableAnimsIcon, disableAnimsText)) + { + var permissions = entry.UserPair.OwnPermissions; + permissions.SetDisableAnimations(!isDisableAnims); + _ = _apiController.UserSetPairPermissions(new UserPermissionsDto(entry.UserData, permissions)); + } + + if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Unpair Permanently") && UiSharedService.CtrlPressed()) + { + _ = _apiController.UserRemovePair(new(entry.UserData)); + } + UiSharedService.AttachToolTip("Hold CTRL and click to unpair permanently from " + entryUID); + } +} \ No newline at end of file diff --git a/MareSynchronos/UI/Components/GroupPanel.cs b/MareSynchronos/UI/Components/GroupPanel.cs index 8420405..234f0d0 100644 --- a/MareSynchronos/UI/Components/GroupPanel.cs +++ b/MareSynchronos/UI/Components/GroupPanel.cs @@ -1,5 +1,4 @@ -using Dalamud.Interface.Colors; -using Dalamud.Interface.Components; +using Dalamud.Interface.Components; using Dalamud.Interface; using Dalamud.Utility; using ImGuiNET; @@ -8,28 +7,26 @@ using System.Numerics; using System.Globalization; using MareSynchronos.API.Data; using MareSynchronos.API.Dto.Group; -using MareSynchronos.API.Dto.User; using MareSynchronos.API.Data.Enum; using MareSynchronos.API.Data.Extensions; using MareSynchronos.API.Data.Comparer; -using MareSynchronos.MareConfiguration; using MareSynchronos.PlayerData.Pairs; using MareSynchronos.Services.ServerConfiguration; +using MareSynchronos.UI.Components; +using MareSynchronos.UI.Handlers; namespace MareSynchronos.UI; internal sealed class GroupPanel { - private readonly MareConfigService _configService; private readonly Dictionary _expandedGroupState = new(StringComparer.Ordinal); private readonly CompactUi _mainUi; private readonly PairManager _pairManager; private readonly ServerConfigurationManager _serverConfigurationManager; private readonly Dictionary _showGidForEntry = new(StringComparer.Ordinal); + private readonly UidDisplayHandler _uidDisplayHandler; private readonly UiSharedService _uiShared; private List _bannedUsers = new(); - private string _banReason = string.Empty; - private bool _banUserPopupOpen; private int _bulkInviteCount = 10; private List _bulkOneTimeInvites = new(); private string _editGroupComment = string.Empty; @@ -43,7 +40,6 @@ internal sealed class GroupPanel private bool _modalChangePwOpened; private string _newSyncShellPassword = string.Empty; private bool _showModalBanList = false; - private bool _showModalBanUser; private bool _showModalBulkOneTimeInvites = false; private bool _showModalChangePassword; private bool _showModalCreateGroup; @@ -51,13 +47,13 @@ internal sealed class GroupPanel private string _syncShellPassword = string.Empty; private string _syncShellToJoin = string.Empty; - public GroupPanel(CompactUi mainUi, UiSharedService uiShared, PairManager pairManager, ServerConfigurationManager serverConfigurationManager, MareConfigService configurationService) + public GroupPanel(CompactUi mainUi, UiSharedService uiShared, PairManager pairManager, UidDisplayHandler uidDisplayHandler, ServerConfigurationManager serverConfigurationManager) { _mainUi = mainUi; _uiShared = uiShared; _pairManager = pairManager; + _uidDisplayHandler = uidDisplayHandler; _serverConfigurationManager = serverConfigurationManager; - _configService = configurationService; } private ApiController ApiController => _uiShared.ApiController; @@ -406,18 +402,24 @@ internal sealed class GroupPanel .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsModerator()) .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned()) .ThenBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase) + .Select(c => new DrawGroupPair(c, ApiController, groupDto, c.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value, + _uidDisplayHandler)) .ToList(); var onlineUsers = pairsInGroup.Where(u => u.IsOnline && !u.IsVisible) .OrderByDescending(u => string.Equals(u.UserData.UID, groupDto.OwnerUID, StringComparison.Ordinal)) .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsModerator()) .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned()) .ThenBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase) + .Select(c => new DrawGroupPair(c, ApiController, groupDto, c.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value, + _uidDisplayHandler)) .ToList(); var offlineUsers = pairsInGroup.Where(u => !u.IsOnline && !u.IsVisible) .OrderByDescending(u => string.Equals(u.UserData.UID, groupDto.OwnerUID, StringComparison.Ordinal)) .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsModerator()) .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned()) .ThenBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase) + .Select(c => new DrawGroupPair(c, ApiController, groupDto, c.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value, + _uidDisplayHandler)) .ToList(); if (visibleUsers.Any()) @@ -426,12 +428,7 @@ internal sealed class GroupPanel ImGui.Separator(); foreach (var entry in visibleUsers) { - UiSharedService.DrawWithID(groupDto.GID + entry.UserData.UID, () => DrawSyncshellPairedClient( - entry, - entry.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value, - groupDto.OwnerUID, - string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal), - groupDto.GroupUserInfo.IsModerator())); + UiSharedService.DrawWithID(groupDto.GID + entry.UID, () => entry.DrawPairedClient()); } } @@ -441,12 +438,7 @@ internal sealed class GroupPanel ImGui.Separator(); foreach (var entry in onlineUsers) { - UiSharedService.DrawWithID(groupDto.GID + entry.UserData.UID, () => DrawSyncshellPairedClient( - entry, - entry.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value, - groupDto.OwnerUID, - string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal), - groupDto.GroupUserInfo.IsModerator())); + UiSharedService.DrawWithID(groupDto.GID + entry.UID, () => entry.DrawPairedClient()); } } @@ -456,12 +448,7 @@ internal sealed class GroupPanel ImGui.Separator(); foreach (var entry in offlineUsers) { - UiSharedService.DrawWithID(groupDto.GID + entry.UserData.UID, () => DrawSyncshellPairedClient( - entry, - entry.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value, - groupDto.OwnerUID, - string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal), - groupDto.GroupUserInfo.IsModerator())); + UiSharedService.DrawWithID(groupDto.GID + entry.UID, () => entry.DrawPairedClient()); } } @@ -724,337 +711,4 @@ internal sealed class GroupPanel } ImGui.EndChild(); } - - private void DrawSyncshellPairedClient(Pair pair, GroupPairFullInfoDto entry, string ownerUid, bool userIsOwner, bool userIsModerator) - { - var plusButtonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Plus); - var barButtonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars); - var entryUID = entry.UserAliasOrUID; - var textSize = ImGui.CalcTextSize(entryUID); - var originalY = ImGui.GetCursorPosY(); - var entryIsMod = entry.GroupPairStatusInfo.IsModerator(); - var entryIsOwner = string.Equals(pair.UserData.UID, ownerUid, StringComparison.Ordinal); - var entryIsPinned = entry.GroupPairStatusInfo.IsPinned(); - var presenceIcon = pair.IsVisible ? FontAwesomeIcon.Eye : (pair.IsOnline ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink); - var presenceColor = (pair.IsOnline || pair.IsVisible) ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; - var presenceText = entryUID + " is offline"; - - var soundsDisabled = entry.GroupUserPermissions.IsDisableSounds(); - var animDisabled = entry.GroupUserPermissions.IsDisableAnimations(); - var individualSoundsDisabled = (pair.UserPair?.OwnPermissions.IsDisableSounds() ?? false) || (pair.UserPair?.OtherPermissions.IsDisableSounds() ?? false); - var individualAnimDisabled = (pair.UserPair?.OwnPermissions.IsDisableAnimations() ?? false) || (pair.UserPair?.OtherPermissions.IsDisableAnimations() ?? false); - - var textPos = originalY + barButtonSize.Y / 2 - textSize.Y / 2; - ImGui.SetCursorPosY(textPos); - if (pair.IsPaused) - { - presenceIcon = FontAwesomeIcon.Question; - presenceColor = ImGuiColors.DalamudGrey; - presenceText = entryUID + " online status is unknown (paused)"; - - ImGui.PushFont(UiBuilder.IconFont); - UiSharedService.ColorText(FontAwesomeIcon.PauseCircle.ToIconString(), ImGuiColors.DalamudYellow); - ImGui.PopFont(); - - UiSharedService.AttachToolTip("Pairing status with " + entryUID + " is paused"); - } - else - { - ImGui.PushFont(UiBuilder.IconFont); - UiSharedService.ColorText(FontAwesomeIcon.Check.ToIconString(), ImGuiColors.ParsedGreen); - ImGui.PopFont(); - - UiSharedService.AttachToolTip("You are paired with " + entryUID); - } - - if (pair.IsOnline && !pair.IsVisible) presenceText = entryUID + " is online"; - else if (pair.IsOnline && pair.IsVisible) presenceText = entryUID + " is visible: " + pair.PlayerName; - - ImGui.SameLine(); - ImGui.SetCursorPosY(textPos); - ImGui.PushFont(UiBuilder.IconFont); - UiSharedService.ColorText(presenceIcon.ToIconString(), presenceColor); - ImGui.PopFont(); - UiSharedService.AttachToolTip(presenceText); - - if (entryIsOwner) - { - ImGui.SameLine(); - ImGui.SetCursorPosY(textPos); - ImGui.PushFont(UiBuilder.IconFont); - ImGui.TextUnformatted(FontAwesomeIcon.Crown.ToIconString()); - ImGui.PopFont(); - UiSharedService.AttachToolTip("User is owner of this Syncshell"); - } - else if (entryIsMod) - { - ImGui.SameLine(); - ImGui.SetCursorPosY(textPos); - ImGui.PushFont(UiBuilder.IconFont); - ImGui.TextUnformatted(FontAwesomeIcon.UserShield.ToIconString()); - ImGui.PopFont(); - UiSharedService.AttachToolTip("User is moderator of this Syncshell"); - } - else if (entryIsPinned) - { - ImGui.SameLine(); - ImGui.SetCursorPosY(textPos); - ImGui.PushFont(UiBuilder.IconFont); - ImGui.TextUnformatted(FontAwesomeIcon.Thumbtack.ToIconString()); - ImGui.PopFont(); - UiSharedService.AttachToolTip("User is pinned in this Syncshell"); - } - - var textIsUid = true; - _mainUi.ShowUidForEntry.TryGetValue(entry.UID, out var showUidInsteadOfName); - var playerText = _serverConfigurationManager.GetNoteForUid(entry.UID); - if (showUidInsteadOfName || string.IsNullOrEmpty(playerText)) - { - playerText = entryUID; - } - else - { - textIsUid = false; - } - - if (_configService.Current.ShowCharacterNameInsteadOfNotesForVisible && pair.IsVisible && !showUidInsteadOfName) - { - playerText = pair.PlayerName; - textIsUid = false; - } - - bool plusButtonShown = !_pairManager.DirectPairs.Any(p => string.Equals(p.UserData.UID, entry.UID, StringComparison.Ordinal)); - - ImGui.SameLine(); - if (!string.Equals(_mainUi.EditNickEntry, entry.UID, StringComparison.Ordinal)) - { - ImGui.SetCursorPosY(textPos); - if (textIsUid) ImGui.PushFont(UiBuilder.MonoFont); - ImGui.TextUnformatted(playerText); - if (textIsUid) ImGui.PopFont(); - UiSharedService.AttachToolTip("Left click to switch between UID display and nick" + Environment.NewLine + - "Right click to change nick for " + entryUID); - if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) - { - var prevState = textIsUid; - if (_mainUi.ShowUidForEntry.ContainsKey(entry.UID)) - { - prevState = _mainUi.ShowUidForEntry[entry.UID]; - } - - _mainUi.ShowUidForEntry[entry.UID] = !prevState; - } - - if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) - { - _serverConfigurationManager.SetNoteForUid(_mainUi.EditNickEntry, _mainUi.EditUserComment); - _mainUi.EditUserComment = _serverConfigurationManager.GetNoteForUid(entry.UID) ?? string.Empty; - _mainUi.EditNickEntry = entry.UID; - } - } - else - { - ImGui.SetCursorPosY(originalY); - var buttonSizes = (plusButtonShown ? plusButtonSize.X : 0) + barButtonSize.X; - var buttons = plusButtonShown ? 2 : 1; - - ImGui.SetNextItemWidth(UiSharedService.GetWindowContentRegionWidth() - ImGui.GetCursorPosX() - buttonSizes - ImGui.GetStyle().ItemSpacing.X * buttons); - if (ImGui.InputTextWithHint("", "Nick/Notes", ref _mainUi.EditUserComment, 255, ImGuiInputTextFlags.EnterReturnsTrue)) - { - _serverConfigurationManager.SetNoteForUid(entry.UID, _mainUi.EditUserComment); - _mainUi.EditNickEntry = string.Empty; - } - - if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) - { - _mainUi.EditNickEntry = string.Empty; - } - UiSharedService.AttachToolTip("Hit ENTER to save\nRight click to cancel"); - } - - if (plusButtonShown) - { - var barWidth = userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner) - ? barButtonSize.X + ImGui.GetStyle().ItemSpacing.X - : 0; - ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - plusButtonSize.X - barWidth); - ImGui.SetCursorPosY(originalY); - - if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus)) - { - _ = ApiController.UserAddPair(new UserDto(entry.User)); - } - UiSharedService.AttachToolTip("Pair with " + entryUID + " individually"); - } - - if (userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner)) - { - ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - barButtonSize.X); - ImGui.SetCursorPosY(originalY); - - if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars)) - { - ImGui.OpenPopup("Popup"); - } - } - - if (individualAnimDisabled || individualSoundsDisabled) - { - var infoIconPosDist = (plusButtonShown ? plusButtonSize.X + ImGui.GetStyle().ItemSpacing.X : 0) - + ((userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner)) ? barButtonSize.X + ImGui.GetStyle().ItemSpacing.X : 0); - var icon = FontAwesomeIcon.ExclamationTriangle; - var iconwidth = UiSharedService.GetIconSize(icon); - - ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - infoIconPosDist - iconwidth.X); - - ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); - UiSharedService.FontText(icon.ToIconString(), UiBuilder.IconFont); - ImGui.PopStyleColor(); - if (ImGui.IsItemHovered()) - { - ImGui.BeginTooltip(); - - ImGui.Text("Individual User permissions"); - - if (individualSoundsDisabled) - { - var userSoundsText = "Sound sync disabled with " + pair.UserData.AliasOrUID; - UiSharedService.FontText(FontAwesomeIcon.VolumeOff.ToIconString(), UiBuilder.IconFont); - ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); - ImGui.Text(userSoundsText); - ImGui.NewLine(); - ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); - ImGui.Text("You: " + (pair.UserPair!.OwnPermissions.IsDisableSounds() ? "Disabled" : "Enabled") + ", They: " + (pair.UserPair!.OtherPermissions.IsDisableSounds() ? "Disabled" : "Enabled")); - } - - if (individualAnimDisabled) - { - var userAnimText = "Animation sync disabled with " + pair.UserData.AliasOrUID; - UiSharedService.FontText(FontAwesomeIcon.Stop.ToIconString(), UiBuilder.IconFont); - ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); - ImGui.Text(userAnimText); - ImGui.NewLine(); - ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); - ImGui.Text("You: " + (pair.UserPair!.OwnPermissions.IsDisableAnimations() ? "Disabled" : "Enabled") + ", They: " + (pair.UserPair!.OtherPermissions.IsDisableAnimations() ? "Disabled" : "Enabled")); - } - - ImGui.EndTooltip(); - } - } - else if ((animDisabled || soundsDisabled)) - { - var infoIconPosDist = (plusButtonShown ? plusButtonSize.X + ImGui.GetStyle().ItemSpacing.X : 0) - + ((userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner)) ? barButtonSize.X + ImGui.GetStyle().ItemSpacing.X : 0); - var icon = FontAwesomeIcon.InfoCircle; - var iconwidth = UiSharedService.GetIconSize(icon); - - ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - infoIconPosDist - iconwidth.X); - ImGui.SetCursorPosY(originalY); - - UiSharedService.FontText(icon.ToIconString(), UiBuilder.IconFont); - if (ImGui.IsItemHovered()) - { - ImGui.BeginTooltip(); - - ImGui.Text("Sycnshell User permissions"); - - if (soundsDisabled) - { - var userSoundsText = "Sound sync disabled by " + pair.UserData.AliasOrUID; - UiSharedService.FontText(FontAwesomeIcon.VolumeOff.ToIconString(), UiBuilder.IconFont); - ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); - ImGui.Text(userSoundsText); - } - - if (animDisabled) - { - var userAnimText = "Animation sync disabled by " + pair.UserData.AliasOrUID; - UiSharedService.FontText(FontAwesomeIcon.Stop.ToIconString(), UiBuilder.IconFont); - ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); - ImGui.Text(userAnimText); - } - - ImGui.EndTooltip(); - } - } - - if (!plusButtonShown && !(userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner))) - { - ImGui.SameLine(); - ImGui.Dummy(barButtonSize with { X = 0 }); - } - - if (ImGui.BeginPopup("Popup")) - { - if ((userIsModerator || userIsOwner) && !(entryIsMod || entryIsOwner)) - { - var pinText = entryIsPinned ? "Unpin user" : "Pin user"; - if (UiSharedService.IconTextButton(FontAwesomeIcon.Thumbtack, pinText)) - { - ImGui.CloseCurrentPopup(); - var userInfo = entry.GroupPairStatusInfo ^ GroupUserInfo.IsPinned; - _ = ApiController.GroupSetUserInfo(new GroupPairUserInfoDto(entry.Group, entry.User, userInfo)); - } - UiSharedService.AttachToolTip("Pin this user to the Syncshell. Pinned users will not be deleted in case of a manually initiated Syncshell clean"); - - if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Remove user") && UiSharedService.CtrlPressed()) - { - ImGui.CloseCurrentPopup(); - _ = ApiController.GroupRemoveUser(entry); - } - - UiSharedService.AttachToolTip("Hold CTRL and click to remove user " + (entry.UserAliasOrUID) + " from Syncshell"); - if (UiSharedService.IconTextButton(FontAwesomeIcon.UserSlash, "Ban User")) - { - _showModalBanUser = true; - ImGui.CloseCurrentPopup(); - } - UiSharedService.AttachToolTip("Ban user from this Syncshell"); - } - - if (userIsOwner) - { - string modText = entryIsMod ? "Demod user" : "Mod user"; - if (UiSharedService.IconTextButton(FontAwesomeIcon.UserShield, modText) && UiSharedService.CtrlPressed()) - { - ImGui.CloseCurrentPopup(); - var userInfo = entry.GroupPairStatusInfo ^ GroupUserInfo.IsModerator; - _ = ApiController.GroupSetUserInfo(new GroupPairUserInfoDto(entry.Group, entry.User, userInfo)); - } - UiSharedService.AttachToolTip("Hold CTRL to change the moderator status for " + (entry.UserAliasOrUID) + Environment.NewLine + - "Moderators can kick, ban/unban, pin/unpin users and clear the Syncshell."); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Crown, "Transfer Ownership") && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed()) - { - ImGui.CloseCurrentPopup(); - _ = ApiController.GroupChangeOwnership(entry); - } - UiSharedService.AttachToolTip("Hold CTRL and SHIFT and click to transfer ownership of this Syncshell to " + (entry.UserAliasOrUID) + Environment.NewLine + "WARNING: This action is irreversible."); - } - ImGui.EndPopup(); - } - - if (_showModalBanUser && !_banUserPopupOpen) - { - ImGui.OpenPopup("Ban User"); - _banUserPopupOpen = true; - } - - if (!_showModalBanUser) _banUserPopupOpen = false; - - if (ImGui.BeginPopupModal("Ban User", ref _showModalBanUser, UiSharedService.PopupWindowFlags)) - { - UiSharedService.TextWrapped("User " + (entry.UserAliasOrUID) + " will be banned and removed from this Syncshell."); - ImGui.InputTextWithHint("##banreason", "Ban Reason", ref _banReason, 255); - if (ImGui.Button("Ban User")) - { - ImGui.CloseCurrentPopup(); - var reason = _banReason; - _ = ApiController.GroupBanUser(entry, reason); - _banReason = string.Empty; - } - UiSharedService.TextWrapped("The reason will be displayed in the banlist. The current server-side alias if present (Vanity ID) will automatically be attached to the reason."); - UiSharedService.SetScaledWindowSize(300); - ImGui.EndPopup(); - } - } } \ No newline at end of file diff --git a/MareSynchronos/UI/Components/PairGroupsUi.cs b/MareSynchronos/UI/Components/PairGroupsUi.cs index 2a617d7..fe42fc5 100644 --- a/MareSynchronos/UI/Components/PairGroupsUi.cs +++ b/MareSynchronos/UI/Components/PairGroupsUi.cs @@ -3,7 +3,6 @@ using Dalamud.Interface.Components; using ImGuiNET; using MareSynchronos.API.Data.Extensions; using MareSynchronos.MareConfiguration; -using MareSynchronos.PlayerData.Pairs; using MareSynchronos.UI.Handlers; using MareSynchronos.WebAPI; @@ -12,58 +11,31 @@ namespace MareSynchronos.UI.Components; public class PairGroupsUi { private readonly ApiController _apiController; - private readonly Action _clientRenderFn; private readonly MareConfigService _mareConfig; private readonly SelectPairForGroupUi _selectGroupForPairUi; private readonly TagHandler _tagHandler; - public PairGroupsUi(MareConfigService mareConfig, TagHandler tagHandler, Action clientRenderFn, ApiController apiController, SelectPairForGroupUi selectGroupForPairUi) + public PairGroupsUi(MareConfigService mareConfig, TagHandler tagHandler, ApiController apiController, SelectPairForGroupUi selectGroupForPairUi) { - _clientRenderFn = clientRenderFn; _mareConfig = mareConfig; _tagHandler = tagHandler; _apiController = apiController; _selectGroupForPairUi = selectGroupForPairUi; } - public void Draw(List visibleUsers, List onlineUsers, List offlineUsers) + public void Draw(List visibleUsers, List onlineUsers, List offlineUsers) where T : DrawPairBase { // Only render those tags that actually have pairs in them, otherwise // we can end up with a bunch of useless pair groups var tagsWithPairsInThem = _tagHandler.GetAllTagsSorted(); var allUsers = visibleUsers.Concat(onlineUsers).Concat(offlineUsers).ToList(); - if (_mareConfig.Current.ShowVisibleUsersSeparately) + if (typeof(T) == typeof(DrawUserPair)) { - UiSharedService.DrawWithID("$group-VisibleCustomTag", () => DrawCategory(TagHandler.CustomVisibleTag, visibleUsers, allUsers)); + DrawUserPairs(tagsWithPairsInThem, allUsers.Cast().ToList(), visibleUsers.Cast(), onlineUsers.Cast(), offlineUsers.Cast()); } - foreach (var tag in tagsWithPairsInThem) - { - if (_mareConfig.Current.ShowOfflineUsersSeparately) - { - UiSharedService.DrawWithID($"group-{tag}", () => DrawCategory(tag, onlineUsers, allUsers, visibleUsers)); - } - else - { - UiSharedService.DrawWithID($"group-{tag}", () => DrawCategory(tag, onlineUsers.Concat(offlineUsers).ToList(), allUsers, visibleUsers)); - } - } - if (_mareConfig.Current.ShowOfflineUsersSeparately) - { - UiSharedService.DrawWithID($"group-OnlineCustomTag", () => DrawCategory(TagHandler.CustomOnlineTag, - onlineUsers.Where(u => !_tagHandler.HasAnyTag(u.UserPair!)).ToList(), allUsers)); - UiSharedService.DrawWithID($"group-OfflineCustomTag", () => DrawCategory(TagHandler.CustomOfflineTag, - offlineUsers.Where(u => u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers)); - } - else - { - UiSharedService.DrawWithID($"group-OnlineCustomTag", () => DrawCategory(TagHandler.CustomOnlineTag, - onlineUsers.Concat(offlineUsers).Where(u => u.UserPair!.OtherPermissions.IsPaired() && !_tagHandler.HasAnyTag(u.UserPair!)).ToList(), allUsers)); - } - UiSharedService.DrawWithID($"group-UnpairedCustomTag", () => DrawCategory(TagHandler.CustomUnpairedTag, - offlineUsers.Where(u => !u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers)); } - private void DrawButtons(string tag, List availablePairsInThisTag) + private void DrawButtons(string tag, List availablePairsInThisTag) { var allArePaused = availablePairsInThisTag.All(pair => pair.UserPair!.OwnPermissions.IsPaused()); var pauseButton = allArePaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; @@ -114,9 +86,9 @@ public class PairGroupsUi } } - private void DrawCategory(string tag, List onlineUsers, List allUsers, List? visibleUsers = null) + private void DrawCategory(string tag, IEnumerable onlineUsers, IEnumerable allUsers, IEnumerable? visibleUsers = null) { - List usersInThisTag; + IEnumerable usersInThisTag; HashSet? otherUidsTaggedWithTag = null; bool isSpecialTag = false; int visibleInThisTag = 0; @@ -129,16 +101,21 @@ public class PairGroupsUi { otherUidsTaggedWithTag = _tagHandler.GetOtherUidsForTag(tag); usersInThisTag = onlineUsers - .Where(pair => otherUidsTaggedWithTag.Contains(pair.UserData.UID)) + .Where(pair => otherUidsTaggedWithTag.Contains(pair.UID)) .ToList(); - visibleInThisTag = visibleUsers?.Count(p => otherUidsTaggedWithTag.Contains(p.UserData.UID)) ?? 0; + visibleInThisTag = visibleUsers?.Count(p => otherUidsTaggedWithTag.Contains(p.UID)) ?? 0; } if (isSpecialTag && !usersInThisTag.Any()) return; - DrawName(tag, isSpecialTag, visibleInThisTag, usersInThisTag.Count, otherUidsTaggedWithTag?.Count); + DrawName(tag, isSpecialTag, visibleInThisTag, usersInThisTag.Count(), otherUidsTaggedWithTag?.Count); if (!isSpecialTag) - UiSharedService.DrawWithID($"group-{tag}-buttons", () => DrawButtons(tag, allUsers.Where(p => otherUidsTaggedWithTag!.Contains(p.UserData.UID)).ToList())); + { + if (onlineUsers.First() is DrawUserPair) + { + UiSharedService.DrawWithID($"group-{tag}-buttons", () => DrawButtons(tag, allUsers.Cast().Where(p => otherUidsTaggedWithTag!.Contains(p.UID)).ToList())); + } + } if (!_tagHandler.IsTagOpen(tag)) return; @@ -201,31 +178,66 @@ public class PairGroupsUi } } - private void DrawPairs(string tag, List availablePairsInThisCategory) + private void DrawPairs(string tag, IEnumerable availablePairsInThisCategory) { // These are all the OtherUIDs that are tagged with this tag - availablePairsInThisCategory - .ForEach(pair => UiSharedService.DrawWithID($"tag-{tag}-pair-${pair.UserData.UID}", () => _clientRenderFn(pair))); + foreach (var pair in availablePairsInThisCategory) + { + UiSharedService.DrawWithID($"tag-{tag}-pair-${pair.UID}", () => pair.DrawPairedClient()); + } ImGui.Separator(); } - private void PauseRemainingPairs(List availablePairs) + private void DrawUserPairs(List tagsWithPairsInThem, List allUsers, IEnumerable visibleUsers, IEnumerable onlineUsers, IEnumerable offlineUsers) + { + if (_mareConfig.Current.ShowVisibleUsersSeparately) + { + UiSharedService.DrawWithID("$group-VisibleCustomTag", () => DrawCategory(TagHandler.CustomVisibleTag, visibleUsers, allUsers)); + } + foreach (var tag in tagsWithPairsInThem) + { + if (_mareConfig.Current.ShowOfflineUsersSeparately) + { + UiSharedService.DrawWithID($"group-{tag}", () => DrawCategory(tag, onlineUsers, allUsers, visibleUsers)); + } + else + { + UiSharedService.DrawWithID($"group-{tag}", () => DrawCategory(tag, onlineUsers.Concat(offlineUsers).ToList(), allUsers, visibleUsers)); + } + } + if (_mareConfig.Current.ShowOfflineUsersSeparately) + { + UiSharedService.DrawWithID($"group-OnlineCustomTag", () => DrawCategory(TagHandler.CustomOnlineTag, + onlineUsers.Where(u => !_tagHandler.HasAnyTag(u.UID)).ToList(), allUsers)); + UiSharedService.DrawWithID($"group-OfflineCustomTag", () => DrawCategory(TagHandler.CustomOfflineTag, + offlineUsers.Where(u => u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers)); + } + else + { + UiSharedService.DrawWithID($"group-OnlineCustomTag", () => DrawCategory(TagHandler.CustomOnlineTag, + onlineUsers.Concat(offlineUsers).Where(u => u.UserPair!.OtherPermissions.IsPaired() && !_tagHandler.HasAnyTag(u.UID)).ToList(), allUsers)); + } + UiSharedService.DrawWithID($"group-UnpairedCustomTag", () => DrawCategory(TagHandler.CustomUnpairedTag, + offlineUsers.Where(u => !u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers)); + } + + private void PauseRemainingPairs(List availablePairs) { foreach (var pairToPause in availablePairs.Where(pair => !pair.UserPair!.OwnPermissions.IsPaused())) { var perm = pairToPause.UserPair!.OwnPermissions; perm.SetPaused(paused: true); - _ = _apiController.UserSetPairPermissions(new(pairToPause.UserData, perm)); + _ = _apiController.UserSetPairPermissions(new(new(pairToPause.UID), perm)); } } - private void ResumeAllPairs(List availablePairs) + private void ResumeAllPairs(List availablePairs) { foreach (var pairToPause in availablePairs) { var perm = pairToPause.UserPair!.OwnPermissions; perm.SetPaused(paused: false); - _ = _apiController.UserSetPairPermissions(new(pairToPause.UserData, perm)); + _ = _apiController.UserSetPairPermissions(new(new(pairToPause.UID), perm)); } } diff --git a/MareSynchronos/UI/Components/SelectGroupForPairUi.cs b/MareSynchronos/UI/Components/SelectGroupForPairUi.cs index 8da81b4..7462fed 100644 --- a/MareSynchronos/UI/Components/SelectGroupForPairUi.cs +++ b/MareSynchronos/UI/Components/SelectGroupForPairUi.cs @@ -11,6 +11,7 @@ namespace MareSynchronos.UI.Components; public class SelectGroupForPairUi { private readonly TagHandler _tagHandler; + private readonly UidDisplayHandler _uidDisplayHandler; /// /// The group UI is always open for a specific pair. This defines which pair the UI is open for. @@ -28,21 +29,22 @@ public class SelectGroupForPairUi /// private string _tagNameToAdd = ""; - public SelectGroupForPairUi(TagHandler tagHandler) + public SelectGroupForPairUi(TagHandler tagHandler, UidDisplayHandler uidDisplayHandler) { _show = false; _pair = null; _tagHandler = tagHandler; + _uidDisplayHandler = uidDisplayHandler; } - public void Draw(Dictionary showUidForEntry) + public void Draw() { if (_pair == null) { return; } - var name = PairName(showUidForEntry, _pair); + var name = PairName(_pair); var popupName = $"Choose Groups for {name}"; // Is the popup supposed to show but did not open yet? Open it if (_show) @@ -93,30 +95,19 @@ public class SelectGroupForPairUi _show = true; } - private static string PairName(Dictionary showUidForEntry, Pair pair) - { - showUidForEntry.TryGetValue(pair.UserData.UID, out var showUidInsteadOfName); - var playerText = pair.GetNote(); - if (showUidInsteadOfName || string.IsNullOrEmpty(playerText)) - { - playerText = pair.UserData.AliasOrUID; - } - return playerText; - } - private void DrawGroupName(Pair pair, string name) { - var hasTagBefore = _tagHandler.HasTag(pair.UserPair!, name); + var hasTagBefore = _tagHandler.HasTag(pair.UserData.UID, name); var hasTag = hasTagBefore; if (ImGui.Checkbox(name, ref hasTag)) { if (hasTag) { - _tagHandler.AddTagToPairedUid(pair.UserPair!, name); + _tagHandler.AddTagToPairedUid(pair.UserData.UID, name); } else { - _tagHandler.RemoveTagFromPairedUid(pair.UserPair!, name); + _tagHandler.RemoveTagFromPairedUid(pair.UserData.UID, name); } } } @@ -128,7 +119,7 @@ public class SelectGroupForPairUi _tagHandler.AddTag(_tagNameToAdd); if (_pair != null) { - _tagHandler.AddTagToPairedUid(_pair.UserPair!, _tagNameToAdd); + _tagHandler.AddTagToPairedUid(_pair.UserData.UID, _tagNameToAdd); } _tagNameToAdd = string.Empty; } @@ -137,4 +128,9 @@ public class SelectGroupForPairUi _tagNameToAdd = string.Empty; } } + + private string PairName(Pair pair) + { + return _uidDisplayHandler.GetPlayerText(pair).text; + } } \ No newline at end of file diff --git a/MareSynchronos/UI/Components/SelectPairForGroupUi.cs b/MareSynchronos/UI/Components/SelectPairForGroupUi.cs index 4ecea8e..c86da26 100644 --- a/MareSynchronos/UI/Components/SelectPairForGroupUi.cs +++ b/MareSynchronos/UI/Components/SelectPairForGroupUi.cs @@ -9,18 +9,20 @@ namespace MareSynchronos.UI.Components; public class SelectPairForGroupUi { private readonly TagHandler _tagHandler; + private readonly UidDisplayHandler _uidDisplayHandler; private string _filter = string.Empty; private bool _opened = false; private HashSet _peopleInGroup = new(StringComparer.Ordinal); private bool _show = false; private string _tag = string.Empty; - public SelectPairForGroupUi(TagHandler tagHandler) + public SelectPairForGroupUi(TagHandler tagHandler, UidDisplayHandler uidDisplayHandler) { _tagHandler = tagHandler; + _uidDisplayHandler = uidDisplayHandler; } - public void Draw(List pairs, Dictionary showUidForEntry) + public void Draw(List pairs) { var workHeight = ImGui.GetMainViewport().WorkSize.Y / ImGuiHelpers.GlobalScale; var minSize = new Vector2(300, workHeight < 400 ? workHeight : 400) * ImGuiHelpers.GlobalScale; @@ -47,21 +49,21 @@ public class SelectPairForGroupUi UiSharedService.FontText($"Select users for group {_tag}", UiBuilder.DefaultFont); ImGui.InputTextWithHint("##filter", "Filter", ref _filter, 255, ImGuiInputTextFlags.None); foreach (var item in pairs - .Where(p => string.IsNullOrEmpty(_filter) || PairName(showUidForEntry, p).Contains(_filter, StringComparison.OrdinalIgnoreCase)) - .OrderBy(p => PairName(showUidForEntry, p), StringComparer.OrdinalIgnoreCase) + .Where(p => string.IsNullOrEmpty(_filter) || PairName(p).Contains(_filter, StringComparison.OrdinalIgnoreCase)) + .OrderBy(p => PairName(p), StringComparer.OrdinalIgnoreCase) .ToList()) { var isInGroup = _peopleInGroup.Contains(item.UserData.UID); - if (ImGui.Checkbox(PairName(showUidForEntry, item), ref isInGroup)) + if (ImGui.Checkbox(PairName(item), ref isInGroup)) { if (isInGroup) { - _tagHandler.AddTagToPairedUid(item.UserPair!, _tag); + _tagHandler.AddTagToPairedUid(item.UserData.UID, _tag); _peopleInGroup.Add(item.UserData.UID); } else { - _tagHandler.RemoveTagFromPairedUid(item.UserPair!, _tag); + _tagHandler.RemoveTagFromPairedUid(item.UserData.UID, _tag); _peopleInGroup.Remove(item.UserData.UID); } } @@ -82,14 +84,8 @@ public class SelectPairForGroupUi _show = true; } - private static string PairName(Dictionary showUidForEntry, Pair pair) + private string PairName(Pair pair) { - showUidForEntry.TryGetValue(pair.UserData.UID, out var showUidInsteadOfName); - var playerText = pair.GetNote(); - if (showUidInsteadOfName || string.IsNullOrEmpty(playerText)) - { - playerText = pair.UserData.AliasOrUID; - } - return playerText; + return _uidDisplayHandler.GetPlayerText(pair).text; } } \ No newline at end of file diff --git a/MareSynchronos/UI/GposeUi.cs b/MareSynchronos/UI/GposeUi.cs index 7b44bc1..fa0d280 100644 --- a/MareSynchronos/UI/GposeUi.cs +++ b/MareSynchronos/UI/GposeUi.cs @@ -46,7 +46,7 @@ public class GposeUi : WindowMediatorSubscriberBase _fileDialogManager.OpenFileDialog("Pick MCDF file", ".mcdf", (success, paths) => { if (!success) return; - if (paths.FirstOrDefault() is not { } path) return; + if (paths.FirstOrDefault() is not string path) return; _configService.Current.ExportFolder = Path.GetDirectoryName(path) ?? string.Empty; _configService.Save(); diff --git a/MareSynchronos/UI/Handlers/TagHandler.cs b/MareSynchronos/UI/Handlers/TagHandler.cs index f3fe01a..a9038b9 100644 --- a/MareSynchronos/UI/Handlers/TagHandler.cs +++ b/MareSynchronos/UI/Handlers/TagHandler.cs @@ -21,9 +21,9 @@ public class TagHandler _serverConfigurationManager.AddTag(tag); } - public void AddTagToPairedUid(UserPairDto pair, string tagName) + public void AddTagToPairedUid(string uid, string tagName) { - _serverConfigurationManager.AddTagForUid(pair.User.UID, tagName); + _serverConfigurationManager.AddTagForUid(uid, tagName); } public List GetAllTagsSorted() @@ -38,14 +38,14 @@ public class TagHandler return _serverConfigurationManager.GetUidsForTag(tag); } - public bool HasAnyTag(UserPairDto pair) + public bool HasAnyTag(string uid) { - return _serverConfigurationManager.HasTags(pair.User.UID); + return _serverConfigurationManager.HasTags(uid); } - public bool HasTag(UserPairDto pair, string tagName) + public bool HasTag(string uid, string tagName) { - return _serverConfigurationManager.ContainsTag(pair.User.UID, tagName); + return _serverConfigurationManager.ContainsTag(uid, tagName); } /// @@ -64,9 +64,9 @@ public class TagHandler _serverConfigurationManager.RemoveTag(tag); } - public void RemoveTagFromPairedUid(UserPairDto pair, string tagName) + public void RemoveTagFromPairedUid(string uid, string tagName) { - _serverConfigurationManager.RemoveTagForUid(pair.User.UID, tagName); + _serverConfigurationManager.RemoveTagForUid(uid, tagName); } public void SetTagOpen(string tag, bool open) diff --git a/MareSynchronos/UI/Handlers/UidDisplayHandler.cs b/MareSynchronos/UI/Handlers/UidDisplayHandler.cs new file mode 100644 index 0000000..89d8083 --- /dev/null +++ b/MareSynchronos/UI/Handlers/UidDisplayHandler.cs @@ -0,0 +1,117 @@ +using Dalamud.Interface; +using ImGuiNET; +using MareSynchronos.PlayerData.Pairs; +using MareSynchronos.Services.ServerConfiguration; +using MareSynchronos.MareConfiguration; + +namespace MareSynchronos.UI.Handlers; + +public class UidDisplayHandler +{ + private readonly MareConfigService _mareConfigService; + private readonly PairManager _pairManager; + private readonly ServerConfigurationManager _serverManager; + private readonly Dictionary _showUidForEntry = new(StringComparer.Ordinal); + private string _editNickEntry = string.Empty; + private string _editUserComment = string.Empty; + + public UidDisplayHandler(PairManager pairManager, ServerConfigurationManager serverManager, MareConfigService mareConfigService) + { + _pairManager = pairManager; + _serverManager = serverManager; + _mareConfigService = mareConfigService; + } + + public void DrawPairText(Pair entry, float textPosX, float originalY, Func editBoxWidth) + { + ImGui.SameLine(textPosX); + (bool textIsUid, string playerText) = GetPlayerText(entry); + if (!string.Equals(_editNickEntry, entry.UserData.UID, StringComparison.Ordinal)) + { + ImGui.SetCursorPosY(originalY); + if (textIsUid) ImGui.PushFont(UiBuilder.MonoFont); + ImGui.TextUnformatted(playerText); + if (textIsUid) ImGui.PopFont(); + UiSharedService.AttachToolTip("Left click to switch between UID display and nick" + Environment.NewLine + + "Right click to change nick for " + entry.UserData.UID); + if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) + { + var prevState = textIsUid; + if (_showUidForEntry.ContainsKey(entry.UserData.UID)) + { + prevState = _showUidForEntry[entry.UserData.UID]; + } + _showUidForEntry[entry.UserData.UID] = !prevState; + } + + if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) + { + var pair = _pairManager.DirectPairs.Find(p => string.Equals(p.UserData.UID, _editNickEntry, StringComparison.Ordinal)); + pair?.SetNote(_editUserComment); + _editUserComment = entry.GetNote() ?? string.Empty; + _editNickEntry = entry.UserData.UID; + } + } + else + { + ImGui.SetCursorPosY(originalY); + + ImGui.SetNextItemWidth(editBoxWidth.Invoke()); + if (ImGui.InputTextWithHint("", "Nick/Notes", ref _editUserComment, 255, ImGuiInputTextFlags.EnterReturnsTrue)) + { + _serverManager.SetNoteForUid(entry.UserData.UID, _editUserComment); + _serverManager.SaveNotes(); + _editNickEntry = string.Empty; + } + + if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) + { + _editNickEntry = string.Empty; + } + UiSharedService.AttachToolTip("Hit ENTER to save\nRight click to cancel"); + } + } + + public (bool isUid, string text) GetPlayerText(Pair pair) + { + var textIsUid = true; + bool showUidInsteadOfName = ShowUidInsteadOfName(pair); + string? playerText = _serverManager.GetNoteForUid(pair.UserData.UID); + if (!showUidInsteadOfName && playerText != null) + { + if (string.IsNullOrEmpty(playerText)) + { + playerText = pair.UserData.UID; + } + else + { + textIsUid = false; + } + } + else + { + playerText = pair.UserData.UID; + } + + if (_mareConfigService.Current.ShowCharacterNameInsteadOfNotesForVisible && pair.IsVisible && !showUidInsteadOfName) + { + playerText = pair.PlayerName; + textIsUid = false; + } + + return (textIsUid, playerText!); + } + + internal void Clear() + { + _editNickEntry = string.Empty; + _editUserComment = string.Empty; + } + + private bool ShowUidInsteadOfName(Pair pair) + { + _showUidForEntry.TryGetValue(pair.UserData.UID, out var showUidInsteadOfName); + + return showUidInsteadOfName; + } +} \ No newline at end of file