Redone syncshell admin ui, fixed some bugs on edit profile.

This commit is contained in:
cake
2025-12-09 05:45:19 +01:00
parent 25f0d41581
commit 675918624d
12 changed files with 684 additions and 344 deletions

View File

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