Merge branch '1.12.0' into ui-redesign

This commit is contained in:
choco
2025-09-28 15:17:48 +02:00
17 changed files with 624 additions and 414 deletions

View File

@@ -230,7 +230,7 @@ public sealed class Plugin : IDalamudPlugin
s.GetRequiredService<LightlessProfileManager>(), s.GetRequiredService<PerformanceCollectorService>()));
collection.AddScoped<WindowMediatorSubscriberBase, PopupHandler>();
collection.AddScoped<WindowMediatorSubscriberBase, BroadcastUI>((s) => new BroadcastUI(s.GetRequiredService<ILogger<BroadcastUI>>(), s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<PerformanceCollectorService>(), s.GetRequiredService<BroadcastService>(), s.GetRequiredService<LightlessConfigService>(), s.GetRequiredService<UiSharedService>(), s.GetRequiredService<ApiController>(), s.GetRequiredService<BroadcastScannerService>()));
collection.AddScoped<WindowMediatorSubscriberBase, SyncshellFinderUI>((s) => new SyncshellFinderUI(s.GetRequiredService<ILogger<SyncshellFinderUI>>(), s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<PerformanceCollectorService>(), s.GetRequiredService<BroadcastService>(), s.GetRequiredService<LightlessConfigService>(), s.GetRequiredService<UiSharedService>(), s.GetRequiredService<ApiController>(), s.GetRequiredService<BroadcastScannerService>()));
collection.AddScoped<WindowMediatorSubscriberBase, SyncshellFinderUI>((s) => new SyncshellFinderUI(s.GetRequiredService<ILogger<SyncshellFinderUI>>(), s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<PerformanceCollectorService>(), s.GetRequiredService<BroadcastService>(), s.GetRequiredService<UiSharedService>(), s.GetRequiredService<ApiController>(), s.GetRequiredService<BroadcastScannerService>(), s.GetRequiredService<PairManager>()));
collection.AddScoped<IPopupHandler, BanUserPopupHandler>();
collection.AddScoped<IPopupHandler, CensusPopupHandler>();
collection.AddScoped<CacheCreationService>();

View File

@@ -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<BroadcastService> _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<BroadcastService> logger, LightlessMediator mediator, HubFactory hubFactory, LightlessConfigService config, DalamudUtilService dalamudUtil, ApiController apiController)
public BroadcastService(ILogger<BroadcastService> 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<Task> 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<EnableBroadcastMessage>(this, OnEnableBroadcast);
@@ -65,7 +64,7 @@ public class BroadcastService : IHostedService, IMediatorSubscriber
_mediator.Subscribe<PriorityFrameworkUpdateMessage>(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<BroadcastStatusInfoDto?>("IsUserBroadcasting", dummy, cancellationToken);
await hub.InvokeAsync("SetBroadcastStatus", dummy, true, null, cancellationToken);
await hub.InvokeAsync<TimeSpan?>("GetBroadcastTtl", dummy, cancellationToken);
await hub.InvokeAsync<Dictionary<string, BroadcastStatusInfoDto?>>("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)
{

View File

@@ -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"))
{

View File

@@ -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<CutsceneEndMessage>(this, (_) => UiSharedService_GposeEnd());
Mediator.Subscribe<DownloadStartedMessage>(this, (msg) => _currentDownloads[msg.DownloadId] = msg.DownloadStatus);
Mediator.Subscribe<DownloadFinishedMessage>(this, (msg) => _currentDownloads.TryRemove(msg.DownloadId, out _));
Mediator.Subscribe<RefreshUiMessage>(this, (msg) => _drawFolders = GetDrawFolders().ToList());
Mediator.Subscribe<RefreshUiMessage>(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,152 +612,151 @@ public class CompactUi : WindowMediatorSubscriberBase
}
}
private IEnumerable<IDrawFolder> GetDrawFolders()
private IEnumerable<IDrawFolder> DrawFolders
{
List<IDrawFolder> drawFolders = [];
var allPairs = _pairManager.PairsWithGroups
.ToDictionary(k => k.Key, k => k.Value);
var filteredPairs = allPairs
.Where(p =>
get
{
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<Pair, List<GroupFullInfoDto>> 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<Pair, List<GroupFullInfoDto>> u)
=> (u.Key.IsOnline || (!u.Key.IsOnline && !_configService.Current.ShowOfflineUsersSeparately)
|| u.Key.UserPair.OwnPermissions.IsPaused());
Dictionary<Pair, List<GroupFullInfoDto>> BasicSortedDictionary(IEnumerable<KeyValuePair<Pair, List<GroupFullInfoDto>>> u)
=> u.OrderByDescending(u => u.Key.IsVisible)
.ThenByDescending(u => u.Key.IsOnline)
.ThenBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase)
.ToDictionary(u => u.Key, u => u.Value);
ImmutableList<Pair> ImmutablePairList(IEnumerable<KeyValuePair<Pair, List<GroupFullInfoDto>>> u)
=> u.Select(k => k.Key).ToImmutableList();
bool FilterVisibleUsers(KeyValuePair<Pair, List<GroupFullInfoDto>> u)
=> u.Key.IsVisible
&& (_configService.Current.ShowSyncshellUsersInVisible || !(!_configService.Current.ShowSyncshellUsersInVisible && !u.Key.IsDirectlyPaired));
bool FilterTagUsers(KeyValuePair<Pair, List<GroupFullInfoDto>> u, string tag)
=> u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && _tagHandler.HasPairTag(u.Key.UserData.UID, tag);
bool FilterGroupUsers(KeyValuePair<Pair, List<GroupFullInfoDto>> u, GroupFullInfoDto group)
=> u.Value.Exists(g => string.Equals(g.GID, group.GID, StringComparison.Ordinal));
bool FilterNotTaggedUsers(KeyValuePair<Pair, List<GroupFullInfoDto>> 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<Pair, List<GroupFullInfoDto>> 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<Pair, List<GroupFullInfoDto>> u)
=> (!u.Key.IsDirectlyPaired && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused());
var drawFolders = new List<IDrawFolder>();
var filter = _tabMenu.Filter;
var allPairs = _pairManager.PairsWithGroups.ToDictionary(k => k.Key, k => k.Value);
var filteredPairs = allPairs.Where(p => PassesFilter(p.Key, filter)).ToDictionary(k => k.Key, k => k.Value);
//Filter of online/visible pairs
if (_configService.Current.ShowVisibleUsersSeparately)
{
var allVisiblePairs = ImmutablePairList(allPairs
.Where(FilterVisibleUsers));
var filteredVisiblePairs = BasicSortedDictionary(filteredPairs
.Where(FilterVisibleUsers));
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));
}
List<IDrawFolder> groupFolders = new();
//Filter of not foldered syncshells
var groupFolders = new List<IDrawFolder>();
foreach (var group in _pairManager.GroupPairs.Select(g => g.Key).OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase))
{
GetGroups(allPairs, filteredPairs, group, out ImmutableList<Pair> allGroupPairs, out Dictionary<Pair, List<GroupFullInfoDto>> filteredGroupPairs);
if (FilterNotTaggedSyncshells(group))
{
groupFolders.Add(_drawEntityFactory.CreateDrawGroupFolder(group, filteredGroupPairs, allGroupPairs));
}
}
//Filter of grouped up syncshells (All Syncshells Folder)
if (_configService.Current.GroupUpSyncshells)
drawFolders.Add(new DrawGroupedGroupFolder(groupFolders, _tagHandler, _uiSharedService, _selectSyncshellForTagUi, _renameSyncshellTagUi, ""));
drawFolders.Add(new DrawGroupedGroupFolder(groupFolders, _tagHandler, _uiSharedService,
_selectSyncshellForTagUi, _renameSyncshellTagUi, ""));
else
drawFolders.AddRange(groupFolders);
var tags = _tagHandler.GetAllPairTagsSorted();
foreach (var tag in tags)
//Filter of grouped/foldered pairs
foreach (var tag in _tagHandler.GetAllPairTagsSorted())
{
var allTagPairs = ImmutablePairList(allPairs
.Where(u => FilterTagUsers(u, tag)));
var filteredTagPairs = BasicSortedDictionary(filteredPairs
.Where(u => FilterTagUsers(u, tag) && FilterOnlineOrPausedSelf(u)));
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 syncshellTags = _tagHandler.GetAllSyncshellTagsSorted();
foreach (var syncshelltag in syncshellTags)
//Filter of grouped/foldered syncshells
foreach (var syncshellTag in _tagHandler.GetAllSyncshellTagsSorted())
{
List<IDrawFolder> syncshellFolderTags = [];
var syncshellFolderTags = new List<IDrawFolder>();
foreach (var group in _pairManager.GroupPairs.Select(g => g.Key).OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase))
{
if (_tagHandler.HasSyncshellTag(group.GID, syncshelltag))
if (_tagHandler.HasSyncshellTag(group.GID, syncshellTag))
{
GetGroups(allPairs, filteredPairs, group, out ImmutableList<Pair> allGroupPairs, out Dictionary<Pair, List<GroupFullInfoDto>> filteredGroupPairs);
GetGroups(allPairs, filteredPairs, group,
out ImmutableList<Pair> allGroupPairs,
out Dictionary<Pair, List<GroupFullInfoDto>> 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));
}
drawFolders.Add(new DrawGroupedGroupFolder(syncshellFolderTags, _tagHandler, _uiSharedService, _selectSyncshellForTagUi, _renameSyncshellTagUi, syncshellTag));
}
var allOnlineNotTaggedPairs = ImmutablePairList(allPairs
.Where(FilterNotTaggedUsers));
var onlineNotTaggedPairs = BasicSortedDictionary(filteredPairs
.Where(u => FilterNotTaggedUsers(u) && FilterOnlineOrPausedSelf(u)));
//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));
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));
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(FilterOfflineSyncshellUsers));
var filteredOfflineSyncshellUsers = BasicSortedDictionary(filteredPairs
.Where(FilterOfflineSyncshellUsers));
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));
drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineSyncshellTag, filteredOfflineSyncshellUsers, allOfflineSyncshellUsers));
}
}
//Unpaired
drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomUnpairedTag,
BasicSortedDictionary(filteredPairs.Where(u => u.Key.IsOneSidedPair)),
ImmutablePairList(allPairs.Where(u => u.Key.IsOneSidedPair))));
BasicSortedDictionary(filteredPairs.Where(p => p.Key.IsOneSidedPair)),
ImmutablePairList(allPairs.Where(p => p.Key.IsOneSidedPair))));
return drawFolders;
}
}
void GetGroups(Dictionary<Pair, List<GroupFullInfoDto>> allPairs, Dictionary<Pair, List<GroupFullInfoDto>> filteredPairs, GroupFullInfoDto group, out ImmutableList<Pair> allGroupPairs, out Dictionary<Pair, List<GroupFullInfoDto>> filteredGroupPairs)
private 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<GroupFullInfoDto> groups, GroupFullInfoDto group) => groups.Exists(g => string.Equals(g.GID, group.GID, StringComparison.Ordinal));
private bool FilterNotTaggedUsers(Pair pair) => pair.IsDirectlyPaired && !pair.IsOneSidedPair && !_tagHandler.HasAnyPairTag(pair.UserData.UID);
private bool FilterNotTaggedSyncshells(GroupFullInfoDto group) => !_tagHandler.HasAnySyncshellTag(group.GID) || _configService.Current.ShowGroupedSyncshellsInAll;
private bool FilterOfflineUsers(Pair pair, List<GroupFullInfoDto> groups) => ((pair.IsDirectlyPaired && _configService.Current.ShowSyncshellOfflineUsersSeparately) || !_configService.Current.ShowSyncshellOfflineUsersSeparately) && (!pair.IsOneSidedPair || groups.Count != 0) && !pair.IsOnline && !pair.UserPair.OwnPermissions.IsPaused();
private static bool FilterOfflineSyncshellUsers(Pair pair) => !pair.IsDirectlyPaired && !pair.IsOnline && !pair.UserPair.OwnPermissions.IsPaused();
private Dictionary<Pair, List<GroupFullInfoDto>> BasicSortedDictionary(IEnumerable<KeyValuePair<Pair, List<GroupFullInfoDto>>> pairs) => pairs.OrderByDescending(u => u.Key.IsVisible).ThenByDescending(u => u.Key.IsOnline).ThenBy(u => AlphabeticalSortKey(u.Key), StringComparer.OrdinalIgnoreCase).ToDictionary(u => u.Key, u => u.Value);
private static ImmutableList<Pair> ImmutablePairList(IEnumerable<KeyValuePair<Pair, List<GroupFullInfoDto>>> pairs) => [.. pairs.Select(k => k.Key)];
private void GetGroups(Dictionary<Pair, List<GroupFullInfoDto>> allPairs,
Dictionary<Pair, List<GroupFullInfoDto>> filteredPairs,
GroupFullInfoDto group,
out ImmutableList<Pair> allGroupPairs,
out Dictionary<Pair, List<GroupFullInfoDto>> filteredGroupPairs)
{
allGroupPairs = ImmutablePairList(allPairs
.Where(u => FilterGroupUsers(u, group)));
.Where(u => FilterGroupUsers(u.Value, group)));
filteredGroupPairs = filteredPairs
.Where(u => FilterGroupUsers(u, group) && FilterOnlineOrPausedSelf(u))
.Where(u => FilterGroupUsers( u.Value, group) && FilterOnlineOrPausedSelf(u.Key))
.OrderByDescending(u => u.Key.IsOnline)
.ThenBy(u =>
{
@@ -768,10 +768,9 @@ public class CompactUi : WindowMediatorSubscriberBase
}
return u.Key.IsVisible ? 3 : 4;
})
.ThenBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase)
.ThenBy(u => AlphabeticalSortKey(u.Key), StringComparer.OrdinalIgnoreCase)
.ToDictionary(k => k.Key, k => k.Value);
}
}
private string GetServerError()
{

View File

@@ -34,8 +34,6 @@ public class DrawGroupedGroupFolder : IDrawFolder
public void Draw()
{
if (!_groups.Any()) return;
string _id = "__folder_syncshells";
if (_tag != "")
{

View File

@@ -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<EditProfileUi> 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,14 +69,61 @@ public class EditProfileUi : WindowMediatorSubscriberBase
_pfpTextureWrap = null;
}
});
Mediator.Subscribe<ConnectedMessage>(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 (ImGui.BeginTabBar("##EditProfileTabs"))
{
if (ImGui.BeginTabItem("Current Profile"))
{
_uiSharedService.MediumText("Current Profile (as saved on server)", UIColors.Get("LightlessPurple"));
ImGui.Dummy(new Vector2(5));
if (profile.IsFlagged)
{
UiSharedService.ColorTextWrapped(profile.Description, ImGuiColors.DalamudRed);
@@ -119,18 +178,14 @@ public class EditProfileUi : WindowMediatorSubscriberBase
ImGui.Checkbox("Is NSFW", ref nsfw);
ImGui.EndDisabled();
ImGui.Separator();
_uiSharedService.BigText("Notes and Rules for Profiles");
_uiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f);
ImGui.EndTabItem();
}
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 (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"))
{
@@ -223,6 +278,105 @@ public class EditProfileUi : WindowMediatorSubscriberBase
_ = _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();
}
}
protected override void Dispose(bool disposing)

View File

@@ -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)

View File

@@ -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.");

View File

@@ -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");

View File

@@ -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))

View File

@@ -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();

View File

@@ -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<GroupJoinDto> _nearbySyncshells = new();
private readonly List<GroupJoinDto> _nearbySyncshells = [];
private List<GroupFullInfoDto> _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<SyncshellBroadcastsUpdatedMessage>(this, async _ => await RefreshSyncshellsAsync());
Mediator.Subscribe<BroadcastStatusChangedMessage>(this, async _ => await RefreshSyncshellsAsync());
Mediator.Subscribe<SyncshellBroadcastsUpdatedMessage>(this, async _ => await RefreshSyncshellsAsync().ConfigureAwait(false));
Mediator.Subscribe<BroadcastStatusChangedMessage>(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,6 +121,10 @@ 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 (!_currentSyncshells.Exists(g => string.Equals(g.GID, shell.GID, StringComparison.Ordinal)))
{
if (ImGui.Button(label))
{
_logger.LogInformation($"Join requested for Syncshell {shell.Group.GID} ({shell.Group.Alias})");
@@ -156,8 +158,16 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
}
});
}
}
else
{
using (ImRaii.Disabled())
{
ImGui.Button(label);
}
UiSharedService.AttachToolTip("Already a member or owner of this Syncshell.");
}
ImGui.PopStyleColor(3);
}
@@ -169,6 +179,8 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
}
private void DrawConfirmation()
{
if (_joinDto != null && _joinInfo != null)
{
ImGui.Separator();
ImGui.TextUnformatted($"Join Syncshell: {_joinDto.Group.AliasOrGID} by {_joinInfo.OwnerAliasOrUID}");
@@ -194,6 +206,7 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
_joinInfo = null;
}
}
}
private void DrawPermissionRow(string label, bool suggested, bool current, Action<bool> apply)
{
@@ -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<GroupJoinDto> updatedList;
List<GroupJoinDto> 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);
}
}

View File

@@ -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();

View File

@@ -277,6 +277,12 @@ public partial class ApiController
_lightlessHub!.On(nameof(Client_GroupSendInfo), act);
}
public void OnGroupUpdateProfile(Action<GroupProfileDto> act)
{
if (_initialized) return;
_lightlessHub!.On(nameof(Client_GroupSendProfile), act);
}
public void OnReceiveServerMessage(Action<MessageSeverity, string> act)
{
if (_initialized) return;

View File

@@ -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<GroupJoinDto> GroupCreate()
{

View File

@@ -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<GroupProfileDto> GroupGetProfile(GroupDto dto)
{
throw new NotImplementedException();
}
public Task GroupSetProfile(GroupProfileDto dto)
{
throw new NotImplementedException();
}
}
#pragma warning restore MA0040