diff --git a/LightlessAPI b/LightlessAPI index fd4cd52..5bfd21a 160000 --- a/LightlessAPI +++ b/LightlessAPI @@ -1 +1 @@ -Subproject commit fd4cd52d2e78c8a621e6b06149e69842bb5ff255 +Subproject commit 5bfd21aaa90817f14c9e2931e77b20f4276f16ed diff --git a/LightlessSync/Plugin.cs b/LightlessSync/Plugin.cs index b9a70fe..09f218e 100644 --- a/LightlessSync/Plugin.cs +++ b/LightlessSync/Plugin.cs @@ -230,7 +230,7 @@ public sealed class Plugin : IDalamudPlugin s.GetRequiredService(), s.GetRequiredService())); collection.AddScoped(); collection.AddScoped((s) => new BroadcastUI(s.GetRequiredService>(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService())); - collection.AddScoped((s) => new SyncshellFinderUI(s.GetRequiredService>(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService())); + collection.AddScoped((s) => new SyncshellFinderUI(s.GetRequiredService>(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService())); collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); diff --git a/LightlessSync/Services/BroadcastService.cs b/LightlessSync/Services/BroadcastService.cs index 09a9674..6d6409e 100644 --- a/LightlessSync/Services/BroadcastService.cs +++ b/LightlessSync/Services/BroadcastService.cs @@ -4,9 +4,7 @@ using LightlessSync.LightlessConfiguration; using LightlessSync.Services.Mediator; using LightlessSync.Utils; using LightlessSync.WebAPI; -using LightlessSync.WebAPI.SignalR; using Microsoft.AspNetCore.SignalR; -using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -16,7 +14,6 @@ public class BroadcastService : IHostedService, IMediatorSubscriber private readonly ILogger _logger; private readonly ApiController _apiController; private readonly LightlessMediator _mediator; - private readonly HubFactory _hubFactory; private readonly LightlessConfigService _config; private readonly DalamudUtilService _dalamudUtil; public LightlessMediator Mediator => _mediator; @@ -29,35 +26,37 @@ public class BroadcastService : IHostedService, IMediatorSubscriber private TimeSpan? _remainingTtl = null; private DateTime _lastTtlCheck = DateTime.MinValue; private DateTime _lastForcedDisableTime = DateTime.MinValue; - private static readonly TimeSpan DisableCooldown = TimeSpan.FromSeconds(5); + private static readonly TimeSpan _disableCooldown = TimeSpan.FromSeconds(5); public TimeSpan? RemainingTtl => _remainingTtl; public TimeSpan? RemainingCooldown { get { var elapsed = DateTime.UtcNow - _lastForcedDisableTime; - if (elapsed >= DisableCooldown) return null; - return DisableCooldown - elapsed; + if (elapsed >= _disableCooldown) return null; + return _disableCooldown - elapsed; } } - public BroadcastService(ILogger logger, LightlessMediator mediator, HubFactory hubFactory, LightlessConfigService config, DalamudUtilService dalamudUtil, ApiController apiController) + + public BroadcastService(ILogger logger, LightlessMediator mediator, LightlessConfigService config, DalamudUtilService dalamudUtil, ApiController apiController) { _logger = logger; _mediator = mediator; - _hubFactory = hubFactory; _config = config; _dalamudUtil = dalamudUtil; _apiController = apiController; } + private async Task RequireConnectionAsync(string context, Func action) { if (!_apiController.IsConnected) { - _logger.LogDebug($"{context} skipped, not connected"); + _logger.LogDebug(context + " skipped, not connected"); return; } await action().ConfigureAwait(false); } + public async Task StartAsync(CancellationToken cancellationToken) { _mediator.Subscribe(this, OnEnableBroadcast); @@ -65,7 +64,7 @@ public class BroadcastService : IHostedService, IMediatorSubscriber _mediator.Subscribe(this, OnTick); _apiController.OnConnected += () => _ = CheckLightfinderSupportAsync(cancellationToken); - _ = CheckLightfinderSupportAsync(cancellationToken); + //_ = CheckLightfinderSupportAsync(cancellationToken); } public Task StopAsync(CancellationToken cancellationToken) @@ -86,13 +85,12 @@ public class BroadcastService : IHostedService, IMediatorSubscriber if (cancellationToken.IsCancellationRequested) return; - var hub = _hubFactory.GetOrCreate(CancellationToken.None); var dummy = "0".PadLeft(64, '0'); - await hub.InvokeAsync("IsUserBroadcasting", dummy, cancellationToken); - await hub.InvokeAsync("SetBroadcastStatus", dummy, true, null, cancellationToken); - await hub.InvokeAsync("GetBroadcastTtl", dummy, cancellationToken); - await hub.InvokeAsync>("AreUsersBroadcasting", new[] { dummy }, cancellationToken); + await _apiController.IsUserBroadcasting(dummy).ConfigureAwait(false); + await _apiController.SetBroadcastStatus(dummy, true, null).ConfigureAwait(false); + await _apiController.GetBroadcastTtl(dummy).ConfigureAwait(false); + await _apiController.AreUsersBroadcasting([dummy]).ConfigureAwait(false); IsLightFinderAvailable = true; _logger.LogInformation("Lightfinder is available."); @@ -119,6 +117,7 @@ public class BroadcastService : IHostedService, IMediatorSubscriber _config.Current.BroadcastEnabled = false; _config.Current.BroadcastTtl = DateTime.MinValue; _config.Save(); + _mediator.Publish(new BroadcastStatusChangedMessage(false, null)); } } @@ -151,13 +150,13 @@ public class BroadcastService : IHostedService, IMediatorSubscriber _config.Save(); _mediator.Publish(new BroadcastStatusChangedMessage(false, null)); - Mediator.Publish(new EventMessage(new Services.Events.Event(nameof(BroadcastService), Services.Events.EventSeverity.Informational,$"Disabled Lightfinder for Player: {msg.HashedCid}"))); + Mediator.Publish(new EventMessage(new Services.Events.Event(nameof(BroadcastService), Services.Events.EventSeverity.Informational, $"Disabled Lightfinder for Player: {msg.HashedCid}"))); return; } _waitingForTtlFetch = true; - var ttl = await GetBroadcastTtlAsync(msg.HashedCid).ConfigureAwait(false); + TimeSpan? ttl = await GetBroadcastTtlAsync(msg.HashedCid).ConfigureAwait(false); if (ttl is { } remaining && remaining > TimeSpan.Zero) { @@ -325,8 +324,8 @@ public class BroadcastService : IHostedService, IMediatorSubscriber _syncedOnStartup = true; try { - var hashedCid = (await _dalamudUtil.GetCIDAsync().ConfigureAwait(false)).ToString().GetHash256(); - var ttl = await GetBroadcastTtlAsync(hashedCid).ConfigureAwait(false); + string hashedCid = (await _dalamudUtil.GetCIDAsync().ConfigureAwait(false)).ToString().GetHash256(); + TimeSpan? ttl = await GetBroadcastTtlAsync(hashedCid).ConfigureAwait(false); if (ttl is { } remaining && remaining > TimeSpan.Zero) { @@ -357,8 +356,8 @@ public class BroadcastService : IHostedService, IMediatorSubscriber return; } - var expiry = _config.Current.BroadcastTtl; - var remaining = expiry - DateTime.UtcNow; + DateTime expiry = _config.Current.BroadcastTtl; + TimeSpan remaining = expiry - DateTime.UtcNow; _remainingTtl = remaining > TimeSpan.Zero ? remaining : null; if (_remainingTtl == null) { diff --git a/LightlessSync/UI/BroadcastUI.cs b/LightlessSync/UI/BroadcastUI.cs index 6f9cdc8..4977fb6 100644 --- a/LightlessSync/UI/BroadcastUI.cs +++ b/LightlessSync/UI/BroadcastUI.cs @@ -104,7 +104,7 @@ namespace LightlessSync.UI try { - _allSyncshells = await _apiController.GroupsGetAll(); + _allSyncshells = await _apiController.GroupsGetAll().ConfigureAwait(false); } catch (Exception ex) { @@ -131,7 +131,7 @@ namespace LightlessSync.UI ImGuiHelpers.ScaledDummy(0.25f); } - if (ImGui.BeginTabBar("##MyTabBar")) + if (ImGui.BeginTabBar("##BroadcastTabs")) { if (ImGui.BeginTabItem("Lightfinder")) { diff --git a/LightlessSync/UI/CompactUI.cs b/LightlessSync/UI/CompactUI.cs index 247e440..d56f035 100644 --- a/LightlessSync/UI/CompactUI.cs +++ b/LightlessSync/UI/CompactUI.cs @@ -141,7 +141,7 @@ public class CompactUi : WindowMediatorSubscriberBase }, }; - _drawFolders = [.. GetDrawFolders()]; + _drawFolders = [.. DrawFolders]; #if DEBUG string dev = "Dev Build"; @@ -158,7 +158,7 @@ public class CompactUi : WindowMediatorSubscriberBase Mediator.Subscribe(this, (_) => UiSharedService_GposeEnd()); Mediator.Subscribe(this, (msg) => _currentDownloads[msg.DownloadId] = msg.DownloadStatus); Mediator.Subscribe(this, (msg) => _currentDownloads.TryRemove(msg.DownloadId, out _)); - Mediator.Subscribe(this, (msg) => _drawFolders = GetDrawFolders().ToList()); + Mediator.Subscribe(this, (msg) => _drawFolders = DrawFolders.ToList()); Flags |= ImGuiWindowFlags.NoDocking; @@ -561,6 +561,7 @@ public class CompactUi : WindowMediatorSubscriberBase if ((isOverTriHold || isOverVRAMUsage) && _playerPerformanceConfig.Current.WarnOnExceedingThresholds) { ImGui.SameLine(); + ImGui.SetCursorPosY(cursorY + 15f); _uiSharedService.IconText(FontAwesomeIcon.ExclamationTriangle, UIColors.Get("LightlessYellow")); string warningMessage = ""; @@ -611,168 +612,166 @@ public class CompactUi : WindowMediatorSubscriberBase } } - private IEnumerable GetDrawFolders() + private IEnumerable DrawFolders { - List drawFolders = []; - - var allPairs = _pairManager.PairsWithGroups - .ToDictionary(k => k.Key, k => k.Value); - var filteredPairs = allPairs - .Where(p => - { - if (_tabMenu.Filter.IsNullOrEmpty()) return true; - return p.Key.UserData.AliasOrUID.Contains(_tabMenu.Filter, StringComparison.OrdinalIgnoreCase) || - (p.Key.GetNote()?.Contains(_tabMenu.Filter, StringComparison.OrdinalIgnoreCase) ?? false) || - (p.Key.PlayerName?.Contains(_tabMenu.Filter, StringComparison.OrdinalIgnoreCase) ?? false); - }) - .ToDictionary(k => k.Key, k => k.Value); - - string? AlphabeticalSort(KeyValuePair> u) - => (_configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(u.Key.PlayerName) - ? (_configService.Current.PreferNotesOverNamesForVisible ? u.Key.GetNote() : u.Key.PlayerName) - : (u.Key.GetNote() ?? u.Key.UserData.AliasOrUID)); - bool FilterOnlineOrPausedSelf(KeyValuePair> u) - => (u.Key.IsOnline || (!u.Key.IsOnline && !_configService.Current.ShowOfflineUsersSeparately) - || u.Key.UserPair.OwnPermissions.IsPaused()); - Dictionary> BasicSortedDictionary(IEnumerable>> u) - => u.OrderByDescending(u => u.Key.IsVisible) - .ThenByDescending(u => u.Key.IsOnline) - .ThenBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase) - .ToDictionary(u => u.Key, u => u.Value); - ImmutableList ImmutablePairList(IEnumerable>> u) - => u.Select(k => k.Key).ToImmutableList(); - 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.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.HasAnyPairTag(u.Key.UserData.UID); - bool FilterNotTaggedSyncshells(GroupFullInfoDto group) - => (!_tagHandler.HasAnySyncshellTag(group.GID) && !_configService.Current.ShowGroupedSyncshellsInAll) || true; - bool FilterOfflineUsers(KeyValuePair> u) - => ((u.Key.IsDirectlyPaired && _configService.Current.ShowSyncshellOfflineUsersSeparately) - || !_configService.Current.ShowSyncshellOfflineUsersSeparately) - && (!u.Key.IsOneSidedPair || u.Value.Any()) && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused(); - bool FilterOfflineSyncshellUsers(KeyValuePair> u) - => (!u.Key.IsDirectlyPaired && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused()); - - - if (_configService.Current.ShowVisibleUsersSeparately) + get { - var allVisiblePairs = ImmutablePairList(allPairs - .Where(FilterVisibleUsers)); - var filteredVisiblePairs = BasicSortedDictionary(filteredPairs - .Where(FilterVisibleUsers)); + var drawFolders = new List(); + var filter = _tabMenu.Filter; - drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomVisibleTag, filteredVisiblePairs, allVisiblePairs)); - } + var allPairs = _pairManager.PairsWithGroups.ToDictionary(k => k.Key, k => k.Value); + var filteredPairs = allPairs.Where(p => PassesFilter(p.Key, filter)).ToDictionary(k => k.Key, k => k.Value); - List groupFolders = new(); - - foreach (var group in _pairManager.GroupPairs.Select(g => g.Key).OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase)) - { - GetGroups(allPairs, filteredPairs, group, out ImmutableList allGroupPairs, out Dictionary> filteredGroupPairs); - if (FilterNotTaggedSyncshells(group)) + //Filter of online/visible pairs + if (_configService.Current.ShowVisibleUsersSeparately) { - groupFolders.Add(_drawEntityFactory.CreateDrawGroupFolder(group, filteredGroupPairs, allGroupPairs)); + var allVisiblePairs = ImmutablePairList(allPairs.Where(p => FilterVisibleUsers(p.Key))); + var filteredVisiblePairs = BasicSortedDictionary(filteredPairs.Where(p => FilterVisibleUsers(p.Key))); + + drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomVisibleTag, filteredVisiblePairs, allVisiblePairs)); } - } - if (_configService.Current.GroupUpSyncshells) - drawFolders.Add(new DrawGroupedGroupFolder(groupFolders, _tagHandler, _uiSharedService, _selectSyncshellForTagUi, _renameSyncshellTagUi, "")); - else - drawFolders.AddRange(groupFolders); - - var tags = _tagHandler.GetAllPairTagsSorted(); - foreach (var tag in tags) - { - var allTagPairs = ImmutablePairList(allPairs - .Where(u => FilterTagUsers(u, tag))); - var filteredTagPairs = BasicSortedDictionary(filteredPairs - .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 = []; + //Filter of not foldered syncshells + var groupFolders = new List(); 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); + + if (FilterNotTaggedSyncshells(group)) { - GetGroups(allPairs, filteredPairs, group, out ImmutableList allGroupPairs, out Dictionary> filteredGroupPairs); - syncshellFolderTags.Add(_drawEntityFactory.CreateDrawGroupFolder($"tag_{group.GID}", group, filteredGroupPairs, allGroupPairs)); + groupFolders.Add(_drawEntityFactory.CreateDrawGroupFolder(group, filteredGroupPairs, allGroupPairs)); } } - if (syncshellFolderTags.Count > 0) + //Filter of grouped up syncshells (All Syncshells Folder) + if (_configService.Current.GroupUpSyncshells) + drawFolders.Add(new DrawGroupedGroupFolder(groupFolders, _tagHandler, _uiSharedService, + _selectSyncshellForTagUi, _renameSyncshellTagUi, "")); + else + drawFolders.AddRange(groupFolders); + + //Filter of grouped/foldered pairs + foreach (var tag in _tagHandler.GetAllPairTagsSorted()) { - drawFolders.Add(new DrawGroupedGroupFolder(syncshellFolderTags, _tagHandler, _uiSharedService, _selectSyncshellForTagUi, _renameSyncshellTagUi, syncshelltag)); + var allTagPairs = ImmutablePairList(allPairs.Where(p => FilterTagUsers(p.Key, tag))); + var filteredTagPairs = BasicSortedDictionary(filteredPairs.Where(p => FilterTagUsers(p.Key, tag) && FilterOnlineOrPausedSelf(p.Key))); + + drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(tag, filteredTagPairs, allTagPairs)); } - } - var allOnlineNotTaggedPairs = ImmutablePairList(allPairs - .Where(FilterNotTaggedUsers)); - var onlineNotTaggedPairs = BasicSortedDictionary(filteredPairs - .Where(u => FilterNotTaggedUsers(u) && FilterOnlineOrPausedSelf(u))); - - drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder((_configService.Current.ShowOfflineUsersSeparately ? TagHandler.CustomOnlineTag : TagHandler.CustomAllTag), - onlineNotTaggedPairs, allOnlineNotTaggedPairs)); - - if (_configService.Current.ShowOfflineUsersSeparately) - { - var allOfflinePairs = ImmutablePairList(allPairs - .Where(FilterOfflineUsers)); - var filteredOfflinePairs = BasicSortedDictionary(filteredPairs - .Where(FilterOfflineUsers)); - - drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineTag, filteredOfflinePairs, allOfflinePairs)); - if (_configService.Current.ShowSyncshellOfflineUsersSeparately) + //Filter of grouped/foldered syncshells + foreach (var syncshellTag in _tagHandler.GetAllSyncshellTagsSorted()) { - var allOfflineSyncshellUsers = ImmutablePairList(allPairs - .Where(FilterOfflineSyncshellUsers)); - var filteredOfflineSyncshellUsers = BasicSortedDictionary(filteredPairs - .Where(FilterOfflineSyncshellUsers)); - - drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineSyncshellTag, - filteredOfflineSyncshellUsers, - allOfflineSyncshellUsers)); - } - } - - drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomUnpairedTag, - BasicSortedDictionary(filteredPairs.Where(u => u.Key.IsOneSidedPair)), - 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 => + var syncshellFolderTags = new List(); + foreach (var group in _pairManager.GroupPairs.Select(g => g.Key).OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase)) { - 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 (_tagHandler.HasSyncshellTag(group.GID, syncshellTag)) { - if (info.IsModerator()) return 1; - if (info.IsPinned()) return 2; + GetGroups(allPairs, filteredPairs, group, + out ImmutableList allGroupPairs, + out Dictionary> filteredGroupPairs); + + syncshellFolderTags.Add(_drawEntityFactory.CreateDrawGroupFolder($"tag_{group.GID}", group, filteredGroupPairs, allGroupPairs)); } - return u.Key.IsVisible ? 3 : 4; - }) - .ThenBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase) - .ToDictionary(k => k.Key, k => k.Value); + } + + drawFolders.Add(new DrawGroupedGroupFolder(syncshellFolderTags, _tagHandler, _uiSharedService, _selectSyncshellForTagUi, _renameSyncshellTagUi, syncshellTag)); + } + + //Filter of not grouped/foldered and offline pairs + var allOnlineNotTaggedPairs = ImmutablePairList(allPairs.Where(p => FilterNotTaggedUsers(p.Key))); + var onlineNotTaggedPairs = BasicSortedDictionary(filteredPairs.Where(p => FilterNotTaggedUsers(p.Key) && FilterOnlineOrPausedSelf(p.Key))); + + drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder((_configService.Current.ShowOfflineUsersSeparately ? TagHandler.CustomOnlineTag : TagHandler.CustomAllTag), onlineNotTaggedPairs, allOnlineNotTaggedPairs)); + + if (_configService.Current.ShowOfflineUsersSeparately) + { + var allOfflinePairs = ImmutablePairList(allPairs.Where(p => FilterOfflineUsers(p.Key, p.Value))); + var filteredOfflinePairs = BasicSortedDictionary(filteredPairs.Where(p => FilterOfflineUsers(p.Key, p.Value))); + + drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineTag, filteredOfflinePairs, allOfflinePairs)); + + if (_configService.Current.ShowSyncshellOfflineUsersSeparately) + { + var allOfflineSyncshellUsers = ImmutablePairList(allPairs.Where(p => FilterOfflineSyncshellUsers(p.Key))); + var filteredOfflineSyncshellUsers = BasicSortedDictionary(filteredPairs.Where(p => FilterOfflineSyncshellUsers(p.Key))); + + drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineSyncshellTag, filteredOfflineSyncshellUsers, allOfflineSyncshellUsers)); + } + } + + //Unpaired + drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomUnpairedTag, + BasicSortedDictionary(filteredPairs.Where(p => p.Key.IsOneSidedPair)), + ImmutablePairList(allPairs.Where(p => p.Key.IsOneSidedPair)))); + + return drawFolders; } } + private static bool PassesFilter(Pair pair, string filter) + { + if (string.IsNullOrEmpty(filter)) return true; + + return pair.UserData.AliasOrUID.Contains(filter, StringComparison.OrdinalIgnoreCase) || (pair.GetNote()?.Contains(filter, StringComparison.OrdinalIgnoreCase) ?? false) || (pair.PlayerName?.Contains(filter, StringComparison.OrdinalIgnoreCase) ?? false); + } + + private string AlphabeticalSortKey(Pair pair) + { + if (_configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(pair.PlayerName)) + { + return _configService.Current.PreferNotesOverNamesForVisible ? (pair.GetNote() ?? string.Empty) : pair.PlayerName; + } + + return pair.GetNote() ?? pair.UserData.AliasOrUID; + } + + private bool FilterOnlineOrPausedSelf(Pair pair) => pair.IsOnline || (!pair.IsOnline && !_configService.Current.ShowOfflineUsersSeparately) || pair.UserPair.OwnPermissions.IsPaused(); + + private bool FilterVisibleUsers(Pair pair) => pair.IsVisible && (_configService.Current.ShowSyncshellUsersInVisible || pair.IsDirectlyPaired); + + private bool FilterTagUsers(Pair pair, string tag) => pair.IsDirectlyPaired && !pair.IsOneSidedPair && _tagHandler.HasPairTag(pair.UserData.UID, tag); + + private static bool FilterGroupUsers(List groups, GroupFullInfoDto group) => groups.Exists(g => string.Equals(g.GID, group.GID, StringComparison.Ordinal)); + + private bool FilterNotTaggedUsers(Pair pair) => pair.IsDirectlyPaired && !pair.IsOneSidedPair && !_tagHandler.HasAnyPairTag(pair.UserData.UID); + + private bool FilterNotTaggedSyncshells(GroupFullInfoDto group) => !_tagHandler.HasAnySyncshellTag(group.GID) || _configService.Current.ShowGroupedSyncshellsInAll; + + private bool FilterOfflineUsers(Pair pair, List groups) => ((pair.IsDirectlyPaired && _configService.Current.ShowSyncshellOfflineUsersSeparately) || !_configService.Current.ShowSyncshellOfflineUsersSeparately) && (!pair.IsOneSidedPair || groups.Count != 0) && !pair.IsOnline && !pair.UserPair.OwnPermissions.IsPaused(); + + private static bool FilterOfflineSyncshellUsers(Pair pair) => !pair.IsDirectlyPaired && !pair.IsOnline && !pair.UserPair.OwnPermissions.IsPaused(); + + private Dictionary> BasicSortedDictionary(IEnumerable>> pairs) => pairs.OrderByDescending(u => u.Key.IsVisible).ThenByDescending(u => u.Key.IsOnline).ThenBy(u => AlphabeticalSortKey(u.Key), StringComparer.OrdinalIgnoreCase).ToDictionary(u => u.Key, u => u.Value); + + private static ImmutableList ImmutablePairList(IEnumerable>> pairs) => [.. pairs.Select(k => k.Key)]; + + private void GetGroups(Dictionary> allPairs, + Dictionary> filteredPairs, + GroupFullInfoDto group, + out ImmutableList allGroupPairs, + out Dictionary> filteredGroupPairs) + { + allGroupPairs = ImmutablePairList(allPairs + .Where(u => FilterGroupUsers(u.Value, group))); + + filteredGroupPairs = filteredPairs + .Where(u => FilterGroupUsers( u.Value, group) && FilterOnlineOrPausedSelf(u.Key)) + .OrderByDescending(u => u.Key.IsOnline) + .ThenBy(u => + { + if (string.Equals(u.Key.UserData.UID, group.OwnerUID, StringComparison.Ordinal)) return 0; + if (group.GroupPairUserInfos.TryGetValue(u.Key.UserData.UID, out var info)) + { + if (info.IsModerator()) return 1; + if (info.IsPinned()) return 2; + } + return u.Key.IsVisible ? 3 : 4; + }) + .ThenBy(u => AlphabeticalSortKey(u.Key), StringComparer.OrdinalIgnoreCase) + .ToDictionary(k => k.Key, k => k.Value); + } + private string GetServerError() { return _apiController.ServerState switch diff --git a/LightlessSync/UI/Components/DrawGroupedGroupFolder.cs b/LightlessSync/UI/Components/DrawGroupedGroupFolder.cs index d1876ac..2aa3d5c 100644 --- a/LightlessSync/UI/Components/DrawGroupedGroupFolder.cs +++ b/LightlessSync/UI/Components/DrawGroupedGroupFolder.cs @@ -34,8 +34,6 @@ public class DrawGroupedGroupFolder : IDrawFolder public void Draw() { - if (!_groups.Any()) return; - string _id = "__folder_syncshells"; if (_tag != "") { diff --git a/LightlessSync/UI/EditProfileUi.cs b/LightlessSync/UI/EditProfileUi.cs index 753c790..6c787b3 100644 --- a/LightlessSync/UI/EditProfileUi.cs +++ b/LightlessSync/UI/EditProfileUi.cs @@ -4,14 +4,17 @@ using Dalamud.Interface.Colors; using Dalamud.Interface.ImGuiFileDialog; using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; using LightlessSync.API.Data; using LightlessSync.API.Dto.User; using LightlessSync.Services; using LightlessSync.Services.Mediator; +using LightlessSync.Utils; using LightlessSync.WebAPI; using Microsoft.Extensions.Logging; using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; +using System.Numerics; namespace LightlessSync.UI; @@ -30,6 +33,15 @@ public class EditProfileUi : WindowMediatorSubscriberBase private bool _showFileDialogError = false; private bool _wasOpen; + private bool vanityInitialized; // useless for now + private bool textEnabled; + private bool glowEnabled; + private Vector4 textColor; + private Vector4 glowColor; + + private record VanityState(bool TextEnabled, bool GlowEnabled, Vector4 TextColor, Vector4 GlowColor); + private VanityState _savedVanity; + public EditProfileUi(ILogger logger, LightlessMediator mediator, ApiController apiController, UiSharedService uiSharedService, FileDialogManager fileDialogManager, LightlessProfileManager lightlessProfileManager, PerformanceCollectorService performanceCollectorService) @@ -38,8 +50,8 @@ public class EditProfileUi : WindowMediatorSubscriberBase IsOpen = false; this.SizeConstraints = new() { - MinimumSize = new(768, 512), - MaximumSize = new(768, 2000) + MinimumSize = new(850, 640), + MaximumSize = new(850, 700) }; _apiController = apiController; _uiSharedService = uiSharedService; @@ -57,172 +69,314 @@ public class EditProfileUi : WindowMediatorSubscriberBase _pfpTextureWrap = null; } }); + Mediator.Subscribe(this, msg => + { + LoadVanity(); + }); + } + + void DrawNoteLine(string icon, Vector4 color, string text) + { + _uiSharedService.MediumText(icon, color); + ImGui.SameLine(); + + ImGui.SetCursorPosY(ImGui.GetCursorPosY() + 3); + ImGui.TextWrapped(text); + } + + private void LoadVanity() + { + textEnabled = !string.IsNullOrEmpty(_apiController.TextColorHex); + glowEnabled = !string.IsNullOrEmpty(_apiController.TextGlowColorHex); + + textColor = textEnabled ? UIColors.HexToRgba(_apiController.TextColorHex!) : Vector4.One; + glowColor = glowEnabled ? UIColors.HexToRgba(_apiController.TextGlowColorHex!) : Vector4.Zero; + + _savedVanity = new VanityState(textEnabled, glowEnabled, textColor, glowColor); + vanityInitialized = true; } protected override void DrawInternal() { - _uiSharedService.BigText("Current Profile (as saved on server)"); + _uiSharedService.UnderlinedBigText("Notes and Rules for Profiles", UIColors.Get("LightlessYellow")); + ImGui.Dummy(new Vector2(5)); + + ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(2, 2)); + + DrawNoteLine("# ", UIColors.Get("LightlessBlue"), "All users that are paired and unpaused with you will be able to see your profile picture and description."); + DrawNoteLine("! ", UIColors.Get("LightlessYellow"), "Other users have the possibility to report your profile for breaking the rules."); + DrawNoteLine("!!! ", UIColors.Get("DimRed"), "AVOID: Anything as profile image that can be considered highly illegal or obscene (bestiality, anything that could be considered a sexual act with a minor (that includes Lalafells), etc.)"); + DrawNoteLine("!!! ", UIColors.Get("DimRed"), "AVOID: Slurs of any kind in the description that can be considered highly offensive"); + DrawNoteLine("! ", UIColors.Get("LightlessYellow"), "In case of valid reports from other users this can lead to disabling your profile forever or terminating your Lightless account indefinitely."); + DrawNoteLine("! ", UIColors.Get("LightlessYellow"), "Judgement of your profile validity from reports through staff is not up to debate and the decisions to disable your profile/account permanent."); + DrawNoteLine("! ", UIColors.Get("LightlessBlue"), "If your profile picture or profile description could be considered NSFW, enable the toggle in profile settings."); + + ImGui.PopStyleVar(); + + ImGui.Dummy(new Vector2(3)); var profile = _lightlessProfileManager.GetLightlessProfile(new UserData(_apiController.UID)); - if (profile.IsFlagged) - { - UiSharedService.ColorTextWrapped(profile.Description, ImGuiColors.DalamudRed); - return; - } - - if (!_profileImage.SequenceEqual(profile.ImageData.Value)) - { - _profileImage = profile.ImageData.Value; - _pfpTextureWrap?.Dispose(); - _pfpTextureWrap = _uiSharedService.LoadImage(_profileImage); - } - - if (!string.Equals(_profileDescription, profile.Description, StringComparison.OrdinalIgnoreCase)) - { - _profileDescription = profile.Description; - _descriptionText = _profileDescription; - } - - if (_pfpTextureWrap != null) - { - ImGui.Image(_pfpTextureWrap.Handle, ImGuiHelpers.ScaledVector2(_pfpTextureWrap.Width, _pfpTextureWrap.Height)); - } - - var spacing = ImGui.GetStyle().ItemSpacing.X; - ImGuiHelpers.ScaledRelativeSameLine(256, spacing); - using (_uiSharedService.GameFont.Push()) - { - var descriptionTextSize = ImGui.CalcTextSize(profile.Description, wrapWidth: 256f); - var childFrame = ImGuiHelpers.ScaledVector2(256 + ImGui.GetStyle().WindowPadding.X + ImGui.GetStyle().WindowBorderSize, 256); - if (descriptionTextSize.Y > childFrame.Y) + if (ImGui.BeginTabBar("##EditProfileTabs")) + { + if (ImGui.BeginTabItem("Current Profile")) { - _adjustedForScollBarsOnlineProfile = true; - } - else - { - _adjustedForScollBarsOnlineProfile = false; - } - childFrame = childFrame with - { - X = childFrame.X + (_adjustedForScollBarsOnlineProfile ? ImGui.GetStyle().ScrollbarSize : 0), - }; - if (ImGui.BeginChildFrame(101, childFrame)) - { - UiSharedService.TextWrapped(profile.Description); - } - ImGui.EndChildFrame(); - } + _uiSharedService.MediumText("Current Profile (as saved on server)", UIColors.Get("LightlessPurple")); + ImGui.Dummy(new Vector2(5)); - var nsfw = profile.IsNSFW; - ImGui.BeginDisabled(); - ImGui.Checkbox("Is NSFW", ref nsfw); - ImGui.EndDisabled(); - - ImGui.Separator(); - _uiSharedService.BigText("Notes and Rules for Profiles"); - - ImGui.TextWrapped($"- All users that are paired and unpaused with you will be able to see your profile picture and description.{Environment.NewLine}" + - $"- Other users have the possibility to report your profile for breaking the rules.{Environment.NewLine}" + - $"- !!! AVOID: anything as profile image that can be considered highly illegal or obscene (bestiality, anything that could be considered a sexual act with a minor (that includes Lalafells), etc.){Environment.NewLine}" + - $"- !!! AVOID: slurs of any kind in the description that can be considered highly offensive{Environment.NewLine}" + - $"- In case of valid reports from other users this can lead to disabling your profile forever or terminating your Lightless account indefinitely.{Environment.NewLine}" + - $"- Judgement of your profile validity from reports through staff is not up to debate and the decisions to disable your profile/account permanent.{Environment.NewLine}" + - $"- If your profile picture or profile description could be considered NSFW, enable the toggle below."); - ImGui.Separator(); - _uiSharedService.BigText("Profile Settings"); - - if (_uiSharedService.IconTextButton(FontAwesomeIcon.FileUpload, "Upload new profile picture")) - { - _fileDialogManager.OpenFileDialog("Select new Profile picture", ".png", (success, file) => - { - if (!success) return; - _ = Task.Run(async () => + if (profile.IsFlagged) { - var fileContent = File.ReadAllBytes(file); - using MemoryStream ms = new(fileContent); - var format = await Image.DetectFormatAsync(ms).ConfigureAwait(false); - if (!format.FileExtensions.Contains("png", StringComparer.OrdinalIgnoreCase)) + UiSharedService.ColorTextWrapped(profile.Description, ImGuiColors.DalamudRed); + return; + } + + if (!_profileImage.SequenceEqual(profile.ImageData.Value)) + { + _profileImage = profile.ImageData.Value; + _pfpTextureWrap?.Dispose(); + _pfpTextureWrap = _uiSharedService.LoadImage(_profileImage); + } + + if (!string.Equals(_profileDescription, profile.Description, StringComparison.OrdinalIgnoreCase)) + { + _profileDescription = profile.Description; + _descriptionText = _profileDescription; + } + + if (_pfpTextureWrap != null) + { + ImGui.Image(_pfpTextureWrap.Handle, ImGuiHelpers.ScaledVector2(_pfpTextureWrap.Width, _pfpTextureWrap.Height)); + } + + var spacing = ImGui.GetStyle().ItemSpacing.X; + ImGuiHelpers.ScaledRelativeSameLine(256, spacing); + using (_uiSharedService.GameFont.Push()) + { + var descriptionTextSize = ImGui.CalcTextSize(profile.Description, wrapWidth: 256f); + var childFrame = ImGuiHelpers.ScaledVector2(256 + ImGui.GetStyle().WindowPadding.X + ImGui.GetStyle().WindowBorderSize, 256); + if (descriptionTextSize.Y > childFrame.Y) { - _showFileDialogError = true; - return; + _adjustedForScollBarsOnlineProfile = true; } - using var image = Image.Load(fileContent); - - if (image.Width > 256 || image.Height > 256 || (fileContent.Length > 250 * 1024)) + else { - _showFileDialogError = true; - return; + _adjustedForScollBarsOnlineProfile = false; } + childFrame = childFrame with + { + X = childFrame.X + (_adjustedForScollBarsOnlineProfile ? ImGui.GetStyle().ScrollbarSize : 0), + }; + if (ImGui.BeginChildFrame(101, childFrame)) + { + UiSharedService.TextWrapped(profile.Description); + } + ImGui.EndChildFrame(); + } - _showFileDialogError = false; - await _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, Convert.ToBase64String(fileContent), Description: null)) - .ConfigureAwait(false); - }); - }); - } - UiSharedService.AttachToolTip("Select and upload a new profile picture"); - ImGui.SameLine(); - if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear uploaded profile picture")) - { - _ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, "", Description: null)); - } - UiSharedService.AttachToolTip("Clear your currently uploaded profile picture"); - if (_showFileDialogError) - { - UiSharedService.ColorTextWrapped("The profile picture must be a PNG file with a maximum height and width of 256px and 250KiB size", ImGuiColors.DalamudRed); - } - var isNsfw = profile.IsNSFW; - if (ImGui.Checkbox("Profile is NSFW", ref isNsfw)) - { - _ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, isNsfw, ProfilePictureBase64: null, Description: null)); - } - _uiSharedService.DrawHelpText("If your profile description or image can be considered NSFW, toggle this to ON"); - var widthTextBox = 400; - var posX = ImGui.GetCursorPosX(); - ImGui.TextUnformatted($"Description {_descriptionText.Length}/1500"); - ImGui.SetCursorPosX(posX); - ImGuiHelpers.ScaledRelativeSameLine(widthTextBox, ImGui.GetStyle().ItemSpacing.X); - ImGui.TextUnformatted("Preview (approximate)"); - using (_uiSharedService.GameFont.Push()) - ImGui.InputTextMultiline("##description", ref _descriptionText, 1500, ImGuiHelpers.ScaledVector2(widthTextBox, 200)); + var nsfw = profile.IsNSFW; + ImGui.BeginDisabled(); + ImGui.Checkbox("Is NSFW", ref nsfw); + ImGui.EndDisabled(); - ImGui.SameLine(); - - using (_uiSharedService.GameFont.Push()) - { - var descriptionTextSizeLocal = ImGui.CalcTextSize(_descriptionText, wrapWidth: 256f); - var childFrameLocal = ImGuiHelpers.ScaledVector2(256 + ImGui.GetStyle().WindowPadding.X + ImGui.GetStyle().WindowBorderSize, 200); - if (descriptionTextSizeLocal.Y > childFrameLocal.Y) - { - _adjustedForScollBarsLocalProfile = true; + _uiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f); + ImGui.EndTabItem(); } - else - { - _adjustedForScollBarsLocalProfile = false; - } - childFrameLocal = childFrameLocal with - { - X = childFrameLocal.X + (_adjustedForScollBarsLocalProfile ? ImGui.GetStyle().ScrollbarSize : 0), - }; - if (ImGui.BeginChildFrame(102, childFrameLocal)) - { - UiSharedService.TextWrapped(_descriptionText); - } - ImGui.EndChildFrame(); - } - if (_uiSharedService.IconTextButton(FontAwesomeIcon.Save, "Save Description")) - { - _ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, ProfilePictureBase64: null, _descriptionText)); + if (ImGui.BeginTabItem("Profile Settings")) + { + _uiSharedService.MediumText("Profile Settings", UIColors.Get("LightlessPurple")); + ImGui.Dummy(new Vector2(5)); + + if (_uiSharedService.IconTextButton(FontAwesomeIcon.FileUpload, "Upload new profile picture")) + { + _fileDialogManager.OpenFileDialog("Select new Profile picture", ".png", (success, file) => + { + if (!success) return; + _ = Task.Run(async () => + { + var fileContent = File.ReadAllBytes(file); + using MemoryStream ms = new(fileContent); + var format = await Image.DetectFormatAsync(ms).ConfigureAwait(false); + if (!format.FileExtensions.Contains("png", StringComparer.OrdinalIgnoreCase)) + { + _showFileDialogError = true; + return; + } + using var image = Image.Load(fileContent); + + if (image.Width > 256 || image.Height > 256 || (fileContent.Length > 250 * 1024)) + { + _showFileDialogError = true; + return; + } + + _showFileDialogError = false; + await _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, Convert.ToBase64String(fileContent), Description: null)) + .ConfigureAwait(false); + }); + }); + } + UiSharedService.AttachToolTip("Select and upload a new profile picture"); + ImGui.SameLine(); + if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear uploaded profile picture")) + { + _ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, "", Description: null)); + } + UiSharedService.AttachToolTip("Clear your currently uploaded profile picture"); + if (_showFileDialogError) + { + UiSharedService.ColorTextWrapped("The profile picture must be a PNG file with a maximum height and width of 256px and 250KiB size", ImGuiColors.DalamudRed); + } + var isNsfw = profile.IsNSFW; + if (ImGui.Checkbox("Profile is NSFW", ref isNsfw)) + { + _ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, isNsfw, ProfilePictureBase64: null, Description: null)); + } + _uiSharedService.DrawHelpText("If your profile description or image can be considered NSFW, toggle this to ON"); + var widthTextBox = 400; + var posX = ImGui.GetCursorPosX(); + ImGui.TextUnformatted($"Description {_descriptionText.Length}/1500"); + ImGui.SetCursorPosX(posX); + ImGuiHelpers.ScaledRelativeSameLine(widthTextBox, ImGui.GetStyle().ItemSpacing.X); + ImGui.TextUnformatted("Preview (approximate)"); + using (_uiSharedService.GameFont.Push()) + ImGui.InputTextMultiline("##description", ref _descriptionText, 1500, ImGuiHelpers.ScaledVector2(widthTextBox, 200)); + + ImGui.SameLine(); + + using (_uiSharedService.GameFont.Push()) + { + var descriptionTextSizeLocal = ImGui.CalcTextSize(_descriptionText, wrapWidth: 256f); + var childFrameLocal = ImGuiHelpers.ScaledVector2(256 + ImGui.GetStyle().WindowPadding.X + ImGui.GetStyle().WindowBorderSize, 200); + if (descriptionTextSizeLocal.Y > childFrameLocal.Y) + { + _adjustedForScollBarsLocalProfile = true; + } + else + { + _adjustedForScollBarsLocalProfile = false; + } + childFrameLocal = childFrameLocal with + { + X = childFrameLocal.X + (_adjustedForScollBarsLocalProfile ? ImGui.GetStyle().ScrollbarSize : 0), + }; + if (ImGui.BeginChildFrame(102, childFrameLocal)) + { + UiSharedService.TextWrapped(_descriptionText); + } + ImGui.EndChildFrame(); + } + + if (_uiSharedService.IconTextButton(FontAwesomeIcon.Save, "Save Description")) + { + _ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, ProfilePictureBase64: null, _descriptionText)); + } + UiSharedService.AttachToolTip("Sets your profile description text"); + ImGui.SameLine(); + if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear Description")) + { + _ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, ProfilePictureBase64: null, "")); + } + UiSharedService.AttachToolTip("Clears your profile description text"); + + ImGui.EndTabItem(); + } + + if (ImGui.BeginTabItem("Vanity Settings")) + { + _uiSharedService.MediumText("Supporter Vanity Settings", UIColors.Get("LightlessPurple")); + ImGui.Dummy(new Vector2(4)); + DrawNoteLine("# ", UIColors.Get("LightlessPurple"), "Must be a supporter through Patreon/Ko-fi to access these settings."); + + var hasVanity = _apiController.HasVanity; + + if (!hasVanity) + { + UiSharedService.ColorTextWrapped("You do not currently have vanity access. Become a supporter to unlock these features.", UIColors.Get("DimRed")); + ImGui.Dummy(new Vector2(8)); + ImGui.BeginDisabled(); + } + + _uiSharedService.ColoredSeparator(UIColors.Get("LightlessPurpleDefault"), 1.5f); + _uiSharedService.MediumText("Colored UID", UIColors.Get("LightlessPurple")); + ImGui.Dummy(new Vector2(5)); + + var font = UiBuilder.MonoFont; + var playerUID = _apiController.UID; + var playerDisplay = _apiController.DisplayName; + + var previewTextColor = textEnabled ? textColor : Vector4.One; + var previewGlowColor = glowEnabled ? glowColor : Vector4.Zero; + + var seString = SeStringUtils.BuildFormattedPlayerName(playerDisplay, previewTextColor, previewGlowColor); + + using (ImRaii.PushFont(font)) + { + var offsetX = 10f; + var pos = ImGui.GetCursorScreenPos() + new Vector2(offsetX, 0); + var size = ImGui.CalcTextSize(seString.TextValue); + + var padding = new Vector2(6, 3); + var rectMin = pos - padding; + var rectMax = pos + size + padding; + + var bgColor = new Vector4(0.15f, 0.15f, 0.15f, 1f); + var borderColor = UIColors.Get("LightlessPurple"); + + var drawList = ImGui.GetWindowDrawList(); + drawList.AddRectFilled(rectMin, rectMax, ImGui.GetColorU32(bgColor), 6.0f); + drawList.AddRect(rectMin, rectMax, ImGui.GetColorU32(borderColor), 6.0f, ImDrawFlags.None, 1.5f); + + SeStringUtils.RenderSeStringWithHitbox(seString, pos, font); + } + + const float colorPickAlign = 90f; + + DrawNoteLine("- ", UIColors.Get("LightlessPurple"), "Text Color"); + ImGui.SameLine(colorPickAlign); + ImGui.Checkbox("##toggleTextColor", ref textEnabled); + ImGui.SameLine(); + ImGui.BeginDisabled(!textEnabled); + ImGui.ColorEdit4($"##color_text", ref textColor, ImGuiColorEditFlags.NoInputs | ImGuiColorEditFlags.AlphaPreviewHalf); + ImGui.EndDisabled(); + + DrawNoteLine("- ", UIColors.Get("LightlessPurple"), "Glow Color"); + ImGui.SameLine(colorPickAlign); + ImGui.Checkbox("##toggleGlowColor", ref glowEnabled); + ImGui.SameLine(); + ImGui.BeginDisabled(!glowEnabled); + ImGui.ColorEdit4($"##color_glow", ref glowColor, ImGuiColorEditFlags.NoInputs | ImGuiColorEditFlags.AlphaPreviewHalf); + ImGui.EndDisabled(); + + bool changed = !Equals(_savedVanity, new VanityState(textEnabled, glowEnabled, textColor, glowColor)); + + if (!changed) + ImGui.BeginDisabled(); + + if (_uiSharedService.IconTextButton(FontAwesomeIcon.Save, "Save Changes")) + { + string? newText = textEnabled ? UIColors.RgbaToHex(textColor) : string.Empty; + string? newGlow = glowEnabled ? UIColors.RgbaToHex(glowColor) : string.Empty; + + _ = _apiController.UserUpdateVanityColors(new UserVanityColorsDto(newText, newGlow)); + + _savedVanity = new VanityState(textEnabled, glowEnabled, textColor, glowColor); + } + + if (!changed) + ImGui.EndDisabled(); + + ImGui.Dummy(new Vector2(5)); + _uiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f); + + if (!hasVanity) + ImGui.EndDisabled(); + + ImGui.EndTabItem(); + } + + ImGui.EndTabBar(); } - UiSharedService.AttachToolTip("Sets your profile description text"); - ImGui.SameLine(); - if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear Description")) - { - _ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, ProfilePictureBase64: null, "")); - } - UiSharedService.AttachToolTip("Clears your profile description text"); } protected override void Dispose(bool disposing) diff --git a/LightlessSync/UI/Handlers/IdDisplayHandler.cs b/LightlessSync/UI/Handlers/IdDisplayHandler.cs index 81ed337..048efe9 100644 --- a/LightlessSync/UI/Handlers/IdDisplayHandler.cs +++ b/LightlessSync/UI/Handlers/IdDisplayHandler.cs @@ -98,20 +98,21 @@ public class IdDisplayHandler var font = UiBuilder.MonoFont; - var isAdmin = pair.UserData.IsAdmin; - var isModerator = pair.UserData.IsModerator; + Vector4? textColor = null; + Vector4? glowColor = null; - Vector4? textColor = isAdmin - ? UIColors.Get("LightlessAdminText") - : isModerator - ? UIColors.Get("LightlessModeratorText") - : null; + if (pair.UserData.HasVanity) + { + if (!string.IsNullOrWhiteSpace(pair.UserData.TextColorHex)) + { + textColor = UIColors.HexToRgba(pair.UserData.TextColorHex); + } - Vector4? glowColor = isAdmin - ? UIColors.Get("LightlessAdminGlow") - : isModerator - ? UIColors.Get("LightlessModeratorGlow") - : null; + if (!string.IsNullOrWhiteSpace(pair.UserData.TextGlowColorHex)) + { + glowColor = UIColors.HexToRgba(pair.UserData.TextGlowColorHex); + } + } var seString = (textColor != null || glowColor != null) ? SeStringUtils.BuildFormattedPlayerName(playerText, textColor, glowColor) diff --git a/LightlessSync/UI/IntroUI.cs b/LightlessSync/UI/IntroUI.cs index bc32128..470cadb 100644 --- a/LightlessSync/UI/IntroUI.cs +++ b/LightlessSync/UI/IntroUI.cs @@ -167,7 +167,7 @@ public partial class IntroUi : WindowMediatorSubscriberBase } else { - UiSharedService.TextWrapped("To not unnecessary download files already present on your computer, Lightless Sync will have to scan your Penumbra mod directory. " + + UiSharedService.TextWrapped("To not unnecessarily download files already present on your computer, Lightless Sync will have to scan your Penumbra mod directory. " + "Additionally, a local storage folder must be set where Lightless Sync will download other character files to. " + "Once the storage folder is set and the scan complete, this page will automatically forward to registration at a service."); UiSharedService.TextWrapped("Note: The initial scan, depending on the amount of mods you have, might take a while. Please wait until it is completed."); diff --git a/LightlessSync/UI/JoinSyncshellUI.cs b/LightlessSync/UI/JoinSyncshellUI.cs index 3de9026..b02a84e 100644 --- a/LightlessSync/UI/JoinSyncshellUI.cs +++ b/LightlessSync/UI/JoinSyncshellUI.cs @@ -63,7 +63,7 @@ internal class JoinSyncshellUI : WindowMediatorSubscriberBase "Joining a Syncshell will pair you implicitly with all existing users in the Syncshell." + Environment.NewLine + "All permissions to all users in the Syncshell will be set to the preferred Syncshell permissions on joining, excluding prior set preferred permissions."); ImGui.Separator(); - ImGui.TextUnformatted("Note: Syncshell ID and Password are case sensitive. MSS- is part of Syncshell IDs, unless using Vanity IDs."); + ImGui.TextUnformatted("Note: Syncshell ID and Password are case sensitive. LLS- is part of Syncshell IDs, unless using Vanity IDs."); ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("Syncshell ID"); diff --git a/LightlessSync/UI/SettingsUi.cs b/LightlessSync/UI/SettingsUi.cs index a973c34..fdbe821 100644 --- a/LightlessSync/UI/SettingsUi.cs +++ b/LightlessSync/UI/SettingsUi.cs @@ -1152,12 +1152,12 @@ public class SettingsUi : WindowMediatorSubscriberBase } _uiShared.DrawHelpText("This will group up all Syncshells in a special 'All Syncshells' folder in the main UI."); - //if (ImGui.Checkbox("Show grouped syncshells in main screen/all syncshells", ref groupedSyncshells)) - //{ - // _configService.Current.ShowGroupedSyncshellsInAll = groupedSyncshells; - // _configService.Save(); - // Mediator.Publish(new RefreshUiMessage()); - //} + if (ImGui.Checkbox("Show grouped syncshells in main screen/all syncshells", ref groupedSyncshells)) + { + _configService.Current.ShowGroupedSyncshellsInAll = groupedSyncshells; + _configService.Save(); + Mediator.Publish(new RefreshUiMessage()); + } _uiShared.DrawHelpText("This will show grouped syncshells in main screen or group 'All Syncshells'."); if (ImGui.Checkbox("Show player name for visible players", ref showNameInsteadOfNotes)) diff --git a/LightlessSync/UI/SyncshellAdminUI.cs b/LightlessSync/UI/SyncshellAdminUI.cs index 3dd90de..fb30067 100644 --- a/LightlessSync/UI/SyncshellAdminUI.cs +++ b/LightlessSync/UI/SyncshellAdminUI.cs @@ -336,7 +336,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase } ImGui.Separator(); - if (_uiSharedService.MediumTreeNode("Mass Cleanup", UIColors.Get("LightlessPurple"))) + if (_uiSharedService.MediumTreeNode("Mass Cleanup", UIColors.Get("DimRed"))) { using (ImRaii.Disabled(!UiSharedService.CtrlPressed())) { @@ -348,6 +348,18 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase UiSharedService.AttachToolTip("This will remove all non-pinned, non-moderator users from the Syncshell." + UiSharedService.TooltipSeparator + "Hold CTRL to enable this button"); + ImGui.SameLine(); + + using (ImRaii.Disabled(!UiSharedService.CtrlPressed())) + { + if (_uiSharedService.IconTextButton(FontAwesomeIcon.Brush, "Clear Lightfinder Users")) + { + _ = _apiController.GroupClearFinder(new(GroupFullInfo.Group)); + } + } + UiSharedService.AttachToolTip("This will remove all users that joined through Lightfinder from the Syncshell." + + UiSharedService.TooltipSeparator + "Hold CTRL to enable this button"); + ImGuiHelpers.ScaledDummy(2f); ImGui.Separator(); ImGuiHelpers.ScaledDummy(2f); @@ -410,12 +422,12 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase UiSharedService.TextWrapped($"Syncshell was pruned and {_pruneTask.Result} inactive user(s) have been removed."); } } - _uiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f); + _uiSharedService.ColoredSeparator(UIColors.Get("DimRed"), 1.5f); ImGui.TreePop(); } ImGui.Separator(); - if (_uiSharedService.MediumTreeNode("User Bans", UIColors.Get("LightlessPurple"))) + if (_uiSharedService.MediumTreeNode("User Bans", UIColors.Get("LightlessYellow"))) { if (_uiSharedService.IconTextButton(FontAwesomeIcon.Retweet, "Refresh Banlist from Server")) { @@ -456,7 +468,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase } ImGui.EndTable(); } - _uiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f); + _uiSharedService.ColoredSeparator(UIColors.Get("LightlessYellow"), 1.5f); ImGui.TreePop(); } ImGui.Separator(); diff --git a/LightlessSync/UI/SyncshellFinderUI.cs b/LightlessSync/UI/SyncshellFinderUI.cs index 6f4050e..81f0b0e 100644 --- a/LightlessSync/UI/SyncshellFinderUI.cs +++ b/LightlessSync/UI/SyncshellFinderUI.cs @@ -4,15 +4,15 @@ using Dalamud.Interface.Colors; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using LightlessSync.API.Data.Enum; +using LightlessSync.API.Data.Extensions; using LightlessSync.API.Dto; using LightlessSync.API.Dto.Group; -using LightlessSync.LightlessConfiguration; +using LightlessSync.PlayerData.Pairs; using LightlessSync.Services; using LightlessSync.Services.Mediator; using LightlessSync.Utils; using LightlessSync.WebAPI; using Microsoft.Extensions.Logging; -using LightlessSync.API.Data.Extensions; using System.Numerics; namespace LightlessSync.UI; @@ -20,12 +20,13 @@ namespace LightlessSync.UI; public class SyncshellFinderUI : WindowMediatorSubscriberBase { private readonly ApiController _apiController; - private readonly LightlessConfigService _configService; private readonly BroadcastService _broadcastService; private readonly UiSharedService _uiSharedService; private readonly BroadcastScannerService _broadcastScannerService; + private readonly PairManager _pairManager; - private readonly List _nearbySyncshells = new(); + private readonly List _nearbySyncshells = []; + private List _currentSyncshells = []; private int _selectedNearbyIndex = -1; private GroupJoinDto? _joinDto; @@ -37,17 +38,16 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase LightlessMediator mediator, PerformanceCollectorService performanceCollectorService, BroadcastService broadcastService, - LightlessConfigService configService, UiSharedService uiShared, ApiController apiController, - BroadcastScannerService broadcastScannerService - ) : base(logger, mediator, "Shellfinder###LightlessSyncshellFinderUI", performanceCollectorService) + BroadcastScannerService broadcastScannerService, + PairManager pairManager) : base(logger, mediator, "Shellfinder###LightlessSyncshellFinderUI", performanceCollectorService) { _broadcastService = broadcastService; _uiSharedService = uiShared; - _configService = configService; _apiController = apiController; _broadcastScannerService = broadcastScannerService; + _pairManager = pairManager; IsOpen = false; SizeConstraints = new() @@ -56,14 +56,14 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase MaximumSize = new(600, 550) }; - Mediator.Subscribe(this, async _ => await RefreshSyncshellsAsync()); - Mediator.Subscribe(this, async _ => await RefreshSyncshellsAsync()); + Mediator.Subscribe(this, async _ => await RefreshSyncshellsAsync().ConfigureAwait(false)); + Mediator.Subscribe(this, async _ => await RefreshSyncshellsAsync().ConfigureAwait(false)); } public override async void OnOpen() { _ownPermissions = _apiController.DefaultPermissions.DeepClone()!; - await RefreshSyncshellsAsync(); + await RefreshSyncshellsAsync().ConfigureAwait(false); } protected override void DrawInternal() @@ -107,10 +107,8 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase ImGui.TableSetupColumn("Join", ImGuiTableColumnFlags.WidthFixed, 80f * ImGuiHelpers.GlobalScale); ImGui.TableHeadersRow(); - for (int i = 0; i < _nearbySyncshells.Count; i++) + foreach (var shell in _nearbySyncshells) { - var shell = _nearbySyncshells[i]; - ImGui.TableNextRow(); ImGui.TableNextColumn(); ImGui.TextUnformatted(shell.Group.Alias ?? "(No Alias)"); @@ -123,41 +121,53 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase ImGui.PushStyleColor(ImGuiCol.ButtonHovered, UIColors.Get("LightlessGreen").WithAlpha(0.85f)); ImGui.PushStyleColor(ImGuiCol.ButtonActive, UIColors.Get("LightlessGreen").WithAlpha(0.75f)); - if (ImGui.Button(label)) + if (!_currentSyncshells.Exists(g => string.Equals(g.GID, shell.GID, StringComparison.Ordinal))) { - _logger.LogInformation($"Join requested for Syncshell {shell.Group.GID} ({shell.Group.Alias})"); - _ = Task.Run(async () => + + if (ImGui.Button(label)) { - try - { - var info = await _apiController.GroupJoinHashed(new GroupJoinHashedDto( - shell.Group, - shell.Password, - shell.GroupUserPreferredPermissions - )).ConfigureAwait(false); + _logger.LogInformation($"Join requested for Syncshell {shell.Group.GID} ({shell.Group.Alias})"); - if (info != null && info.Success) - { - _joinDto = new GroupJoinDto(shell.Group, shell.Password, shell.GroupUserPreferredPermissions); - _joinInfo = info; - _ownPermissions = _apiController.DefaultPermissions.DeepClone()!; - - _logger.LogInformation($"Fetched join info for {shell.Group.GID}"); - } - else - { - _logger.LogWarning($"Failed to join {shell.Group.GID}: info was null or unsuccessful"); - } - } - catch (Exception ex) + _ = Task.Run(async () => { - _logger.LogError(ex, $"Join failed for {shell.Group.GID}"); - } - }); + try + { + var info = await _apiController.GroupJoinHashed(new GroupJoinHashedDto( + shell.Group, + shell.Password, + shell.GroupUserPreferredPermissions + )).ConfigureAwait(false); + + if (info != null && info.Success) + { + _joinDto = new GroupJoinDto(shell.Group, shell.Password, shell.GroupUserPreferredPermissions); + _joinInfo = info; + _ownPermissions = _apiController.DefaultPermissions.DeepClone()!; + + _logger.LogInformation($"Fetched join info for {shell.Group.GID}"); + } + else + { + _logger.LogWarning($"Failed to join {shell.Group.GID}: info was null or unsuccessful"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"Join failed for {shell.Group.GID}"); + } + }); + } } + else + { - + using (ImRaii.Disabled()) + { + ImGui.Button(label); + } + UiSharedService.AttachToolTip("Already a member or owner of this Syncshell."); + } ImGui.PopStyleColor(3); } @@ -170,28 +180,31 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase private void DrawConfirmation() { - ImGui.Separator(); - ImGui.TextUnformatted($"Join Syncshell: {_joinDto.Group.AliasOrGID} by {_joinInfo.OwnerAliasOrUID}"); - ImGuiHelpers.ScaledDummy(2f); - ImGui.TextUnformatted("Suggested Syncshell Permissions:"); - - DrawPermissionRow("Sounds", _joinInfo.GroupPermissions.IsPreferDisableSounds(), _ownPermissions.DisableGroupSounds, v => _ownPermissions.DisableGroupSounds = v); - DrawPermissionRow("Animations", _joinInfo.GroupPermissions.IsPreferDisableAnimations(), _ownPermissions.DisableGroupAnimations, v => _ownPermissions.DisableGroupAnimations = v); - DrawPermissionRow("VFX", _joinInfo.GroupPermissions.IsPreferDisableVFX(), _ownPermissions.DisableGroupVFX, v => _ownPermissions.DisableGroupVFX = v); - - ImGui.NewLine(); - ImGui.NewLine(); - - if (_uiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.Plus, $"Finalize and join {_joinDto.Group.AliasOrGID}")) + if (_joinDto != null && _joinInfo != null) { - var finalPermissions = GroupUserPreferredPermissions.NoneSet; - finalPermissions.SetDisableSounds(_ownPermissions.DisableGroupSounds); - finalPermissions.SetDisableAnimations(_ownPermissions.DisableGroupAnimations); - finalPermissions.SetDisableVFX(_ownPermissions.DisableGroupVFX); + ImGui.Separator(); + ImGui.TextUnformatted($"Join Syncshell: {_joinDto.Group.AliasOrGID} by {_joinInfo.OwnerAliasOrUID}"); + ImGuiHelpers.ScaledDummy(2f); + ImGui.TextUnformatted("Suggested Syncshell Permissions:"); - _ = _apiController.GroupJoinFinalize(new GroupJoinDto(_joinDto.Group, _joinDto.Password, finalPermissions)); - _joinDto = null; - _joinInfo = null; + DrawPermissionRow("Sounds", _joinInfo.GroupPermissions.IsPreferDisableSounds(), _ownPermissions.DisableGroupSounds, v => _ownPermissions.DisableGroupSounds = v); + DrawPermissionRow("Animations", _joinInfo.GroupPermissions.IsPreferDisableAnimations(), _ownPermissions.DisableGroupAnimations, v => _ownPermissions.DisableGroupAnimations = v); + DrawPermissionRow("VFX", _joinInfo.GroupPermissions.IsPreferDisableVFX(), _ownPermissions.DisableGroupVFX, v => _ownPermissions.DisableGroupVFX = v); + + ImGui.NewLine(); + ImGui.NewLine(); + + if (_uiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.Plus, $"Finalize and join {_joinDto.Group.AliasOrGID}")) + { + var finalPermissions = GroupUserPreferredPermissions.NoneSet; + finalPermissions.SetDisableSounds(_ownPermissions.DisableGroupSounds); + finalPermissions.SetDisableAnimations(_ownPermissions.DisableGroupAnimations); + finalPermissions.SetDisableVFX(_ownPermissions.DisableGroupVFX); + + _ = _apiController.GroupJoinFinalize(new GroupJoinDto(_joinDto.Group, _joinDto.Password, finalPermissions)); + _joinDto = null; + _joinInfo = null; + } } } @@ -224,6 +237,7 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase private async Task RefreshSyncshellsAsync() { var syncshellBroadcasts = _broadcastScannerService.GetActiveSyncshellBroadcasts(); + _currentSyncshells = _pairManager.GroupPairs.Select(g => g.Key).ToList(); if (syncshellBroadcasts.Count == 0) { @@ -231,11 +245,11 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase return; } - List updatedList; + List updatedList = []; try { - var groups = await _apiController.GetBroadcastedGroups(syncshellBroadcasts); - updatedList = groups?.ToList() ?? new(); + var groups = await _apiController.GetBroadcastedGroups(syncshellBroadcasts).ConfigureAwait(false); + updatedList = groups?.ToList(); } catch (Exception ex) { @@ -243,8 +257,8 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase return; } - var currentGids = _nearbySyncshells.Select(s => s.Group.GID).ToHashSet(); - var newGids = updatedList.Select(s => s.Group.GID).ToHashSet(); + var currentGids = _nearbySyncshells.Select(s => s.Group.GID).ToHashSet(StringComparer.Ordinal); + var newGids = updatedList.Select(s => s.Group.GID).ToHashSet(StringComparer.Ordinal); if (currentGids.SetEquals(newGids)) return; @@ -256,7 +270,7 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase if (previousGid != null) { - var newIndex = _nearbySyncshells.FindIndex(s => s.Group.GID == previousGid); + var newIndex = _nearbySyncshells.FindIndex(s => string.Equals(s.Group.GID, previousGid, StringComparison.Ordinal)); if (newIndex >= 0) { _selectedNearbyIndex = newIndex; @@ -290,9 +304,4 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase return _nearbySyncshells[_selectedNearbyIndex].Group.GID; } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - } } diff --git a/LightlessSync/WebAPI/SignalR/ApIController.Functions.Users.cs b/LightlessSync/WebAPI/SignalR/ApIController.Functions.Users.cs index 95f1de8..d268dd8 100644 --- a/LightlessSync/WebAPI/SignalR/ApIController.Functions.Users.cs +++ b/LightlessSync/WebAPI/SignalR/ApIController.Functions.Users.cs @@ -134,6 +134,12 @@ public partial class ApiController await _lightlessHub!.InvokeAsync(nameof(UserSetProfile), userDescription).ConfigureAwait(false); } + public async Task UserUpdateVanityColors(UserVanityColorsDto dto) + { + if (!IsConnected) return; + await _lightlessHub!.InvokeAsync(nameof(UserUpdateVanityColors), dto).ConfigureAwait(false); + } + public async Task UserUpdateDefaultPermissions(DefaultPermissionsDto defaultPermissionsDto) { CheckConnection(); diff --git a/LightlessSync/WebAPI/SignalR/ApiController.Functions.Callbacks.cs b/LightlessSync/WebAPI/SignalR/ApiController.Functions.Callbacks.cs index 154dbe6..457361a 100644 --- a/LightlessSync/WebAPI/SignalR/ApiController.Functions.Callbacks.cs +++ b/LightlessSync/WebAPI/SignalR/ApiController.Functions.Callbacks.cs @@ -277,6 +277,12 @@ public partial class ApiController _lightlessHub!.On(nameof(Client_GroupSendInfo), act); } + public void OnGroupUpdateProfile(Action act) + { + if (_initialized) return; + _lightlessHub!.On(nameof(Client_GroupSendProfile), act); + } + public void OnReceiveServerMessage(Action act) { if (_initialized) return; diff --git a/LightlessSync/WebAPI/SignalR/ApiController.Functions.Groups.cs b/LightlessSync/WebAPI/SignalR/ApiController.Functions.Groups.cs index cd17ea8..c7581d1 100644 --- a/LightlessSync/WebAPI/SignalR/ApiController.Functions.Groups.cs +++ b/LightlessSync/WebAPI/SignalR/ApiController.Functions.Groups.cs @@ -45,6 +45,11 @@ public partial class ApiController CheckConnection(); await _lightlessHub!.SendAsync(nameof(GroupClear), group).ConfigureAwait(false); } + public async Task GroupClearFinder(GroupDto group) + { + CheckConnection(); + await _lightlessHub!.SendAsync(nameof(GroupClearFinder), group).ConfigureAwait(false); + } public async Task GroupCreate() { diff --git a/LightlessSync/WebAPI/SignalR/ApiController.cs b/LightlessSync/WebAPI/SignalR/ApiController.cs index 7653529..efa6e6f 100644 --- a/LightlessSync/WebAPI/SignalR/ApiController.cs +++ b/LightlessSync/WebAPI/SignalR/ApiController.cs @@ -2,6 +2,7 @@ using LightlessSync.API.Data; using LightlessSync.API.Data.Extensions; using LightlessSync.API.Dto; +using LightlessSync.API.Dto.Group; using LightlessSync.API.Dto.User; using LightlessSync.API.SignalR; using LightlessSync.LightlessConfiguration; @@ -76,6 +77,10 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IL public DefaultPermissionsDto? DefaultPermissions => _connectionDto?.DefaultPreferredPermissions ?? null; public string DisplayName => _connectionDto?.User.AliasOrUID ?? string.Empty; + public bool HasVanity => _connectionDto?.HasVanity ?? false; + public string TextColorHex => _connectionDto?.TextColorHex ?? string.Empty; + public string TextGlowColorHex => _connectionDto?.TextGlowColorHex ?? string.Empty; + public bool IsConnected => ServerState == ServerState.Connected; public bool IsCurrentVersion => (Assembly.GetExecutingAssembly().GetName().Version ?? new Version(0, 0, 0, 0)) >= (_connectionDto?.CurrentClientVersion ?? new Version(0, 0, 0, 0)); @@ -444,6 +449,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IL OnGroupPairLeft((dto) => _ = Client_GroupPairLeft(dto)); OnGroupSendFullInfo((dto) => _ = Client_GroupSendFullInfo(dto)); OnGroupSendInfo((dto) => _ = Client_GroupSendInfo(dto)); + OnGroupUpdateProfile((dto) => _ = Client_GroupSendProfile(dto)); OnGroupChangeUserPairPermissions((dto) => _ = Client_GroupChangeUserPairPermissions(dto)); OnGposeLobbyJoin((dto) => _ = Client_GposeLobbyJoin(dto)); @@ -596,5 +602,20 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IL ServerState = state; } + + public Task Client_GroupSendProfile(GroupProfileDto groupInfo) + { + throw new NotImplementedException(); + } + + public Task GroupGetProfile(GroupDto dto) + { + throw new NotImplementedException(); + } + + public Task GroupSetProfile(GroupProfileDto dto) + { + throw new NotImplementedException(); + } } #pragma warning restore MA0040 \ No newline at end of file