diff --git a/LightlessSync/LightlessConfiguration/Configurations/PairTagStorage.cs b/LightlessSync/LightlessConfiguration/Configurations/PairTagStorage.cs new file mode 100644 index 0000000..37d1966 --- /dev/null +++ b/LightlessSync/LightlessConfiguration/Configurations/PairTagStorage.cs @@ -0,0 +1,7 @@ +namespace LightlessSync.LightlessConfiguration.Configurations; + +public class PairTagStorage : ILightlessConfiguration +{ + public Dictionary ServerTagStorage { get; set; } = new(StringComparer.OrdinalIgnoreCase); + public int Version { get; set; } = 0; +} \ No newline at end of file diff --git a/LightlessSync/LightlessConfiguration/Configurations/ServerTagConfig.cs b/LightlessSync/LightlessConfiguration/Configurations/ServerTagConfig.cs deleted file mode 100644 index bdfe68f..0000000 --- a/LightlessSync/LightlessConfiguration/Configurations/ServerTagConfig.cs +++ /dev/null @@ -1,9 +0,0 @@ -using LightlessSync.LightlessConfiguration.Models; - -namespace LightlessSync.LightlessConfiguration.Configurations; - -public class ServerTagConfig : ILightlessConfiguration -{ - public Dictionary ServerTagStorage { get; set; } = new(StringComparer.OrdinalIgnoreCase); - public int Version { get; set; } = 0; -} \ No newline at end of file diff --git a/LightlessSync/LightlessConfiguration/Configurations/SyncshellTagStorage.cs b/LightlessSync/LightlessConfiguration/Configurations/SyncshellTagStorage.cs new file mode 100644 index 0000000..73cc9ec --- /dev/null +++ b/LightlessSync/LightlessConfiguration/Configurations/SyncshellTagStorage.cs @@ -0,0 +1,7 @@ +namespace LightlessSync.LightlessConfiguration.Configurations; + +public class SyncshellTagStorage : ILightlessConfiguration +{ + public Dictionary ServerTagStorage { get; set; } = new(StringComparer.OrdinalIgnoreCase); + public int Version { get; set; } = 0; +} \ No newline at end of file diff --git a/LightlessSync/LightlessConfiguration/Models/ServerTagStorage.cs b/LightlessSync/LightlessConfiguration/Models/PairTagStorage.cs similarity index 92% rename from LightlessSync/LightlessConfiguration/Models/ServerTagStorage.cs rename to LightlessSync/LightlessConfiguration/Models/PairTagStorage.cs index 75d3f46..b8edeb8 100644 --- a/LightlessSync/LightlessConfiguration/Models/ServerTagStorage.cs +++ b/LightlessSync/LightlessConfiguration/Models/PairTagStorage.cs @@ -1,7 +1,7 @@ namespace LightlessSync.LightlessConfiguration.Models; [Serializable] -public class ServerTagStorage +public class PairTagStorage { public HashSet OpenPairTags { get; set; } = new(StringComparer.Ordinal); public HashSet ServerAvailablePairTags { get; set; } = new(StringComparer.Ordinal); diff --git a/LightlessSync/LightlessConfiguration/Models/SyncshellTagStorage.cs b/LightlessSync/LightlessConfiguration/Models/SyncshellTagStorage.cs new file mode 100644 index 0000000..7a3d409 --- /dev/null +++ b/LightlessSync/LightlessConfiguration/Models/SyncshellTagStorage.cs @@ -0,0 +1,8 @@ +namespace LightlessSync.LightlessConfiguration.Models; + +[Serializable] +public class SyncshellTagStorage +{ + public HashSet ServerAvailableSyncshellTags { get; set; } = new(StringComparer.Ordinal); + public Dictionary> SyncshellPairedTags { get; set; } = new(StringComparer.Ordinal); +} diff --git a/LightlessSync/LightlessConfiguration/ServerTagConfigService.cs b/LightlessSync/LightlessConfiguration/PairTagConfigService.cs similarity index 62% rename from LightlessSync/LightlessConfiguration/ServerTagConfigService.cs rename to LightlessSync/LightlessConfiguration/PairTagConfigService.cs index b31e746..cc4c559 100644 --- a/LightlessSync/LightlessConfiguration/ServerTagConfigService.cs +++ b/LightlessSync/LightlessConfiguration/PairTagConfigService.cs @@ -2,11 +2,11 @@ namespace LightlessSync.LightlessConfiguration; -public class ServerTagConfigService : ConfigurationServiceBase +public class PairTagConfigService : ConfigurationServiceBase { public const string ConfigName = "servertags.json"; - public ServerTagConfigService(string configDir) : base(configDir) + public PairTagConfigService(string configDir) : base(configDir) { } diff --git a/LightlessSync/LightlessConfiguration/SyncshellTagConfigService.cs b/LightlessSync/LightlessConfiguration/SyncshellTagConfigService.cs new file mode 100644 index 0000000..b62bf7c --- /dev/null +++ b/LightlessSync/LightlessConfiguration/SyncshellTagConfigService.cs @@ -0,0 +1,14 @@ +using LightlessSync.LightlessConfiguration.Configurations; + +namespace LightlessSync.LightlessConfiguration; + +public class SyncshellTagConfigService : ConfigurationServiceBase +{ + public const string ConfigName = "syncshelltags.json"; + + public SyncshellTagConfigService(string configDir) : base(configDir) + { + } + + public override string ConfigurationName => ConfigName; +} \ No newline at end of file diff --git a/LightlessSync/Plugin.cs b/LightlessSync/Plugin.cs index f09832e..4f704f5 100644 --- a/LightlessSync/Plugin.cs +++ b/LightlessSync/Plugin.cs @@ -130,7 +130,9 @@ public sealed class Plugin : IDalamudPlugin s.GetRequiredService(), s.GetRequiredService())); collection.AddSingleton(); - collection.AddSingleton(); + collection.AddSingleton(); + collection.AddSingleton(); + collection.AddSingleton(); collection.AddSingleton((s) => new EventAggregator(pluginInterface.ConfigDirectory.FullName, s.GetRequiredService>(), s.GetRequiredService())); collection.AddSingleton((s) => new DalamudUtilService(s.GetRequiredService>(), @@ -175,7 +177,8 @@ public sealed class Plugin : IDalamudPlugin collection.AddSingleton((s) => new LightlessConfigService(pluginInterface.ConfigDirectory.FullName)); collection.AddSingleton((s) => new ServerConfigService(pluginInterface.ConfigDirectory.FullName)); collection.AddSingleton((s) => new NotesConfigService(pluginInterface.ConfigDirectory.FullName)); - collection.AddSingleton((s) => new ServerTagConfigService(pluginInterface.ConfigDirectory.FullName)); + collection.AddSingleton((s) => new PairTagConfigService(pluginInterface.ConfigDirectory.FullName)); + collection.AddSingleton((s) => new SyncshellTagConfigService(pluginInterface.ConfigDirectory.FullName)); collection.AddSingleton((s) => new TransientConfigService(pluginInterface.ConfigDirectory.FullName)); collection.AddSingleton((s) => new XivDataStorageService(pluginInterface.ConfigDirectory.FullName)); collection.AddSingleton((s) => new PlayerPerformanceConfigService(pluginInterface.ConfigDirectory.FullName)); @@ -183,7 +186,8 @@ public sealed class Plugin : IDalamudPlugin collection.AddSingleton>(s => s.GetRequiredService()); collection.AddSingleton>(s => s.GetRequiredService()); collection.AddSingleton>(s => s.GetRequiredService()); - collection.AddSingleton>(s => s.GetRequiredService()); + collection.AddSingleton>(s => s.GetRequiredService()); + collection.AddSingleton>(s => s.GetRequiredService()); collection.AddSingleton>(s => s.GetRequiredService()); collection.AddSingleton>(s => s.GetRequiredService()); collection.AddSingleton>(s => s.GetRequiredService()); @@ -198,6 +202,7 @@ public sealed class Plugin : IDalamudPlugin collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); + collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); diff --git a/LightlessSync/Services/ServerConfiguration/ServerConfigurationManager.cs b/LightlessSync/Services/ServerConfiguration/ServerConfigurationManager.cs index 8bff7f9..6b2ac42 100644 --- a/LightlessSync/Services/ServerConfiguration/ServerConfigurationManager.cs +++ b/LightlessSync/Services/ServerConfiguration/ServerConfigurationManager.cs @@ -3,6 +3,7 @@ using LightlessSync.API.Routes; using LightlessSync.LightlessConfiguration; using LightlessSync.LightlessConfiguration.Models; using LightlessSync.Services.Mediator; +using LightlessSync.UI.Components; using LightlessSync.WebAPI; using Microsoft.AspNetCore.Http.Connections; using Microsoft.Extensions.Logging; @@ -23,15 +24,17 @@ public class ServerConfigurationManager private readonly ILogger _logger; private readonly LightlessMediator _lightlessMediator; private readonly NotesConfigService _notesConfig; - private readonly ServerTagConfigService _serverTagConfig; + private readonly PairTagConfigService _pairTagConfig; + private readonly SyncshellTagConfigService _syncshellTagConfig; public ServerConfigurationManager(ILogger logger, ServerConfigService configService, - ServerTagConfigService serverTagConfig, NotesConfigService notesConfig, DalamudUtilService dalamudUtil, + PairTagConfigService pairTagConfig, SyncshellTagConfigService syncshellTagConfig, NotesConfigService notesConfig, DalamudUtilService dalamudUtil, LightlessConfigService lightlessConfigService, HttpClient httpClient, LightlessMediator lightlessMediator) { _logger = logger; _configService = configService; - _serverTagConfig = serverTagConfig; + _pairTagConfig = pairTagConfig; + _syncshellTagConfig = syncshellTagConfig; _notesConfig = notesConfig; _dalamudUtil = dalamudUtil; _lightlessConfigService = lightlessConfigService; @@ -258,7 +261,7 @@ public class ServerConfigurationManager { if (serverSelectionIndex == -1) serverSelectionIndex = CurrentServerIndex; var server = GetServerByIndex(serverSelectionIndex); - if (server.Authentications.Any(c => string.Equals(c.CharacterName, _dalamudUtil.GetPlayerNameAsync().GetAwaiter().GetResult(), StringComparison.Ordinal) + if (server.Authentications.Exists(c => string.Equals(c.CharacterName, _dalamudUtil.GetPlayerNameAsync().GetAwaiter().GetResult(), StringComparison.Ordinal) && c.WorldId == _dalamudUtil.GetHomeWorldIdAsync().GetAwaiter().GetResult())) return; @@ -277,15 +280,15 @@ public class ServerConfigurationManager var server = GetServerByIndex(serverSelectionIndex); server.Authentications.Add(new Authentication() { - SecretKeyIdx = server.SecretKeys.Any() ? server.SecretKeys.First().Key : -1, + SecretKeyIdx = server.SecretKeys.Count != 0 ? server.SecretKeys.First().Key : -1, }); Save(); } internal void AddOpenPairTag(string tag) { - CurrentServerTagStorage().OpenPairTags.Add(tag); - _serverTagConfig.Save(); + CurrentPairTagStorage().OpenPairTags.Add(tag); + _pairTagConfig.Save(); } internal void AddServer(ServerStorage serverStorage) @@ -294,36 +297,65 @@ public class ServerConfigurationManager Save(); } - internal void AddTag(string tag) + internal void AddPairTag(string tag) { - CurrentServerTagStorage().ServerAvailablePairTags.Add(tag); - _serverTagConfig.Save(); + CurrentPairTagStorage().ServerAvailablePairTags.Add(tag); + _pairTagConfig.Save(); + _lightlessMediator.Publish(new RefreshUiMessage()); + } + + internal void AddSyncshellTag(string tag) + { + CurrentSyncshellTagStorage().ServerAvailableSyncshellTags.Add(tag); + _syncshellTagConfig.Save(); _lightlessMediator.Publish(new RefreshUiMessage()); } internal void AddTagForUid(string uid, string tagName) { - if (CurrentServerTagStorage().UidServerPairedUserTags.TryGetValue(uid, out var tags)) + if (CurrentPairTagStorage().UidServerPairedUserTags.TryGetValue(uid, out var tags)) { tags.Add(tagName); _lightlessMediator.Publish(new RefreshUiMessage()); } else { - CurrentServerTagStorage().UidServerPairedUserTags[uid] = [tagName]; + CurrentPairTagStorage().UidServerPairedUserTags[uid] = [tagName]; } - _serverTagConfig.Save(); + _pairTagConfig.Save(); } - internal bool ContainsOpenPairTag(string tag) + internal void AddTagForSyncshell(string syncshellName, string tagName) { - return CurrentServerTagStorage().OpenPairTags.Contains(tag); + if (CurrentSyncshellTagStorage().SyncshellPairedTags.TryGetValue(syncshellName, out var tags)) + { + tags.Add(tagName); + _lightlessMediator.Publish(new RefreshUiMessage()); + } + else + { + CurrentSyncshellTagStorage().SyncshellPairedTags[syncshellName] = [tagName]; + } + + _syncshellTagConfig.Save(); } - internal bool ContainsTag(string uid, string tag) + internal bool ContainsOpenPairTag(string tag) => CurrentPairTagStorage().OpenPairTags.Contains(tag); + + internal bool ContainsPairTag(string uid, string tag) { - if (CurrentServerTagStorage().UidServerPairedUserTags.TryGetValue(uid, out var tags)) + if (CurrentPairTagStorage().UidServerPairedUserTags.TryGetValue(uid, out var tags)) + { + return tags.Contains(tag, StringComparer.Ordinal); + } + + return false; + } + + internal bool ContainsSyncshellTag(string name, string tag) + { + if (CurrentSyncshellTagStorage().SyncshellPairedTags.TryGetValue(name, out var tags)) { return tags.Contains(tag, StringComparer.Ordinal); } @@ -364,30 +396,19 @@ public class ServerConfigurationManager return null; } - internal HashSet GetServerAvailablePairTags() - { - return CurrentServerTagStorage().ServerAvailablePairTags; - } + internal HashSet GetServerAvailablePairTags() => CurrentPairTagStorage().ServerAvailablePairTags; - internal Dictionary> GetUidServerPairedUserTags() - { - return CurrentServerTagStorage().UidServerPairedUserTags; - } + internal HashSet GetServerAvailableSyncshellTags() => CurrentSyncshellTagStorage().ServerAvailableSyncshellTags; - internal HashSet GetUidsForTag(string tag) - { - return CurrentServerTagStorage().UidServerPairedUserTags.Where(p => p.Value.Contains(tag, StringComparer.Ordinal)).Select(p => p.Key).ToHashSet(StringComparer.Ordinal); - } + internal Dictionary> GetUidServerPairedUserTags() => CurrentPairTagStorage().UidServerPairedUserTags; - internal bool HasTags(string uid) - { - if (CurrentServerTagStorage().UidServerPairedUserTags.TryGetValue(uid, out var tags)) - { - return tags.Any(); - } + internal HashSet GetUidsForPairTag(string tag) => CurrentPairTagStorage().UidServerPairedUserTags.Where(p => p.Value.Contains(tag, StringComparer.Ordinal)).Select(p => p.Key).ToHashSet(StringComparer.Ordinal); - return false; - } + internal HashSet GetNamesForSyncshellTag(string tag) => CurrentSyncshellTagStorage().SyncshellPairedTags.Where(p => p.Value.Contains(tag, StringComparer.Ordinal)).Select(p => p.Key).ToHashSet(StringComparer.Ordinal); + + internal bool HasPairTags(string uid) => CurrentPairTagStorage().UidServerPairedUserTags.TryGetValue(uid, out var tags) && tags.Count != 0; + + internal bool HasSyncshellTags(string name) => CurrentSyncshellTagStorage().SyncshellPairedTags.TryGetValue(name, out var tags) && tags.Count != 0; internal void RemoveCharacterFromServer(int serverSelectionIndex, Authentication item) { @@ -398,50 +419,89 @@ public class ServerConfigurationManager internal void RemoveOpenPairTag(string tag) { - CurrentServerTagStorage().OpenPairTags.Remove(tag); - _serverTagConfig.Save(); + CurrentPairTagStorage().OpenPairTags.Remove(tag); + _pairTagConfig.Save(); } - internal void RemoveTag(string tag) + internal void RemovePairTag(string tag) { - CurrentServerTagStorage().ServerAvailablePairTags.Remove(tag); - foreach (var uid in GetUidsForTag(tag)) - { - RemoveTagForUid(uid, tag, save: false); - } - _serverTagConfig.Save(); + RemoveTag(CurrentPairTagStorage().ServerAvailablePairTags, tag); + _pairTagConfig.Save(); _lightlessMediator.Publish(new RefreshUiMessage()); } + internal void RemoveSyncshellTag(string tag) + { + RemoveTag(CurrentSyncshellTagStorage().ServerAvailableSyncshellTags, tag, true); + _syncshellTagConfig.Save(); + _lightlessMediator.Publish(new RefreshUiMessage()); + } + + internal void RemoveTag(HashSet storage, string tag, bool syncshell = false) + { + storage.Remove(tag); + if (syncshell) + { + foreach (var uid in GetNamesForSyncshellTag(tag)) + { + RemoveTagForSyncshell(uid, tag, save: false); + } + } + else + { + foreach (var uid in GetUidsForPairTag(tag)) + { + RemoveTagForUid(uid, tag, save: false); + } + } + + } + internal void RemoveTagForUid(string uid, string tagName, bool save = true) { - if (CurrentServerTagStorage().UidServerPairedUserTags.TryGetValue(uid, out var tags)) + if (CurrentPairTagStorage().UidServerPairedUserTags.TryGetValue(uid, out var tags)) { tags.Remove(tagName); if (save) { - _serverTagConfig.Save(); + _pairTagConfig.Save(); _lightlessMediator.Publish(new RefreshUiMessage()); } } } - internal void RenameTag(string oldName, string newName) + internal void RemoveTagForSyncshell(string name, string tagName, bool save = true) { - CurrentServerTagStorage().ServerAvailablePairTags.Remove(oldName); - CurrentServerTagStorage().ServerAvailablePairTags.Add(newName); - foreach (var existingTags in CurrentServerTagStorage().UidServerPairedUserTags.Select(k => k.Value)) + if (CurrentSyncshellTagStorage().SyncshellPairedTags.TryGetValue(name, out var tags)) + { + tags.Remove(tagName); + + if (save) + { + _syncshellTagConfig.Save(); + _lightlessMediator.Publish(new RefreshUiMessage()); + } + } + } + + internal void RenamePairTag(string oldName, string newName) => RenameTag(CurrentPairTagStorage().UidServerPairedUserTags, CurrentPairTagStorage().ServerAvailablePairTags, oldName, newName); + + internal void RenameSyncshellTag(string oldName, string newName) => RenameTag(CurrentSyncshellTagStorage().SyncshellPairedTags, CurrentSyncshellTagStorage().ServerAvailableSyncshellTags, oldName, newName); + + internal void RenameTag(Dictionary> tags, HashSet storage, string oldName, string newName) + { + storage.Remove(oldName); + storage.Add(newName); + foreach (var existingTags in tags.Select(k => k.Value)) { if (existingTags.Remove(oldName)) existingTags.Add(newName); } + _lightlessMediator.Publish(new RefreshUiMessage()); } - internal void SaveNotes() - { - _notesConfig.Save(); - } + internal void SaveNotes() => _notesConfig.Save(); internal void SetNoteForGid(string gid, string note, bool save = true) { @@ -476,10 +536,16 @@ public class ServerConfigurationManager return _notesConfig.Current.ServerNotes[CurrentApiUrl]; } - private ServerTagStorage CurrentServerTagStorage() + private PairTagStorage CurrentPairTagStorage() { - TryCreateCurrentServerTagStorage(); - return _serverTagConfig.Current.ServerTagStorage[CurrentApiUrl]; + TryCreateCurrentPairTagStorage(); + return _pairTagConfig.Current.ServerTagStorage[CurrentApiUrl]; + } + + private SyncshellTagStorage CurrentSyncshellTagStorage() + { + TryCreateCurrentSyncshellTagStorage(); + return _syncshellTagConfig.Current.ServerTagStorage[CurrentApiUrl]; } private void EnsureMainExists() @@ -499,11 +565,19 @@ public class ServerConfigurationManager } } - private void TryCreateCurrentServerTagStorage() + private void TryCreateCurrentPairTagStorage() { - if (!_serverTagConfig.Current.ServerTagStorage.ContainsKey(CurrentApiUrl)) + if (!_pairTagConfig.Current.ServerTagStorage.ContainsKey(CurrentApiUrl)) { - _serverTagConfig.Current.ServerTagStorage[CurrentApiUrl] = new(); + _pairTagConfig.Current.ServerTagStorage[CurrentApiUrl] = new(); + } + } + + private void TryCreateCurrentSyncshellTagStorage() + { + if (!_syncshellTagConfig.Current.ServerTagStorage.ContainsKey(CurrentApiUrl)) + { + _syncshellTagConfig.Current.ServerTagStorage[CurrentApiUrl] = new(); } } diff --git a/LightlessSync/UI/CompactUI.cs b/LightlessSync/UI/CompactUI.cs index b1c0c80..6c81f9a 100644 --- a/LightlessSync/UI/CompactUI.cs +++ b/LightlessSync/UI/CompactUI.cs @@ -1,6 +1,5 @@ using Dalamud.Bindings.ImGui; using Dalamud.Interface; -using Dalamud.Interface.Colors; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Utility; @@ -8,6 +7,7 @@ using LightlessSync.API.Data.Extensions; using LightlessSync.API.Dto.Group; using LightlessSync.Interop.Ipc; using LightlessSync.LightlessConfiguration; +using LightlessSync.LightlessConfiguration.Configurations; using LightlessSync.PlayerData.Handlers; using LightlessSync.PlayerData.Pairs; using LightlessSync.Services; @@ -36,9 +36,12 @@ public class CompactUi : WindowMediatorSubscriberBase private readonly DrawEntityFactory _drawEntityFactory; private readonly FileUploadManager _fileTransferManager; private readonly PairManager _pairManager; - private readonly SelectTagForPairUi _selectGroupForPairUi; + private readonly SelectTagForPairUi _selectTagForPairUi; + private readonly SelectTagForSyncshellUi _selectTagForSyncshellUi; + private readonly SelectSyncshellForTagUi _selectSyncshellForTagUi; + private readonly RenameSyncshellTagUi _renameSyncshellTagUi; private readonly SelectPairForTagUi _selectPairsForGroupUi; - private readonly RenameTagUi _renameTagUi; + private readonly RenamePairTagUi _renamePairTagUi; private readonly IpcManager _ipcManager; private readonly ServerConfigurationManager _serverManager; private readonly TopTabMenu _tabMenu; @@ -57,7 +60,9 @@ public class CompactUi : WindowMediatorSubscriberBase public CompactUi(ILogger logger, UiSharedService uiShared, LightlessConfigService configService, ApiController apiController, PairManager pairManager, ServerConfigurationManager serverManager, LightlessMediator mediator, FileUploadManager fileTransferManager, - TagHandler tagHandler, DrawEntityFactory drawEntityFactory, SelectTagForPairUi selectTagForPairUi, SelectPairForTagUi selectPairForTagUi, RenameTagUi renameTagUi, + TagHandler tagHandler, DrawEntityFactory drawEntityFactory, + SelectTagForPairUi selectTagForPairUi, SelectPairForTagUi selectPairForTagUi, RenamePairTagUi renameTagUi, + SelectTagForSyncshellUi selectTagForSyncshellUi, SelectSyncshellForTagUi selectSyncshellForTagUi, RenameSyncshellTagUi renameSyncshellTagUi, PerformanceCollectorService performanceCollectorService, IpcManager ipcManager) : base(logger, mediator, "###LightlessSyncMainUI", performanceCollectorService) { @@ -69,9 +74,12 @@ public class CompactUi : WindowMediatorSubscriberBase _fileTransferManager = fileTransferManager; _tagHandler = tagHandler; _drawEntityFactory = drawEntityFactory; - _selectGroupForPairUi = selectTagForPairUi; + _selectTagForPairUi = selectTagForPairUi; + _selectTagForSyncshellUi = selectTagForSyncshellUi; + _selectSyncshellForTagUi = selectSyncshellForTagUi; + _renameSyncshellTagUi = renameSyncshellTagUi; _selectPairsForGroupUi = selectPairForTagUi; - _renameTagUi = renameTagUi; + _renamePairTagUi = renameTagUi; _ipcManager = ipcManager; _tabMenu = new TopTabMenu(Mediator, _apiController, _pairManager, _uiSharedService); @@ -199,9 +207,12 @@ public class CompactUi : WindowMediatorSubscriberBase float pairlistEnd = ImGui.GetCursorPosY(); using (ImRaii.PushId("transfers")) DrawTransfers(); _transferPartHeight = ImGui.GetCursorPosY() - pairlistEnd - ImGui.GetTextLineHeight(); - using (ImRaii.PushId("group-user-popup")) _selectPairsForGroupUi.Draw(_pairManager.DirectPairs); - using (ImRaii.PushId("group-user-edit")) _renameTagUi.Draw(_pairManager.DirectPairs); - using (ImRaii.PushId("grouping-popup")) _selectGroupForPairUi.Draw(); + 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-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) @@ -462,12 +473,12 @@ public class CompactUi : WindowMediatorSubscriberBase bool FilterVisibleUsers(KeyValuePair> u) => u.Key.IsVisible && (_configService.Current.ShowSyncshellUsersInVisible || !(!_configService.Current.ShowSyncshellUsersInVisible && !u.Key.IsDirectlyPaired)); - bool FilterTagusers(KeyValuePair> u, string tag) - => u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && _tagHandler.HasTag(u.Key.UserData.UID, tag); + bool FilterTagUsers(KeyValuePair> u, string tag) + => u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && _tagHandler.HasPairTag(u.Key.UserData.UID, tag); bool FilterGroupUsers(KeyValuePair> u, GroupFullInfoDto group) => u.Value.Exists(g => string.Equals(g.GID, group.GID, StringComparison.Ordinal)); bool FilterNotTaggedUsers(KeyValuePair> u) - => u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && !_tagHandler.HasAnyTag(u.Key.UserData.UID); + => u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && !_tagHandler.HasAnyPairTag(u.Key.UserData.UID); bool FilterOfflineUsers(KeyValuePair> u) => ((u.Key.IsDirectlyPaired && _configService.Current.ShowSyncshellOfflineUsersSeparately) || !_configService.Current.ShowSyncshellOfflineUsersSeparately) @@ -487,46 +498,48 @@ public class CompactUi : WindowMediatorSubscriberBase } List groupFolders = new(); + foreach (var group in _pairManager.GroupPairs.Select(g => g.Key).OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase)) { - var allGroupPairs = ImmutablePairList(allPairs - .Where(u => FilterGroupUsers(u, group))); - - var filteredGroupPairs = filteredPairs - .Where(u => FilterGroupUsers(u, group) && FilterOnlineOrPausedSelf(u)) - .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(AlphabeticalSort, StringComparer.OrdinalIgnoreCase) - .ToDictionary(k => k.Key, k => k.Value); - + GetGroups(allPairs, filteredPairs, group, out ImmutableList allGroupPairs, out Dictionary> filteredGroupPairs); groupFolders.Add(_drawEntityFactory.CreateDrawGroupFolder(group, filteredGroupPairs, allGroupPairs)); } if (_configService.Current.GroupUpSyncshells) - drawFolders.Add(new DrawGroupedGroupFolder(groupFolders, _tagHandler, _uiSharedService)); + drawFolders.Add(new DrawGroupedGroupFolder(groupFolders, _tagHandler, _uiSharedService, _selectSyncshellForTagUi, _renameSyncshellTagUi, "")); else drawFolders.AddRange(groupFolders); - var tags = _tagHandler.GetAllTagsSorted(); + var tags = _tagHandler.GetAllPairTagsSorted(); foreach (var tag in tags) { var allTagPairs = ImmutablePairList(allPairs - .Where(u => FilterTagusers(u, tag))); + .Where(u => FilterTagUsers(u, tag))); var filteredTagPairs = BasicSortedDictionary(filteredPairs - .Where(u => FilterTagusers(u, tag) && FilterOnlineOrPausedSelf(u))); + .Where(u => FilterTagUsers(u, tag) && FilterOnlineOrPausedSelf(u))); drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(tag, filteredTagPairs, allTagPairs)); } + var syncshellTags = _tagHandler.GetAllSyncshellTagsSorted(); + foreach (var syncshelltag in syncshellTags) + { + List syncshellFolderTags = []; + foreach (var group in _pairManager.GroupPairs.Select(g => g.Key).OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase)) + { + if (_tagHandler.HasSyncshellTag(group.GID, syncshelltag)) + { + GetGroups(allPairs, filteredPairs, group, out ImmutableList allGroupPairs, out Dictionary> filteredGroupPairs); + syncshellFolderTags.Add(_drawEntityFactory.CreateDrawGroupFolder($"tag_{group.GID}", group, filteredGroupPairs, allGroupPairs)); + } + } + + if (syncshellFolderTags.Count > 0) + { + drawFolders.Add(new DrawGroupedGroupFolder(syncshellFolderTags, _tagHandler, _uiSharedService, _selectSyncshellForTagUi, _renameSyncshellTagUi, syncshelltag)); + } + } + var allOnlineNotTaggedPairs = ImmutablePairList(allPairs .Where(FilterNotTaggedUsers)); var onlineNotTaggedPairs = BasicSortedDictionary(filteredPairs @@ -561,6 +574,27 @@ public class CompactUi : WindowMediatorSubscriberBase ImmutablePairList(allPairs.Where(u => u.Key.IsOneSidedPair)))); return drawFolders; + + void GetGroups(Dictionary> allPairs, Dictionary> filteredPairs, GroupFullInfoDto group, out ImmutableList allGroupPairs, out Dictionary> filteredGroupPairs) + { + allGroupPairs = ImmutablePairList(allPairs + .Where(u => FilterGroupUsers(u, group))); + filteredGroupPairs = filteredPairs + .Where(u => FilterGroupUsers(u, group) && FilterOnlineOrPausedSelf(u)) + .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(AlphabeticalSort, StringComparer.OrdinalIgnoreCase) + .ToDictionary(k => k.Key, k => k.Value); + } } private string GetServerError() diff --git a/LightlessSync/UI/Components/DrawFolderGroup.cs b/LightlessSync/UI/Components/DrawFolderGroup.cs index 11737e9..6de9e28 100644 --- a/LightlessSync/UI/Components/DrawFolderGroup.cs +++ b/LightlessSync/UI/Components/DrawFolderGroup.cs @@ -19,16 +19,18 @@ public class DrawFolderGroup : DrawFolderBase private readonly GroupFullInfoDto _groupFullInfoDto; private readonly IdDisplayHandler _idDisplayHandler; private readonly LightlessMediator _lightlessMediator; + private readonly SelectTagForSyncshellUi _selectTagForSyncshellUi; public DrawFolderGroup(string id, GroupFullInfoDto groupFullInfoDto, ApiController apiController, IImmutableList drawPairs, IImmutableList allPairs, TagHandler tagHandler, IdDisplayHandler idDisplayHandler, - LightlessMediator lightlessMediator, UiSharedService uiSharedService) : + LightlessMediator lightlessMediator, UiSharedService uiSharedService, SelectTagForSyncshellUi selectTagForSyncshellUi) : base(id, drawPairs, allPairs, tagHandler, uiSharedService) { _groupFullInfoDto = groupFullInfoDto; _apiController = apiController; _idDisplayHandler = idDisplayHandler; _lightlessMediator = lightlessMediator; + _selectTagForSyncshellUi = selectTagForSyncshellUi; } protected override bool RenderIfEmpty => true; @@ -99,6 +101,13 @@ public class DrawFolderGroup : DrawFolderBase } UiSharedService.AttachToolTip("Copies all your notes for all users in this Syncshell to the clipboard." + Environment.NewLine + "They can be imported via Settings -> General -> Notes -> Import notes from clipboard"); + if (_uiSharedService.IconTextButton(FontAwesomeIcon.Folder, "Syncshell Groups", menuWidth, true)) + { + ImGui.CloseCurrentPopup(); + _selectTagForSyncshellUi.Open(_groupFullInfoDto); + } + UiSharedService.AttachToolTip("Choose syncshell groups for " + _groupFullInfoDto.GID); + if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowCircleLeft, "Leave Syncshell", menuWidth, true) && UiSharedService.CtrlPressed()) { _ = _apiController.GroupLeave(_groupFullInfoDto); diff --git a/LightlessSync/UI/Components/DrawFolderTag.cs b/LightlessSync/UI/Components/DrawFolderTag.cs index 79ec3c0..0c114e1 100644 --- a/LightlessSync/UI/Components/DrawFolderTag.cs +++ b/LightlessSync/UI/Components/DrawFolderTag.cs @@ -13,10 +13,10 @@ public class DrawFolderTag : DrawFolderBase { private readonly ApiController _apiController; private readonly SelectPairForTagUi _selectPairForTagUi; - private readonly RenameTagUi _renameTagUi; + private readonly RenamePairTagUi _renameTagUi; public DrawFolderTag(string id, IImmutableList drawPairs, IImmutableList allPairs, - TagHandler tagHandler, ApiController apiController, SelectPairForTagUi selectPairForTagUi, RenameTagUi renameTagUi, UiSharedService uiSharedService) + TagHandler tagHandler, ApiController apiController, SelectPairForTagUi selectPairForTagUi, RenamePairTagUi renameTagUi, UiSharedService uiSharedService) : base(id, drawPairs, allPairs, tagHandler, uiSharedService) { _apiController = apiController; @@ -112,7 +112,7 @@ public class DrawFolderTag : DrawFolderBase } if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Pair Group", menuWidth, isInPopup: true) && UiSharedService.CtrlPressed()) { - _tagHandler.RemoveTag(_id); + _tagHandler.RemovePairTag(_id); } UiSharedService.AttachToolTip("Hold CTRL to remove this Group permanently." + Environment.NewLine + "Note: this will not unpair with users in this Group."); diff --git a/LightlessSync/UI/Components/DrawGroupedGroupFolder.cs b/LightlessSync/UI/Components/DrawGroupedGroupFolder.cs index 5410554..d1876ac 100644 --- a/LightlessSync/UI/Components/DrawGroupedGroupFolder.cs +++ b/LightlessSync/UI/Components/DrawGroupedGroupFolder.cs @@ -9,20 +9,27 @@ namespace LightlessSync.UI.Components; public class DrawGroupedGroupFolder : IDrawFolder { + private readonly string _tag; private readonly IEnumerable _groups; private readonly TagHandler _tagHandler; private readonly UiSharedService _uiSharedService; + private readonly SelectSyncshellForTagUi _selectSyncshellForTagUi; + private readonly RenameSyncshellTagUi _renameSyncshellTagUi; private bool _wasHovered = false; + private float _menuWidth; public IImmutableList DrawPairs => throw new NotSupportedException(); public int OnlinePairs => _groups.SelectMany(g => g.DrawPairs).Where(g => g.Pair.IsOnline).DistinctBy(g => g.Pair.UserData.UID).Count(); public int TotalPairs => _groups.Sum(g => g.TotalPairs); - public DrawGroupedGroupFolder(IEnumerable groups, TagHandler tagHandler, UiSharedService uiSharedService) + public DrawGroupedGroupFolder(IEnumerable groups, TagHandler tagHandler, UiSharedService uiSharedService, SelectSyncshellForTagUi selectSyncshellForTagUi, RenameSyncshellTagUi renameSyncshellTagUi, string tag) { _groups = groups; _tagHandler = tagHandler; _uiSharedService = uiSharedService; + _selectSyncshellForTagUi = selectSyncshellForTagUi; + _renameSyncshellTagUi = renameSyncshellTagUi; + _tag = tag; } public void Draw() @@ -30,6 +37,11 @@ public class DrawGroupedGroupFolder : IDrawFolder if (!_groups.Any()) return; string _id = "__folder_syncshells"; + if (_tag != "") + { + _id = $"__folder_{_tag}"; + } + using var id = ImRaii.PushId(_id); var color = ImRaii.PushColor(ImGuiCol.ChildBg, ImGui.GetColorU32(ImGuiCol.FrameBgHovered), _wasHovered); using (ImRaii.Child("folder__" + _id, new System.Numerics.Vector2(UiSharedService.GetWindowContentRegionWidth() - ImGui.GetCursorPosX(), ImGui.GetFrameHeight()))) @@ -49,18 +61,36 @@ public class DrawGroupedGroupFolder : IDrawFolder ImGui.SameLine(); ImGui.AlignTextToFramePadding(); - _uiSharedService.IconText(FontAwesomeIcon.UsersRectangle); - using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X / 2f })) + + if (_tag != "") { - ImGui.SameLine(); - ImGui.AlignTextToFramePadding(); - ImGui.TextUnformatted("[" + OnlinePairs.ToString() + "]"); + _uiSharedService.IconText(FontAwesomeIcon.FolderPlus); + } + else + { + _uiSharedService.IconText(FontAwesomeIcon.UsersRectangle); } + + using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X / 2f })) + { + ImGui.SameLine(); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted("[" + OnlinePairs.ToString() + "]"); + } UiSharedService.AttachToolTip(OnlinePairs + " online in all of your joined syncshells" + Environment.NewLine + TotalPairs + " pairs combined in all of your joined syncshells"); ImGui.SameLine(); ImGui.AlignTextToFramePadding(); - ImGui.TextUnformatted("All Syncshells"); + if (_tag != "") + { + ImGui.TextUnformatted(_tag); + + ImGui.SameLine(); + DrawMenu(); + } else + { + ImGui.TextUnformatted("All Syncshells"); + } } color.Dispose(); _wasHovered = ImGui.IsItemHovered(); @@ -76,4 +106,40 @@ public class DrawGroupedGroupFolder : IDrawFolder } } } + + protected void DrawMenu() + { + var barButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.EllipsisV); + var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth(); + + ImGui.SameLine(windowEndX - barButtonSize.X); + if (_uiSharedService.IconButton(FontAwesomeIcon.EllipsisV)) + { + ImGui.OpenPopup("User Flyout Menu"); + } + if (ImGui.BeginPopup("User Flyout Menu")) + { + using (ImRaii.PushId($"buttons-syncshell-{_tag}")) GroupMenu(_menuWidth); + _menuWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X; + ImGui.EndPopup(); + } + } + + protected void GroupMenu(float menuWidth) + { + ImGui.TextUnformatted("Syncshell Group Menu"); + if (_uiSharedService.IconTextButton(FontAwesomeIcon.Users, "Select Syncshells", menuWidth, isInPopup: true)) + { + _selectSyncshellForTagUi.Open(_tag); + } + if (_uiSharedService.IconTextButton(FontAwesomeIcon.Edit, "Rename Syncshell Group", menuWidth, isInPopup: true)) + { + _renameSyncshellTagUi.Open(_tag); + } + if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Syncshell Group", menuWidth, isInPopup: true) && UiSharedService.CtrlPressed()) + { + _tagHandler.RemoveSyncshellTag(_tag); + } + UiSharedService.AttachToolTip("Hold CTRL to remove this Group permanently."); + } } diff --git a/LightlessSync/UI/Components/RenameTagUi.cs b/LightlessSync/UI/Components/RenamePairTagUi.cs similarity index 68% rename from LightlessSync/UI/Components/RenameTagUi.cs rename to LightlessSync/UI/Components/RenamePairTagUi.cs index 1d71f64..aa77783 100644 --- a/LightlessSync/UI/Components/RenameTagUi.cs +++ b/LightlessSync/UI/Components/RenamePairTagUi.cs @@ -1,36 +1,34 @@ using Dalamud.Bindings.ImGui; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; -using LightlessSync.PlayerData.Pairs; using LightlessSync.UI.Handlers; using System.Numerics; namespace LightlessSync.UI.Components; -public class RenameTagUi +public class RenamePairTagUi { private readonly TagHandler _tagHandler; private readonly UiSharedService _uiSharedService; private string _desiredName = string.Empty; private bool _opened = false; - private HashSet _peopleInGroup = new(StringComparer.Ordinal); private bool _show = false; private string _tag = string.Empty; - public RenameTagUi(TagHandler tagHandler, UiSharedService uiSharedService) + public RenamePairTagUi(TagHandler tagHandler, UiSharedService uiSharedService) { _tagHandler = tagHandler; _uiSharedService = uiSharedService; } - public void Draw(List pairs) + public void Draw() { var workHeight = ImGui.GetMainViewport().WorkSize.Y / ImGuiHelpers.GlobalScale; var minSize = new Vector2(300, workHeight < 110 ? workHeight : 110) * ImGuiHelpers.GlobalScale; var maxSize = new Vector2(300, 110) * ImGuiHelpers.GlobalScale; - var popupName = $"Renaming Group {_tag}"; + var popupName = $"Renaming Pair Group {_tag}"; if (!_show) { @@ -55,7 +53,7 @@ public class RenameTagUi { if (_uiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.Plus, "Rename Group")) { - RenameTag(pairs, _tag, _desiredName); + RenameTag(_tag, _desiredName); _show = false; } } @@ -69,25 +67,13 @@ public class RenameTagUi public void Open(string tag) { - _peopleInGroup = _tagHandler.GetOtherUidsForTag(tag); _tag = tag; _desiredName = ""; _show = true; } - public void RenameTag(List pairs, string oldTag, string newTag) - { - //Removal of old tag - _tagHandler.RemoveTag(oldTag); - //Creation of new tag and adding of old group pairs in new one. - _tagHandler.AddTag(newTag); - foreach (Pair pair in pairs) - { - var isInTag = _peopleInGroup.Contains(pair.UserData.UID); - if (isInTag) - { - _tagHandler.AddTagToPairedUid(pair.UserData.UID, newTag); - } - } + public void RenameTag(string oldTag, string newTag) + { + _tagHandler.RenamePairTag(oldTag, newTag); } } \ No newline at end of file diff --git a/LightlessSync/UI/Components/RenameSyncshellTagUi.cs b/LightlessSync/UI/Components/RenameSyncshellTagUi.cs new file mode 100644 index 0000000..b92870c --- /dev/null +++ b/LightlessSync/UI/Components/RenameSyncshellTagUi.cs @@ -0,0 +1,79 @@ +using Dalamud.Bindings.ImGui; +using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; +using LightlessSync.UI.Handlers; + +using System.Numerics; + +namespace LightlessSync.UI.Components; + +public class RenameSyncshellTagUi +{ + private readonly TagHandler _tagHandler; + private readonly UiSharedService _uiSharedService; + private string _desiredName = string.Empty; + private bool _opened = false; + private bool _show = false; + private string _tag = string.Empty; + + public RenameSyncshellTagUi(TagHandler tagHandler, UiSharedService uiSharedService) + { + _tagHandler = tagHandler; + _uiSharedService = uiSharedService; + } + + public void Draw() + { + var workHeight = ImGui.GetMainViewport().WorkSize.Y / ImGuiHelpers.GlobalScale; + var minSize = new Vector2(300, workHeight < 110 ? workHeight : 110) * ImGuiHelpers.GlobalScale; + var maxSize = new Vector2(300, 110) * ImGuiHelpers.GlobalScale; + + var popupName = $"Renaming Syncshell Group {_tag}"; + + if (!_show) + { + _opened = false; + } + + if (_show && !_opened) + { + ImGui.SetNextWindowSize(minSize); + UiSharedService.CenterNextWindow(minSize.X, minSize.Y, ImGuiCond.Always); + ImGui.OpenPopup(popupName); + _opened = true; + } + + ImGui.SetNextWindowSizeConstraints(minSize, maxSize); + if (ImGui.BeginPopupModal(popupName, ref _show, ImGuiWindowFlags.Popup | ImGuiWindowFlags.Modal)) + { + ImGui.TextUnformatted($"Renaming {_tag}"); + + ImGui.InputTextWithHint("##desiredname", "Enter new group name", ref _desiredName, 255, ImGuiInputTextFlags.None); + using (ImRaii.Disabled(string.IsNullOrEmpty(_desiredName))) + { + if (_uiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.Plus, "Rename Group")) + { + RenameTag(_tag, _desiredName); + _show = false; + } + } + ImGui.EndPopup(); + } + else + { + _show = false; + } + } + + public void Open(string tag) + { + _tag = tag; + _desiredName = ""; + _show = true; + } + + public void RenameTag(string oldTag, string newTag) + { + _tagHandler.RenameSyncshellTag(oldTag, newTag); + } +} \ No newline at end of file diff --git a/LightlessSync/UI/Components/SelectPairForTagUi.cs b/LightlessSync/UI/Components/SelectPairForTagUi.cs index 58caf91..89db40e 100644 --- a/LightlessSync/UI/Components/SelectPairForTagUi.cs +++ b/LightlessSync/UI/Components/SelectPairForTagUi.cs @@ -60,7 +60,7 @@ public class SelectPairForTagUi { if (isInGroup) { - _tagHandler.AddTagToPairedUid(item.UserData.UID, _tag); + _tagHandler.AddPairTagToPairedUid(item.UserData.UID, _tag); _peopleInGroup.Add(item.UserData.UID); } else diff --git a/LightlessSync/UI/Components/SelectSyncshellForTagUi.cs b/LightlessSync/UI/Components/SelectSyncshellForTagUi.cs new file mode 100644 index 0000000..62dd1d6 --- /dev/null +++ b/LightlessSync/UI/Components/SelectSyncshellForTagUi.cs @@ -0,0 +1,86 @@ +using Dalamud.Bindings.ImGui; +using Dalamud.Interface.Utility; +using LightlessSync.API.Dto.Group; +using LightlessSync.UI.Handlers; + +using System.Numerics; + +namespace LightlessSync.UI.Components; + +public class SelectSyncshellForTagUi +{ + private readonly TagHandler _tagHandler; + private string _filter = string.Empty; + private bool _opened = false; + private HashSet _syncshellsInGroup = new(StringComparer.Ordinal); + private bool _show = false; + private string _tag = string.Empty; + + public SelectSyncshellForTagUi(TagHandler tagHandler) + { + _tagHandler = tagHandler; + } + + public void Draw(List groups) + { + var workHeight = ImGui.GetMainViewport().WorkSize.Y / ImGuiHelpers.GlobalScale; + var minSize = new Vector2(300, workHeight < 400 ? workHeight : 400) * ImGuiHelpers.GlobalScale; + var maxSize = new Vector2(300, 1000) * ImGuiHelpers.GlobalScale; + + var popupName = $"Choose Syncshells for Group {_tag}"; + + if (!_show) + { + _opened = false; + } + + if (_show && !_opened) + { + ImGui.SetNextWindowSize(minSize); + UiSharedService.CenterNextWindow(minSize.X, minSize.Y, ImGuiCond.Always); + ImGui.OpenPopup(popupName); + _opened = true; + } + + ImGui.SetNextWindowSizeConstraints(minSize, maxSize); + if (ImGui.BeginPopupModal(popupName, ref _show, ImGuiWindowFlags.Popup | ImGuiWindowFlags.Modal)) + { + ImGui.TextUnformatted($"Select syncshells for group {_tag}"); + + ImGui.InputTextWithHint("##filter", "Filter", ref _filter, 255, ImGuiInputTextFlags.None); + foreach (var group in groups + .Where(g => string.IsNullOrEmpty(_filter) || g.GID.Contains(_filter, StringComparison.OrdinalIgnoreCase)) + .OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase) + .ToList()) + { + var isInGroup = _syncshellsInGroup.Contains(group.GID); + if (ImGui.Checkbox(group.GroupAliasOrGID, ref isInGroup)) + { + if (isInGroup) + { + _tagHandler.AddTagToSyncshell(group.GID, _tag); + _syncshellsInGroup.Add(group.GID); + } + else + { + _tagHandler.RemoveTagFromSyncshell(group.GID, _tag); + _syncshellsInGroup.Remove(group.GID); + } + } + } + ImGui.EndPopup(); + } + else + { + _filter = string.Empty; + _show = false; + } + } + + public void Open(string tag) + { + _syncshellsInGroup = _tagHandler.GetOtherSyncshellsForTag(tag); + _tag = tag; + _show = true; + } +} \ No newline at end of file diff --git a/LightlessSync/UI/Components/SelectTagForPairUi.cs b/LightlessSync/UI/Components/SelectTagForPairUi.cs index e258f9c..4c840fb 100644 --- a/LightlessSync/UI/Components/SelectTagForPairUi.cs +++ b/LightlessSync/UI/Components/SelectTagForPairUi.cs @@ -59,7 +59,7 @@ public class SelectTagForPairUi if (ImGui.BeginPopup(popupName)) { - var tags = _tagHandler.GetAllTagsSorted(); + var tags = _tagHandler.GetAllPairTagsSorted(); var childHeight = tags.Count != 0 ? tags.Count * 25 : 1; var childSize = new Vector2(0, childHeight > 100 ? 100 : childHeight) * ImGuiHelpers.GlobalScale; @@ -101,13 +101,13 @@ public class SelectTagForPairUi private void DrawGroupName(Pair pair, string name) { - var hasTagBefore = _tagHandler.HasTag(pair.UserData.UID, name); + var hasTagBefore = _tagHandler.HasPairTag(pair.UserData.UID, name); var hasTag = hasTagBefore; if (ImGui.Checkbox(name, ref hasTag)) { if (hasTag) { - _tagHandler.AddTagToPairedUid(pair.UserData.UID, name); + _tagHandler.AddPairTagToPairedUid(pair.UserData.UID, name); } else { @@ -120,10 +120,10 @@ public class SelectTagForPairUi { if (!_tagNameToAdd.IsNullOrWhitespace() && _tagNameToAdd is not (TagHandler.CustomOfflineTag or TagHandler.CustomOnlineTag or TagHandler.CustomVisibleTag)) { - _tagHandler.AddTag(_tagNameToAdd); + _tagHandler.AddPairTag(_tagNameToAdd); if (_pair != null) { - _tagHandler.AddTagToPairedUid(_pair.UserData.UID, _tagNameToAdd); + _tagHandler.AddPairTagToPairedUid(_pair.UserData.UID, _tagNameToAdd); } _tagNameToAdd = string.Empty; } diff --git a/LightlessSync/UI/Components/SelectTagForSyncshellUi.cs b/LightlessSync/UI/Components/SelectTagForSyncshellUi.cs new file mode 100644 index 0000000..f9947e9 --- /dev/null +++ b/LightlessSync/UI/Components/SelectTagForSyncshellUi.cs @@ -0,0 +1,132 @@ +using Dalamud.Bindings.ImGui; +using Dalamud.Interface; +using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; +using Dalamud.Utility; +using LightlessSync.API.Dto.Group; +using LightlessSync.UI.Handlers; + +using System.Numerics; + +namespace LightlessSync.UI.Components; + +public class SelectTagForSyncshellUi +{ + private readonly TagHandler _tagHandler; + private readonly UiSharedService _uiSharedService; + + /// + /// The group UI is always open for a specific pair. This defines which pair the UI is open for. + /// + /// + private GroupFullInfoDto? _group; + + /// + /// Should the panel show, yes/no + /// + private bool _show; + + /// + /// For the add category option, this stores the currently typed in tag name + /// + private string _tagNameToAdd = ""; + + public SelectTagForSyncshellUi(TagHandler tagHandler, UiSharedService uiSharedService) + { + _show = false; + _group = null; + _tagHandler = tagHandler; + _uiSharedService = uiSharedService; + } + + public void Draw() + { + if (_group == null) + { + return; + } + + var name = _group.GroupAliasOrGID; + var popupName = $"Choose Groups for {_group.GroupAliasOrGID}"; + // Is the popup supposed to show but did not open yet? Open it + if (_show) + { + ImGui.OpenPopup(popupName); + _show = false; + } + + if (ImGui.BeginPopup(popupName)) + { + var tags = _tagHandler.GetAllSyncshellTagsSorted(); + var childHeight = tags.Count != 0 ? tags.Count * 25 : 1; + var childSize = new Vector2(0, childHeight > 100 ? 100 : childHeight) * ImGuiHelpers.GlobalScale; + + ImGui.TextUnformatted($"Select the groups you want {name} to be in."); + if (ImGui.BeginChild(name + "##listGroups", childSize)) + { + foreach (var tag in tags) + { + using (ImRaii.PushId($"groups-syncshell-{_group.GID}-{tag}")) DrawGroupName(_group, tag); + } + ImGui.EndChild(); + } + + ImGui.Separator(); + ImGui.TextUnformatted($"Create a new group for {name}."); + if (_uiSharedService.IconButton(FontAwesomeIcon.Plus)) + { + HandleAddTag(); + } + ImGui.SameLine(); + ImGui.InputTextWithHint("##category_name", "New Group", ref _tagNameToAdd, 40); + if (ImGui.IsKeyDown(ImGuiKey.Enter)) + { + HandleAddTag(); + } + ImGui.EndPopup(); + } + } + + public void Open(GroupFullInfoDto group) + { + _group = group; + // Using "_show" here to de-couple the opening of the popup + // The popup name is derived from the name the user currently sees, which is + // based on the showUidForEntry dictionary. + // We'd have to derive the name here to open it popup modal here, when the Open() is called + _show = true; + } + + private void DrawGroupName(GroupFullInfoDto group, string name) + { + var hasTag = _tagHandler.HasSyncshellTag(group.GID, name); + if (ImGui.Checkbox(name, ref hasTag)) + { + if (hasTag) + { + _tagHandler.AddTagToSyncshell(group.GID, name); + } + else + { + _tagHandler.RemoveTagFromSyncshell(group.GID, name); + } + } + } + + private void HandleAddTag() + { + if (!_tagNameToAdd.IsNullOrWhitespace()) + { + _tagHandler.AddSyncshellTag(_tagNameToAdd); + if (_group != null) + { + _tagHandler.AddTagToSyncshell(_group.GID, _tagNameToAdd); + } + _tagNameToAdd = string.Empty; + } + else + { + _tagNameToAdd = string.Empty; + } + } +} \ No newline at end of file diff --git a/LightlessSync/UI/DrawEntityFactory.cs b/LightlessSync/UI/DrawEntityFactory.cs index 01b6738..d1410ad 100644 --- a/LightlessSync/UI/DrawEntityFactory.cs +++ b/LightlessSync/UI/DrawEntityFactory.cs @@ -23,21 +23,25 @@ public class DrawEntityFactory private readonly PlayerPerformanceConfigService _playerPerformanceConfigService; private readonly CharaDataManager _charaDataManager; private readonly SelectTagForPairUi _selectTagForPairUi; - private readonly RenameTagUi _renameTagUi; + private readonly RenamePairTagUi _renamePairTagUi; + private readonly SelectTagForSyncshellUi _selectTagForSyncshellUi; + private readonly RenameSyncshellTagUi _renameSyncshellTagUi; + private readonly SelectSyncshellForTagUi _selectSyncshellForTagUi; private readonly TagHandler _tagHandler; private readonly IdDisplayHandler _uidDisplayHandler; public DrawEntityFactory(ILogger logger, ApiController apiController, IdDisplayHandler uidDisplayHandler, - SelectTagForPairUi selectTagForPairUi, RenameTagUi renameTagUi, LightlessMediator mediator, + SelectTagForPairUi selectTagForPairUi, RenamePairTagUi renamePairTagUi, LightlessMediator mediator, TagHandler tagHandler, SelectPairForTagUi selectPairForTagUi, ServerConfigurationManager serverConfigurationManager, UiSharedService uiSharedService, - PlayerPerformanceConfigService playerPerformanceConfigService, CharaDataManager charaDataManager) + PlayerPerformanceConfigService playerPerformanceConfigService, CharaDataManager charaDataManager, + SelectTagForSyncshellUi selectTagForSyncshellUi, RenameSyncshellTagUi renameSyncshellTagUi, SelectSyncshellForTagUi selectSyncshellForTagUi) { _logger = logger; _apiController = apiController; _uidDisplayHandler = uidDisplayHandler; _selectTagForPairUi = selectTagForPairUi; - _renameTagUi = renameTagUi; + _renamePairTagUi = renamePairTagUi; _mediator = mediator; _tagHandler = tagHandler; _selectPairForTagUi = selectPairForTagUi; @@ -45,6 +49,9 @@ public class DrawEntityFactory _uiSharedService = uiSharedService; _playerPerformanceConfigService = playerPerformanceConfigService; _charaDataManager = charaDataManager; + _selectTagForSyncshellUi = selectTagForSyncshellUi; + _renameSyncshellTagUi = renameSyncshellTagUi; + _selectSyncshellForTagUi = selectSyncshellForTagUi; } public DrawFolderGroup CreateDrawGroupFolder(GroupFullInfoDto groupFullInfoDto, @@ -53,7 +60,16 @@ public class DrawEntityFactory { return new DrawFolderGroup(groupFullInfoDto.Group.GID, groupFullInfoDto, _apiController, filteredPairs.Select(p => CreateDrawPair(groupFullInfoDto.Group.GID + p.Key.UserData.UID, p.Key, p.Value, groupFullInfoDto)).ToImmutableList(), - allPairs, _tagHandler, _uidDisplayHandler, _mediator, _uiSharedService); + allPairs, _tagHandler, _uidDisplayHandler, _mediator, _uiSharedService, _selectTagForSyncshellUi); + } + + public DrawFolderGroup CreateDrawGroupFolder(string id, GroupFullInfoDto groupFullInfoDto, + Dictionary> filteredPairs, + IImmutableList allPairs) + { + return new DrawFolderGroup(id, groupFullInfoDto, _apiController, + filteredPairs.Select(p => CreateDrawPair(groupFullInfoDto.Group.GID + p.Key.UserData.UID, p.Key, p.Value, groupFullInfoDto)).ToImmutableList(), + allPairs, _tagHandler, _uidDisplayHandler, _mediator, _uiSharedService, _selectTagForSyncshellUi); } public DrawFolderTag CreateDrawTagFolder(string tag, @@ -61,7 +77,7 @@ public class DrawEntityFactory IImmutableList allPairs) { return new(tag, filteredPairs.Select(u => CreateDrawPair(tag, u.Key, u.Value, currentGroup: null)).ToImmutableList(), - allPairs, _tagHandler, _apiController, _selectPairForTagUi, _renameTagUi, _uiSharedService); + allPairs, _tagHandler, _apiController, _selectPairForTagUi, _renamePairTagUi, _uiSharedService); } public DrawUserPair CreateDrawPair(string id, Pair user, List groups, GroupFullInfoDto? currentGroup) diff --git a/LightlessSync/UI/Handlers/IdDisplayHandler.cs b/LightlessSync/UI/Handlers/IdDisplayHandler.cs index 2683489..e77093b 100644 --- a/LightlessSync/UI/Handlers/IdDisplayHandler.cs +++ b/LightlessSync/UI/Handlers/IdDisplayHandler.cs @@ -43,9 +43,9 @@ public class IdDisplayHandler if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) { var prevState = textIsUid; - if (_showIdForEntry.ContainsKey(group.GID)) + if (_showIdForEntry.TryGetValue(group.GID, out bool value)) { - prevState = _showIdForEntry[group.GID]; + prevState = value; } _showIdForEntry[group.GID] = !prevState; } diff --git a/LightlessSync/UI/Handlers/TagHandler.cs b/LightlessSync/UI/Handlers/TagHandler.cs index f721508..9790d89 100644 --- a/LightlessSync/UI/Handlers/TagHandler.cs +++ b/LightlessSync/UI/Handlers/TagHandler.cs @@ -17,61 +17,146 @@ public class TagHandler _serverConfigurationManager = serverConfigurationManager; } - public void AddTag(string tag) - { - _serverConfigurationManager.AddTag(tag); - } + /// + /// Creation of an pair tag + /// + /// Name of the tag + public void AddPairTag(string tag) => _serverConfigurationManager.AddPairTag(tag); - public void AddTagToPairedUid(string uid, string tagName) - { - _serverConfigurationManager.AddTagForUid(uid, tagName); - } + /// + /// Creation of an syncshell tag + /// + /// Name of the tag + public void AddSyncshellTag(string tag) => _serverConfigurationManager.AddSyncshellTag(tag); - public List GetAllTagsSorted() - { - return - [ + /// + /// Add pair to tag + /// + /// UID that will be added to tag/param> + /// Name of the tag + public void AddPairTagToPairedUid(string uid, string tagName) => _serverConfigurationManager.AddTagForUid(uid, tagName); + + /// + /// Add syncshell to tag + /// + /// Syncshell that will be added to tag/param> + /// Name of the tag + public void AddTagToSyncshell(string name, string tagName) => _serverConfigurationManager.AddTagForSyncshell(name, tagName); + + /// + /// Get all pair tags + /// + public List GetAllPairTagsSorted() => [ .. _serverConfigurationManager.GetServerAvailablePairTags() - .OrderBy(s => s, StringComparer.OrdinalIgnoreCase) + .Order(StringComparer.OrdinalIgnoreCase) , ]; - } - public HashSet GetOtherUidsForTag(string tag) - { - return _serverConfigurationManager.GetUidsForTag(tag); - } + /// + /// Get all syncshell tags + /// + public List GetAllSyncshellTagsSorted() => [ + .. _serverConfigurationManager.GetServerAvailableSyncshellTags() + .Order(StringComparer.OrdinalIgnoreCase) +, + ]; - public bool HasAnyTag(string uid) - { - return _serverConfigurationManager.HasTags(uid); - } + /// + /// Get all UIDs bound to an given tag + /// + /// Name of the tag + public HashSet GetOtherUidsForTag(string tag) => _serverConfigurationManager.GetUidsForPairTag(tag); - public bool HasTag(string uid, string tagName) - { - return _serverConfigurationManager.ContainsTag(uid, tagName); - } + /// + /// Get all syncshells bound to an given tag + /// + /// Name of the tag + public HashSet GetOtherSyncshellsForTag(string tag) => _serverConfigurationManager.GetNamesForSyncshellTag(tag); + + /// + /// Checking if the UID is connected to any tag + /// + /// Syncshell that needs to be checked + public bool HasAnyPairTag(string uid) => _serverConfigurationManager.HasPairTags(uid); + + /// + /// Checking if the syncshell is connected to the tag + /// + /// Syncshell that needs to be checked + public bool HasAnySyncshellTag(string name) => _serverConfigurationManager.HasSyncshellTags(name); + + /// + /// Checking if the UID is connected to the tag + /// + /// UID that needs to be checked + /// Name of the tag + public bool HasPairTag(string uid, string tagName) => _serverConfigurationManager.ContainsPairTag(uid, tagName); + + /// + /// Checking if the syncshell is connected to the tag + /// + /// Syncshell that needs to be checked + /// Name of the tag + public bool HasSyncshellTag(string name, string tagName) => _serverConfigurationManager.ContainsSyncshellTag(name, tagName); /// /// Is this tag opened in the paired clients UI? /// /// the tag /// open true/false - public bool IsTagOpen(string tag) + public bool IsTagOpen(string tag) => _serverConfigurationManager.ContainsOpenPairTag(tag); + + /// + /// Removal of Pair Tags from Storage + /// + /// Name of the tag + public void RemovePairTag(string tag) => _serverConfigurationManager.RemovePairTag(tag); + + /// + /// Removal of Syncshell Tags from Storage + /// + /// Name of the tag + public void RemoveSyncshellTag(string tag) => _serverConfigurationManager.RemoveSyncshellTag(tag); + + /// + /// Removal of UID in a Tag + /// + /// UID of user thats bound to the tag + /// Name of the tag + public void RemoveTagFromPairedUid(string uid, string tagName) => _serverConfigurationManager.RemoveTagForUid(uid, tagName); + + /// + /// Removal of Syncshell in a Tag + /// + /// Syncshell thats bound to the tag + /// Name of the tag + public void RemoveTagFromSyncshell(string name, string tagName) => _serverConfigurationManager.RemoveTagForSyncshell(name, tagName); + + /// + /// Rename of a pair tag + /// + /// Old pair tag name + /// New pair tag name + public void RenamePairTag(string oldName, string newName) { - return _serverConfigurationManager.ContainsOpenPairTag(tag); + _serverConfigurationManager.RenamePairTag(oldName, newName); } - public void RemoveTag(string tag) + /// + /// Rename of a syncshell tag + /// + /// Old syncshell tag name + /// New syncshell tag name + public void RenameSyncshellTag(string oldName, string newName) { - _serverConfigurationManager.RemoveTag(tag); - } - - public void RemoveTagFromPairedUid(string uid, string tagName) - { - _serverConfigurationManager.RemoveTagForUid(uid, tagName); + _serverConfigurationManager.RenameSyncshellTag(oldName, newName); } + /// + /// Changes the tag to open/close + /// + /// The Tag that will be modified + /// True/False public void SetTagOpen(string tag, bool open) { if (open)