2.0.0 #92
Submodule LightlessAPI updated: 0170ac377d...dfb0594a5b
@@ -379,7 +379,8 @@ public sealed class PairManager
|
|||||||
dto.GroupPermissions,
|
dto.GroupPermissions,
|
||||||
shell.GroupFullInfo.GroupUserPermissions,
|
shell.GroupFullInfo.GroupUserPermissions,
|
||||||
shell.GroupFullInfo.GroupUserInfo,
|
shell.GroupFullInfo.GroupUserInfo,
|
||||||
new Dictionary<string, GroupPairUserInfo>(shell.GroupFullInfo.GroupPairUserInfos, StringComparer.Ordinal));
|
new Dictionary<string, GroupPairUserInfo>(shell.GroupFullInfo.GroupPairUserInfos, StringComparer.Ordinal),
|
||||||
|
0);
|
||||||
|
|
||||||
shell.Update(updated);
|
shell.Update(updated);
|
||||||
return PairOperationResult.Ok();
|
return PairOperationResult.Ok();
|
||||||
@@ -514,7 +515,8 @@ public sealed class PairManager
|
|||||||
GroupPermissions.NoneSet,
|
GroupPermissions.NoneSet,
|
||||||
GroupUserPreferredPermissions.NoneSet,
|
GroupUserPreferredPermissions.NoneSet,
|
||||||
GroupPairUserInfo.None,
|
GroupPairUserInfo.None,
|
||||||
new Dictionary<string, GroupPairUserInfo>(StringComparer.Ordinal));
|
new Dictionary<string, GroupPairUserInfo>(StringComparer.Ordinal),
|
||||||
|
0);
|
||||||
|
|
||||||
shell = new Syncshell(placeholder);
|
shell = new Syncshell(placeholder);
|
||||||
_groups[group.GID] = shell;
|
_groups[group.GID] = shell;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class VisibleUserDataDistributor : DisposableMediatorSubscriberBase
|
|||||||
private readonly List<UserData> _previouslyVisiblePlayers = [];
|
private readonly List<UserData> _previouslyVisiblePlayers = [];
|
||||||
private Task<CharacterData>? _fileUploadTask = null;
|
private Task<CharacterData>? _fileUploadTask = null;
|
||||||
private readonly HashSet<UserData> _usersToPushDataTo = new(UserDataComparer.Instance);
|
private readonly HashSet<UserData> _usersToPushDataTo = new(UserDataComparer.Instance);
|
||||||
private readonly SemaphoreSlim _pushDataSemaphore = new(1, 1);
|
private readonly SemaphoreSlim _pushLock = new(1, 1);
|
||||||
private readonly CancellationTokenSource _runtimeCts = new();
|
private readonly CancellationTokenSource _runtimeCts = new();
|
||||||
|
|
||||||
public VisibleUserDataDistributor(ILogger<VisibleUserDataDistributor> logger, ApiController apiController, DalamudUtilService dalamudUtil,
|
public VisibleUserDataDistributor(ILogger<VisibleUserDataDistributor> logger, ApiController apiController, DalamudUtilService dalamudUtil,
|
||||||
@@ -108,53 +108,49 @@ public class VisibleUserDataDistributor : DisposableMediatorSubscriberBase
|
|||||||
private void PushCharacterData(bool forced = false)
|
private void PushCharacterData(bool forced = false)
|
||||||
{
|
{
|
||||||
if (_lastCreatedData == null || _usersToPushDataTo.Count == 0) return;
|
if (_lastCreatedData == null || _usersToPushDataTo.Count == 0) return;
|
||||||
|
_ = PushCharacterDataAsync(forced);
|
||||||
|
}
|
||||||
|
|
||||||
_ = Task.Run(async () =>
|
private async Task PushCharacterDataAsync(bool forced = false)
|
||||||
|
{
|
||||||
|
await _pushLock.WaitAsync(_runtimeCts.Token).ConfigureAwait(false);
|
||||||
|
try
|
||||||
{
|
{
|
||||||
try
|
if (_lastCreatedData == null || _usersToPushDataTo.Count == 0)
|
||||||
{
|
return;
|
||||||
forced |= _uploadingCharacterData?.DataHash != _lastCreatedData.DataHash;
|
|
||||||
|
|
||||||
if (_fileUploadTask == null || (_fileUploadTask?.IsCompleted ?? false) || forced)
|
var hashChanged = _uploadingCharacterData?.DataHash != _lastCreatedData.DataHash;
|
||||||
|
forced |= hashChanged;
|
||||||
|
|
||||||
|
if (_fileUploadTask == null || _fileUploadTask.IsCompleted || forced)
|
||||||
{
|
{
|
||||||
_uploadingCharacterData = _lastCreatedData.DeepClone();
|
_uploadingCharacterData = _lastCreatedData.DeepClone();
|
||||||
|
var uploadTargets = _usersToPushDataTo.ToList();
|
||||||
Logger.LogDebug("Starting UploadTask for {hash}, Reason: TaskIsNull: {task}, TaskIsCompleted: {taskCpl}, Forced: {frc}",
|
Logger.LogDebug("Starting UploadTask for {hash}, Reason: TaskIsNull: {task}, TaskIsCompleted: {taskCpl}, Forced: {frc}",
|
||||||
_lastCreatedData.DataHash, _fileUploadTask == null, _fileUploadTask?.IsCompleted ?? false, forced);
|
_lastCreatedData.DataHash,
|
||||||
_fileUploadTask = _fileTransferManager.UploadFiles(_uploadingCharacterData, [.. _usersToPushDataTo]);
|
_fileUploadTask == null,
|
||||||
|
_fileUploadTask?.IsCompleted ?? false,
|
||||||
|
forced);
|
||||||
|
|
||||||
|
_fileUploadTask = _fileTransferManager.UploadFiles(_uploadingCharacterData, uploadTargets);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_fileUploadTask != null)
|
var dataToSend = await _fileUploadTask.ConfigureAwait(false);
|
||||||
{
|
|
||||||
var dataToSend = await _fileUploadTask.ConfigureAwait(false);
|
var users = _usersToPushDataTo.ToList();
|
||||||
await _pushDataSemaphore.WaitAsync(_runtimeCts.Token).ConfigureAwait(false);
|
if (users.Count == 0)
|
||||||
try
|
return;
|
||||||
{
|
|
||||||
if (_usersToPushDataTo.Count == 0) return;
|
Logger.LogDebug("Pushing {data} to {users}", dataToSend.DataHash, string.Join(", ", users.Select(k => k.AliasOrUID)));
|
||||||
Logger.LogDebug("Pushing {data} to {users}", dataToSend.DataHash, string.Join(", ", _usersToPushDataTo.Select(k => k.AliasOrUID)));
|
|
||||||
await _apiController.PushCharacterData(dataToSend, [.. _usersToPushDataTo]).ConfigureAwait(false);
|
await _apiController.PushCharacterData(dataToSend, users).ConfigureAwait(false);
|
||||||
_usersToPushDataTo.Clear();
|
_usersToPushDataTo.Clear();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_pushDataSemaphore.Release();
|
_pushLock.Release();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException) when (_runtimeCts.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
Logger.LogDebug("PushCharacterData cancelled");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogError(ex, "Failed to push character data");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<UserData> GetVisibleUsers()
|
private List<UserData> GetVisibleUsers() => [.. _pairLedger.GetVisiblePairs().Select(connection => connection.User)];
|
||||||
{
|
|
||||||
return _pairLedger.GetVisiblePairs()
|
|
||||||
.Select(connection => connection.User)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -957,11 +957,10 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
private ImmutableList<PairUiEntry> SortEntries(IEnumerable<PairUiEntry> entries)
|
private ImmutableList<PairUiEntry> SortEntries(IEnumerable<PairUiEntry> entries)
|
||||||
{
|
{
|
||||||
return entries
|
return [.. entries
|
||||||
.OrderByDescending(e => e.IsVisible)
|
.OrderByDescending(e => e.IsVisible)
|
||||||
.ThenByDescending(e => e.IsOnline)
|
.ThenByDescending(e => e.IsOnline)
|
||||||
.ThenBy(e => AlphabeticalSortKey(e), StringComparer.OrdinalIgnoreCase)
|
.ThenBy(e => AlphabeticalSortKey(e), StringComparer.OrdinalIgnoreCase)];
|
||||||
.ToImmutableList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImmutableList<PairUiEntry> SortVisibleEntries(IEnumerable<PairUiEntry> entries)
|
private ImmutableList<PairUiEntry> SortVisibleEntries(IEnumerable<PairUiEntry> entries)
|
||||||
@@ -972,9 +971,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
VisiblePairSortMode.VramUsage => SortVisibleByMetric(entryList, e => e.LastAppliedApproximateVramBytes),
|
VisiblePairSortMode.VramUsage => SortVisibleByMetric(entryList, e => e.LastAppliedApproximateVramBytes),
|
||||||
VisiblePairSortMode.EffectiveVramUsage => SortVisibleByMetric(entryList, e => e.LastAppliedApproximateEffectiveVramBytes),
|
VisiblePairSortMode.EffectiveVramUsage => SortVisibleByMetric(entryList, e => e.LastAppliedApproximateEffectiveVramBytes),
|
||||||
VisiblePairSortMode.TriangleCount => SortVisibleByMetric(entryList, e => e.LastAppliedDataTris),
|
VisiblePairSortMode.TriangleCount => SortVisibleByMetric(entryList, e => e.LastAppliedDataTris),
|
||||||
VisiblePairSortMode.Alphabetical => entryList
|
VisiblePairSortMode.Alphabetical => [.. entryList.OrderBy(e => AlphabeticalSortKey(e), StringComparer.OrdinalIgnoreCase)],
|
||||||
.OrderBy(e => AlphabeticalSortKey(e), StringComparer.OrdinalIgnoreCase)
|
|
||||||
.ToImmutableList(),
|
|
||||||
VisiblePairSortMode.PreferredDirectPairs => SortVisibleByPreferred(entryList),
|
VisiblePairSortMode.PreferredDirectPairs => SortVisibleByPreferred(entryList),
|
||||||
_ => SortEntries(entryList),
|
_ => SortEntries(entryList),
|
||||||
};
|
};
|
||||||
@@ -982,31 +979,28 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
private ImmutableList<PairUiEntry> SortVisibleByMetric(IEnumerable<PairUiEntry> entries, Func<PairUiEntry, long> selector)
|
private ImmutableList<PairUiEntry> SortVisibleByMetric(IEnumerable<PairUiEntry> entries, Func<PairUiEntry, long> selector)
|
||||||
{
|
{
|
||||||
return entries
|
return [.. entries
|
||||||
.OrderByDescending(entry => selector(entry) >= 0)
|
.OrderByDescending(entry => selector(entry) >= 0)
|
||||||
.ThenByDescending(selector)
|
.ThenByDescending(selector)
|
||||||
.ThenByDescending(entry => entry.IsOnline)
|
.ThenByDescending(entry => entry.IsOnline)
|
||||||
.ThenBy(entry => AlphabeticalSortKey(entry), StringComparer.OrdinalIgnoreCase)
|
.ThenBy(entry => AlphabeticalSortKey(entry), StringComparer.OrdinalIgnoreCase)];
|
||||||
.ToImmutableList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImmutableList<PairUiEntry> SortVisibleByPreferred(IEnumerable<PairUiEntry> entries)
|
private ImmutableList<PairUiEntry> SortVisibleByPreferred(IEnumerable<PairUiEntry> entries)
|
||||||
{
|
{
|
||||||
return entries
|
return [.. entries
|
||||||
.OrderByDescending(entry => entry.IsDirectlyPaired && entry.SelfPermissions.IsSticky())
|
.OrderByDescending(entry => entry.IsDirectlyPaired && entry.SelfPermissions.IsSticky())
|
||||||
.ThenByDescending(entry => entry.IsDirectlyPaired)
|
.ThenByDescending(entry => entry.IsDirectlyPaired)
|
||||||
.ThenByDescending(entry => entry.IsOnline)
|
.ThenByDescending(entry => entry.IsOnline)
|
||||||
.ThenBy(entry => AlphabeticalSortKey(entry), StringComparer.OrdinalIgnoreCase)
|
.ThenBy(entry => AlphabeticalSortKey(entry), StringComparer.OrdinalIgnoreCase)];
|
||||||
.ToImmutableList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImmutableList<PairUiEntry> SortGroupEntries(IEnumerable<PairUiEntry> entries, GroupFullInfoDto group)
|
private ImmutableList<PairUiEntry> SortGroupEntries(IEnumerable<PairUiEntry> entries, GroupFullInfoDto group)
|
||||||
{
|
{
|
||||||
return entries
|
return [.. entries
|
||||||
.OrderByDescending(e => e.IsOnline)
|
.OrderByDescending(e => e.IsOnline)
|
||||||
.ThenBy(e => GroupSortWeight(e, group))
|
.ThenBy(e => GroupSortWeight(e, group))
|
||||||
.ThenBy(e => AlphabeticalSortKey(e), StringComparer.OrdinalIgnoreCase)
|
.ThenBy(e => AlphabeticalSortKey(e), StringComparer.OrdinalIgnoreCase)];
|
||||||
.ToImmutableList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GroupSortWeight(PairUiEntry entry, GroupFullInfoDto group)
|
private int GroupSortWeight(PairUiEntry entry, GroupFullInfoDto group)
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
// Check if download notifications are enabled (not set to TextOverlay)
|
// Check if download notifications are enabled (not set to TextOverlay)
|
||||||
var useNotifications = _configService.Current.UseLightlessNotifications
|
var useNotifications = _configService.Current.UseLightlessNotifications
|
||||||
? _configService.Current.LightlessDownloadNotification != NotificationLocation.TextOverlay
|
? _configService.Current.LightlessDownloadNotification != NotificationLocation.LightlessUi
|
||||||
: _configService.Current.UseNotificationsForDownloads;
|
: _configService.Current.UseNotificationsForDownloads;
|
||||||
|
|
||||||
if (useNotifications)
|
if (useNotifications)
|
||||||
@@ -534,6 +534,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
if (lineSize.X > contentWidth)
|
if (lineSize.X > contentWidth)
|
||||||
contentWidth = lineSize.X;
|
contentWidth = lineSize.X;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var lineHeight = ImGui.GetTextLineHeight();
|
var lineHeight = ImGui.GetTextLineHeight();
|
||||||
@@ -635,32 +636,40 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
foreach (var p in orderedPlayers)
|
foreach (var p in orderedPlayers)
|
||||||
{
|
{
|
||||||
var playerSpeedText = p.SpeedBytesPerSecond > 0
|
var hasSpeed = p.SpeedBytesPerSecond > 0;
|
||||||
|
var playerSpeedText = hasSpeed
|
||||||
? $"{UiSharedService.ByteToString((long)p.SpeedBytesPerSecond)}/s"
|
? $"{UiSharedService.ByteToString((long)p.SpeedBytesPerSecond)}/s"
|
||||||
: "-";
|
: "-";
|
||||||
|
|
||||||
|
// Label line for the player
|
||||||
var labelLine =
|
var labelLine =
|
||||||
$"{p.Name} [W:{p.DlSlot}/Q:{p.DlQueue}/P:{p.DlProg}/D:{p.DlDecomp}] {p.TransferredFiles}/{p.TotalFiles}";
|
$"{p.Name} [W:{p.DlSlot}/Q:{p.DlQueue}/P:{p.DlProg}/D:{p.DlDecomp}] {p.TransferredFiles}/{p.TotalFiles}";
|
||||||
|
|
||||||
if (!_configService.Current.ShowPlayerSpeedBarsTransferWindow || p.DlProg <= 0)
|
// State flags
|
||||||
{
|
var isDownloading = p.DlProg > 0;
|
||||||
var fullLine =
|
var isDecompressing = p.DlDecomp > 0
|
||||||
$"{labelLine} " +
|
|| (!isDownloading && p.TotalBytes > 0 && p.TransferredBytes >= p.TotalBytes);
|
||||||
$"({UiSharedService.ByteToString(p.TransferredBytes, addSuffix: false)}/{UiSharedService.ByteToString(p.TotalBytes)}) " +
|
|
||||||
$"@ {playerSpeedText}";
|
|
||||||
|
|
||||||
|
|
||||||
|
var showBar = _configService.Current.ShowPlayerSpeedBarsTransferWindow
|
||||||
|
&& (isDownloading || isDecompressing);
|
||||||
|
|
||||||
|
if (!showBar)
|
||||||
|
{
|
||||||
UiSharedService.DrawOutlinedFont(
|
UiSharedService.DrawOutlinedFont(
|
||||||
drawList,
|
drawList,
|
||||||
fullLine,
|
labelLine,
|
||||||
cursor,
|
cursor,
|
||||||
UiSharedService.Color(255, 255, 255, _transferBoxTransparency),
|
UiSharedService.Color(255, 255, 255, _transferBoxTransparency),
|
||||||
UiSharedService.Color(0, 0, 0, _transferBoxTransparency),
|
UiSharedService.Color(0, 0, 0, _transferBoxTransparency),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
cursor.Y += lineHeight + spacingY;
|
cursor.Y += lineHeight + spacingY;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Top label line (only name + W/Q/P/D + files)
|
||||||
UiSharedService.DrawOutlinedFont(
|
UiSharedService.DrawOutlinedFont(
|
||||||
drawList,
|
drawList,
|
||||||
labelLine,
|
labelLine,
|
||||||
@@ -671,6 +680,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
);
|
);
|
||||||
cursor.Y += lineHeight + spacingY;
|
cursor.Y += lineHeight + spacingY;
|
||||||
|
|
||||||
|
// Bar background
|
||||||
var barBgMin = new Vector2(boxMin.X + padding, cursor.Y);
|
var barBgMin = new Vector2(boxMin.X + padding, cursor.Y);
|
||||||
var barBgMax = new Vector2(boxMax.X - padding, cursor.Y + perPlayerBarHeight);
|
var barBgMax = new Vector2(boxMax.X - padding, cursor.Y + perPlayerBarHeight);
|
||||||
|
|
||||||
@@ -682,8 +692,14 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
);
|
);
|
||||||
|
|
||||||
float ratio = 0f;
|
float ratio = 0f;
|
||||||
if (maxSpeed > 0)
|
if (isDownloading && p.TotalBytes > 0)
|
||||||
ratio = (float)(p.SpeedBytesPerSecond / maxSpeed);
|
{
|
||||||
|
ratio = (float)p.TransferredBytes / p.TotalBytes;
|
||||||
|
}
|
||||||
|
else if (isDecompressing)
|
||||||
|
{
|
||||||
|
ratio = 1f;
|
||||||
|
}
|
||||||
|
|
||||||
if (ratio < 0f) ratio = 0f;
|
if (ratio < 0f) ratio = 0f;
|
||||||
if (ratio > 1f) ratio = 1f;
|
if (ratio > 1f) ratio = 1f;
|
||||||
@@ -698,24 +714,43 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
3f
|
3f
|
||||||
);
|
);
|
||||||
|
|
||||||
var barText =
|
string barText;
|
||||||
$"{UiSharedService.ByteToString(p.TransferredBytes, addSuffix: false)}/{UiSharedService.ByteToString(p.TotalBytes)} @ {playerSpeedText}";
|
|
||||||
|
|
||||||
var barTextSize = ImGui.CalcTextSize(barText);
|
if (isDownloading)
|
||||||
|
{
|
||||||
|
var bytesInside =
|
||||||
|
$"{UiSharedService.ByteToString(p.TransferredBytes, addSuffix: false)}/{UiSharedService.ByteToString(p.TotalBytes)}";
|
||||||
|
|
||||||
var barTextPos = new Vector2(
|
barText = hasSpeed
|
||||||
barBgMin.X + ((barBgMax.X - barBgMin.X) - barTextSize.X) / 2f - 1,
|
? $"{bytesInside} @ {playerSpeedText}"
|
||||||
barBgMin.Y + ((perPlayerBarHeight - barTextSize.Y) / 2f) - 1
|
: bytesInside;
|
||||||
);
|
}
|
||||||
|
else if (isDecompressing)
|
||||||
|
{
|
||||||
|
barText = "Decompressing...";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
barText = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
UiSharedService.DrawOutlinedFont(
|
if (!string.IsNullOrEmpty(barText))
|
||||||
drawList,
|
{
|
||||||
barText,
|
var barTextSize = ImGui.CalcTextSize(barText);
|
||||||
barTextPos,
|
var barTextPos = new Vector2(
|
||||||
UiSharedService.Color(255, 255, 255, _transferBoxTransparency),
|
barBgMin.X + ((barBgMax.X - barBgMin.X) - barTextSize.X) / 2f - 1,
|
||||||
UiSharedService.Color(0, 0, 0, _transferBoxTransparency),
|
barBgMin.Y + ((perPlayerBarHeight - barTextSize.Y) / 2f) - 1
|
||||||
1
|
);
|
||||||
);
|
|
||||||
|
UiSharedService.DrawOutlinedFont(
|
||||||
|
drawList,
|
||||||
|
barText,
|
||||||
|
barTextPos,
|
||||||
|
UiSharedService.Color(255, 255, 255, _transferBoxTransparency),
|
||||||
|
UiSharedService.Color(0, 0, 0, _transferBoxTransparency),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
cursor.Y += perPlayerBarHeight + spacingY;
|
cursor.Y += perPlayerBarHeight + spacingY;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Dalamud.Game.Gui.Dtr;
|
|||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Dalamud.Utility;
|
|
||||||
using LightlessSync.LightlessConfiguration;
|
using LightlessSync.LightlessConfiguration;
|
||||||
using LightlessSync.LightlessConfiguration.Configurations;
|
using LightlessSync.LightlessConfiguration.Configurations;
|
||||||
using LightlessSync.Services;
|
using LightlessSync.Services;
|
||||||
@@ -13,11 +12,9 @@ using LightlessSync.WebAPI;
|
|||||||
using LightlessSync.WebAPI.SignalR.Utils;
|
using LightlessSync.WebAPI.SignalR.Utils;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Primitives;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using LightlessSync.UI.Services;
|
using LightlessSync.UI.Services;
|
||||||
using LightlessSync.PlayerData.Pairs;
|
|
||||||
using static LightlessSync.Services.PairRequestService;
|
using static LightlessSync.Services.PairRequestService;
|
||||||
using LightlessSync.Services.LightFinder;
|
using LightlessSync.Services.LightFinder;
|
||||||
|
|
||||||
|
|||||||
@@ -2,26 +2,17 @@ using Dalamud.Bindings.ImGui;
|
|||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.Components;
|
using Dalamud.Interface.Components;
|
||||||
using Dalamud.Interface.ImGuiFileDialog;
|
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using LightlessSync.API.Data;
|
|
||||||
using LightlessSync.API.Dto.Group;
|
using LightlessSync.API.Dto.Group;
|
||||||
using LightlessSync.Services;
|
|
||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
using LightlessSync.Services.Profiles;
|
using LightlessSync.Services.Profiles;
|
||||||
using LightlessSync.UI.Tags;
|
using LightlessSync.UI.Tags;
|
||||||
using LightlessSync.Utils;
|
using LightlessSync.Utils;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.Formats;
|
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LightlessSync.UI;
|
namespace LightlessSync.UI;
|
||||||
|
|
||||||
@@ -68,6 +59,7 @@ public partial class EditProfileUi
|
|||||||
_bannerTextureWrap = null;
|
_bannerTextureWrap = null;
|
||||||
_showProfileImageError = false;
|
_showProfileImageError = false;
|
||||||
_showBannerImageError = false;
|
_showBannerImageError = false;
|
||||||
|
_groupVisibilityInitialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawGroupEditor(float scale)
|
private void DrawGroupEditor(float scale)
|
||||||
@@ -376,6 +368,8 @@ public partial class EditProfileUi
|
|||||||
|
|
||||||
private void DrawGroupProfileVisibilityControls()
|
private void DrawGroupProfileVisibilityControls()
|
||||||
{
|
{
|
||||||
|
EnsureGroupVisibilityStateInitialised();
|
||||||
|
|
||||||
bool changedNsfw = DrawCheckboxRow("Profile is NSFW", _groupIsNsfw, out var newNsfw, "Flag this profile as not safe for work.");
|
bool changedNsfw = DrawCheckboxRow("Profile is NSFW", _groupIsNsfw, out var newNsfw, "Flag this profile as not safe for work.");
|
||||||
if (changedNsfw)
|
if (changedNsfw)
|
||||||
_groupIsNsfw = newNsfw;
|
_groupIsNsfw = newNsfw;
|
||||||
@@ -504,33 +498,36 @@ public partial class EditProfileUi
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var fileContent = await File.ReadAllBytesAsync(filePath).ConfigureAwait(false);
|
var fileContent = await File.ReadAllBytesAsync(filePath).ConfigureAwait(false);
|
||||||
await using var stream = new MemoryStream(fileContent);
|
var stream = new MemoryStream(fileContent);
|
||||||
var format = await Image.DetectFormatAsync(stream).ConfigureAwait(false);
|
await using (stream.ConfigureAwait(false))
|
||||||
if (!IsSupportedImageFormat(format))
|
|
||||||
{
|
{
|
||||||
_showBannerImageError = true;
|
var format = await Image.DetectFormatAsync(stream).ConfigureAwait(false);
|
||||||
return;
|
if (!IsSupportedImageFormat(format))
|
||||||
|
{
|
||||||
|
_showBannerImageError = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var image = Image.Load<Rgba32>(fileContent);
|
||||||
|
if (image.Width > 840 || image.Height > 260 || fileContent.Length > 2000 * 1024)
|
||||||
|
{
|
||||||
|
_showBannerImageError = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _apiController.GroupSetProfile(new GroupProfileDto(
|
||||||
|
_groupInfo.Group,
|
||||||
|
Description: null,
|
||||||
|
Tags: null,
|
||||||
|
PictureBase64: null,
|
||||||
|
BannerBase64: Convert.ToBase64String(fileContent),
|
||||||
|
IsNsfw: null,
|
||||||
|
IsDisabled: null)).ConfigureAwait(false);
|
||||||
|
|
||||||
|
_showBannerImageError = false;
|
||||||
|
_queuedBannerImage = fileContent;
|
||||||
|
Mediator.Publish(new ClearProfileGroupDataMessage(_groupInfo.Group));
|
||||||
}
|
}
|
||||||
|
|
||||||
using var image = Image.Load<Rgba32>(fileContent);
|
|
||||||
if (image.Width > 840 || image.Height > 260 || fileContent.Length > 2000 * 1024)
|
|
||||||
{
|
|
||||||
_showBannerImageError = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await _apiController.GroupSetProfile(new GroupProfileDto(
|
|
||||||
_groupInfo.Group,
|
|
||||||
Description: null,
|
|
||||||
Tags: null,
|
|
||||||
PictureBase64: null,
|
|
||||||
BannerBase64: Convert.ToBase64String(fileContent),
|
|
||||||
IsNsfw: null,
|
|
||||||
IsDisabled: null)).ConfigureAwait(false);
|
|
||||||
|
|
||||||
_showBannerImageError = false;
|
|
||||||
_queuedBannerImage = fileContent;
|
|
||||||
Mediator.Publish(new ClearProfileGroupDataMessage(_groupInfo.Group));
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -588,6 +585,16 @@ public partial class EditProfileUi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void EnsureGroupVisibilityStateInitialised()
|
||||||
|
{
|
||||||
|
if (_groupInfo == null || _groupVisibilityInitialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_groupIsNsfw = _groupServerIsNsfw;
|
||||||
|
_groupIsDisabled = _groupServerIsDisabled;
|
||||||
|
_groupVisibilityInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SubmitGroupTagChanges(int[] payload)
|
private async Task SubmitGroupTagChanges(int[] payload)
|
||||||
{
|
{
|
||||||
if (_groupInfo is null)
|
if (_groupInfo is null)
|
||||||
@@ -695,11 +702,15 @@ public partial class EditProfileUi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_groupIsNsfw = profile.IsNsfw;
|
|
||||||
_groupIsDisabled = profile.IsDisabled;
|
|
||||||
_groupServerIsNsfw = profile.IsNsfw;
|
_groupServerIsNsfw = profile.IsNsfw;
|
||||||
_groupServerIsDisabled = profile.IsDisabled;
|
_groupServerIsDisabled = profile.IsDisabled;
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!_groupVisibilityInitialized)
|
||||||
|
{
|
||||||
|
_groupIsNsfw = _groupServerIsNsfw;
|
||||||
|
_groupIsDisabled = _groupServerIsDisabled;
|
||||||
|
_groupVisibilityInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public partial class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
"<bold(1|0)> <italic(1|0)> <shadow(1|0)> <edge(1|0)> - toggle style flags.\n" +
|
"<bold(1|0)> <italic(1|0)> <shadow(1|0)> <edge(1|0)> - toggle style flags.\n" +
|
||||||
"<link(0x0E,...)> - create clickable links.";
|
"<link(0x0E,...)> - create clickable links.";
|
||||||
|
|
||||||
private static readonly HashSet<string> SupportedImageExtensions = new(StringComparer.OrdinalIgnoreCase)
|
private static readonly HashSet<string> _supportedImageExtensions = new(StringComparer.OrdinalIgnoreCase)
|
||||||
{
|
{
|
||||||
"png",
|
"png",
|
||||||
"jpg",
|
"jpg",
|
||||||
@@ -52,7 +52,7 @@ public partial class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
"bmp"
|
"bmp"
|
||||||
};
|
};
|
||||||
private const string _imageFileDialogFilter = "Images{.png,.jpg,.jpeg,.webp,.bmp}";
|
private const string _imageFileDialogFilter = "Images{.png,.jpg,.jpeg,.webp,.bmp}";
|
||||||
private readonly List<int> _tagEditorSelection = new();
|
private readonly List<int> _tagEditorSelection = [];
|
||||||
private int[] _profileTagIds = [];
|
private int[] _profileTagIds = [];
|
||||||
private readonly List<SeStringUtils.SeStringSegment> _tagPreviewSegments = new();
|
private readonly List<SeStringUtils.SeStringSegment> _tagPreviewSegments = new();
|
||||||
private enum ProfileEditorMode
|
private enum ProfileEditorMode
|
||||||
@@ -68,6 +68,7 @@ public partial class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
private bool _groupIsDisabled;
|
private bool _groupIsDisabled;
|
||||||
private bool _groupServerIsNsfw;
|
private bool _groupServerIsNsfw;
|
||||||
private bool _groupServerIsDisabled;
|
private bool _groupServerIsDisabled;
|
||||||
|
private bool _groupVisibilityInitialized;
|
||||||
private byte[]? _queuedProfileImage;
|
private byte[]? _queuedProfileImage;
|
||||||
private byte[]? _queuedBannerImage;
|
private byte[]? _queuedBannerImage;
|
||||||
private readonly Vector4 _tagBackgroundColor = new(0.18f, 0.18f, 0.18f, 0.95f);
|
private readonly Vector4 _tagBackgroundColor = new(0.18f, 0.18f, 0.18f, 0.95f);
|
||||||
@@ -85,6 +86,9 @@ public partial class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
private bool _showProfileImageError = false;
|
private bool _showProfileImageError = false;
|
||||||
private bool _showBannerImageError = false;
|
private bool _showBannerImageError = false;
|
||||||
private bool _wasOpen;
|
private bool _wasOpen;
|
||||||
|
private bool _userServerIsNsfw;
|
||||||
|
private bool _isNsfwInitialized;
|
||||||
|
private bool _isNsfw;
|
||||||
|
|
||||||
private Vector4 _currentBg = new(0.15f, 0.15f, 0.15f, 1f);
|
private Vector4 _currentBg = new(0.15f, 0.15f, 0.15f, 1f);
|
||||||
private bool _textEnabled;
|
private bool _textEnabled;
|
||||||
@@ -171,6 +175,8 @@ public partial class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_isNsfwInitialized = false;
|
||||||
|
|
||||||
var user = await EnsureSelfProfileUserDataAsync().ConfigureAwait(false);
|
var user = await EnsureSelfProfileUserDataAsync().ConfigureAwait(false);
|
||||||
if (user is not null)
|
if (user is not null)
|
||||||
{
|
{
|
||||||
@@ -339,13 +345,12 @@ public partial class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
SyncProfileState(profile);
|
SyncProfileState(profile);
|
||||||
|
|
||||||
DrawSection("Profile Preview", scale, () => DrawProfileSnapshot(profile, scale));
|
DrawSection("Profile Preview", scale, () => DrawProfileSnapshot(profile, scale));
|
||||||
DrawSection("Profile Image", scale, () => DrawProfileImageControls(profile, scale));
|
DrawSection("Profile Image", scale, () => DrawProfileImageControls(profile, scale));
|
||||||
DrawSection("Profile Banner", scale, () => DrawProfileBannerControls(profile, scale));
|
DrawSection("Profile Banner", scale, () => DrawProfileBannerControls(profile, scale));
|
||||||
DrawSection("Profile Description", scale, () => DrawProfileDescriptionEditor(profile, scale));
|
DrawSection("Profile Description", scale, () => DrawProfileDescriptionEditor(profile, scale));
|
||||||
DrawSection("Profile Tags", scale, () => DrawProfileTagsEditor(profile, scale));
|
DrawSection("Profile Tags", scale, () => DrawProfileTagsEditor(profile, scale));
|
||||||
DrawSection("Visibility", scale, () => DrawProfileVisibilityControls(profile));
|
DrawSection("Visibility", scale, () => DrawProfileVisibilityControls());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawProfileSnapshot(LightlessUserProfileData profile, float scale)
|
private void DrawProfileSnapshot(LightlessUserProfileData profile, float scale)
|
||||||
@@ -877,21 +882,46 @@ public partial class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
UiSharedService.AttachToolTip(saveTooltip);
|
UiSharedService.AttachToolTip(saveTooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawProfileVisibilityControls(LightlessUserProfileData profile)
|
private void DrawProfileVisibilityControls()
|
||||||
{
|
{
|
||||||
var isNsfw = profile.IsNSFW;
|
if (!_isNsfwInitialized)
|
||||||
if (DrawCheckboxRow("Mark profile as NSFW", isNsfw, out var newValue, "Enable when your profile could be considered NSFW."))
|
ImGui.BeginDisabled();
|
||||||
|
|
||||||
|
bool changed = DrawCheckboxRow("Mark profile as NSFW", _isNsfw, out var newValue, "Enable when your profile could be considered NSFW.");
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
_isNsfw = newValue;
|
||||||
|
|
||||||
|
bool visibilityChanged = _isNsfwInitialized && (_isNsfw != _userServerIsNsfw);
|
||||||
|
|
||||||
|
if (!_isNsfwInitialized)
|
||||||
|
ImGui.EndDisabled();
|
||||||
|
|
||||||
|
if (!_isNsfwInitialized || !visibilityChanged)
|
||||||
|
ImGui.BeginDisabled();
|
||||||
|
|
||||||
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Save, "Apply Visibility Changes"))
|
||||||
{
|
{
|
||||||
|
_userServerIsNsfw = _isNsfw;
|
||||||
|
|
||||||
_ = _apiController.UserSetProfile(new UserProfileDto(
|
_ = _apiController.UserSetProfile(new UserProfileDto(
|
||||||
new UserData(_apiController.UID),
|
new UserData(_apiController.UID),
|
||||||
Disabled: false,
|
Disabled: false,
|
||||||
newValue,
|
IsNSFW: _isNsfw,
|
||||||
ProfilePictureBase64: GetCurrentProfilePictureBase64(profile),
|
ProfilePictureBase64: null,
|
||||||
|
BannerPictureBase64: null,
|
||||||
Description: null,
|
Description: null,
|
||||||
BannerPictureBase64: GetCurrentProfileBannerBase64(profile),
|
Tags: null));
|
||||||
Tags: GetServerTagPayload()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UiSharedService.AttachToolTip("Apply the visibility toggles above.");
|
||||||
|
|
||||||
|
if (!_isNsfwInitialized || !visibilityChanged)
|
||||||
|
ImGui.EndDisabled();
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.SyncAlt, "Reset") && _isNsfwInitialized)
|
||||||
|
_isNsfw = _userServerIsNsfw;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string? GetCurrentProfilePictureBase64(LightlessUserProfileData profile)
|
private string? GetCurrentProfilePictureBase64(LightlessUserProfileData profile)
|
||||||
@@ -932,7 +962,7 @@ public partial class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
foreach (var ext in format.FileExtensions)
|
foreach (var ext in format.FileExtensions)
|
||||||
{
|
{
|
||||||
if (SupportedImageExtensions.Contains(ext))
|
if (_supportedImageExtensions.Contains(ext))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1183,7 +1213,7 @@ public partial class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawSection(string title, float scale, Action body)
|
private static void DrawSection(string title, float scale, Action body)
|
||||||
{
|
{
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(6f, 4f) * scale);
|
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(6f, 4f) * scale);
|
||||||
|
|
||||||
@@ -1199,9 +1229,8 @@ public partial class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool DrawCheckboxRow(string label, bool currentValue, out bool newValue, string? tooltip = null)
|
private static bool DrawCheckboxRow(string label, bool currentValue, out bool newValue, string? tooltip = null)
|
||||||
{
|
{
|
||||||
|
|
||||||
bool value = currentValue;
|
bool value = currentValue;
|
||||||
bool changed = UiSharedService.CheckboxWithBorder(label, ref value, UIColors.Get("LightlessPurple"), 1.5f);
|
bool changed = UiSharedService.CheckboxWithBorder(label, ref value, UIColors.Get("LightlessPurple"), 1.5f);
|
||||||
if (!string.IsNullOrEmpty(tooltip))
|
if (!string.IsNullOrEmpty(tooltip))
|
||||||
@@ -1214,7 +1243,17 @@ public partial class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
private void SyncProfileState(LightlessUserProfileData profile)
|
private void SyncProfileState(LightlessUserProfileData profile)
|
||||||
{
|
{
|
||||||
if (string.Equals(profile.Description, LoadingProfileDescription, StringComparison.Ordinal))
|
if (string.Equals(profile.Description, LoadingProfileDescription, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
_isNsfwInitialized = false;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_isNsfwInitialized)
|
||||||
|
{
|
||||||
|
_userServerIsNsfw = profile.IsNSFW;
|
||||||
|
_isNsfw = profile.IsNSFW;
|
||||||
|
_isNsfwInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
var profileBytes = profile.ImageData.Value;
|
var profileBytes = profile.ImageData.Value;
|
||||||
if (_pfpTextureWrap == null || !_profileImage.SequenceEqual(profileBytes))
|
if (_pfpTextureWrap == null || !_profileImage.SequenceEqual(profileBytes))
|
||||||
@@ -1239,11 +1278,11 @@ public partial class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
_descriptionText = _profileDescription;
|
_descriptionText = _profileDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
var serverTags = profile.Tags ?? Array.Empty<int>();
|
var serverTags = profile.Tags ?? [];
|
||||||
if (!TagsEqual(serverTags, _profileTagIds))
|
if (!TagsEqual(serverTags, _profileTagIds))
|
||||||
{
|
{
|
||||||
var previous = _profileTagIds;
|
var previous = _profileTagIds;
|
||||||
_profileTagIds = serverTags.Count == 0 ? Array.Empty<int>() : serverTags.ToArray();
|
_profileTagIds = serverTags.Count == 0 ? [] : [.. serverTags];
|
||||||
|
|
||||||
if (TagsEqual(_tagEditorSelection, previous))
|
if (TagsEqual(_tagEditorSelection, previous))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ public class StandaloneProfileUi : WindowMediatorSubscriberBase
|
|||||||
var profile = _lightlessProfileManager.GetLightlessProfile(userData);
|
var profile = _lightlessProfileManager.GetLightlessProfile(userData);
|
||||||
IReadOnlyList<ProfileTagDefinition> profileTags = profile.Tags.Count > 0
|
IReadOnlyList<ProfileTagDefinition> profileTags = profile.Tags.Count > 0
|
||||||
? ProfileTagService.ResolveTags(profile.Tags)
|
? ProfileTagService.ResolveTags(profile.Tags)
|
||||||
: Array.Empty<ProfileTagDefinition>();
|
: [];
|
||||||
|
|
||||||
if (_textureWrap == null || !profile.ImageData.Value.SequenceEqual(_lastProfilePicture))
|
if (_textureWrap == null || !profile.ImageData.Value.SequenceEqual(_lastProfilePicture))
|
||||||
{
|
{
|
||||||
@@ -225,7 +225,7 @@ public class StandaloneProfileUi : WindowMediatorSubscriberBase
|
|||||||
bool directPair = false;
|
bool directPair = false;
|
||||||
bool youPaused = false;
|
bool youPaused = false;
|
||||||
bool theyPaused = false;
|
bool theyPaused = false;
|
||||||
List<string> syncshellLines = new();
|
List<string> syncshellLines = [];
|
||||||
|
|
||||||
if (!_isLightfinderContext && Pair != null)
|
if (!_isLightfinderContext && Pair != null)
|
||||||
{
|
{
|
||||||
@@ -245,7 +245,7 @@ public class StandaloneProfileUi : WindowMediatorSubscriberBase
|
|||||||
theyPaused = pairInfo.OtherPermissions.IsPaused();
|
theyPaused = pairInfo.OtherPermissions.IsPaused();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pairInfo.Groups.Any())
|
if (pairInfo.Groups.Count != 0)
|
||||||
{
|
{
|
||||||
foreach (var gid in pairInfo.Groups)
|
foreach (var gid in pairInfo.Groups)
|
||||||
{
|
{
|
||||||
@@ -276,8 +276,11 @@ public class StandaloneProfileUi : WindowMediatorSubscriberBase
|
|||||||
presenceTokens.Add(new PresenceToken("They paused syncing", true));
|
presenceTokens.Add(new PresenceToken("They paused syncing", true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (profile.IsNSFW)
|
||||||
|
presenceTokens.Add(new PresenceToken("NSFW", Emphasis: true));
|
||||||
|
|
||||||
if (syncshellLines.Count > 0)
|
if (syncshellLines.Count > 0)
|
||||||
presenceTokens.Add(new PresenceToken($"Sharing Syncshells ({syncshellLines.Count})", false, syncshellLines, "Shared Syncshells"));
|
presenceTokens.Add(new PresenceToken($"Sharing Syncshells ({syncshellLines.Count})", Emphasis: false, syncshellLines, "Shared Syncshells"));
|
||||||
|
|
||||||
var drawList = ImGui.GetWindowDrawList();
|
var drawList = ImGui.GetWindowDrawList();
|
||||||
var style = ImGui.GetStyle();
|
var style = ImGui.GetStyle();
|
||||||
@@ -780,7 +783,7 @@ public class StandaloneProfileUi : WindowMediatorSubscriberBase
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (profile.IsNsfw)
|
if (profile.IsNsfw)
|
||||||
presenceTokens.Add(new PresenceToken("NSFW", true));
|
presenceTokens.Add(new PresenceToken("NSFW", Emphasis: true));
|
||||||
|
|
||||||
int memberCount = 0;
|
int memberCount = 0;
|
||||||
List<Pair>? groupMembers = null;
|
List<Pair>? groupMembers = null;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ using LightlessSync.Services;
|
|||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
using LightlessSync.Services.Profiles;
|
using LightlessSync.Services.Profiles;
|
||||||
using LightlessSync.UI.Services;
|
using LightlessSync.UI.Services;
|
||||||
using LightlessSync.UI.Style;
|
|
||||||
using LightlessSync.WebAPI;
|
using LightlessSync.WebAPI;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
@@ -42,6 +41,11 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
private Task<int>? _pruneTask;
|
private Task<int>? _pruneTask;
|
||||||
private int _pruneDays = 14;
|
private int _pruneDays = 14;
|
||||||
|
|
||||||
|
private Task<GroupPruneSettingsDto>? _pruneSettingsTask;
|
||||||
|
private bool _pruneSettingsLoaded;
|
||||||
|
private bool _autoPruneEnabled;
|
||||||
|
private int _autoPruneDays = 14;
|
||||||
|
|
||||||
public SyncshellAdminUI(ILogger<SyncshellAdminUI> logger, LightlessMediator mediator, ApiController apiController,
|
public SyncshellAdminUI(ILogger<SyncshellAdminUI> logger, LightlessMediator mediator, ApiController apiController,
|
||||||
UiSharedService uiSharedService, PairUiService pairUiService, GroupFullInfoDto groupFullInfo, PerformanceCollectorService performanceCollectorService, LightlessProfileManager lightlessProfileManager)
|
UiSharedService uiSharedService, PairUiService pairUiService, GroupFullInfoDto groupFullInfo, PerformanceCollectorService performanceCollectorService, LightlessProfileManager lightlessProfileManager)
|
||||||
: base(logger, mediator, "Syncshell Admin Panel (" + groupFullInfo.GroupAliasOrGID + ")", performanceCollectorService)
|
: base(logger, mediator, "Syncshell Admin Panel (" + groupFullInfo.GroupAliasOrGID + ")", performanceCollectorService)
|
||||||
@@ -89,36 +93,147 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
_profileData = _lightlessProfileManager.GetLightlessGroupProfile(GroupFullInfo.Group);
|
_profileData = _lightlessProfileManager.GetLightlessGroupProfile(GroupFullInfo.Group);
|
||||||
using var id = ImRaii.PushId("syncshell_admin_" + GroupFullInfo.GID);
|
using var id = ImRaii.PushId("syncshell_admin_" + GroupFullInfo.GID);
|
||||||
|
|
||||||
using (_uiSharedService.UidFont.Push())
|
DrawAdminHeader();
|
||||||
{
|
|
||||||
var headerText = $"{GroupFullInfo.GroupAliasOrGID} Administrative Panel";
|
ImGui.Separator();
|
||||||
_uiSharedService.UnderlinedBigText(headerText, UIColors.Get("LightlessBlue"));
|
var perm = GroupFullInfo.GroupPermissions;
|
||||||
}
|
|
||||||
|
DrawAdminTopBar(perm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawAdminHeader()
|
||||||
|
{
|
||||||
|
float scale = ImGuiHelpers.GlobalScale;
|
||||||
|
var style = ImGui.GetStyle();
|
||||||
|
|
||||||
|
var cursorLocal = ImGui.GetCursorPos();
|
||||||
|
var pMin = ImGui.GetCursorScreenPos();
|
||||||
|
float width = ImGui.GetContentRegionAvail().X;
|
||||||
|
float height = 64f * scale;
|
||||||
|
|
||||||
|
var pMax = new Vector2(pMin.X + width, pMin.Y + height);
|
||||||
|
var drawList = ImGui.GetWindowDrawList();
|
||||||
|
|
||||||
|
var purple = UIColors.Get("LightlessPurple");
|
||||||
|
var gradLeft = purple.WithAlpha(0.0f);
|
||||||
|
var gradRight = purple.WithAlpha(0.85f);
|
||||||
|
|
||||||
|
uint colTopLeft = ImGui.ColorConvertFloat4ToU32(gradLeft);
|
||||||
|
uint colTopRight = ImGui.ColorConvertFloat4ToU32(gradRight);
|
||||||
|
uint colBottomRight = ImGui.ColorConvertFloat4ToU32(gradRight);
|
||||||
|
uint colBottomLeft = ImGui.ColorConvertFloat4ToU32(gradLeft);
|
||||||
|
|
||||||
|
drawList.AddRectFilledMultiColor(
|
||||||
|
pMin,
|
||||||
|
pMax,
|
||||||
|
colTopLeft,
|
||||||
|
colTopRight,
|
||||||
|
colBottomRight,
|
||||||
|
colBottomLeft);
|
||||||
|
|
||||||
|
float accentHeight = 3f * scale;
|
||||||
|
var accentMin = new Vector2(pMin.X, pMax.Y - accentHeight);
|
||||||
|
var accentMax = new Vector2(pMax.X, pMax.Y);
|
||||||
|
var accentColor = UIColors.Get("LightlessBlue");
|
||||||
|
uint accentU32 = ImGui.ColorConvertFloat4ToU32(accentColor);
|
||||||
|
drawList.AddRectFilled(accentMin, accentMax, accentU32);
|
||||||
|
|
||||||
|
ImGui.InvisibleButton("##adminHeaderHitbox", new Vector2(width, height));
|
||||||
|
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
{
|
{
|
||||||
ImGui.BeginTooltip();
|
ImGui.BeginTooltip();
|
||||||
ImGui.Text($"{GroupFullInfo.GroupAliasOrGID} is created at:");
|
ImGui.Text($"{GroupFullInfo.GroupAliasOrGID} is created at:");
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
ImGui.Text(text: GroupFullInfo.Group.CreatedAt?.ToString("yyyy-MM-dd HH:mm:ss 'UTC'"));
|
ImGui.Text(GroupFullInfo.Group.CreatedAt?.ToString("yyyy-MM-dd HH:mm:ss 'UTC'") ?? "Unknown");
|
||||||
ImGui.EndTooltip();
|
ImGui.EndTooltip();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Separator();
|
var titlePos = new Vector2(pMin.X + 12f * scale, pMin.Y + 8f * scale);
|
||||||
var perm = GroupFullInfo.GroupPermissions;
|
ImGui.SetCursorScreenPos(titlePos);
|
||||||
|
|
||||||
using var tabbar = ImRaii.TabBar("syncshell_tab_" + GroupFullInfo.GID);
|
float titleHeight;
|
||||||
|
using (_uiSharedService.UidFont.Push())
|
||||||
if (tabbar)
|
|
||||||
{
|
{
|
||||||
DrawInvites(perm);
|
ImGui.TextColored(UIColors.Get("LightlessBlue"), GroupFullInfo.GroupAliasOrGID);
|
||||||
|
titleHeight = ImGui.GetTextLineHeightWithSpacing();
|
||||||
DrawManagement();
|
|
||||||
|
|
||||||
DrawPermission(perm);
|
|
||||||
|
|
||||||
DrawProfile();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var subtitlePos = new Vector2(
|
||||||
|
pMin.X + 12f * scale,
|
||||||
|
titlePos.Y + titleHeight - 2f * scale);
|
||||||
|
|
||||||
|
ImGui.SetCursorScreenPos(subtitlePos);
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey);
|
||||||
|
ImGui.TextUnformatted("Administrative Panel");
|
||||||
|
ImGui.PopStyleColor();
|
||||||
|
|
||||||
|
string roleLabel = _isOwner ? "Owner" : (_isModerator ? "Moderator" : string.Empty);
|
||||||
|
if (!string.IsNullOrEmpty(roleLabel))
|
||||||
|
{
|
||||||
|
float roleTextW = ImGui.CalcTextSize(roleLabel).X;
|
||||||
|
float pillPaddingX = 8f * scale;
|
||||||
|
float pillPaddingY = -1f * scale;
|
||||||
|
|
||||||
|
float pillWidth = roleTextW + pillPaddingX * 2f;
|
||||||
|
float pillHeight = ImGui.GetTextLineHeight() + pillPaddingY * 2f;
|
||||||
|
|
||||||
|
var pillMin = new Vector2(
|
||||||
|
pMax.X - pillWidth - style.WindowPadding.X,
|
||||||
|
subtitlePos.Y - pillPaddingY);
|
||||||
|
var pillMax = new Vector2(pillMin.X + pillWidth, pillMin.Y + pillHeight);
|
||||||
|
|
||||||
|
var pillBg = _isOwner ? UIColors.Get("LightlessYellow") : UIColors.Get("LightlessOrange");
|
||||||
|
uint pillBgU = ImGui.ColorConvertFloat4ToU32(pillBg.WithAlpha(0.9f));
|
||||||
|
|
||||||
|
drawList.AddRectFilled(pillMin, pillMax, pillBgU, 8f * scale);
|
||||||
|
|
||||||
|
ImGui.SetCursorScreenPos(new Vector2(pillMin.X + pillPaddingX, pillMin.Y + pillPaddingY));
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("FullBlack"));
|
||||||
|
ImGui.TextUnformatted(roleLabel);
|
||||||
|
ImGui.PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SetCursorPos(new Vector2(cursorLocal.X, cursorLocal.Y + height + 6f * scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawAdminTopBar(GroupPermissions perm)
|
||||||
|
{
|
||||||
|
var style = ImGui.GetStyle();
|
||||||
|
|
||||||
|
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(12f, 6f) * ImGuiHelpers.GlobalScale);
|
||||||
|
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(10f, style.ItemSpacing.Y));
|
||||||
|
ImGui.PushStyleVar(ImGuiStyleVar.TabRounding, 6f * ImGuiHelpers.GlobalScale);
|
||||||
|
|
||||||
|
var baseTab = UIColors.Get("FullBlack").WithAlpha(0.0f);
|
||||||
|
var baseTabDim = UIColors.Get("FullBlack").WithAlpha(0.1f);
|
||||||
|
var accent = UIColors.Get("LightlessPurple");
|
||||||
|
var accentHover = accent.WithAlpha(0.90f);
|
||||||
|
var accentActive = accent;
|
||||||
|
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Tab, baseTab);
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.TabHovered, accentHover);
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.TabActive, accentActive);
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.TabUnfocused, baseTabDim);
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.TabUnfocusedActive, accentActive.WithAlpha(0.80f));
|
||||||
|
|
||||||
|
using (var tabbar = ImRaii.TabBar("syncshell_tab_" + GroupFullInfo.GID))
|
||||||
|
{
|
||||||
|
if (tabbar)
|
||||||
|
{
|
||||||
|
DrawInvites(perm);
|
||||||
|
DrawManagement();
|
||||||
|
DrawPermission(perm);
|
||||||
|
DrawProfile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.PopStyleColor(5);
|
||||||
|
ImGui.PopStyleVar(3);
|
||||||
|
|
||||||
|
ImGuiHelpers.ScaledDummy(2f);
|
||||||
|
UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f);
|
||||||
|
ImGuiHelpers.ScaledDummy(2f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawPermission(GroupPermissions perm)
|
private void DrawPermission(GroupPermissions perm)
|
||||||
@@ -218,6 +333,70 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawAutoPruneSettings()
|
||||||
|
{
|
||||||
|
ImGuiHelpers.ScaledDummy(2f);
|
||||||
|
UiSharedService.TextWrapped("Automatic prune (server-side scheduled cleanup of inactive users).");
|
||||||
|
|
||||||
|
_pruneSettingsTask ??= _apiController.GroupGetPruneSettings(new GroupDto(GroupFullInfo.Group));
|
||||||
|
|
||||||
|
if (!_pruneSettingsLoaded)
|
||||||
|
{
|
||||||
|
if (!_pruneSettingsTask!.IsCompleted)
|
||||||
|
{
|
||||||
|
UiSharedService.ColorTextWrapped("Loading prune settings from server...", ImGuiColors.DalamudGrey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_pruneSettingsTask.IsFaulted || _pruneSettingsTask.IsCanceled)
|
||||||
|
{
|
||||||
|
UiSharedService.ColorTextWrapped("Failed to load auto-prune settings.", ImGuiColors.DalamudRed);
|
||||||
|
_pruneSettingsTask = null;
|
||||||
|
_pruneSettingsLoaded = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dto = _pruneSettingsTask.GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
_autoPruneEnabled = dto.AutoPruneEnabled && dto.AutoPruneDays > 0;
|
||||||
|
_autoPruneDays = dto.AutoPruneDays > 0 ? dto.AutoPruneDays : 14;
|
||||||
|
|
||||||
|
_pruneSettingsLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool enabled = _autoPruneEnabled;
|
||||||
|
if (ImGui.Checkbox("Enable automatic pruning", ref enabled))
|
||||||
|
{
|
||||||
|
_autoPruneEnabled = enabled;
|
||||||
|
SavePruneSettings();
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip("When enabled, inactive non-pinned, non-moderator users will be pruned automatically on the server.");
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.SetNextItemWidth(150);
|
||||||
|
|
||||||
|
using (ImRaii.Disabled(!_autoPruneEnabled))
|
||||||
|
{
|
||||||
|
_uiSharedService.DrawCombo(
|
||||||
|
"Day(s) of inactivity",
|
||||||
|
[1, 3, 7, 14, 30, 90],
|
||||||
|
days => $"{days} day(s)",
|
||||||
|
selected =>
|
||||||
|
{
|
||||||
|
_autoPruneDays = selected;
|
||||||
|
SavePruneSettings();
|
||||||
|
},
|
||||||
|
_autoPruneDays);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_autoPruneEnabled)
|
||||||
|
{
|
||||||
|
UiSharedService.ColorTextWrapped(
|
||||||
|
"Automatic prune is currently disabled. Enable it and choose an inactivity threshold to let the server clean up inactive users automatically.",
|
||||||
|
ImGuiColors.DalamudGrey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawProfile()
|
private void DrawProfile()
|
||||||
{
|
{
|
||||||
var profileTab = ImRaii.TabItem("Profile");
|
var profileTab = ImRaii.TabItem("Profile");
|
||||||
@@ -268,167 +447,230 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
private void DrawManagement()
|
private void DrawManagement()
|
||||||
{
|
{
|
||||||
var mgmtTab = ImRaii.TabItem("User Management");
|
var mgmtTab = ImRaii.TabItem("User Management");
|
||||||
if (mgmtTab)
|
if (!mgmtTab)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ImGuiHelpers.ScaledDummy(3f);
|
||||||
|
|
||||||
|
var style = ImGui.GetStyle();
|
||||||
|
|
||||||
|
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(10f, 5f) * ImGuiHelpers.GlobalScale);
|
||||||
|
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(8f, style.ItemSpacing.Y));
|
||||||
|
ImGui.PushStyleVar(ImGuiStyleVar.TabRounding, 5f * ImGuiHelpers.GlobalScale);
|
||||||
|
|
||||||
|
var baseTab = UIColors.Get("FullBlack").WithAlpha(0.0f);
|
||||||
|
var baseTabDim = UIColors.Get("FullBlack").WithAlpha(0.1f);
|
||||||
|
var accent = UIColors.Get("LightlessPurple");
|
||||||
|
var accentHover = accent.WithAlpha(0.90f);
|
||||||
|
var accentActive = accent;
|
||||||
|
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Tab, baseTab);
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.TabHovered, accentHover);
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.TabActive, accentActive);
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.TabUnfocused, baseTabDim);
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.TabUnfocusedActive, accentActive.WithAlpha(0.80f));
|
||||||
|
|
||||||
|
using (var innerTabBar = ImRaii.TabBar("user_mgmt_inner_tab_" + GroupFullInfo.GID))
|
||||||
{
|
{
|
||||||
if (_uiSharedService.MediumTreeNode("User List & Administration", UIColors.Get("LightlessPurple")))
|
if (innerTabBar)
|
||||||
{
|
{
|
||||||
var snapshot = _pairUiService.GetSnapshot();
|
// Users tab
|
||||||
if (!snapshot.GroupPairs.TryGetValue(GroupFullInfo, out var pairs))
|
var usersTab = ImRaii.TabItem("Users");
|
||||||
|
if (usersTab)
|
||||||
{
|
{
|
||||||
UiSharedService.ColorTextWrapped("No users found in this Syncshell", ImGuiColors.DalamudYellow);
|
DrawUserListSection();
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DrawUserListCustom(pairs, GroupFullInfo);
|
|
||||||
}
|
}
|
||||||
|
usersTab.Dispose();
|
||||||
|
|
||||||
ImGui.TreePop();
|
// Cleanup tab
|
||||||
|
var cleanupTab = ImRaii.TabItem("Cleanup");
|
||||||
|
if (cleanupTab)
|
||||||
|
{
|
||||||
|
DrawMassCleanupSection();
|
||||||
|
}
|
||||||
|
cleanupTab.Dispose();
|
||||||
|
|
||||||
|
// Bans tab
|
||||||
|
var bansTab = ImRaii.TabItem("Bans");
|
||||||
|
if (bansTab)
|
||||||
|
{
|
||||||
|
DrawUserBansSection();
|
||||||
|
}
|
||||||
|
bansTab.Dispose();
|
||||||
}
|
}
|
||||||
ImGui.Separator();
|
|
||||||
|
|
||||||
if (_uiSharedService.MediumTreeNode("Mass Cleanup", UIColors.Get("DimRed")))
|
|
||||||
{
|
|
||||||
using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
|
|
||||||
{
|
|
||||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Broom, "Clear Syncshell"))
|
|
||||||
{
|
|
||||||
_ = _apiController.GroupClear(new(GroupFullInfo.Group));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Unlink, "Check for Inactive Users"))
|
|
||||||
{
|
|
||||||
_pruneTestTask = _apiController.GroupPrune(new(GroupFullInfo.Group), _pruneDays, execute: false);
|
|
||||||
_pruneTask = null;
|
|
||||||
}
|
|
||||||
UiSharedService.AttachToolTip($"This will start the prune process for this Syncshell of inactive Lightless users that have not logged in in the past {_pruneDays} day(s)."
|
|
||||||
+ Environment.NewLine + "You will be able to review the amount of inactive users before executing the prune."
|
|
||||||
+ UiSharedService.TooltipSeparator + "Note: this check excludes pinned users and moderators of this Syncshell.");
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.SetNextItemWidth(150);
|
|
||||||
_uiSharedService.DrawCombo(
|
|
||||||
"Day(s) of inactivity",
|
|
||||||
[0, 1, 3, 7, 14, 30, 90],
|
|
||||||
(count) =>
|
|
||||||
{
|
|
||||||
return count == 0 ? "15 minute(s)" : count + " day(s)";
|
|
||||||
},
|
|
||||||
(selected) =>
|
|
||||||
{
|
|
||||||
_pruneDays = selected;
|
|
||||||
_pruneTestTask = null;
|
|
||||||
_pruneTask = null;
|
|
||||||
},
|
|
||||||
_pruneDays);
|
|
||||||
|
|
||||||
if (_pruneTestTask != null)
|
|
||||||
{
|
|
||||||
if (!_pruneTestTask.IsCompleted)
|
|
||||||
{
|
|
||||||
UiSharedService.ColorTextWrapped("Calculating inactive users...", ImGuiColors.DalamudYellow);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.AlignTextToFramePadding();
|
|
||||||
UiSharedService.TextWrapped($"Found {_pruneTestTask.Result} user(s) that have not logged into Lightless in the past {_pruneDays} day(s).");
|
|
||||||
if (_pruneTestTask.Result > 0)
|
|
||||||
{
|
|
||||||
using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
|
|
||||||
{
|
|
||||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Broom, "Prune Inactive Users"))
|
|
||||||
{
|
|
||||||
_pruneTask = _apiController.GroupPrune(new(GroupFullInfo.Group), _pruneDays, execute: true);
|
|
||||||
_pruneTestTask = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UiSharedService.AttachToolTip($"Pruning will remove {_pruneTestTask?.Result ?? 0} inactive user(s)."
|
|
||||||
+ UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_pruneTask != null)
|
|
||||||
{
|
|
||||||
if (!_pruneTask.IsCompleted)
|
|
||||||
{
|
|
||||||
UiSharedService.ColorTextWrapped("Pruning Syncshell...", ImGuiColors.DalamudYellow);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UiSharedService.TextWrapped($"Syncshell was pruned and {_pruneTask.Result} inactive user(s) have been removed.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UiSharedService.ColoredSeparator(UIColors.Get("DimRed"), 1.5f);
|
|
||||||
ImGui.TreePop();
|
|
||||||
}
|
|
||||||
ImGui.Separator();
|
|
||||||
|
|
||||||
if (_uiSharedService.MediumTreeNode("User Bans", UIColors.Get("LightlessYellow")))
|
|
||||||
{
|
|
||||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Retweet, "Refresh Banlist from Server"))
|
|
||||||
{
|
|
||||||
_bannedUsers = _apiController.GroupGetBannedUsers(new GroupDto(GroupFullInfo.Group)).Result;
|
|
||||||
}
|
|
||||||
var tableFlags = ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp;
|
|
||||||
if (_bannedUsers.Count > 10) tableFlags |= ImGuiTableFlags.ScrollY;
|
|
||||||
if (ImGui.BeginTable("bannedusertable" + GroupFullInfo.GID, 6, tableFlags))
|
|
||||||
{
|
|
||||||
ImGui.TableSetupColumn("UID", ImGuiTableColumnFlags.None, 1);
|
|
||||||
ImGui.TableSetupColumn("Alias", ImGuiTableColumnFlags.None, 1);
|
|
||||||
ImGui.TableSetupColumn("By", ImGuiTableColumnFlags.None, 1);
|
|
||||||
ImGui.TableSetupColumn("Date", ImGuiTableColumnFlags.None, 2);
|
|
||||||
ImGui.TableSetupColumn("Reason", ImGuiTableColumnFlags.None, 3);
|
|
||||||
ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.None, 1);
|
|
||||||
|
|
||||||
ImGui.TableHeadersRow();
|
|
||||||
|
|
||||||
foreach (var bannedUser in _bannedUsers.ToList())
|
|
||||||
{
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
ImGui.TextUnformatted(bannedUser.UID);
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
ImGui.TextUnformatted(bannedUser.UserAlias ?? string.Empty);
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
ImGui.TextUnformatted(bannedUser.BannedBy);
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
ImGui.TextUnformatted(bannedUser.BannedOn.ToLocalTime().ToString(CultureInfo.CurrentCulture));
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
UiSharedService.TextWrapped(bannedUser.Reason);
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
using var _ = ImRaii.PushId(bannedUser.UID);
|
|
||||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Check, "Unban"))
|
|
||||||
{
|
|
||||||
_apiController.GroupUnbanUser(bannedUser);
|
|
||||||
_bannedUsers.RemoveAll(b => string.Equals(b.UID, bannedUser.UID, StringComparison.Ordinal));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui.EndTable();
|
|
||||||
}
|
|
||||||
UiSharedService.ColoredSeparator(UIColors.Get("LightlessYellow"), 1.5f);
|
|
||||||
ImGui.TreePop();
|
|
||||||
}
|
|
||||||
ImGui.Separator();
|
|
||||||
}
|
}
|
||||||
mgmtTab.Dispose();
|
mgmtTab.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawUserListSection()
|
||||||
|
{
|
||||||
|
var snapshot = _pairUiService.GetSnapshot();
|
||||||
|
if (!snapshot.GroupPairs.TryGetValue(GroupFullInfo, out var pairs))
|
||||||
|
{
|
||||||
|
UiSharedService.ColorTextWrapped("No users found in this Syncshell", ImGuiColors.DalamudYellow);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_uiSharedService.MediumText("User List & Administration", UIColors.Get("LightlessPurple"));
|
||||||
|
ImGuiHelpers.ScaledDummy(2f);
|
||||||
|
DrawUserListCustom(pairs, GroupFullInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawMassCleanupSection()
|
||||||
|
{
|
||||||
|
_uiSharedService.MediumText("Mass Cleanup", UIColors.Get("DimRed"));
|
||||||
|
UiSharedService.TextWrapped("Tools to bulk-clean inactive or unwanted users from this Syncshell.");
|
||||||
|
ImGuiHelpers.ScaledDummy(3f);
|
||||||
|
|
||||||
|
using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
|
||||||
|
{
|
||||||
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Broom, "Clear Syncshell"))
|
||||||
|
{
|
||||||
|
_ = _apiController.GroupClear(new(GroupFullInfo.Group));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.0f);
|
||||||
|
ImGuiHelpers.ScaledDummy(2f);
|
||||||
|
|
||||||
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Unlink, "Check for Inactive Users"))
|
||||||
|
{
|
||||||
|
_pruneTestTask = _apiController.GroupPrune(new(GroupFullInfo.Group), _pruneDays, execute: false);
|
||||||
|
_pruneTask = null;
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip($"This will start the prune process for this Syncshell of inactive Lightless users that have not logged in in the past {_pruneDays} day(s)."
|
||||||
|
+ Environment.NewLine + "You will be able to review the amount of inactive users before executing the prune."
|
||||||
|
+ UiSharedService.TooltipSeparator + "Note: this check excludes pinned users and moderators of this Syncshell.");
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.SetNextItemWidth(150);
|
||||||
|
_uiSharedService.DrawCombo(
|
||||||
|
"Day(s) of inactivity",
|
||||||
|
[0, 1, 3, 7, 14, 30, 90],
|
||||||
|
(count) => count == 0 ? "15 minute(s)" : count + " day(s)",
|
||||||
|
(selected) =>
|
||||||
|
{
|
||||||
|
_pruneDays = selected;
|
||||||
|
_pruneTestTask = null;
|
||||||
|
_pruneTask = null;
|
||||||
|
},
|
||||||
|
_pruneDays);
|
||||||
|
|
||||||
|
if (_pruneTestTask != null)
|
||||||
|
{
|
||||||
|
if (!_pruneTestTask.IsCompleted)
|
||||||
|
{
|
||||||
|
UiSharedService.ColorTextWrapped("Calculating inactive users...", ImGuiColors.DalamudYellow);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
UiSharedService.TextWrapped($"Found {_pruneTestTask.Result} user(s) that have not logged into Lightless in the past {_pruneDays} day(s).");
|
||||||
|
if (_pruneTestTask.Result > 0)
|
||||||
|
{
|
||||||
|
using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
|
||||||
|
{
|
||||||
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Broom, "Prune Inactive Users"))
|
||||||
|
{
|
||||||
|
_pruneTask = _apiController.GroupPrune(new(GroupFullInfo.Group), _pruneDays, execute: true);
|
||||||
|
_pruneTestTask = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip($"Pruning will remove {_pruneTestTask?.Result ?? 0} inactive user(s)."
|
||||||
|
+ UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_pruneTask != null)
|
||||||
|
{
|
||||||
|
if (!_pruneTask.IsCompleted)
|
||||||
|
{
|
||||||
|
UiSharedService.ColorTextWrapped("Pruning Syncshell...", ImGuiColors.DalamudYellow);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UiSharedService.TextWrapped($"Syncshell was pruned and {_pruneTask.Result} inactive user(s) have been removed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiHelpers.ScaledDummy(4f);
|
||||||
|
UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.0f);
|
||||||
|
ImGuiHelpers.ScaledDummy(2f);
|
||||||
|
|
||||||
|
DrawAutoPruneSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawUserBansSection()
|
||||||
|
{
|
||||||
|
_uiSharedService.MediumText("User Bans", UIColors.Get("LightlessYellow"));
|
||||||
|
ImGuiHelpers.ScaledDummy(3f);
|
||||||
|
|
||||||
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Retweet, "Refresh Banlist from Server"))
|
||||||
|
{
|
||||||
|
_bannedUsers = _apiController.GroupGetBannedUsers(new GroupDto(GroupFullInfo.Group)).Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tableFlags = ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp;
|
||||||
|
if (_bannedUsers.Count > 10)
|
||||||
|
tableFlags |= ImGuiTableFlags.ScrollY;
|
||||||
|
|
||||||
|
if (ImGui.BeginTable("bannedusertable" + GroupFullInfo.GID, 6, tableFlags))
|
||||||
|
{
|
||||||
|
ImGui.TableSetupColumn("UID", ImGuiTableColumnFlags.None, 1);
|
||||||
|
ImGui.TableSetupColumn("Alias", ImGuiTableColumnFlags.None, 1);
|
||||||
|
ImGui.TableSetupColumn("By", ImGuiTableColumnFlags.None, 1);
|
||||||
|
ImGui.TableSetupColumn("Date", ImGuiTableColumnFlags.None, 2);
|
||||||
|
ImGui.TableSetupColumn("Reason", ImGuiTableColumnFlags.None, 3);
|
||||||
|
ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.None, 1);
|
||||||
|
|
||||||
|
ImGui.TableHeadersRow();
|
||||||
|
|
||||||
|
foreach (var bannedUser in _bannedUsers.ToList())
|
||||||
|
{
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TextUnformatted(bannedUser.UID);
|
||||||
|
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TextUnformatted(bannedUser.UserAlias ?? string.Empty);
|
||||||
|
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TextUnformatted(bannedUser.BannedBy);
|
||||||
|
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TextUnformatted(bannedUser.BannedOn.ToLocalTime().ToString(CultureInfo.CurrentCulture));
|
||||||
|
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
UiSharedService.TextWrapped(bannedUser.Reason);
|
||||||
|
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
using var _ = ImRaii.PushId(bannedUser.UID);
|
||||||
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Check, "Unban"))
|
||||||
|
{
|
||||||
|
_apiController.GroupUnbanUser(bannedUser);
|
||||||
|
_bannedUsers.RemoveAll(b => string.Equals(b.UID, bannedUser.UID, StringComparison.Ordinal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndTable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawInvites(GroupPermissions perm)
|
private void DrawInvites(GroupPermissions perm)
|
||||||
{
|
{
|
||||||
var inviteTab = ImRaii.TabItem("Invites");
|
var inviteTab = ImRaii.TabItem("Invites");
|
||||||
@@ -476,6 +718,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
private void DrawUserListCustom(IReadOnlyList<Pair> pairs, GroupFullInfoDto GroupFullInfo)
|
private void DrawUserListCustom(IReadOnlyList<Pair> pairs, GroupFullInfoDto GroupFullInfo)
|
||||||
{
|
{
|
||||||
|
// Search bar (unchanged)
|
||||||
ImGui.PushItemWidth(0);
|
ImGui.PushItemWidth(0);
|
||||||
_uiSharedService.IconText(FontAwesomeIcon.Search, UIColors.Get("LightlessPurple"));
|
_uiSharedService.IconText(FontAwesomeIcon.Search, UIColors.Get("LightlessPurple"));
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
@@ -511,21 +754,20 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
if (p.Value.Value.IsPinned()) return 2;
|
if (p.Value.Value.IsPinned()) return 2;
|
||||||
return 10;
|
return 10;
|
||||||
})
|
})
|
||||||
.ThenBy(p => p.Key.GetNote() ?? p.Key.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase);
|
.ThenBy(p => p.Key.GetNote() ?? p.Key.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
ImGui.BeginChild("userListScroll#" + GroupFullInfo.Group.AliasOrGID, new Vector2(0, 0), true);
|
||||||
|
|
||||||
var style = ImGui.GetStyle();
|
var style = ImGui.GetStyle();
|
||||||
float fullW = ImGui.GetContentRegionAvail().X;
|
float fullW = ImGui.GetContentRegionAvail().X;
|
||||||
|
|
||||||
float colUid = fullW * 0.50f;
|
float colUid = fullW * 0.50f;
|
||||||
float colFlags = fullW * 0.10f;
|
float colFlags = fullW * 0.10f;
|
||||||
float colActions = fullW - colUid - colFlags - style.ItemSpacing.X * 2.5f;
|
float colActions = fullW - colUid - colFlags - style.ItemSpacing.X * 2.0f;
|
||||||
|
|
||||||
DrawUserListHeader(colUid, colFlags);
|
DrawUserListHeader(colUid, colFlags);
|
||||||
|
|
||||||
bool useScroll = pairs.Count > 10;
|
|
||||||
float childHeight = useScroll ? 260f * ImGuiHelpers.GlobalScale : 0f;
|
|
||||||
|
|
||||||
ImGui.BeginChild("userListScroll#" + GroupFullInfo.Group.AliasOrGID, new Vector2(0, childHeight), true);
|
|
||||||
|
|
||||||
int rowIndex = 0;
|
int rowIndex = 0;
|
||||||
foreach (var kv in orderedPairs)
|
foreach (var kv in orderedPairs)
|
||||||
{
|
{
|
||||||
@@ -533,8 +775,8 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
var userInfoOpt = kv.Value;
|
var userInfoOpt = kv.Value;
|
||||||
DrawUserRowCustom(pair, userInfoOpt, GroupFullInfo, rowIndex++, colUid, colFlags, colActions);
|
DrawUserRowCustom(pair, userInfoOpt, GroupFullInfo, rowIndex++, colUid, colFlags, colActions);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndChild();
|
ImGui.EndChild();
|
||||||
UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DrawUserListHeader(float colUid, float colFlags)
|
private static void DrawUserListHeader(float colUid, float colFlags)
|
||||||
@@ -544,18 +786,18 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("LightlessPurple"));
|
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("LightlessPurple"));
|
||||||
|
|
||||||
// Alias/UID/Note
|
// Alias / UID / Note
|
||||||
ImGui.SetCursorPosX(x0);
|
ImGui.SetCursorPosX(x0);
|
||||||
ImGui.TextUnformatted("Alias / UID / Note");
|
ImGui.TextUnformatted("Alias / UID / Note");
|
||||||
|
|
||||||
// User Flags
|
// Flags
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.SetCursorPosX(x0 + colUid + style.ItemSpacing.X);
|
ImGui.SetCursorPosX(x0 + colUid + style.ItemSpacing.X);
|
||||||
ImGui.TextUnformatted("Flags");
|
ImGui.TextUnformatted("Flags");
|
||||||
|
|
||||||
// User Actions
|
// Actions
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.SetCursorPosX(x0 + colUid + colFlags + style.ItemSpacing.X * 2.5f);
|
ImGui.SetCursorPosX(x0 + colUid + colFlags + style.ItemSpacing.X * 2.0f);
|
||||||
ImGui.TextUnformatted("Actions");
|
ImGui.TextUnformatted("Actions");
|
||||||
|
|
||||||
ImGui.PopStyleColor();
|
ImGui.PopStyleColor();
|
||||||
@@ -724,6 +966,27 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SavePruneSettings()
|
||||||
|
{
|
||||||
|
if (_autoPruneDays <= 0)
|
||||||
|
{
|
||||||
|
_autoPruneEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var enabled = _autoPruneEnabled && _autoPruneDays > 0;
|
||||||
|
var dto = new GroupPruneSettingsDto(Group: GroupFullInfo.Group, AutoPruneEnabled: enabled, AutoPruneDays: enabled ? _autoPruneDays : 0);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_apiController.GroupSetPruneSettings(dto).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to save auto prune settings for group {GID}", GroupFullInfo.Group.GID);
|
||||||
|
UiSharedService.ColorTextWrapped("Failed to save auto-prune settings.", ImGuiColors.DalamudRed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static bool MatchesUserFilter(Pair pair, string filterLower)
|
private static bool MatchesUserFilter(Pair pair, string filterLower)
|
||||||
{
|
{
|
||||||
var note = pair.GetNote() ?? string.Empty;
|
var note = pair.GetNote() ?? string.Empty;
|
||||||
|
|||||||
@@ -151,6 +151,20 @@ public partial class ApiController
|
|||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<GroupPruneSettingsDto> GroupGetPruneSettings(GroupDto dto)
|
||||||
|
{
|
||||||
|
CheckConnection();
|
||||||
|
return await _lightlessHub!.InvokeAsync<GroupPruneSettingsDto>(nameof(GroupGetPruneSettings), dto)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task GroupSetPruneSettings(GroupPruneSettingsDto dto)
|
||||||
|
{
|
||||||
|
CheckConnection();
|
||||||
|
await _lightlessHub!.SendAsync(nameof(GroupSetPruneSettings), dto)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
private void CheckConnection()
|
private void CheckConnection()
|
||||||
{
|
{
|
||||||
if (ServerState is not (ServerState.Connected or ServerState.Connecting or ServerState.Reconnecting)) throw new InvalidDataException("Not connected");
|
if (ServerState is not (ServerState.Connected or ServerState.Connecting or ServerState.Reconnecting)) throw new InvalidDataException("Not connected");
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ public class HubFactory : MediatorSubscriberBase
|
|||||||
};
|
};
|
||||||
|
|
||||||
Logger.LogDebug("Building new HubConnection using transport {transport}", transportType);
|
Logger.LogDebug("Building new HubConnection using transport {transport}", transportType);
|
||||||
|
var msgpackOptions = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4Block).WithResolver(ContractlessStandardResolver.Instance);
|
||||||
|
|
||||||
_instance = new HubConnectionBuilder()
|
_instance = new HubConnectionBuilder()
|
||||||
.WithUrl(_serverConfigurationManager.CurrentApiUrl + ILightlessHub.Path, options =>
|
.WithUrl(_serverConfigurationManager.CurrentApiUrl + ILightlessHub.Path, options =>
|
||||||
@@ -80,22 +81,7 @@ public class HubFactory : MediatorSubscriberBase
|
|||||||
})
|
})
|
||||||
.AddMessagePackProtocol(opt =>
|
.AddMessagePackProtocol(opt =>
|
||||||
{
|
{
|
||||||
var resolver = CompositeResolver.Create(StandardResolverAllowPrivate.Instance,
|
opt.SerializerOptions = msgpackOptions;
|
||||||
BuiltinResolver.Instance,
|
|
||||||
AttributeFormatterResolver.Instance,
|
|
||||||
// replace enum resolver
|
|
||||||
DynamicEnumAsStringResolver.Instance,
|
|
||||||
DynamicGenericResolver.Instance,
|
|
||||||
DynamicUnionResolver.Instance,
|
|
||||||
DynamicObjectResolver.Instance,
|
|
||||||
PrimitiveObjectResolver.Instance,
|
|
||||||
// final fallback(last priority)
|
|
||||||
StandardResolver.Instance);
|
|
||||||
|
|
||||||
opt.SerializerOptions =
|
|
||||||
MessagePackSerializerOptions.Standard
|
|
||||||
.WithCompression(MessagePackCompression.Lz4Block)
|
|
||||||
.WithResolver(resolver);
|
|
||||||
})
|
})
|
||||||
.WithAutomaticReconnect(new ForeverRetryPolicy(Mediator))
|
.WithAutomaticReconnect(new ForeverRetryPolicy(Mediator))
|
||||||
.ConfigureLogging(a =>
|
.ConfigureLogging(a =>
|
||||||
|
|||||||
Reference in New Issue
Block a user