Redone syncshell admin ui, fixed some bugs on edit profile.
This commit is contained in:
@@ -12,7 +12,6 @@ using LightlessSync.Services;
|
||||
using LightlessSync.Services.Mediator;
|
||||
using LightlessSync.Services.Profiles;
|
||||
using LightlessSync.UI.Services;
|
||||
using LightlessSync.UI.Style;
|
||||
using LightlessSync.WebAPI;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SixLabors.ImageSharp;
|
||||
@@ -42,6 +41,11 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
||||
private Task<int>? _pruneTask;
|
||||
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,
|
||||
UiSharedService uiSharedService, PairUiService pairUiService, GroupFullInfoDto groupFullInfo, PerformanceCollectorService performanceCollectorService, LightlessProfileManager lightlessProfileManager)
|
||||
: base(logger, mediator, "Syncshell Admin Panel (" + groupFullInfo.GroupAliasOrGID + ")", performanceCollectorService)
|
||||
@@ -89,36 +93,147 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
||||
_profileData = _lightlessProfileManager.GetLightlessGroupProfile(GroupFullInfo.Group);
|
||||
using var id = ImRaii.PushId("syncshell_admin_" + GroupFullInfo.GID);
|
||||
|
||||
using (_uiSharedService.UidFont.Push())
|
||||
{
|
||||
var headerText = $"{GroupFullInfo.GroupAliasOrGID} Administrative Panel";
|
||||
_uiSharedService.UnderlinedBigText(headerText, UIColors.Get("LightlessBlue"));
|
||||
}
|
||||
DrawAdminHeader();
|
||||
|
||||
ImGui.Separator();
|
||||
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())
|
||||
{
|
||||
ImGui.BeginTooltip();
|
||||
ImGui.Text($"{GroupFullInfo.GroupAliasOrGID} is created at:");
|
||||
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.Separator();
|
||||
var perm = GroupFullInfo.GroupPermissions;
|
||||
var titlePos = new Vector2(pMin.X + 12f * scale, pMin.Y + 8f * scale);
|
||||
ImGui.SetCursorScreenPos(titlePos);
|
||||
|
||||
using var tabbar = ImRaii.TabBar("syncshell_tab_" + GroupFullInfo.GID);
|
||||
|
||||
if (tabbar)
|
||||
float titleHeight;
|
||||
using (_uiSharedService.UidFont.Push())
|
||||
{
|
||||
DrawInvites(perm);
|
||||
|
||||
DrawManagement();
|
||||
|
||||
DrawPermission(perm);
|
||||
|
||||
DrawProfile();
|
||||
ImGui.TextColored(UIColors.Get("LightlessBlue"), GroupFullInfo.GroupAliasOrGID);
|
||||
titleHeight = ImGui.GetTextLineHeightWithSpacing();
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -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()
|
||||
{
|
||||
var profileTab = ImRaii.TabItem("Profile");
|
||||
@@ -268,167 +447,230 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
||||
private void DrawManagement()
|
||||
{
|
||||
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();
|
||||
if (!snapshot.GroupPairs.TryGetValue(GroupFullInfo, out var pairs))
|
||||
// Users tab
|
||||
var usersTab = ImRaii.TabItem("Users");
|
||||
if (usersTab)
|
||||
{
|
||||
UiSharedService.ColorTextWrapped("No users found in this Syncshell", ImGuiColors.DalamudYellow);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawUserListCustom(pairs, GroupFullInfo);
|
||||
DrawUserListSection();
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
var inviteTab = ImRaii.TabItem("Invites");
|
||||
@@ -476,6 +718,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
||||
|
||||
private void DrawUserListCustom(IReadOnlyList<Pair> pairs, GroupFullInfoDto GroupFullInfo)
|
||||
{
|
||||
// Search bar (unchanged)
|
||||
ImGui.PushItemWidth(0);
|
||||
_uiSharedService.IconText(FontAwesomeIcon.Search, UIColors.Get("LightlessPurple"));
|
||||
ImGui.SameLine();
|
||||
@@ -511,21 +754,20 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
||||
if (p.Value.Value.IsPinned()) return 2;
|
||||
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();
|
||||
float fullW = ImGui.GetContentRegionAvail().X;
|
||||
|
||||
float colUid = fullW * 0.50f;
|
||||
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);
|
||||
|
||||
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;
|
||||
foreach (var kv in orderedPairs)
|
||||
{
|
||||
@@ -533,8 +775,8 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
||||
var userInfoOpt = kv.Value;
|
||||
DrawUserRowCustom(pair, userInfoOpt, GroupFullInfo, rowIndex++, colUid, colFlags, colActions);
|
||||
}
|
||||
|
||||
ImGui.EndChild();
|
||||
UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.0f);
|
||||
}
|
||||
|
||||
private static void DrawUserListHeader(float colUid, float colFlags)
|
||||
@@ -544,18 +786,18 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
||||
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("LightlessPurple"));
|
||||
|
||||
// Alias/UID/Note
|
||||
// Alias / UID / Note
|
||||
ImGui.SetCursorPosX(x0);
|
||||
ImGui.TextUnformatted("Alias / UID / Note");
|
||||
|
||||
// User Flags
|
||||
// Flags
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPosX(x0 + colUid + style.ItemSpacing.X);
|
||||
ImGui.TextUnformatted("Flags");
|
||||
|
||||
// User Actions
|
||||
// Actions
|
||||
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.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)
|
||||
{
|
||||
var note = pair.GetNote() ?? string.Empty;
|
||||
|
||||
Reference in New Issue
Block a user