Merge branch '1.11.6' of https://git.lightless-sync.org/choco/LightlessClient into choco-ui-color-picker

This commit is contained in:
choco
2025-09-07 20:46:29 +02:00
5 changed files with 378 additions and 301 deletions

View File

@@ -138,7 +138,7 @@ public sealed class Plugin : IDalamudPlugin
s.GetRequiredService<BlockedCharacterHandler>(), s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<PerformanceCollectorService>(),
s.GetRequiredService<LightlessConfigService>()));
collection.AddSingleton((s) => new DtrEntry(s.GetRequiredService<ILogger<DtrEntry>>(), dtrBar, s.GetRequiredService<LightlessConfigService>(),
s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<PairManager>(), s.GetRequiredService<ApiController>()));
s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<PairManager>(), s.GetRequiredService<ApiController>(), s.GetRequiredService<ServerConfigurationManager>()));
collection.AddSingleton(s => new PairManager(s.GetRequiredService<ILogger<PairManager>>(), s.GetRequiredService<PairFactory>(),
s.GetRequiredService<LightlessConfigService>(), s.GetRequiredService<LightlessMediator>(), contextMenu));
collection.AddSingleton<RedrawManager>();

View File

@@ -3,7 +3,6 @@ using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services.Mediator;
using LightlessSync.Services.ServerConfiguration;
using LightlessSync.UI;
using LightlessSync.UI.Components.Popup;
using LightlessSync.WebAPI;
using Microsoft.Extensions.Logging;

View File

@@ -4,7 +4,6 @@ using Dalamud.Interface.Windowing;
using LightlessSync.LightlessConfiguration;
using LightlessSync.Services.Mediator;
using LightlessSync.UI;
using LightlessSync.UI.Components.Popup;
using Microsoft.Extensions.Logging;
namespace LightlessSync.Services;

View File

@@ -6,7 +6,9 @@ using LightlessSync.LightlessConfiguration;
using LightlessSync.LightlessConfiguration.Configurations;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services.Mediator;
using LightlessSync.Services.ServerConfiguration;
using LightlessSync.WebAPI;
using LightlessSync.WebAPI.SignalR.Utils;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Runtime.InteropServices;
@@ -16,6 +18,7 @@ namespace LightlessSync.UI;
public sealed class DtrEntry : IDisposable, IHostedService
{
private readonly ApiController _apiController;
private readonly ServerConfigurationManager _serverManager;
private readonly CancellationTokenSource _cancellationTokenSource = new();
private readonly ConfigurationServiceBase<LightlessConfig> _configService;
private readonly IDtrBar _dtrBar;
@@ -28,7 +31,7 @@ public sealed class DtrEntry : IDisposable, IHostedService
private string? _tooltip;
private Colors _colors;
public DtrEntry(ILogger<DtrEntry> logger, IDtrBar dtrBar, ConfigurationServiceBase<LightlessConfig> configService, LightlessMediator lightlessMediator, PairManager pairManager, ApiController apiController)
public DtrEntry(ILogger<DtrEntry> logger, IDtrBar dtrBar, ConfigurationServiceBase<LightlessConfig> configService, LightlessMediator lightlessMediator, PairManager pairManager, ApiController apiController, ServerConfigurationManager serverManager)
{
_logger = logger;
_dtrBar = dtrBar;
@@ -37,6 +40,7 @@ public sealed class DtrEntry : IDisposable, IHostedService
_lightlessMediator = lightlessMediator;
_pairManager = pairManager;
_apiController = apiController;
_serverManager = serverManager;
}
public void Dispose()
@@ -59,7 +63,7 @@ public sealed class DtrEntry : IDisposable, IHostedService
public async Task StopAsync(CancellationToken cancellationToken)
{
_cancellationTokenSource.Cancel();
await _cancellationTokenSource.CancelAsync().ConfigureAwait(false);
try
{
await _runTask!.ConfigureAwait(false);
@@ -89,10 +93,43 @@ public sealed class DtrEntry : IDisposable, IHostedService
{
_logger.LogTrace("Creating new DtrBar entry");
var entry = _dtrBar.Get("Lightless Sync");
entry.OnClick = _ => _lightlessMediator.Publish(new UiToggleMessage(typeof(CompactUi)));
entry.OnClick = interactionEvent => OnClickEvent(interactionEvent);
return entry;
}
private void OnClickEvent(DtrInteractionEvent interactionEvent)
{
if (interactionEvent.ClickType.Equals(MouseClickType.Left) && !interactionEvent.ModifierKeys.Equals(ClickModifierKeys.Shift))
{
_lightlessMediator.Publish(new UiToggleMessage(typeof(CompactUi)));
}
else if (interactionEvent.ClickType.Equals(MouseClickType.Left) && interactionEvent.ModifierKeys.Equals(ClickModifierKeys.Shift))
{
_lightlessMediator.Publish(new UiToggleMessage(typeof(SettingsUi)));
}
if (interactionEvent.ClickType.Equals(MouseClickType.Right))
{
bool isConnectingOrConnected = _apiController.ServerState is ServerState.Connected or ServerState.Connecting or ServerState.Reconnecting;
if (_apiController.ServerState is not (ServerState.Reconnecting or ServerState.Disconnecting))
{
if (isConnectingOrConnected && !_serverManager.CurrentServer.FullPause)
{
_serverManager.CurrentServer.FullPause = true;
_serverManager.Save();
}
else if (!isConnectingOrConnected && _serverManager.CurrentServer.FullPause)
{
_serverManager.CurrentServer.FullPause = false;
_serverManager.Save();
}
_ = _apiController.CreateConnectionsAsync();
}
}
}
private async Task RunAsync()
{

View File

@@ -13,7 +13,7 @@ using LightlessSync.WebAPI;
using Microsoft.Extensions.Logging;
using System.Globalization;
namespace LightlessSync.UI.Components.Popup;
namespace LightlessSync.UI;
public class SyncshellAdminUI : WindowMediatorSubscriberBase
{
@@ -63,7 +63,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
using var id = ImRaii.PushId("syncshell_admin_" + GroupFullInfo.GID);
using (_uiSharedService.UidFont.Push())
ImGui.TextUnformatted(GroupFullInfo.GroupAliasOrGID + " Administrative Panel");
_uiSharedService.UnderlinedBigText(GroupFullInfo.GroupAliasOrGID + " Administrative Panel", UIColors.Get("LightlessBlue"));
ImGui.Separator();
var perm = GroupFullInfo.GroupPermissions;
@@ -72,133 +72,203 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
if (tabbar)
{
var inviteTab = ImRaii.TabItem("Invites");
if (inviteTab)
{
bool isInvitesDisabled = perm.IsDisableInvites();
DrawInvites(perm);
if (_uiSharedService.IconTextButton(isInvitesDisabled ? FontAwesomeIcon.Unlock : FontAwesomeIcon.Lock,
isInvitesDisabled ? "Unlock Syncshell" : "Lock Syncshell"))
DrawManagement();
DrawPermission(perm);
}
}
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.SetDisableInvites(!isInvitesDisabled);
perm.SetPreferDisableSounds(!perm.IsPreferDisableSounds());
_ = _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.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"))
{
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);
}
perm.SetPreferDisableAnimations(!perm.IsPreferDisableAnimations());
_ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm));
}
}
inviteTab.Dispose();
var mgmtTab = ImRaii.TabItem("User Management");
if (mgmtTab)
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")))
{
var userNode = ImRaii.TreeNode("User List & Administration");
if (userNode)
if (_uiSharedService.IconTextButton(isDisableVfx ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle,
isDisableVfx ? "Suggest to enable vfx sync" : "Suggest to disable vfx sync"))
{
if (!_pairManager.GroupPairs.TryGetValue(GroupFullInfo, out var pairs))
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"))
{
UiSharedService.ColorTextWrapped("No users found in this Syncshell", UIColors.Get("LightlessYellow"));
_pwChangeSuccess = _apiController.GroupChangePassword(new GroupPasswordDto(GroupFullInfo.Group, _newPassword)).Result;
_newPassword = string.Empty;
}
else
}
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 DrawManagement()
{
var mgmtTab = ImRaii.TabItem("User Management");
if (mgmtTab)
{
if (_uiSharedService.MediumTreeNode("User List & Administration", UIColors.Get("LightlessPurple")))
{
if (!_pairManager.GroupPairs.TryGetValue(GroupFullInfo, out var pairs))
{
UiSharedService.ColorTextWrapped("No users found in this Syncshell", ImGuiColors.DalamudYellow);
}
else
{
var tableFlags = ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp;
if (pairs.Count > 10) tableFlags |= ImGuiTableFlags.ScrollY;
using var table = ImRaii.Table("userList#" + GroupFullInfo.Group.GID, 3, tableFlags);
if (table)
{
using var table = ImRaii.Table("userList#" + GroupFullInfo.Group.GID, 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.ScrollY);
if (table)
ImGui.TableSetupColumn("Alias/UID/Note", ImGuiTableColumnFlags.None, 5);
ImGui.TableSetupColumn("Flags", ImGuiTableColumnFlags.None, 1);
ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.None, 2);
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 =>
{
ImGui.TableSetupColumn("Alias/UID/Note", ImGuiTableColumnFlags.None, 3);
ImGui.TableSetupColumn("Online/Name", ImGuiTableColumnFlags.None, 2);
ImGui.TableSetupColumn("Flags", ImGuiTableColumnFlags.None, 1);
ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.None, 2);
ImGui.TableHeadersRow();
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);
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 =>
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))
{
if (p.Value == null) return 10;
if (p.Value.Value.IsModerator()) return 0;
if (p.Value.Value.IsPinned()) return 1;
return 10;
}).ThenBy(p => p.Key.GetNote() ?? p.Key.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase))
UiSharedService.AttachToolTip(pair.Key.PlayerName);
ImGui.SameLine();
}
ImGui.TableNextColumn(); // special flags
if (pair.Value != null && (pair.Value.Value.IsModerator() || pair.Value.Value.IsPinned() || isUserOwner))
{
using var tableId = ImRaii.PushId("userTable_" + pair.Key.UserData.UID);
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();
ImGui.TextUnformatted(text);
ImGui.TableNextColumn(); // online/name
string onlineText = pair.Key.IsOnline ? "Online" : "Offline";
if (!string.IsNullOrEmpty(pair.Key.PlayerName))
if (pair.Value.Value.IsModerator())
{
onlineText += " (" + pair.Key.PlayerName + ")";
_uiSharedService.IconText(FontAwesomeIcon.UserShield, UIColors.Get("LightlessPurple"));
UiSharedService.AttachToolTip("Moderator");
}
var boolcolor = UiSharedService.GetBoolColor(pair.Key.IsOnline);
ImGui.AlignTextToFramePadding();
UiSharedService.ColorText(onlineText, boolcolor);
ImGui.TableNextColumn(); // special flags
if (pair.Value != null && (pair.Value.Value.IsModerator() || pair.Value.Value.IsPinned()))
if (pair.Value.Value.IsPinned() && !isUserOwner)
{
if (pair.Value.Value.IsModerator())
{
_uiSharedService.IconText(FontAwesomeIcon.UserShield);
UiSharedService.AttachToolTip("Moderator");
}
if (pair.Value.Value.IsPinned())
{
_uiSharedService.IconText(FontAwesomeIcon.Thumbtack);
UiSharedService.AttachToolTip("Pinned");
}
_uiSharedService.IconText(FontAwesomeIcon.Thumbtack);
UiSharedService.AttachToolTip("Pinned");
}
else
if (isUserOwner)
{
_uiSharedService.IconText(FontAwesomeIcon.None);
_uiSharedService.IconText(FontAwesomeIcon.Crown, UIColors.Get("LightlessYellow"));
UiSharedService.AttachToolTip("Owner");
}
}
else
{
_uiSharedService.IconText(FontAwesomeIcon.None);
}
ImGui.TableNextColumn(); // actions
if (_isOwner)
ImGui.TableNextColumn(); // actions
if (_isOwner)
{
if (_uiSharedService.IconButton(FontAwesomeIcon.UserShield))
{
if (_uiSharedService.IconButton(FontAwesomeIcon.UserShield))
{
GroupPairUserInfo userInfo = pair.Value ?? GroupPairUserInfo.None;
GroupPairUserInfo userInfo = pair.Value ?? GroupPairUserInfo.None;
userInfo.SetModerator(!userInfo.IsModerator());
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();
_ = _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 (_isOwner || (pair.Value == null || (pair.Value != null && !pair.Value.Value.IsModerator())))
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))
{
@@ -208,9 +278,12 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
_ = _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();
}
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))
@@ -218,10 +291,13 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
_ = _apiController.GroupRemoveUser(new GroupPairDto(GroupFullInfo.Group, pair.Key.UserData));
}
}
UiSharedService.AttachToolTip("Remove user from Syncshell"
+ UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
}
UiSharedService.AttachToolTip("Remove user from Syncshell"
+ UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
ImGui.SameLine();
ImGui.SameLine();
using (ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("DimRed")))
{
using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
{
if (_uiSharedService.IconButton(FontAwesomeIcon.Ban))
@@ -229,224 +305,190 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
Mediator.Publish(new OpenBanUserPopupMessage(pair.Key, GroupFullInfo));
}
}
UiSharedService.AttachToolTip("Ban user from Syncshell"
+ UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
}
}
}
}
}
userNode.Dispose();
var clearNode = ImRaii.TreeNode("Mass Cleanup");
if (clearNode)
{
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");
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} days."
+ 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("Days of inactivity", [7, 14, 30, 90], (count) =>
{
return count + " days";
},
(selected) =>
{
_pruneDays = selected;
_pruneTestTask = null;
_pruneTask = null;
},
_pruneDays);
if (_pruneTestTask != null)
{
if (!_pruneTestTask.IsCompleted)
{
UiSharedService.ColorTextWrapped("Calculating inactive users...", UIColors.Get("LightlessYellow"));
}
else
{
ImGui.AlignTextToFramePadding();
UiSharedService.TextWrapped($"Found {_pruneTestTask.Result} user(s) that have not logged into Lightless in the past {_pruneDays} days.");
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.AttachToolTip("Ban user from Syncshell"
+ UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
}
}
}
if (_pruneTask != null)
}
_uiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f);
ImGui.TreePop();
}
ImGui.Separator();
if (_uiSharedService.MediumTreeNode("Mass Cleanup", UIColors.Get("LightlessPurple")))
{
using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
{
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Broom, "Clear Syncshell"))
{
if (!_pruneTask.IsCompleted)
{
UiSharedService.ColorTextWrapped("Pruning Syncshell...", UIColors.Get("LightlessYellow"));
}
else
{
UiSharedService.TextWrapped($"Syncshell was pruned and {_pruneTask.Result} inactive user(s) have been removed.");
}
_ = _apiController.GroupClear(new(GroupFullInfo.Group));
}
}
clearNode.Dispose();
UiSharedService.AttachToolTip("This will remove all non-pinned, non-moderator users from the Syncshell."
+ UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
var banNode = ImRaii.TreeNode("User Bans");
if (banNode)
ImGuiHelpers.ScaledDummy(2f);
ImGui.Separator();
ImGuiHelpers.ScaledDummy(2f);
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Unlink, "Check for Inactive Users"))
{
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Retweet, "Refresh Banlist from Server"))
_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)
{
_bannedUsers = _apiController.GroupGetBannedUsers(new GroupDto(GroupFullInfo.Group)).Result;
UiSharedService.ColorTextWrapped("Calculating inactive users...", ImGuiColors.DalamudYellow);
}
if (ImGui.BeginTable("bannedusertable" + GroupFullInfo.GID, 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.ScrollY))
else
{
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.AlignTextToFramePadding();
UiSharedService.TextWrapped($"Found {_pruneTestTask.Result} user(s) that have not logged into Lightless in the past {_pruneDays} day(s).");
if (_pruneTestTask.Result > 0)
{
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"))
using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
{
_apiController.GroupUnbanUser(bannedUser);
_bannedUsers.RemoveAll(b => string.Equals(b.UID, bannedUser.UID, StringComparison.Ordinal));
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");
}
ImGui.EndTable();
}
}
banNode.Dispose();
}
mgmtTab.Dispose();
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);
if (_uiSharedService.IconTextButton(isDisableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeMute,
isDisableSounds ? "Suggest to enable sound sync" : "Suggest to disable sound sync"))
if (_pruneTask != null)
{
perm.SetPreferDisableSounds(!perm.IsPreferDisableSounds());
_ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm));
}
ImGui.AlignTextToFramePadding();
ImGui.Text("Suggest Animation Sync");
_uiSharedService.BooleanToColoredIcon(!isDisableAnimations);
ImGui.SameLine(230);
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);
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 (!_pruneTask.IsCompleted)
{
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Passport, "Change Password"))
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("LightlessPurple"), 1.5f);
ImGui.TreePop();
}
ImGui.Separator();
if (_uiSharedService.MediumTreeNode("User Bans", UIColors.Get("LightlessPurple")))
{
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"))
{
_pwChangeSuccess = _apiController.GroupChangePassword(new GroupPasswordDto(GroupFullInfo.Group, _newPassword)).Result;
_newPassword = string.Empty;
_apiController.GroupUnbanUser(bannedUser);
_bannedUsers.RemoveAll(b => string.Equals(b.UID, bannedUser.UID, StringComparison.Ordinal));
}
}
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.", UIColors.Get("LightlessYellow"));
}
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.");
ImGui.EndTable();
}
_uiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 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);
}
ownerTab.Dispose();
}
}
inviteTab.Dispose();
}
public override void OnClose()