From d9cf5aecf400873e5593a48fdb91f55d4ac9d89b Mon Sep 17 00:00:00 2001 From: CakeAndBanana Date: Wed, 3 Sep 2025 23:04:33 +0200 Subject: [PATCH 1/5] Added owner icon and removed ban/kick/pin buttons for owner if he/she is in list. --- LightlessSync/Services/UiFactory.cs | 1 - LightlessSync/Services/UiService.cs | 1 - LightlessSync/UI/SyncshellAdminUI.cs | 25 +++++++++++++++++-------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/LightlessSync/Services/UiFactory.cs b/LightlessSync/Services/UiFactory.cs index aaa7f96..5aaab5e 100644 --- a/LightlessSync/Services/UiFactory.cs +++ b/LightlessSync/Services/UiFactory.cs @@ -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; diff --git a/LightlessSync/Services/UiService.cs b/LightlessSync/Services/UiService.cs index 0aceb78..4071b61 100644 --- a/LightlessSync/Services/UiService.cs +++ b/LightlessSync/Services/UiService.cs @@ -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; diff --git a/LightlessSync/UI/SyncshellAdminUI.cs b/LightlessSync/UI/SyncshellAdminUI.cs index b3c70d2..dc59a44 100644 --- a/LightlessSync/UI/SyncshellAdminUI.cs +++ b/LightlessSync/UI/SyncshellAdminUI.cs @@ -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 { @@ -126,7 +126,9 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase } else { - using var table = ImRaii.Table("userList#" + GroupFullInfo.Group.GID, 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.ScrollY); + var tableFlags = ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp; + if (pairs.Count > 10) tableFlags |= ImGuiTableFlags.ScrollY; + using var table = ImRaii.Table("userList#" + GroupFullInfo.Group.GID, 4, tableFlags); if (table) { ImGui.TableSetupColumn("Alias/UID/Note", ImGuiTableColumnFlags.None, 3); @@ -141,12 +143,14 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase foreach (var pair in groupedPairs.OrderBy(p => { if (p.Value == null) return 10; - if (p.Value.Value.IsModerator()) return 0; - if (p.Value.Value.IsPinned()) return 1; + 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(); @@ -165,18 +169,23 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase UiSharedService.ColorText(onlineText, boolcolor); ImGui.TableNextColumn(); // special flags - if (pair.Value != null && (pair.Value.Value.IsModerator() || pair.Value.Value.IsPinned())) + if (pair.Value != null && (pair.Value.Value.IsModerator() || pair.Value.Value.IsPinned() || isUserOwner)) { if (pair.Value.Value.IsModerator()) { - _uiSharedService.IconText(FontAwesomeIcon.UserShield); + _uiSharedService.IconText(FontAwesomeIcon.UserShield, UIColors.Get("LightlessPurple")); UiSharedService.AttachToolTip("Moderator"); } - if (pair.Value.Value.IsPinned()) + 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 { @@ -198,7 +207,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase 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) { if (_uiSharedService.IconButton(FontAwesomeIcon.Thumbtack)) { From 4b8445a1207a76ccaa9796a26f4ad290ca3fe00e Mon Sep 17 00:00:00 2001 From: CakeAndBanana Date: Thu, 4 Sep 2025 03:53:24 +0200 Subject: [PATCH 2/5] Redid syncshell admin panel with same style as settings --- LightlessSync/UI/SyncshellAdminUI.cs | 639 ++++++++++++++------------- 1 file changed, 336 insertions(+), 303 deletions(-) diff --git a/LightlessSync/UI/SyncshellAdminUI.cs b/LightlessSync/UI/SyncshellAdminUI.cs index dc59a44..90fbbaf 100644 --- a/LightlessSync/UI/SyncshellAdminUI.cs +++ b/LightlessSync/UI/SyncshellAdminUI.cs @@ -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,142 +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", ImGuiColors.DalamudYellow); + _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) { - var tableFlags = ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp; - if (pairs.Count > 10) tableFlags |= ImGuiTableFlags.ScrollY; - using var table = ImRaii.Table("userList#" + GroupFullInfo.Group.GID, 4, tableFlags); - 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(pairs.Select(p => new KeyValuePair(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(pairs.Select(p => new KeyValuePair(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 (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)) + 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); - 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(); - 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() || isUserOwner)) + if (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"); - } + _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 (pair.Value == null || pair.Value != null && !pair.Value.Value.IsModerator() && !isUserOwner) + 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)) { @@ -217,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)) @@ -227,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)) @@ -238,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...", ImGuiColors.DalamudYellow); - } - 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...", ImGuiColors.DalamudYellow); - } - 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} 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) { - _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} days."); + 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.", 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."); + 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() From 8facbafff9e3e566301d64167adc78263b1c877d Mon Sep 17 00:00:00 2001 From: CakeAndBanana Date: Sun, 7 Sep 2025 16:00:46 +0200 Subject: [PATCH 3/5] Added different left and right click function. Added disconnect on right click, settings if you shift+leftclick. --- LightlessSync/Plugin.cs | 2 +- LightlessSync/UI/DtrEntry.cs | 43 +++++++++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/LightlessSync/Plugin.cs b/LightlessSync/Plugin.cs index 7e00038..0549a6a 100644 --- a/LightlessSync/Plugin.cs +++ b/LightlessSync/Plugin.cs @@ -138,7 +138,7 @@ public sealed class Plugin : IDalamudPlugin s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService())); collection.AddSingleton((s) => new DtrEntry(s.GetRequiredService>(), dtrBar, s.GetRequiredService(), - s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService())); + s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService())); collection.AddSingleton(s => new PairManager(s.GetRequiredService>(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), contextMenu)); collection.AddSingleton(); diff --git a/LightlessSync/UI/DtrEntry.cs b/LightlessSync/UI/DtrEntry.cs index f83c4c6..0335136 100644 --- a/LightlessSync/UI/DtrEntry.cs +++ b/LightlessSync/UI/DtrEntry.cs @@ -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 _configService; private readonly IDtrBar _dtrBar; @@ -28,7 +31,7 @@ public sealed class DtrEntry : IDisposable, IHostedService private string? _tooltip; private Colors _colors; - public DtrEntry(ILogger logger, IDtrBar dtrBar, ConfigurationServiceBase configService, LightlessMediator lightlessMediator, PairManager pairManager, ApiController apiController) + public DtrEntry(ILogger logger, IDtrBar dtrBar, ConfigurationServiceBase 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() { From 7bb4e89a0d45617165b02dfd33d11cd9b336b799 Mon Sep 17 00:00:00 2001 From: CakeAndBanana Date: Sun, 7 Sep 2025 16:39:34 +0200 Subject: [PATCH 4/5] Added 1 and 3 option in the inactive setting. --- LightlessSync/UI/SyncshellAdminUI.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LightlessSync/UI/SyncshellAdminUI.cs b/LightlessSync/UI/SyncshellAdminUI.cs index 90fbbaf..0eb0310 100644 --- a/LightlessSync/UI/SyncshellAdminUI.cs +++ b/LightlessSync/UI/SyncshellAdminUI.cs @@ -343,7 +343,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase + 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) => + _uiSharedService.DrawCombo("Day(s) of inactivity", [1, 3, 7, 14, 30, 90], (count) => { return count + " days"; }, @@ -364,7 +364,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase else { ImGui.AlignTextToFramePadding(); - UiSharedService.TextWrapped($"Found {_pruneTestTask.Result} user(s) that have not logged into Lightless in the past {_pruneDays} days."); + 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())) From 2f6dbc273c576b172cdbf9633c9232da505eb6d0 Mon Sep 17 00:00:00 2001 From: CakeAndBanana Date: Sun, 7 Sep 2025 16:42:33 +0200 Subject: [PATCH 5/5] Changed some strings --- LightlessSync/UI/SyncshellAdminUI.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LightlessSync/UI/SyncshellAdminUI.cs b/LightlessSync/UI/SyncshellAdminUI.cs index 0eb0310..4cf254b 100644 --- a/LightlessSync/UI/SyncshellAdminUI.cs +++ b/LightlessSync/UI/SyncshellAdminUI.cs @@ -338,14 +338,14 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase _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." + 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 + " days"; + return count + " day(s)"; }, (selected) => {