Changed syncshell admin user list, added filter and copy. show creation date while hoving over text.

This commit is contained in:
cake
2025-11-30 00:00:39 +01:00
parent 1e88fe0cf3
commit 0b36c1bdc2

View File

@@ -16,6 +16,7 @@ using LightlessSync.WebAPI;
using Microsoft.Extensions.Logging;
using SixLabors.ImageSharp;
using System.Globalization;
using System.Numerics;
namespace LightlessSync.UI;
@@ -30,6 +31,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
private readonly PairUiService _pairUiService;
private List<BannedGroupUserDto> _bannedUsers = [];
private LightlessGroupProfileData? _profileData = null;
private string _userSearchFilter = string.Empty;
private IDalamudTextureWrap? _pfpTextureWrap;
private string _profileDescription = string.Empty;
private int _multiInvites;
@@ -84,10 +86,22 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
}
_profileData = _lightlessProfileManager.GetLightlessGroupProfile(GroupFullInfo.Group);
using var id = ImRaii.PushId("syncshell_admin_" + GroupFullInfo.GID);
using (_uiSharedService.UidFont.Push())
_uiSharedService.UnderlinedBigText(GroupFullInfo.GroupAliasOrGID + " Administrative Panel", UIColors.Get("LightlessBlue"));
{
var headerText = $"{GroupFullInfo.GroupAliasOrGID} Administrative Panel";
_uiSharedService.UnderlinedBigText(headerText, UIColors.Get("LightlessBlue"));
}
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.EndTooltip();
}
ImGui.Separator();
var perm = GroupFullInfo.GroupPermissions;
@@ -264,151 +278,12 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
}
else
{
var tableFlags = ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp;
if (pairs.Count > 10) tableFlags |= ImGuiTableFlags.ScrollY;
using var table = ImRaii.Table("userList#" + GroupFullInfo.Group.AliasOrGID, 3, tableFlags);
if (table)
{
ImGui.TableSetupColumn("Alias/UID/Note", ImGuiTableColumnFlags.None, 4);
ImGui.TableSetupColumn("Flags", ImGuiTableColumnFlags.None, 1);
ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.None, 3);
ImGui.TableHeadersRow();
var groupedPairs = new Dictionary<Pair, GroupPairUserInfo?>(pairs.Select(p => new KeyValuePair<Pair, GroupPairUserInfo?>(p,
GroupFullInfo.GroupPairUserInfos.TryGetValue(p.UserData.UID, out GroupPairUserInfo value) ? value : null)));
foreach (var pair in groupedPairs.OrderBy(p =>
{
if (p.Value == null) return 10;
if (string.Equals(p.Key.UserData.UID, GroupFullInfo.OwnerUID, StringComparison.Ordinal)) return 0;
if (p.Value.Value.IsModerator()) return 1;
if (p.Value.Value.IsPinned()) return 2;
return 10;
}).ThenBy(p => p.Key.GetNote() ?? p.Key.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase))
{
using var tableId = ImRaii.PushId("userTable_" + pair.Key.UserData.UID);
var isUserOwner = string.Equals(pair.Key.UserData.UID, GroupFullInfo.OwnerUID, StringComparison.Ordinal);
ImGui.TableNextColumn(); // alias/uid/note
var note = pair.Key.GetNote();
var text = note == null ? pair.Key.UserData.AliasOrUID : note + " (" + pair.Key.UserData.AliasOrUID + ")";
ImGui.AlignTextToFramePadding();
var boolcolor = UiSharedService.GetBoolColor(pair.Key.IsOnline);
UiSharedService.ColorText(text, boolcolor);
if (!string.IsNullOrEmpty(pair.Key.PlayerName))
{
UiSharedService.AttachToolTip(pair.Key.PlayerName);
ImGui.SameLine();
}
ImGui.TableNextColumn(); // special flags
if (pair.Value != null && (pair.Value.Value.IsModerator() || pair.Value.Value.IsPinned() || isUserOwner))
{
if (pair.Value.Value.IsModerator())
{
_uiSharedService.IconText(FontAwesomeIcon.UserShield, UIColors.Get("LightlessPurple"));
UiSharedService.AttachToolTip("Moderator");
}
if (pair.Value.Value.IsPinned() && !isUserOwner)
{
_uiSharedService.IconText(FontAwesomeIcon.Thumbtack);
UiSharedService.AttachToolTip("Pinned");
}
if (isUserOwner)
{
_uiSharedService.IconText(FontAwesomeIcon.Crown, UIColors.Get("LightlessYellow"));
UiSharedService.AttachToolTip("Owner");
}
}
else
{
_uiSharedService.IconText(FontAwesomeIcon.None);
}
ImGui.TableNextColumn(); // actions
if (_isOwner)
{
using (ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("LightlessYellow")))
{
using (ImRaii.Disabled(!UiSharedService.ShiftPressed()))
{
if (_uiSharedService.IconButton(FontAwesomeIcon.Crown))
{
_ = _apiController.GroupChangeOwnership(new(GroupFullInfo.Group, pair.Key.UserData));
IsOpen = false;
}
}
}
UiSharedService.AttachToolTip("Hold SHIFT and click to transfer ownership of this Syncshell to "
+ (pair.Key.UserData.AliasOrUID) + Environment.NewLine + "WARNING: This action is irreversible and will close screen.");
ImGui.SameLine();
using (ImRaii.PushColor(ImGuiCol.Text, pair.Value != null && pair.Value.Value.IsModerator() ? UIColors.Get("DimRed") : UIColors.Get("PairBlue")))
{
if (_uiSharedService.IconButton(FontAwesomeIcon.UserShield))
{
GroupPairUserInfo userInfo = pair.Value ?? GroupPairUserInfo.None;
userInfo.SetModerator(!userInfo.IsModerator());
_ = _apiController.GroupSetUserInfo(new GroupPairUserInfoDto(GroupFullInfo.Group, pair.Key.UserData, userInfo));
}
}
UiSharedService.AttachToolTip(pair.Value != null && pair.Value.Value.IsModerator() ? "Demod user" : "Mod user");
ImGui.SameLine();
}
if (pair.Value == null || pair.Value != null && !pair.Value.Value.IsModerator() && !isUserOwner)
{
using (ImRaii.PushColor(ImGuiCol.Text, pair.Value != null && pair.Value.Value.IsPinned() ? UIColors.Get("DimRed") : UIColors.Get("PairBlue")))
{
if (_uiSharedService.IconButton(FontAwesomeIcon.Thumbtack))
{
GroupPairUserInfo userInfo = pair.Value ?? GroupPairUserInfo.None;
userInfo.SetPinned(!userInfo.IsPinned());
_ = _apiController.GroupSetUserInfo(new GroupPairUserInfoDto(GroupFullInfo.Group, pair.Key.UserData, userInfo));
}
}
UiSharedService.AttachToolTip(pair.Value != null && pair.Value.Value.IsPinned() ? "Unpin user" : "Pin user");
ImGui.SameLine();
using (ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("DimRed")))
{
using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
{
if (_uiSharedService.IconButton(FontAwesomeIcon.Trash))
{
_ = _apiController.GroupRemoveUser(new GroupPairDto(GroupFullInfo.Group, pair.Key.UserData));
}
}
}
UiSharedService.AttachToolTip("Remove user from Syncshell"
+ UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
ImGui.SameLine();
using (ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("DimRed")))
{
using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
{
if (_uiSharedService.IconButton(FontAwesomeIcon.Ban))
{
Mediator.Publish(new OpenBanUserPopupMessage(pair.Key, GroupFullInfo));
}
}
}
UiSharedService.AttachToolTip("Ban user from Syncshell"
+ UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
}
}
}
DrawUserListCustom(pairs, GroupFullInfo);
}
UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f);
ImGui.TreePop();
ImGui.Separator();
}
ImGui.Separator();
if (_uiSharedService.MediumTreeNode("Mass Cleanup", UIColors.Get("DimRed")))
{
@@ -595,6 +470,266 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
inviteTab.Dispose();
}
private void DrawUserListCustom(IReadOnlyList<Pair> pairs, GroupFullInfoDto GroupFullInfo)
{
ImGui.PushItemWidth(0);
_uiSharedService.IconText(FontAwesomeIcon.Search, UIColors.Get("LightlessPurple"));
ImGui.SameLine();
ImGui.InputTextWithHint(
"##UserSearchFilter",
"Search UID/alias or note...",
ref _userSearchFilter,
64);
ImGui.PopItemWidth();
ImGui.Dummy(new Vector2(0, 4 * ImGuiHelpers.GlobalScale));
var groupedPairs = new Dictionary<Pair, GroupPairUserInfo?>(
pairs.Select(p => new KeyValuePair<Pair, GroupPairUserInfo?>(
p,
GroupFullInfo.GroupPairUserInfos.TryGetValue(p.UserData.UID, out var value) ? value : null
))
);
var filter = _userSearchFilter?.Trim();
bool hasFilter = !string.IsNullOrEmpty(filter);
if (hasFilter)
filter = filter!.ToLowerInvariant();
var orderedPairs = groupedPairs
.Where(p => !hasFilter || MatchesUserFilter(p.Key, filter!))
.OrderBy(p =>
{
if (p.Value == null) return 10;
if (string.Equals(p.Key.UserData.UID, GroupFullInfo.OwnerUID, StringComparison.Ordinal)) return 0;
if (p.Value.Value.IsModerator()) return 1;
if (p.Value.Value.IsPinned()) return 2;
return 10;
})
.ThenBy(p => p.Key.GetNote() ?? p.Key.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase);
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;
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)
{
var pair = kv.Key;
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)
{
var style = ImGui.GetStyle();
float x0 = ImGui.GetCursorPosX();
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("LightlessPurple"));
// Alias/UID/Note
ImGui.SetCursorPosX(x0);
ImGui.TextUnformatted("Alias / UID / Note");
// User Flags
ImGui.SameLine();
ImGui.SetCursorPosX(x0 + colUid + style.ItemSpacing.X);
ImGui.TextUnformatted("Flags");
// User Actions
ImGui.SameLine();
ImGui.SetCursorPosX(x0 + colUid + colFlags + style.ItemSpacing.X * 2.5f);
ImGui.TextUnformatted("Actions");
ImGui.PopStyleColor();
UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.0f);
}
private void DrawUserRowCustom(Pair pair, GroupPairUserInfo? userInfoOpt, GroupFullInfoDto GroupFullInfo, int rowIndex, float colUid, float colFlags, float colActions)
{
using var id = ImRaii.PushId("userRow_" + pair.UserData.UID);
var style = ImGui.GetStyle();
float x0 = ImGui.GetCursorPosX();
if (rowIndex % 2 == 0)
{
var drawList = ImGui.GetWindowDrawList();
var pMin = ImGui.GetCursorScreenPos();
var rowHeight = ImGui.GetTextLineHeightWithSpacing() * 2.5f;
var pMax = new Vector2(pMin.X + colUid + colFlags + colActions + style.ItemSpacing.X * 2.0f,
pMin.Y + rowHeight);
var bgColor = UIColors.Get("FullBlack") with { W = 0.0f };
drawList.AddRectFilled(pMin, pMax, ImGui.ColorConvertFloat4ToU32(bgColor));
}
var isUserOwner = string.Equals(pair.UserData.UID, GroupFullInfo.OwnerUID, StringComparison.Ordinal);
var userInfo = userInfoOpt ?? GroupPairUserInfo.None;
ImGui.SetCursorPosX(x0);
ImGui.AlignTextToFramePadding();
var note = pair.GetNote();
var text = note == null
? pair.UserData.AliasOrUID
: $"{note} ({pair.UserData.AliasOrUID})";
var boolcolor = UiSharedService.GetBoolColor(pair.IsOnline);
UiSharedService.ColorText(text, boolcolor);
if (ImGui.IsItemClicked())
{
ImGui.SetClipboardText(text);
}
if (!string.IsNullOrEmpty(pair.PlayerName))
{
UiSharedService.AttachToolTip(pair.PlayerName);
}
ImGui.SameLine();
ImGui.SetCursorPosX(x0 + colUid + style.ItemSpacing.X);
if (userInfoOpt != null && (userInfo.IsModerator() || userInfo.IsPinned() || isUserOwner))
{
if (userInfo.IsModerator())
{
_uiSharedService.IconText(FontAwesomeIcon.UserShield, UIColors.Get("LightlessPurple"));
UiSharedService.AttachToolTip("Moderator");
}
if (userInfo.IsPinned() && !isUserOwner)
{
_uiSharedService.IconText(FontAwesomeIcon.Thumbtack);
UiSharedService.AttachToolTip("Pinned");
}
if (isUserOwner)
{
_uiSharedService.IconText(FontAwesomeIcon.Crown, UIColors.Get("LightlessYellow"));
UiSharedService.AttachToolTip("Owner");
}
}
else
{
_uiSharedService.IconText(FontAwesomeIcon.None);
}
ImGui.SameLine();
ImGui.SetCursorPosX(x0 + colUid + colFlags + style.ItemSpacing.X * 2.0f);
DrawUserActions(pair, GroupFullInfo, userInfo, isUserOwner);
ImGui.Dummy(new Vector2(0, 4 * ImGuiHelpers.GlobalScale));
}
private void DrawUserActions(Pair pair, GroupFullInfoDto GroupFullInfo, GroupPairUserInfo userInfo, bool isUserOwner)
{
if (_isOwner)
{
// Transfer ownership to user
using (ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("LightlessYellow")))
using (ImRaii.Disabled(!UiSharedService.ShiftPressed()))
{
if (_uiSharedService.IconButton(FontAwesomeIcon.Crown))
{
_ = _apiController.GroupChangeOwnership(new(GroupFullInfo.Group, pair.UserData));
IsOpen = false;
}
}
UiSharedService.AttachToolTip("Hold SHIFT and click to transfer ownership of this Syncshell to "
+ pair.UserData.AliasOrUID + Environment.NewLine
+ "WARNING: This action is irreversible and will close screen.");
ImGui.SameLine();
// Mod / Demod user
using (ImRaii.PushColor(ImGuiCol.Text,
userInfo.IsModerator() ? UIColors.Get("DimRed") : UIColors.Get("PairBlue")))
{
if (_uiSharedService.IconButton(FontAwesomeIcon.UserShield))
{
userInfo.SetModerator(!userInfo.IsModerator());
_ = _apiController.GroupSetUserInfo(
new GroupPairUserInfoDto(GroupFullInfo.Group, pair.UserData, userInfo));
}
}
UiSharedService.AttachToolTip(
userInfo.IsModerator() ? $"Demod {pair.UserData.AliasOrUID}" : $"Mod {pair.UserData.AliasOrUID}");
ImGui.SameLine();
}
if (userInfo == GroupPairUserInfo.None || (!userInfo.IsModerator() && !isUserOwner))
{
// Pin user
using (ImRaii.PushColor(ImGuiCol.Text,
userInfo.IsPinned() ? UIColors.Get("DimRed") : UIColors.Get("PairBlue")))
{
if (_uiSharedService.IconButton(FontAwesomeIcon.Thumbtack))
{
userInfo.SetPinned(!userInfo.IsPinned());
_ = _apiController.GroupSetUserInfo(
new GroupPairUserInfoDto(GroupFullInfo.Group, pair.UserData, userInfo));
}
}
UiSharedService.AttachToolTip(
userInfo.IsPinned() ? $"Unpin {pair.UserData.AliasOrUID}" : $"Pin {pair.UserData.AliasOrUID}");
ImGui.SameLine();
// Remove user
using (ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("DimRed")))
using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
{
if (_uiSharedService.IconButton(FontAwesomeIcon.Trash))
{
_ = _apiController.GroupRemoveUser(new GroupPairDto(GroupFullInfo.Group, pair.UserData));
}
}
UiSharedService.AttachToolTip($"Remove {pair.UserData.AliasOrUID} from Syncshell"
+ UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
ImGui.SameLine();
// Ban user
using (ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("DimRed")))
using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
{
if (_uiSharedService.IconButton(FontAwesomeIcon.Ban))
{
Mediator.Publish(new OpenBanUserPopupMessage(pair, GroupFullInfo));
}
}
UiSharedService.AttachToolTip($"Ban {pair.UserData.AliasOrUID} from Syncshell"
+ UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
}
}
private static bool MatchesUserFilter(Pair pair, string filterLower)
{
var note = pair.GetNote() ?? string.Empty;
var uid = pair.UserData.UID ?? string.Empty;
var alias = pair.UserData.AliasOrUID ?? string.Empty;
return note.Contains(filterLower, StringComparison.OrdinalIgnoreCase)
|| uid.Contains(filterLower, StringComparison.OrdinalIgnoreCase)
|| alias.Contains(filterLower, StringComparison.OrdinalIgnoreCase);
}
public override void OnClose()
{
Mediator.Publish(new RemoveWindowMessage(this));