Files
LightlessClient/LightlessSync/UI/SyncshellAdminUI.cs

738 lines
32 KiB
C#

using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Textures.TextureWraps;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using LightlessSync.API.Data.Enum;
using LightlessSync.API.Data.Extensions;
using LightlessSync.API.Dto.Group;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services;
using LightlessSync.Services.Mediator;
using LightlessSync.Services.Profiles;
using LightlessSync.UI.Services;
using LightlessSync.WebAPI;
using Microsoft.Extensions.Logging;
using SixLabors.ImageSharp;
using System.Globalization;
using System.Numerics;
namespace LightlessSync.UI;
public class SyncshellAdminUI : WindowMediatorSubscriberBase
{
private readonly ApiController _apiController;
private readonly bool _isModerator = false;
private readonly bool _isOwner = false;
private readonly List<string> _oneTimeInvites = [];
private readonly LightlessProfileManager _lightlessProfileManager;
private readonly UiSharedService _uiSharedService;
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;
private string _newPassword;
private bool _pwChangeSuccess;
private Task<int>? _pruneTestTask;
private Task<int>? _pruneTask;
private int _pruneDays = 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)
{
GroupFullInfo = groupFullInfo;
_apiController = apiController;
_uiSharedService = uiSharedService;
_lightlessProfileManager = lightlessProfileManager;
_pairUiService = pairUiService;
_isOwner = string.Equals(GroupFullInfo.OwnerUID, _apiController.UID, StringComparison.Ordinal);
_isModerator = GroupFullInfo.GroupUserInfo.IsModerator();
_newPassword = string.Empty;
_multiInvites = 30;
_pwChangeSuccess = true;
IsOpen = true;
Mediator.Subscribe<ClearProfileGroupDataMessage>(this, (msg) =>
{
if (msg.GroupData == null || string.Equals(msg.GroupData.AliasOrGID, GroupFullInfo.Group.AliasOrGID, StringComparison.Ordinal))
{
_pfpTextureWrap?.Dispose();
_pfpTextureWrap = null;
}
});
SizeConstraints = new WindowSizeConstraints()
{
MinimumSize = new(700, 500),
MaximumSize = new(700, 2000),
};
_pairUiService = pairUiService;
}
public GroupFullInfoDto GroupFullInfo { get; private set; }
protected override void DrawInternal()
{
if (!_isModerator && !_isOwner) return;
_logger.LogTrace("Drawing Syncshell Admin UI for {group}", GroupFullInfo.GroupAliasOrGID);
var snapshot = _pairUiService.GetSnapshot();
if (snapshot.GroupsByGid.TryGetValue(GroupFullInfo.Group.GID, out var updatedInfo))
{
GroupFullInfo = updatedInfo;
}
_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"));
}
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;
using var tabbar = ImRaii.TabBar("syncshell_tab_" + GroupFullInfo.GID);
if (tabbar)
{
DrawInvites(perm);
DrawManagement();
DrawPermission(perm);
DrawProfile();
}
}
private void DrawPermission(GroupPermissions perm)
{
var permissionTab = ImRaii.TabItem("Permissions");
if (permissionTab)
{
bool isDisableAnimations = perm.IsPreferDisableAnimations();
bool isDisableSounds = perm.IsPreferDisableSounds();
bool isDisableVfx = perm.IsPreferDisableVFX();
ImGui.AlignTextToFramePadding();
ImGui.Text("Suggest Sound Sync");
_uiSharedService.BooleanToColoredIcon(!isDisableSounds);
ImGui.SameLine(230);
using (ImRaii.PushColor(ImGuiCol.Text, isDisableSounds ? UIColors.Get("PairBlue") : UIColors.Get("DimRed")))
{
if (_uiSharedService.IconTextButton(isDisableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeMute,
isDisableSounds ? "Suggest to enable sound sync" : "Suggest to disable sound sync"))
{
perm.SetPreferDisableSounds(!perm.IsPreferDisableSounds());
_ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm));
}
}
ImGui.AlignTextToFramePadding();
ImGui.Text("Suggest Animation Sync");
_uiSharedService.BooleanToColoredIcon(!isDisableAnimations);
ImGui.SameLine(230);
using (ImRaii.PushColor(ImGuiCol.Text, isDisableAnimations ? UIColors.Get("PairBlue") : UIColors.Get("DimRed")))
{
if (_uiSharedService.IconTextButton(isDisableAnimations ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop,
isDisableAnimations ? "Suggest to enable animation sync" : "Suggest to disable animation sync"))
{
perm.SetPreferDisableAnimations(!perm.IsPreferDisableAnimations());
_ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm));
}
}
ImGui.AlignTextToFramePadding();
ImGui.Text("Suggest VFX Sync");
_uiSharedService.BooleanToColoredIcon(!isDisableVfx);
ImGui.SameLine(230);
using (ImRaii.PushColor(ImGuiCol.Text, isDisableVfx ? UIColors.Get("PairBlue") : UIColors.Get("DimRed")))
{
if (_uiSharedService.IconTextButton(isDisableVfx ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle,
isDisableVfx ? "Suggest to enable vfx sync" : "Suggest to disable vfx sync"))
{
perm.SetPreferDisableVFX(!perm.IsPreferDisableVFX());
_ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm));
}
}
UiSharedService.TextWrapped("Note: those suggested permissions will be shown to users on joining the Syncshell.");
}
permissionTab.Dispose();
if (_isOwner)
{
var ownerTab = ImRaii.TabItem("Owner Settings");
if (ownerTab)
{
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("New Password");
var availableWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X;
var buttonSize = _uiSharedService.GetIconTextButtonSize(FontAwesomeIcon.Passport, "Change Password");
var textSize = ImGui.CalcTextSize("New Password").X;
var spacing = ImGui.GetStyle().ItemSpacing.X;
ImGui.SameLine();
ImGui.SetNextItemWidth(availableWidth - buttonSize - textSize - spacing * 2);
ImGui.InputTextWithHint("##changepw", "Min 10 characters", ref _newPassword, 50);
ImGui.SameLine();
using (ImRaii.Disabled(_newPassword.Length < 10))
{
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Passport, "Change Password"))
{
_pwChangeSuccess = _apiController.GroupChangePassword(new GroupPasswordDto(GroupFullInfo.Group, _newPassword)).Result;
_newPassword = string.Empty;
}
}
UiSharedService.AttachToolTip("Password requires to be at least 10 characters long. This action is irreversible.");
if (!_pwChangeSuccess)
{
UiSharedService.ColorTextWrapped("Failed to change the password. Password requires to be at least 10 characters long.", ImGuiColors.DalamudYellow);
}
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Syncshell") && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed())
{
IsOpen = false;
_ = _apiController.GroupDelete(new(GroupFullInfo.Group));
}
UiSharedService.AttachToolTip("Hold CTRL and Shift and click to delete this Syncshell." + Environment.NewLine + "WARNING: this action is irreversible.");
}
ownerTab.Dispose();
}
}
private void DrawProfile()
{
var profileTab = ImRaii.TabItem("Profile");
if (!profileTab)
return;
if (_profileData != null)
{
if (!string.Equals(_profileDescription, _profileData.Description, StringComparison.Ordinal))
{
_profileDescription = _profileData.Description;
}
UiSharedService.TextWrapped("Preview the Syncshell profile in a standalone window.");
if (_uiSharedService.IconTextButton(FontAwesomeIcon.AddressCard, "Open Syncshell Profile"))
{
Mediator.Publish(new GroupProfileOpenStandaloneMessage(GroupFullInfo));
}
UiSharedService.AttachToolTip("Opens the standalone Syncshell profile window for this group.");
ImGuiHelpers.ScaledDummy(2f);
ImGui.TextDisabled("Profile Flags");
ImGui.BulletText(_profileData.IsNsfw ? "Marked as NSFW" : "Marked as SFW");
ImGui.BulletText(_profileData.IsDisabled ? "Profile disabled for viewers" : "Profile active");
ImGuiHelpers.ScaledDummy(2f);
UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f);
ImGuiHelpers.ScaledDummy(2f);
UiSharedService.TextWrapped("Open the syncshell profile editor to update images, description, tags, and visibility settings.");
ImGuiHelpers.ScaledDummy(2f);
if (_uiSharedService.IconTextButton(FontAwesomeIcon.UserEdit, "Open Syncshell Profile Editor"))
{
Mediator.Publish(new OpenGroupProfileEditorMessage(GroupFullInfo));
}
UiSharedService.AttachToolTip("Launches the editor window and associated live preview for this syncshell.");
}
else
{
UiSharedService.TextWrapped("Profile information is loading...");
}
profileTab.Dispose();
}
private void DrawManagement()
{
var mgmtTab = ImRaii.TabItem("User Management");
if (mgmtTab)
{
if (_uiSharedService.MediumTreeNode("User List & Administration", UIColors.Get("LightlessPurple")))
{
var snapshot = _pairUiService.GetSnapshot();
if (!snapshot.GroupPairs.TryGetValue(GroupFullInfo, out var pairs))
{
UiSharedService.ColorTextWrapped("No users found in this Syncshell", ImGuiColors.DalamudYellow);
}
else
{
DrawUserListCustom(pairs, GroupFullInfo);
}
ImGui.TreePop();
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", [1, 3, 7, 14, 30, 90], (count) =>
{
return 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 DrawInvites(GroupPermissions perm)
{
var inviteTab = ImRaii.TabItem("Invites");
if (inviteTab)
{
bool isInvitesDisabled = perm.IsDisableInvites();
if (_uiSharedService.IconTextButton(isInvitesDisabled ? FontAwesomeIcon.Unlock : FontAwesomeIcon.Lock,
isInvitesDisabled ? "Unlock Syncshell" : "Lock Syncshell"))
{
perm.SetDisableInvites(!isInvitesDisabled);
_ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm));
}
ImGuiHelpers.ScaledDummy(2f);
UiSharedService.TextWrapped("One-time invites work as single-use passwords. Use those if you do not want to distribute your Syncshell password.");
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Envelope, "Single one-time invite"))
{
ImGui.SetClipboardText(_apiController.GroupCreateTempInvite(new(GroupFullInfo.Group), 1).Result.FirstOrDefault() ?? string.Empty);
}
UiSharedService.AttachToolTip("Creates a single-use password for joining the syncshell which is valid for 24h and copies it to the clipboard.");
ImGui.InputInt("##amountofinvites", ref _multiInvites);
ImGui.SameLine();
using (ImRaii.Disabled(_multiInvites <= 1 || _multiInvites > 100))
{
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Envelope, "Generate " + _multiInvites + " one-time invites"))
{
_oneTimeInvites.AddRange(_apiController.GroupCreateTempInvite(new(GroupFullInfo.Group), _multiInvites).Result);
}
}
if (_oneTimeInvites.Any())
{
var invites = string.Join(Environment.NewLine, _oneTimeInvites);
ImGui.InputTextMultiline("Generated Multi Invites", ref invites, 5000, new(0, 0), ImGuiInputTextFlags.ReadOnly);
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Copy, "Copy Invites to clipboard"))
{
ImGui.SetClipboardText(invites);
}
}
}
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));
_pfpTextureWrap?.Dispose();
}
}