Merge remote-tracking branch 'origin/notification-reworks' into notification-reworks

This commit is contained in:
choco
2025-11-10 21:05:48 +01:00
22 changed files with 1884 additions and 467 deletions

View File

@@ -170,7 +170,7 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase
if (!_charaDataManager.BrioAvailable)
{
ImGuiHelpers.ScaledDummy(3);
UiSharedService.DrawGroupedCenteredColorText("To utilize any features related to posing or spawning characters you require to have Brio installed.", ImGuiColors.DalamudRed);
UiSharedService.DrawGroupedCenteredColorText("To utilize any features related to posing or spawning characters, you are required to have Brio installed.", ImGuiColors.DalamudRed);
UiSharedService.DistanceSeparator();
}

View File

@@ -1,4 +1,3 @@
using System;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility;
@@ -16,12 +15,14 @@ using LightlessSync.Services.Mediator;
using LightlessSync.Services.ServerConfiguration;
using LightlessSync.UI.Components;
using LightlessSync.UI.Handlers;
using LightlessSync.UI.Models;
using LightlessSync.Utils;
using LightlessSync.WebAPI;
using LightlessSync.WebAPI.Files;
using LightlessSync.WebAPI.Files.Models;
using LightlessSync.WebAPI.SignalR.Utils;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Globalization;
@@ -708,23 +709,23 @@ public class CompactUi : WindowMediatorSubscriberBase
}
//Filter of not foldered syncshells
var groupFolders = new List<IDrawFolder>();
var groupFolders = new List<GroupFolder>();
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));
groupFolders.Add(new GroupFolder(group, _drawEntityFactory.CreateDrawGroupFolder(group, filteredGroupPairs, allGroupPairs)));
}
}
//Filter of grouped up syncshells (All Syncshells Folder)
if (_configService.Current.GroupUpSyncshells)
drawFolders.Add(new DrawGroupedGroupFolder(groupFolders, _tagHandler, _uiSharedService,
drawFolders.Add(new DrawGroupedGroupFolder(groupFolders, _tagHandler, _apiController, _uiSharedService,
_selectSyncshellForTagUi, _renameSyncshellTagUi, ""));
else
drawFolders.AddRange(groupFolders);
drawFolders.AddRange(groupFolders.Select(v => v.GroupDrawFolder));
//Filter of grouped/foldered pairs
foreach (var tag in _tagHandler.GetAllPairTagsSorted())
@@ -738,7 +739,7 @@ public class CompactUi : WindowMediatorSubscriberBase
//Filter of grouped/foldered syncshells
foreach (var syncshellTag in _tagHandler.GetAllSyncshellTagsSorted())
{
var syncshellFolderTags = new List<IDrawFolder>();
var syncshellFolderTags = new List<GroupFolder>();
foreach (var group in _pairManager.GroupPairs.Select(g => g.Key).OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase))
{
if (_tagHandler.HasSyncshellTag(group.GID, syncshellTag))
@@ -747,11 +748,11 @@ public class CompactUi : WindowMediatorSubscriberBase
out ImmutableList<Pair> allGroupPairs,
out Dictionary<Pair, List<GroupFullInfoDto>> filteredGroupPairs);
syncshellFolderTags.Add(_drawEntityFactory.CreateDrawGroupFolder($"tag_{group.GID}", group, filteredGroupPairs, allGroupPairs));
syncshellFolderTags.Add(new GroupFolder(group, _drawEntityFactory.CreateDrawGroupFolder($"tag_{group.GID}", group, filteredGroupPairs, allGroupPairs)));
}
}
drawFolders.Add(new DrawGroupedGroupFolder(syncshellFolderTags, _tagHandler, _uiSharedService, _selectSyncshellForTagUi, _renameSyncshellTagUi, syncshellTag));
drawFolders.Add(new DrawGroupedGroupFolder(syncshellFolderTags, _tagHandler, _apiController, _uiSharedService, _selectSyncshellForTagUi, _renameSyncshellTagUi, syncshellTag));
}
//Filter of not grouped/foldered and offline pairs

View File

@@ -1,7 +1,11 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using LightlessSync.API.Data.Extensions;
using LightlessSync.API.Dto.Group;
using LightlessSync.UI.Handlers;
using LightlessSync.UI.Models;
using LightlessSync.WebAPI;
using System.Collections.Immutable;
using System.Numerics;
@@ -10,19 +14,20 @@ namespace LightlessSync.UI.Components;
public class DrawGroupedGroupFolder : IDrawFolder
{
private readonly string _tag;
private readonly IEnumerable<IDrawFolder> _groups;
private readonly IEnumerable<GroupFolder> _groups;
private readonly TagHandler _tagHandler;
private readonly UiSharedService _uiSharedService;
private readonly ApiController _apiController;
private readonly SelectSyncshellForTagUi _selectSyncshellForTagUi;
private readonly RenameSyncshellTagUi _renameSyncshellTagUi;
private bool _wasHovered = false;
private float _menuWidth;
public IImmutableList<DrawUserPair> DrawPairs => throw new NotSupportedException();
public int OnlinePairs => _groups.SelectMany(g => g.DrawPairs).Where(g => g.Pair.IsOnline).DistinctBy(g => g.Pair.UserData.UID).Count();
public int TotalPairs => _groups.Sum(g => g.TotalPairs);
public IImmutableList<DrawUserPair> DrawPairs => _groups.SelectMany(g => g.GroupDrawFolder.DrawPairs).ToImmutableList();
public int OnlinePairs => _groups.SelectMany(g => g.GroupDrawFolder.DrawPairs).Where(g => g.Pair.IsOnline).DistinctBy(g => g.Pair.UserData.UID).Count();
public int TotalPairs => _groups.Sum(g => g.GroupDrawFolder.TotalPairs);
public DrawGroupedGroupFolder(IEnumerable<IDrawFolder> groups, TagHandler tagHandler, UiSharedService uiSharedService, SelectSyncshellForTagUi selectSyncshellForTagUi, RenameSyncshellTagUi renameSyncshellTagUi, string tag)
public DrawGroupedGroupFolder(IEnumerable<GroupFolder> groups, TagHandler tagHandler, ApiController apiController, UiSharedService uiSharedService, SelectSyncshellForTagUi selectSyncshellForTagUi, RenameSyncshellTagUi renameSyncshellTagUi, string tag)
{
_groups = groups;
_tagHandler = tagHandler;
@@ -30,6 +35,7 @@ public class DrawGroupedGroupFolder : IDrawFolder
_selectSyncshellForTagUi = selectSyncshellForTagUi;
_renameSyncshellTagUi = renameSyncshellTagUi;
_tag = tag;
_apiController = apiController;
}
public void Draw()
@@ -42,7 +48,7 @@ public class DrawGroupedGroupFolder : IDrawFolder
using var id = ImRaii.PushId(_id);
var color = ImRaii.PushColor(ImGuiCol.ChildBg, ImGui.GetColorU32(ImGuiCol.FrameBgHovered), _wasHovered);
using (ImRaii.Child("folder__" + _id, new System.Numerics.Vector2(UiSharedService.GetWindowContentRegionWidth() - ImGui.GetCursorPosX(), ImGui.GetFrameHeight())))
using (ImRaii.Child("folder__" + _id, new Vector2(UiSharedService.GetWindowContentRegionWidth() - ImGui.GetCursorPosX(), ImGui.GetFrameHeight())))
{
ImGui.Dummy(new Vector2(0f, ImGui.GetFrameHeight()));
using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(0f, 0f)))
@@ -83,11 +89,16 @@ public class DrawGroupedGroupFolder : IDrawFolder
{
ImGui.TextUnformatted(_tag);
ImGui.SameLine();
DrawPauseButton();
ImGui.SameLine();
DrawMenu();
} else
{
ImGui.TextUnformatted("All Syncshells");
ImGui.SameLine();
DrawPauseButton();
}
}
color.Dispose();
@@ -100,11 +111,49 @@ public class DrawGroupedGroupFolder : IDrawFolder
using var indent = ImRaii.PushIndent(20f);
foreach (var entry in _groups)
{
entry.Draw();
entry.GroupDrawFolder.Draw();
}
}
}
protected void DrawPauseButton()
{
if (DrawPairs.Count > 0)
{
var isPaused = _groups.Select(g => g.GroupFullInfo).All(g => g.GroupUserPermissions.IsPaused());
FontAwesomeIcon pauseIcon = isPaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
var pauseButtonSize = _uiSharedService.GetIconButtonSize(pauseIcon);
var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth();
if (_tag != "")
{
var spacingX = ImGui.GetStyle().ItemSpacing.X;
var menuButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.EllipsisV);
ImGui.SameLine(windowEndX - pauseButtonSize.X - menuButtonSize.X - spacingX);
}
else
{
ImGui.SameLine(windowEndX - pauseButtonSize.X);
}
if (_uiSharedService.IconButton(pauseIcon))
{
ChangePauseStateGroups();
}
}
}
protected void ChangePauseStateGroups()
{
foreach(var group in _groups)
{
var perm = group.GroupFullInfo.GroupUserPermissions;
perm.SetPaused(!perm.IsPaused());
_ = _apiController.GroupChangeIndividualPermissionState(new GroupPairUserPermissionDto(group.GroupFullInfo.Group, new(_apiController.UID), perm));
}
}
protected void DrawMenu()
{
var barButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.EllipsisV);

View File

@@ -1,5 +1,4 @@

using System.Collections.Immutable;
using System.Collections.Immutable;
namespace LightlessSync.UI.Components;

View File

@@ -2,19 +2,22 @@ using Dalamud.Game.Gui.Dtr;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using LightlessSync.LightlessConfiguration;
using LightlessSync.LightlessConfiguration.Configurations;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services;
using LightlessSync.Services.Mediator;
using LightlessSync.Services.ServerConfiguration;
using LightlessSync.Utils;
using LightlessSync.WebAPI;
using LightlessSync.WebAPI.SignalR.Utils;
using LightlessSync.Utils;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using System.Runtime.InteropServices;
using System.Text;
using static LightlessSync.Services.PairRequestService;
namespace LightlessSync.UI;
@@ -106,7 +109,7 @@ public sealed class DtrEntry : IDisposable, IHostedService
}
catch (OperationCanceledException)
{
_logger.LogInformation("Lightfinder operation was canceled.");
}
finally
{
@@ -363,29 +366,46 @@ public sealed class DtrEntry : IDisposable, IHostedService
}
}
private int GetNearbyBroadcastCount()
{
var localHashedCid = GetLocalHashedCid();
return _broadcastScannerService.CountActiveBroadcasts(
string.IsNullOrEmpty(localHashedCid) ? null : localHashedCid);
}
private int GetPendingPairRequestCount()
private List<string> GetNearbyBroadcasts()
{
try
{
return _pairRequestService.GetActiveRequests().Count;
var localHashedCid = GetLocalHashedCid();
return [.. _broadcastScannerService
.GetActiveBroadcasts(string.IsNullOrEmpty(localHashedCid) ? null : localHashedCid)
.Select(b => _dalamudUtilService.FindPlayerByNameHash(b.Key).Name)];
}
catch (Exception ex)
{
var now = DateTime.UtcNow;
if (now >= _pairRequestNextErrorLog)
{
_logger.LogDebug(ex, "Failed to retrieve nearby broadcasts for Lightfinder DTR entry.");
_pairRequestNextErrorLog = now + _localHashedCidErrorCooldown;
}
return [];
}
}
private IReadOnlyList<PairRequestDisplay> GetPendingPairRequest()
{
try
{
return _pairRequestService.GetActiveRequests();
}
catch (Exception ex)
{
var now = DateTime.UtcNow;
if (now >= _pairRequestNextErrorLog)
{
_logger.LogDebug(ex, "Failed to retrieve pair request count for Lightfinder DTR entry.");
_pairRequestNextErrorLog = now + _localHashedCidErrorCooldown;
}
return 0;
return [];
}
}
@@ -400,23 +420,15 @@ public sealed class DtrEntry : IDisposable, IHostedService
if (_broadcastService.IsBroadcasting)
{
var tooltipBuilder = new StringBuilder("Lightfinder - Enabled");
switch (config.LightfinderDtrDisplayMode)
{
case LightfinderDtrDisplayMode.PendingPairRequests:
{
var requestCount = GetPendingPairRequestCount();
tooltipBuilder.AppendLine();
tooltipBuilder.Append("Pending pair requests: ").Append(requestCount);
return ($"{icon} Requests {requestCount}", SwapColorChannels(config.DtrColorsLightfinderEnabled), tooltipBuilder.ToString());
return FormatTooltip("Pending pair requests", GetPendingPairRequest().Select(x => x.DisplayName), icon, SwapColorChannels(config.DtrColorsLightfinderEnabled));
}
default:
{
var broadcastCount = GetNearbyBroadcastCount();
tooltipBuilder.AppendLine();
tooltipBuilder.Append("Nearby Lightfinder users: ").Append(broadcastCount);
return ($"{icon} {broadcastCount}", SwapColorChannels(config.DtrColorsLightfinderEnabled), tooltipBuilder.ToString());
return FormatTooltip("Nearby Lightfinder users", GetNearbyBroadcasts(), icon, SwapColorChannels(config.DtrColorsLightfinderEnabled));
}
}
}
@@ -433,6 +445,18 @@ public sealed class DtrEntry : IDisposable, IHostedService
return ($"{icon} OFF", colors, tooltip.ToString());
}
private (string, Colors, string) FormatTooltip(string title, IEnumerable<string> names, string icon, Colors color)
{
var list = names.Where(x => !string.IsNullOrEmpty(x)).ToList();
var tooltip = new StringBuilder()
.Append($"Lightfinder - Enabled{Environment.NewLine}")
.Append($"{title}: {list.Count}{Environment.NewLine}")
.AppendJoin(Environment.NewLine, list)
.ToString();
return ($"{icon} {list.Count}", color, tooltip);
}
private static string BuildLightfinderTooltip(string baseTooltip)
{
var builder = new StringBuilder();

View File

@@ -0,0 +1,6 @@
using LightlessSync.API.Dto.Group;
using LightlessSync.UI.Components;
namespace LightlessSync.UI.Models;
public record GroupFolder(GroupFullInfoDto GroupFullInfo, IDrawFolder GroupDrawFolder);

View File

@@ -1227,16 +1227,16 @@ public class SettingsUi : WindowMediatorSubscriberBase
ImGui.TextUnformatted($"Currently utilized local storage: Calculating...");
ImGui.TextUnformatted(
$"Remaining space free on drive: {UiSharedService.ByteToString(_cacheMonitor.FileCacheDriveFree)}");
bool useFileCompactor = _configService.Current.UseCompactor;
bool isLinux = _dalamudUtilService.IsWine;
if (!useFileCompactor && !isLinux)
if (!useFileCompactor)
{
UiSharedService.ColorTextWrapped(
"Hint: To free up space when using Lightless consider enabling the File Compactor",
UIColors.Get("LightlessYellow"));
}
if (isLinux || !_cacheMonitor.StorageisNTFS) ImGui.BeginDisabled();
if (!_cacheMonitor.StorageIsBtrfs && !_cacheMonitor.StorageisNTFS) ImGui.BeginDisabled();
if (ImGui.Checkbox("Use file compactor", ref useFileCompactor))
{
_configService.Current.UseCompactor = useFileCompactor;
@@ -1281,10 +1281,20 @@ public class SettingsUi : WindowMediatorSubscriberBase
UIColors.Get("LightlessYellow"));
}
if (isLinux || !_cacheMonitor.StorageisNTFS)
if (!_cacheMonitor.StorageIsBtrfs && !_cacheMonitor.StorageisNTFS)
{
ImGui.EndDisabled();
ImGui.TextUnformatted("The file compactor is only available on Windows and NTFS drives.");
ImGui.TextUnformatted("The file compactor is only available on BTRFS and NTFS drives.");
}
if (_cacheMonitor.StorageisNTFS)
{
ImGui.TextUnformatted("The file compactor is running on NTFS Drive.");
}
if (_cacheMonitor.StorageIsBtrfs)
{
ImGui.TextUnformatted("The file compactor is running on Btrfs Drive.");
}
ImGuiHelpers.ScaledDummy(new Vector2(10, 10));
@@ -3113,22 +3123,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
UiSharedService.TooltipSeparator
+ "Note: if the server does not support a specific Transport Type it will fall through to the next automatically: WebSockets > ServerSentEvents > LongPolling");
if (_dalamudUtilService.IsWine)
{
bool forceWebSockets = selectedServer.ForceWebSockets;
if (ImGui.Checkbox("[wine only] Force WebSockets", ref forceWebSockets))
{
selectedServer.ForceWebSockets = forceWebSockets;
_serverConfigurationManager.Save();
}
_uiShared.DrawHelpText(
"On wine, Lightless will automatically fall back to ServerSentEvents/LongPolling, even if WebSockets is selected. "
+ "WebSockets are known to crash XIV entirely on wine 8.5 shipped with Dalamud. "
+ "Only enable this if you are not running wine 8.5." + Environment.NewLine
+ "Note: If the issue gets resolved at some point this option will be removed.");
}
ImGuiHelpers.ScaledDummy(5);
if (ImGui.Checkbox("Use Discord OAuth2 Authentication", ref useOauth))