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()