Merge pull request 'Changes of admin ui for banning users.' (#128) from ban-admin-changes into 2.0.3
Reviewed-on: #128
This commit was merged in pull request #128.
This commit is contained in:
@@ -8,6 +8,7 @@ using LightlessSync.UI.Tags;
|
|||||||
using LightlessSync.WebAPI;
|
using LightlessSync.WebAPI;
|
||||||
using LightlessSync.UI.Services;
|
using LightlessSync.UI.Services;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using LightlessSync.PlayerData.Factories;
|
||||||
|
|
||||||
namespace LightlessSync.Services;
|
namespace LightlessSync.Services;
|
||||||
|
|
||||||
@@ -23,6 +24,7 @@ public class UiFactory
|
|||||||
private readonly PerformanceCollectorService _performanceCollectorService;
|
private readonly PerformanceCollectorService _performanceCollectorService;
|
||||||
private readonly ProfileTagService _profileTagService;
|
private readonly ProfileTagService _profileTagService;
|
||||||
private readonly DalamudUtilService _dalamudUtilService;
|
private readonly DalamudUtilService _dalamudUtilService;
|
||||||
|
private readonly PairFactory _pairFactory;
|
||||||
|
|
||||||
public UiFactory(
|
public UiFactory(
|
||||||
ILoggerFactory loggerFactory,
|
ILoggerFactory loggerFactory,
|
||||||
@@ -34,7 +36,8 @@ public class UiFactory
|
|||||||
LightlessProfileManager lightlessProfileManager,
|
LightlessProfileManager lightlessProfileManager,
|
||||||
PerformanceCollectorService performanceCollectorService,
|
PerformanceCollectorService performanceCollectorService,
|
||||||
ProfileTagService profileTagService,
|
ProfileTagService profileTagService,
|
||||||
DalamudUtilService dalamudUtilService)
|
DalamudUtilService dalamudUtilService,
|
||||||
|
PairFactory pairFactory)
|
||||||
{
|
{
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
_lightlessMediator = lightlessMediator;
|
_lightlessMediator = lightlessMediator;
|
||||||
@@ -46,6 +49,7 @@ public class UiFactory
|
|||||||
_performanceCollectorService = performanceCollectorService;
|
_performanceCollectorService = performanceCollectorService;
|
||||||
_profileTagService = profileTagService;
|
_profileTagService = profileTagService;
|
||||||
_dalamudUtilService = dalamudUtilService;
|
_dalamudUtilService = dalamudUtilService;
|
||||||
|
_pairFactory = pairFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SyncshellAdminUI CreateSyncshellAdminUi(GroupFullInfoDto dto)
|
public SyncshellAdminUI CreateSyncshellAdminUi(GroupFullInfoDto dto)
|
||||||
@@ -58,7 +62,8 @@ public class UiFactory
|
|||||||
_pairUiService,
|
_pairUiService,
|
||||||
dto,
|
dto,
|
||||||
_performanceCollectorService,
|
_performanceCollectorService,
|
||||||
_lightlessProfileManager);
|
_lightlessProfileManager,
|
||||||
|
_pairFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StandaloneProfileUi CreateStandaloneProfileUi(Pair pair)
|
public StandaloneProfileUi CreateStandaloneProfileUi(Pair pair)
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ using Dalamud.Interface.Colors;
|
|||||||
using Dalamud.Interface.Textures.TextureWraps;
|
using Dalamud.Interface.Textures.TextureWraps;
|
||||||
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.Data.Enum;
|
using LightlessSync.API.Data.Enum;
|
||||||
using LightlessSync.API.Data.Extensions;
|
using LightlessSync.API.Data.Extensions;
|
||||||
using LightlessSync.API.Dto.Group;
|
using LightlessSync.API.Dto.Group;
|
||||||
|
using LightlessSync.PlayerData.Factories;
|
||||||
using LightlessSync.PlayerData.Pairs;
|
using LightlessSync.PlayerData.Pairs;
|
||||||
using LightlessSync.Services;
|
using LightlessSync.Services;
|
||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
@@ -42,13 +44,32 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
private Task<int>? _pruneTask;
|
private Task<int>? _pruneTask;
|
||||||
private int _pruneDays = 14;
|
private int _pruneDays = 14;
|
||||||
|
|
||||||
|
// Ban management fields
|
||||||
|
private Task<List<BannedGroupUserDto>>? _bannedUsersTask;
|
||||||
|
private bool _bannedUsersLoaded;
|
||||||
|
private string? _bannedUsersLoadError;
|
||||||
|
|
||||||
|
private string _newBanUid = string.Empty;
|
||||||
|
private string _newBanReason = string.Empty;
|
||||||
|
private Task? _newBanTask;
|
||||||
|
private string? _newBanError;
|
||||||
|
private DateTime _newBanBusyUntilUtc;
|
||||||
|
|
||||||
|
// Ban editing fields
|
||||||
|
private string? _editingBanUid;
|
||||||
|
private readonly Dictionary<string, string> _banReasonEdits = new(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
private Task? _banEditTask;
|
||||||
|
private string? _banEditError;
|
||||||
|
|
||||||
private Task<GroupPruneSettingsDto>? _pruneSettingsTask;
|
private Task<GroupPruneSettingsDto>? _pruneSettingsTask;
|
||||||
private bool _pruneSettingsLoaded;
|
private bool _pruneSettingsLoaded;
|
||||||
private bool _autoPruneEnabled;
|
private bool _autoPruneEnabled;
|
||||||
private int _autoPruneDays = 14;
|
private int _autoPruneDays = 14;
|
||||||
|
private readonly PairFactory _pairFactory;
|
||||||
|
|
||||||
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, PairFactory pairFactory)
|
||||||
: base(logger, mediator, "Syncshell Admin Panel (" + groupFullInfo.GroupAliasOrGID + ")", performanceCollectorService)
|
: base(logger, mediator, "Syncshell Admin Panel (" + groupFullInfo.GroupAliasOrGID + ")", performanceCollectorService)
|
||||||
{
|
{
|
||||||
GroupFullInfo = groupFullInfo;
|
GroupFullInfo = groupFullInfo;
|
||||||
@@ -76,6 +97,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
MaximumSize = new(700, 2000),
|
MaximumSize = new(700, 2000),
|
||||||
};
|
};
|
||||||
_pairUiService = pairUiService;
|
_pairUiService = pairUiService;
|
||||||
|
_pairFactory = pairFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupFullInfoDto GroupFullInfo { get; private set; }
|
public GroupFullInfoDto GroupFullInfo { get; private set; }
|
||||||
@@ -654,34 +676,345 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
_uiSharedService.MediumText("User Bans", UIColors.Get("LightlessYellow"));
|
_uiSharedService.MediumText("User Bans", UIColors.Get("LightlessYellow"));
|
||||||
ImGuiHelpers.ScaledDummy(3f);
|
ImGuiHelpers.ScaledDummy(3f);
|
||||||
|
|
||||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Retweet, "Refresh Banlist from Server"))
|
EnsureBanListLoaded();
|
||||||
|
|
||||||
|
DrawNewBanEntryRow();
|
||||||
|
|
||||||
|
ImGuiHelpers.ScaledDummy(4f);
|
||||||
|
|
||||||
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Retweet, "Refresh Banlist"))
|
||||||
{
|
{
|
||||||
_bannedUsers = _apiController.GroupGetBannedUsers(new GroupDto(GroupFullInfo.Group)).Result;
|
QueueBanListRefresh(force: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(2f);
|
ImGuiHelpers.ScaledDummy(2f);
|
||||||
|
|
||||||
|
if (!_bannedUsersLoaded)
|
||||||
|
{
|
||||||
|
UiSharedService.ColorTextWrapped("Loading banlist from server...", ImGuiColors.DalamudGrey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(_bannedUsersLoadError))
|
||||||
|
{
|
||||||
|
UiSharedService.ColorTextWrapped(_bannedUsersLoadError!, ImGuiColors.DalamudRed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.BeginChild("bannedListScroll#" + GroupFullInfo.GID, new Vector2(0, 0), true);
|
ImGui.BeginChild("bannedListScroll#" + GroupFullInfo.GID, new Vector2(0, 0), true);
|
||||||
|
|
||||||
var style = ImGui.GetStyle();
|
var style = ImGui.GetStyle();
|
||||||
float fullW = ImGui.GetContentRegionAvail().X;
|
float fullW = ImGui.GetContentRegionAvail().X;
|
||||||
|
float scale = ImGuiHelpers.GlobalScale;
|
||||||
|
|
||||||
|
float frame = ImGui.GetFrameHeight();
|
||||||
|
float actionIcons = 3;
|
||||||
|
float colActions = actionIcons * frame + (actionIcons - 1) * style.ItemSpacing.X + 10f * scale;
|
||||||
|
|
||||||
float colIdentity = fullW * 0.45f;
|
|
||||||
float colMeta = fullW * 0.35f;
|
float colMeta = fullW * 0.35f;
|
||||||
float colActions = fullW - colIdentity - colMeta - style.ItemSpacing.X * 2.0f;
|
|
||||||
|
|
||||||
// Header
|
float colIdentity = fullW - colMeta - colActions - style.ItemSpacing.X * 2.0f;
|
||||||
|
|
||||||
|
float minIdentity = fullW * 0.40f;
|
||||||
|
if (colIdentity < minIdentity)
|
||||||
|
{
|
||||||
|
colIdentity = minIdentity;
|
||||||
|
colMeta = fullW - colIdentity - colActions - style.ItemSpacing.X * 2.0f;
|
||||||
|
if (colMeta < 80f * scale) colMeta = 80f * scale;
|
||||||
|
}
|
||||||
|
|
||||||
DrawBannedListHeader(colIdentity, colMeta);
|
DrawBannedListHeader(colIdentity, colMeta);
|
||||||
|
|
||||||
int rowIndex = 0;
|
int rowIndex = 0;
|
||||||
foreach (var bannedUser in _bannedUsers.ToList())
|
foreach (var bannedUser in _bannedUsers.ToList())
|
||||||
{
|
{
|
||||||
// Each row
|
|
||||||
DrawBannedRow(bannedUser, rowIndex++, colIdentity, colMeta, colActions);
|
DrawBannedRow(bannedUser, rowIndex++, colIdentity, colMeta, colActions);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndChild();
|
ImGui.EndChild();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawNewBanEntryRow()
|
||||||
|
{
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("LightlessYellow"));
|
||||||
|
ImGui.TextUnformatted("Add new ban");
|
||||||
|
ImGui.PopStyleColor();
|
||||||
|
|
||||||
|
UiSharedService.TextWrapped("Enter a UID (Not Alias!) and optional reason. (Hold CTRL to enable the ban button.)");
|
||||||
|
|
||||||
|
var style = ImGui.GetStyle();
|
||||||
|
float fullW = ImGui.GetContentRegionAvail().X;
|
||||||
|
|
||||||
|
float uidW = fullW * 0.35f;
|
||||||
|
float reasonW = fullW * 0.50f;
|
||||||
|
float btnW = fullW - uidW - reasonW - style.ItemSpacing.X * 2f;
|
||||||
|
|
||||||
|
// UID
|
||||||
|
ImGui.SetNextItemWidth(uidW);
|
||||||
|
ImGui.InputTextWithHint("##newBanUid", "UID...", ref _newBanUid, 128);
|
||||||
|
|
||||||
|
// Reason
|
||||||
|
ImGui.SameLine(0f, style.ItemSpacing.X);
|
||||||
|
ImGui.SetNextItemWidth(reasonW);
|
||||||
|
ImGui.InputTextWithHint("##newBanReason", "Reason (optional)...", ref _newBanReason, 256);
|
||||||
|
|
||||||
|
// Ban button
|
||||||
|
ImGui.SameLine(0f, style.ItemSpacing.X);
|
||||||
|
|
||||||
|
var trimmedUid = (_newBanUid ?? string.Empty).Trim();
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
bool taskRunning = _newBanTask != null && !_newBanTask.IsCompleted;
|
||||||
|
bool busyLatched = now < _newBanBusyUntilUtc;
|
||||||
|
bool busy = taskRunning || busyLatched;
|
||||||
|
|
||||||
|
bool canBan = UiSharedService.CtrlPressed()
|
||||||
|
&& !string.IsNullOrWhiteSpace(_newBanUid)
|
||||||
|
&& !busy;
|
||||||
|
|
||||||
|
using (ImRaii.Disabled(!canBan))
|
||||||
|
using (ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("DimRed")))
|
||||||
|
{
|
||||||
|
ImGui.SetNextItemWidth(btnW);
|
||||||
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.UserSlash, "Ban"))
|
||||||
|
{
|
||||||
|
_newBanError = null;
|
||||||
|
|
||||||
|
_newBanBusyUntilUtc = DateTime.UtcNow.AddMilliseconds(750);
|
||||||
|
|
||||||
|
_newBanTask = SubmitNewBanByUidAsync(trimmedUid, _newBanReason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip("Hold CTRL to enable banning by UID.");
|
||||||
|
|
||||||
|
if (busy)
|
||||||
|
{
|
||||||
|
UiSharedService.ColorTextWrapped("Banning user...", ImGuiColors.DalamudGrey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_newBanTask != null && _newBanTask.IsCompleted && DateTime.UtcNow >= _newBanBusyUntilUtc)
|
||||||
|
{
|
||||||
|
if (_newBanTask.IsFaulted)
|
||||||
|
{
|
||||||
|
var _ = _newBanTask.Exception;
|
||||||
|
_newBanError ??= "Ban failed (see log).";
|
||||||
|
}
|
||||||
|
|
||||||
|
QueueBanListRefresh(force: true);
|
||||||
|
_newBanTask = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SubmitNewBanByUidAsync(string uidOrAlias, string reason)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Yield();
|
||||||
|
|
||||||
|
uidOrAlias = (uidOrAlias ?? string.Empty).Trim();
|
||||||
|
reason = (reason ?? string.Empty).Trim();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(uidOrAlias))
|
||||||
|
{
|
||||||
|
_newBanError = "UID is empty.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string targetUid = uidOrAlias;
|
||||||
|
string? typedAlias = null;
|
||||||
|
|
||||||
|
var snap = _pairUiService.GetSnapshot();
|
||||||
|
if (snap.GroupPairs.TryGetValue(GroupFullInfo, out var pairs))
|
||||||
|
{
|
||||||
|
var match = pairs.FirstOrDefault(p =>
|
||||||
|
string.Equals(p.UserData.UID, uidOrAlias, StringComparison.Ordinal) ||
|
||||||
|
string.Equals(p.UserData.AliasOrUID, uidOrAlias, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
if (match != null)
|
||||||
|
{
|
||||||
|
targetUid = match.UserData.UID;
|
||||||
|
typedAlias = match.UserData.Alias;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
typedAlias = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var userData = new UserData(UID: targetUid, Alias: typedAlias);
|
||||||
|
|
||||||
|
await _apiController
|
||||||
|
.GroupBanUser(new GroupPairDto(GroupFullInfo.Group, userData), reason)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
_newBanUid = string.Empty;
|
||||||
|
_newBanReason = string.Empty;
|
||||||
|
_newBanError = null;
|
||||||
|
|
||||||
|
QueueBanListRefresh(force: true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Failed to ban '{uidOrAlias}' in group {gid}", uidOrAlias, GroupFullInfo.Group.GID);
|
||||||
|
_newBanError = "Failed to ban user (see log).";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveBanReasonViaBanUserAsync(string uid)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!_banReasonEdits.TryGetValue(uid, out var newReason))
|
||||||
|
newReason = string.Empty;
|
||||||
|
|
||||||
|
newReason = (newReason ?? string.Empty).Trim();
|
||||||
|
|
||||||
|
var userData = new UserData(uid.Trim());
|
||||||
|
|
||||||
|
await _apiController
|
||||||
|
.GroupBanUser(new GroupPairDto(GroupFullInfo.Group, userData), newReason)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
_editingBanUid = null;
|
||||||
|
_banEditError = null;
|
||||||
|
|
||||||
|
await Task.Delay(450).ConfigureAwait(false);
|
||||||
|
|
||||||
|
QueueBanListRefresh(force: true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Failed to edit ban reason for {uid} in group {gid}", uid, GroupFullInfo.Group.GID);
|
||||||
|
_banEditError = "Failed to update reason (see log).";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawBannedRow(BannedGroupUserDto bannedUser, int rowIndex, float colIdentity, float colMeta, float colActions)
|
||||||
|
{
|
||||||
|
using var id = ImRaii.PushId("banRow_" + bannedUser.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.6f;
|
||||||
|
var pMax = new Vector2(
|
||||||
|
pMin.X + colIdentity + colMeta + colActions + style.ItemSpacing.X * 2.0f,
|
||||||
|
pMin.Y + rowHeight);
|
||||||
|
|
||||||
|
var bgColor = UIColors.Get("FullBlack").WithAlpha(0.10f);
|
||||||
|
drawList.AddRectFilled(pMin, pMax, ImGui.ColorConvertFloat4ToU32(bgColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SetCursorPosX(x0);
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
|
||||||
|
string alias = bannedUser.UserAlias ?? string.Empty;
|
||||||
|
string line1 = string.IsNullOrEmpty(alias)
|
||||||
|
? bannedUser.UID
|
||||||
|
: $"{alias} ({bannedUser.UID})";
|
||||||
|
|
||||||
|
ImGui.TextUnformatted(line1);
|
||||||
|
|
||||||
|
var fullReason = bannedUser.Reason ?? string.Empty;
|
||||||
|
|
||||||
|
if (string.Equals(_editingBanUid, bannedUser.UID, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
_banReasonEdits.TryGetValue(bannedUser.UID, out var editReason);
|
||||||
|
editReason ??= StripAliasSuffix(fullReason);
|
||||||
|
|
||||||
|
ImGui.SetCursorPosX(x0);
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey);
|
||||||
|
ImGui.SetNextItemWidth(colIdentity);
|
||||||
|
ImGui.InputTextWithHint("##banReasonEdit", "Reason...", ref editReason, 255);
|
||||||
|
ImGui.PopStyleColor();
|
||||||
|
|
||||||
|
_banReasonEdits[bannedUser.UID] = editReason;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(_banEditError))
|
||||||
|
UiSharedService.ColorTextWrapped(_banEditError!, ImGuiColors.DalamudRed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(fullReason))
|
||||||
|
{
|
||||||
|
ImGui.SetCursorPosX(x0);
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey);
|
||||||
|
|
||||||
|
ImGui.PushTextWrapPos(x0 + colIdentity);
|
||||||
|
UiSharedService.TextWrapped(fullReason);
|
||||||
|
ImGui.PopTextWrapPos();
|
||||||
|
|
||||||
|
ImGui.PopStyleColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.SetCursorPosX(x0 + colIdentity + style.ItemSpacing.X);
|
||||||
|
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.TextUnformatted($"By: {bannedUser.BannedBy}");
|
||||||
|
|
||||||
|
var dateText = bannedUser.BannedOn.ToLocalTime().ToString(CultureInfo.CurrentCulture);
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey);
|
||||||
|
ImGui.TextUnformatted(dateText);
|
||||||
|
ImGui.PopStyleColor();
|
||||||
|
ImGui.SameLine();
|
||||||
|
|
||||||
|
float frame = ImGui.GetFrameHeight();
|
||||||
|
float actionsX0 = x0 + colIdentity + colMeta + style.ItemSpacing.X * 2.0f;
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.SetCursorPosX(actionsX0);
|
||||||
|
|
||||||
|
bool isEditing = string.Equals(_editingBanUid, bannedUser.UID, StringComparison.Ordinal);
|
||||||
|
int actionCount = 1 + (isEditing ? 2 : 1);
|
||||||
|
|
||||||
|
float totalW = actionCount * frame + (actionCount - 1) * style.ItemSpacing.X;
|
||||||
|
float startX = actionsX0 + MathF.Max(0, colActions - totalW) - 36f;
|
||||||
|
ImGui.SetCursorPosX(startX);
|
||||||
|
|
||||||
|
if (_uiSharedService.IconButton(FontAwesomeIcon.Check))
|
||||||
|
{
|
||||||
|
_apiController.GroupUnbanUser(bannedUser);
|
||||||
|
_bannedUsers.RemoveAll(b => string.Equals(b.UID, bannedUser.UID, StringComparison.Ordinal));
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip("Unban");
|
||||||
|
|
||||||
|
ImGui.SameLine(0f, style.ItemSpacing.X);
|
||||||
|
|
||||||
|
if (!isEditing)
|
||||||
|
{
|
||||||
|
if (_uiSharedService.IconButton(FontAwesomeIcon.Edit))
|
||||||
|
{
|
||||||
|
_banEditError = null;
|
||||||
|
_editingBanUid = bannedUser.UID;
|
||||||
|
_banReasonEdits[bannedUser.UID] = StripAliasSuffix(bannedUser.Reason ?? string.Empty);
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip("Edit reason");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_uiSharedService.IconButton(FontAwesomeIcon.Save))
|
||||||
|
{
|
||||||
|
_banEditError = null;
|
||||||
|
_banEditTask = SaveBanReasonViaBanUserAsync(bannedUser.UID);
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip("Save");
|
||||||
|
|
||||||
|
ImGui.SameLine(0f, style.ItemSpacing.X);
|
||||||
|
|
||||||
|
if (_uiSharedService.IconButton(FontAwesomeIcon.Times))
|
||||||
|
{
|
||||||
|
_banEditError = null;
|
||||||
|
_editingBanUid = null;
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip("Cancel");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawInvites(GroupPermissions perm)
|
private void DrawInvites(GroupPermissions perm)
|
||||||
{
|
{
|
||||||
var inviteTab = ImRaii.TabItem("Invites");
|
var inviteTab = ImRaii.TabItem("Invites");
|
||||||
@@ -902,7 +1235,9 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
if (buttonCount == 0)
|
if (buttonCount == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float totalWidth = buttonCount * frameH + (buttonCount - 1) * style.ItemSpacing.X;
|
float totalWidth = _isOwner
|
||||||
|
? buttonCount * frameH + buttonCount * style.ItemSpacing.X + 20f
|
||||||
|
: buttonCount * frameH + buttonCount * style.ItemSpacing.X;
|
||||||
|
|
||||||
float curX = ImGui.GetCursorPosX();
|
float curX = ImGui.GetCursorPosX();
|
||||||
float avail = ImGui.GetContentRegionAvail().X;
|
float avail = ImGui.GetContentRegionAvail().X;
|
||||||
@@ -1031,69 +1366,40 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
UiSharedService.ColoredSeparator(UIColors.Get("LightlessYellow"), 1.0f);
|
UiSharedService.ColoredSeparator(UIColors.Get("LightlessYellow"), 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawBannedRow(BannedGroupUserDto bannedUser, int rowIndex, float colIdentity, float colMeta, float colActions)
|
private void QueueBanListRefresh(bool force = false)
|
||||||
{
|
{
|
||||||
using var id = ImRaii.PushId("banRow_" + bannedUser.UID);
|
if (!force)
|
||||||
|
|
||||||
var style = ImGui.GetStyle();
|
|
||||||
float x0 = ImGui.GetCursorPosX();
|
|
||||||
|
|
||||||
if (rowIndex % 2 == 0)
|
|
||||||
{
|
{
|
||||||
var drawList = ImGui.GetWindowDrawList();
|
if (_bannedUsersTask != null && !_bannedUsersTask.IsCompleted)
|
||||||
var pMin = ImGui.GetCursorScreenPos();
|
return;
|
||||||
var rowHeight = ImGui.GetTextLineHeightWithSpacing() * 2.6f;
|
|
||||||
var pMax = new Vector2(
|
|
||||||
pMin.X + colIdentity + colMeta + colActions + style.ItemSpacing.X * 2.0f,
|
|
||||||
pMin.Y + rowHeight);
|
|
||||||
|
|
||||||
var bgColor = UIColors.Get("FullBlack").WithAlpha(0.10f);
|
|
||||||
drawList.AddRectFilled(pMin, pMax, ImGui.ColorConvertFloat4ToU32(bgColor));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SetCursorPosX(x0);
|
_bannedUsersLoaded = false;
|
||||||
ImGui.AlignTextToFramePadding();
|
_bannedUsersLoadError = null;
|
||||||
|
|
||||||
string alias = bannedUser.UserAlias ?? string.Empty;
|
_bannedUsersTask = _apiController.GroupGetBannedUsers(new GroupDto(GroupFullInfo.Group));
|
||||||
string line1 = string.IsNullOrEmpty(alias)
|
|
||||||
? bannedUser.UID
|
|
||||||
: $"{alias} ({bannedUser.UID})";
|
|
||||||
|
|
||||||
ImGui.TextUnformatted(line1);
|
|
||||||
|
|
||||||
var reason = bannedUser.Reason ?? string.Empty;
|
|
||||||
if (!string.IsNullOrWhiteSpace(reason))
|
|
||||||
{
|
|
||||||
var reasonPos = new Vector2(x0, ImGui.GetCursorPosY());
|
|
||||||
ImGui.SetCursorPos(reasonPos);
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey);
|
|
||||||
UiSharedService.TextWrapped(reason);
|
|
||||||
ImGui.PopStyleColor();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
private void EnsureBanListLoaded()
|
||||||
ImGui.SetCursorPosX(x0 + colIdentity + style.ItemSpacing.X);
|
|
||||||
|
|
||||||
ImGui.AlignTextToFramePadding();
|
|
||||||
ImGui.TextUnformatted($"By: {bannedUser.BannedBy}");
|
|
||||||
|
|
||||||
var dateText = bannedUser.BannedOn.ToLocalTime().ToString(CultureInfo.CurrentCulture);
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey);
|
|
||||||
ImGui.TextUnformatted(dateText);
|
|
||||||
ImGui.PopStyleColor();
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.SetCursorPosX(x0 + colIdentity + colMeta + style.ItemSpacing.X * 2.0f);
|
|
||||||
|
|
||||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Check, "Unban"))
|
|
||||||
{
|
{
|
||||||
_apiController.GroupUnbanUser(bannedUser);
|
_bannedUsersTask ??= _apiController.GroupGetBannedUsers(new GroupDto(GroupFullInfo.Group));
|
||||||
_bannedUsers.RemoveAll(b => string.Equals(b.UID, bannedUser.UID, StringComparison.Ordinal));
|
|
||||||
|
if (_bannedUsersLoaded || _bannedUsersTask == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_bannedUsersTask.IsCompleted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_bannedUsersTask.IsFaulted || _bannedUsersTask.IsCanceled)
|
||||||
|
{
|
||||||
|
_bannedUsersLoadError = "Failed to load banlist from server.";
|
||||||
|
_bannedUsers = [];
|
||||||
|
_bannedUsersLoaded = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UiSharedService.AttachToolTip($"Unban {alias} ({bannedUser.UID}) from this Syncshell");
|
_bannedUsers = _bannedUsersTask.GetAwaiter().GetResult() ?? [];
|
||||||
|
_bannedUsersLoaded = true;
|
||||||
ImGui.Dummy(new Vector2(0, 4 * ImGuiHelpers.GlobalScale));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SavePruneSettings()
|
private void SavePruneSettings()
|
||||||
@@ -1116,6 +1422,13 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string StripAliasSuffix(string reason)
|
||||||
|
{
|
||||||
|
const string marker = " (Alias at time of ban:";
|
||||||
|
var idx = reason.IndexOf(marker, StringComparison.Ordinal);
|
||||||
|
return idx >= 0 ? reason[..idx] : reason;
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -1127,6 +1440,11 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
|| alias.Contains(filterLower, StringComparison.OrdinalIgnoreCase);
|
|| alias.Contains(filterLower, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void OnOpen()
|
||||||
|
{
|
||||||
|
base.OnOpen();
|
||||||
|
QueueBanListRefresh(force: true);
|
||||||
|
}
|
||||||
public override void OnClose()
|
public override void OnClose()
|
||||||
{
|
{
|
||||||
Mediator.Publish(new RemoveWindowMessage(this));
|
Mediator.Publish(new RemoveWindowMessage(this));
|
||||||
|
|||||||
Reference in New Issue
Block a user