805 lines
36 KiB
C#
805 lines
36 KiB
C#
using Dalamud.Bindings.ImGui;
|
|
using Dalamud.Interface;
|
|
using Dalamud.Interface.Utility;
|
|
using Dalamud.Interface.Utility.Raii;
|
|
using LightlessSync.API.Data.Enum;
|
|
using LightlessSync.API.Data.Extensions;
|
|
using LightlessSync.API.Dto.Group;
|
|
using LightlessSync.API.Dto.User;
|
|
using LightlessSync.LightlessConfiguration;
|
|
using LightlessSync.PlayerData.Pairs;
|
|
using LightlessSync.Services;
|
|
using LightlessSync.Services.Mediator;
|
|
using LightlessSync.Services.ServerConfiguration;
|
|
using LightlessSync.UI.Handlers;
|
|
using LightlessSync.UI.Models;
|
|
using LightlessSync.UI.Style;
|
|
using LightlessSync.Utils;
|
|
using LightlessSync.WebAPI;
|
|
using System.Collections.Immutable;
|
|
using System.Text;
|
|
|
|
namespace LightlessSync.UI.Components;
|
|
|
|
public class DrawUserPair
|
|
{
|
|
protected readonly ApiController _apiController;
|
|
protected readonly IdDisplayHandler _displayHandler;
|
|
protected readonly LightlessMediator _mediator;
|
|
protected readonly List<GroupFullInfoDto> _syncedGroups;
|
|
private readonly GroupFullInfoDto? _currentGroup;
|
|
protected Pair? _pair;
|
|
private PairUiEntry _uiEntry;
|
|
protected PairDisplayEntry _displayEntry;
|
|
private readonly string _id;
|
|
private readonly SelectTagForPairUi _selectTagForPairUi;
|
|
private readonly ServerConfigurationManager _serverConfigurationManager;
|
|
private readonly UiSharedService _uiSharedService;
|
|
private readonly PlayerPerformanceConfigService _performanceConfigService;
|
|
private readonly LightlessConfigService _configService;
|
|
private readonly CharaDataManager _charaDataManager;
|
|
private readonly PairLedger _pairLedger;
|
|
private float _menuWidth = -1;
|
|
private bool _wasHovered = false;
|
|
private TooltipSnapshot _tooltipSnapshot = TooltipSnapshot.Empty;
|
|
private string _cachedTooltip = string.Empty;
|
|
|
|
public DrawUserPair(
|
|
string id,
|
|
PairUiEntry uiEntry,
|
|
Pair? legacyPair,
|
|
GroupFullInfoDto? currentGroup,
|
|
ApiController apiController,
|
|
IdDisplayHandler uIDDisplayHandler,
|
|
LightlessMediator lightlessMediator,
|
|
SelectTagForPairUi selectTagForPairUi,
|
|
ServerConfigurationManager serverConfigurationManager,
|
|
UiSharedService uiSharedService,
|
|
PlayerPerformanceConfigService performanceConfigService,
|
|
LightlessConfigService configService,
|
|
CharaDataManager charaDataManager,
|
|
PairLedger pairLedger)
|
|
{
|
|
_id = id;
|
|
_uiEntry = uiEntry;
|
|
_displayEntry = uiEntry.DisplayEntry;
|
|
_pair = legacyPair ?? throw new ArgumentNullException(nameof(legacyPair));
|
|
_syncedGroups = uiEntry.DisplayEntry.Groups.ToList();
|
|
_currentGroup = currentGroup;
|
|
_apiController = apiController;
|
|
_displayHandler = uIDDisplayHandler;
|
|
_mediator = lightlessMediator;
|
|
_selectTagForPairUi = selectTagForPairUi;
|
|
_serverConfigurationManager = serverConfigurationManager;
|
|
_uiSharedService = uiSharedService;
|
|
_performanceConfigService = performanceConfigService;
|
|
_configService = configService;
|
|
_charaDataManager = charaDataManager;
|
|
_pairLedger = pairLedger;
|
|
}
|
|
|
|
public PairDisplayEntry DisplayEntry => _displayEntry;
|
|
public PairUiEntry UiEntry => _uiEntry;
|
|
|
|
public void UpdateDisplayEntry(PairUiEntry entry)
|
|
{
|
|
_uiEntry = entry;
|
|
_displayEntry = entry.DisplayEntry;
|
|
_syncedGroups.Clear();
|
|
_syncedGroups.AddRange(entry.DisplayEntry.Groups);
|
|
}
|
|
|
|
public Pair Pair => _pair;
|
|
public UserFullPairDto UserPair => _pair.UserPair!;
|
|
|
|
public void DrawPairedClient()
|
|
{
|
|
using var id = ImRaii.PushId(GetType() + _id);
|
|
var color = ImRaii.PushColor(ImGuiCol.ChildBg, ImGui.GetColorU32(ImGuiCol.FrameBgHovered), _wasHovered);
|
|
using (ImRaii.Child(GetType() + _id, new System.Numerics.Vector2(UiSharedService.GetWindowContentRegionWidth() - ImGui.GetCursorPosX(), ImGui.GetFrameHeight())))
|
|
{
|
|
DrawLeftSide();
|
|
ImGui.SameLine();
|
|
var posX = ImGui.GetCursorPosX();
|
|
var rightSide = DrawRightSide();
|
|
DrawName(posX, rightSide);
|
|
}
|
|
_wasHovered = ImGui.IsItemHovered();
|
|
if (_wasHovered)
|
|
{
|
|
Selune.RegisterHighlight(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), spanFullWidth: true);
|
|
}
|
|
color.Dispose();
|
|
}
|
|
|
|
private void DrawCommonClientMenu()
|
|
{
|
|
if (!_pair.IsPaused)
|
|
{
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.User, "Open Profile", _menuWidth, true))
|
|
{
|
|
_displayHandler.OpenProfile(_pair);
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
UiSharedService.AttachToolTip("Opens the profile for this user in a new window");
|
|
}
|
|
if (_pair.IsVisible)
|
|
{
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Sync, "Reload last data", _menuWidth, true))
|
|
{
|
|
_pair.ApplyLastReceivedData(forced: true);
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
UiSharedService.AttachToolTip("This reapplies the last received character data to this character");
|
|
}
|
|
|
|
var isPaused = _pair.UserPair!.OwnPermissions.IsPaused();
|
|
if (!isPaused)
|
|
{
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Pause, "Toggle Pause State", _menuWidth, true))
|
|
{
|
|
_ = _apiController.PauseAsync(_pair.UserData);
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
UiSharedService.AttachToolTip("Pauses syncing with this user.");
|
|
}
|
|
else
|
|
{
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Play, "Toggle Unpause State", _menuWidth, true))
|
|
{
|
|
_ = _apiController.UnpauseAsync(_pair.UserData);
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
UiSharedService.AttachToolTip("Resumes syncing with this user.");
|
|
}
|
|
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.PlayCircle, "Cycle pause state", _menuWidth, true))
|
|
{
|
|
_ = _apiController.CyclePauseAsync(_pair);
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
ImGui.Separator();
|
|
|
|
ImGui.TextUnformatted("Pair Permission Functions");
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.WindowMaximize, "Open Permissions Window", _menuWidth, true))
|
|
{
|
|
_mediator.Publish(new OpenPermissionWindow(_pair));
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
UiSharedService.AttachToolTip("Opens the Permissions Window which allows you to manage multiple permissions at once.");
|
|
|
|
var isSticky = _pair.UserPair!.OwnPermissions.IsSticky();
|
|
string stickyText = isSticky ? "Disable Preferred Permissions" : "Enable Preferred Permissions";
|
|
var stickyIcon = isSticky ? FontAwesomeIcon.ArrowCircleDown : FontAwesomeIcon.ArrowCircleUp;
|
|
if (_uiSharedService.IconTextButton(stickyIcon, stickyText, _menuWidth, true))
|
|
{
|
|
var permissions = _pair.UserPair.OwnPermissions;
|
|
permissions.SetSticky(!isSticky);
|
|
_ = _apiController.UserSetPairPermissions(new(_pair.UserData, permissions));
|
|
}
|
|
UiSharedService.AttachToolTip("Preferred permissions means that this pair will not" + Environment.NewLine + " be affected by any syncshell permission changes through you.");
|
|
|
|
string individualText = Environment.NewLine + Environment.NewLine + "Note: changing this permission will turn the permissions for this"
|
|
+ Environment.NewLine + "user to preferred permissions. You can change this behavior"
|
|
+ Environment.NewLine + "in the permission settings.";
|
|
bool individual = !_pair.IsDirectlyPaired && _apiController.DefaultPermissions!.IndividualIsSticky;
|
|
|
|
var isDisableSounds = _pair.UserPair!.OwnPermissions.IsDisableSounds();
|
|
string disableSoundsText = isDisableSounds ? "Enable sound sync" : "Disable sound sync";
|
|
var disableSoundsIcon = isDisableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeMute;
|
|
if (_uiSharedService.IconTextButton(disableSoundsIcon, disableSoundsText, _menuWidth, true))
|
|
{
|
|
var permissions = _pair.UserPair.OwnPermissions;
|
|
permissions.SetDisableSounds(!isDisableSounds);
|
|
_ = _apiController.UserSetPairPermissions(new UserPermissionsDto(_pair.UserData, permissions));
|
|
}
|
|
UiSharedService.AttachToolTip("Changes sound sync permissions with this user." + (individual ? individualText : string.Empty));
|
|
|
|
var isDisableAnims = _pair.UserPair!.OwnPermissions.IsDisableAnimations();
|
|
string disableAnimsText = isDisableAnims ? "Enable animation sync" : "Disable animation sync";
|
|
var disableAnimsIcon = isDisableAnims ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop;
|
|
if (_uiSharedService.IconTextButton(disableAnimsIcon, disableAnimsText, _menuWidth, true))
|
|
{
|
|
var permissions = _pair.UserPair.OwnPermissions;
|
|
permissions.SetDisableAnimations(!isDisableAnims);
|
|
_ = _apiController.UserSetPairPermissions(new UserPermissionsDto(_pair.UserData, permissions));
|
|
}
|
|
UiSharedService.AttachToolTip("Changes animation sync permissions with this user." + (individual ? individualText : string.Empty));
|
|
|
|
var isDisableVFX = _pair.UserPair!.OwnPermissions.IsDisableVFX();
|
|
string disableVFXText = isDisableVFX ? "Enable VFX sync" : "Disable VFX sync";
|
|
var disableVFXIcon = isDisableVFX ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle;
|
|
if (_uiSharedService.IconTextButton(disableVFXIcon, disableVFXText, _menuWidth, true))
|
|
{
|
|
var permissions = _pair.UserPair.OwnPermissions;
|
|
permissions.SetDisableVFX(!isDisableVFX);
|
|
_ = _apiController.UserSetPairPermissions(new UserPermissionsDto(_pair.UserData, permissions));
|
|
}
|
|
UiSharedService.AttachToolTip("Changes VFX sync permissions with this user." + (individual ? individualText : string.Empty));
|
|
}
|
|
|
|
private void DrawIndividualMenu()
|
|
{
|
|
ImGui.TextUnformatted("Individual Pair Functions");
|
|
var entryUID = _pair.UserData.AliasOrUID;
|
|
|
|
if (_pair.IndividualPairStatus != API.Data.Enum.IndividualPairStatus.None)
|
|
{
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Folder, "Pair Groups", _menuWidth, true))
|
|
{
|
|
_selectTagForPairUi.Open(_pair);
|
|
}
|
|
UiSharedService.AttachToolTip("Choose pair groups for " + entryUID);
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Unpair Permanently", _menuWidth, true) && UiSharedService.CtrlPressed())
|
|
{
|
|
_ = _apiController.UserRemovePair(new(_pair.UserData));
|
|
}
|
|
UiSharedService.AttachToolTip("Hold CTRL and click to unpair permanently from " + entryUID);
|
|
}
|
|
else
|
|
{
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Pair individually", _menuWidth, true))
|
|
{
|
|
_ = _apiController.UserAddPair(new(_pair.UserData));
|
|
}
|
|
UiSharedService.AttachToolTip("Pair individually with " + entryUID);
|
|
}
|
|
}
|
|
|
|
private void DrawLeftSide()
|
|
{
|
|
ImGui.AlignTextToFramePadding();
|
|
|
|
if (_pair == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_pair.IsPaused)
|
|
{
|
|
using var _ = ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("LightlessYellow"));
|
|
_uiSharedService.IconText(FontAwesomeIcon.PauseCircle);
|
|
}
|
|
else if (!_pair.IsOnline)
|
|
{
|
|
using var _ = ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("DimRed"));
|
|
_uiSharedService.IconText(_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.OneSided
|
|
? FontAwesomeIcon.ArrowsLeftRight
|
|
: (_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional
|
|
? FontAwesomeIcon.User : FontAwesomeIcon.Users));
|
|
}
|
|
else if (_pair.IsVisible)
|
|
{
|
|
if (_configService.Current.ShowVisiblePairsGreenEye)
|
|
{
|
|
_uiSharedService.IconText(FontAwesomeIcon.Eye, UIColors.Get("LightlessGreen"));
|
|
}
|
|
else
|
|
{
|
|
_uiSharedService.IconText(FontAwesomeIcon.Eye, UIColors.Get("LightlessBlue"));
|
|
}
|
|
|
|
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenBlockedByActiveItem | ImGuiHoveredFlags.AllowWhenOverlapped | ImGuiHoveredFlags.AllowWhenDisabled))
|
|
{
|
|
_mediator.Publish(new PairFocusCharacterMessage(_pair));
|
|
}
|
|
if (ImGui.IsItemClicked())
|
|
{
|
|
_mediator.Publish(new TargetPairMessage(_pair));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
using var _ = ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("PairBlue"));
|
|
_uiSharedService.IconText(_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional
|
|
? FontAwesomeIcon.User : FontAwesomeIcon.Users);
|
|
}
|
|
|
|
UiSharedService.AttachToolTip(GetUserTooltip());
|
|
|
|
if (_performanceConfigService.Current.ShowPerformanceIndicator
|
|
&& !_performanceConfigService.Current.UIDsToIgnore
|
|
.Exists(uid => string.Equals(uid, UserPair.User.Alias, StringComparison.Ordinal) || string.Equals(uid, UserPair.User.UID, StringComparison.Ordinal))
|
|
&& ((_performanceConfigService.Current.VRAMSizeWarningThresholdMiB > 0 && _performanceConfigService.Current.VRAMSizeWarningThresholdMiB * 1024 * 1024 < _pair.LastAppliedApproximateVRAMBytes)
|
|
|| (_performanceConfigService.Current.TrisWarningThresholdThousands > 0 && _performanceConfigService.Current.TrisWarningThresholdThousands * 1000 < _pair.LastAppliedDataTris))
|
|
&& (!_pair.UserPair.OwnPermissions.IsSticky()
|
|
|| _performanceConfigService.Current.WarnOnPreferredPermissionsExceedingThresholds))
|
|
{
|
|
ImGui.SameLine();
|
|
|
|
_uiSharedService.IconText(FontAwesomeIcon.ExclamationTriangle, UIColors.Get("LightlessYellow"));
|
|
|
|
string userWarningText = "WARNING: This user exceeds one or more of your defined thresholds:" + UiSharedService.TooltipSeparator;
|
|
bool shownVram = false;
|
|
if (_performanceConfigService.Current.VRAMSizeWarningThresholdMiB > 0
|
|
&& _performanceConfigService.Current.VRAMSizeWarningThresholdMiB * 1024 * 1024 < _pair.LastAppliedApproximateVRAMBytes)
|
|
{
|
|
shownVram = true;
|
|
userWarningText += $"Approx. VRAM Usage: Used: {UiSharedService.ByteToString(_pair.LastAppliedApproximateVRAMBytes)}, Threshold: {_performanceConfigService.Current.VRAMSizeWarningThresholdMiB} MiB";
|
|
}
|
|
if (_performanceConfigService.Current.TrisWarningThresholdThousands > 0
|
|
&& _performanceConfigService.Current.TrisWarningThresholdThousands * 1024 < _pair.LastAppliedDataTris)
|
|
{
|
|
if (shownVram) userWarningText += Environment.NewLine;
|
|
userWarningText += $"Approx. Triangle count: Used: {_pair.LastAppliedDataTris}, Threshold: {_performanceConfigService.Current.TrisWarningThresholdThousands * 1000}";
|
|
}
|
|
|
|
UiSharedService.AttachToolTip(userWarningText);
|
|
}
|
|
|
|
ImGui.SameLine();
|
|
|
|
if (_pair.UserData.IsAdmin || _pair.UserData.IsModerator)
|
|
{
|
|
ImGui.SameLine();
|
|
|
|
var iconId = _pair.UserData.IsAdmin ? 67 : 68;
|
|
var colorKey = _pair.UserData.IsAdmin ? "LightlessAdminText" : "LightlessModeratorText";
|
|
var roleColor = UIColors.Get(colorKey);
|
|
|
|
var iconPos = ImGui.GetCursorScreenPos();
|
|
SeStringUtils.RenderIconWithHitbox(iconId, iconPos);
|
|
|
|
if (ImGui.IsItemHovered())
|
|
{
|
|
ImGui.BeginTooltip();
|
|
using (ImRaii.PushColor(ImGuiCol.Text, roleColor))
|
|
{
|
|
ImGui.TextUnformatted(_pair.UserData.IsAdmin
|
|
? "Official Lightless Developer"
|
|
: "Official Lightless Moderator");
|
|
}
|
|
ImGui.EndTooltip();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private void DrawName(float leftSide, float rightSide)
|
|
{
|
|
_displayHandler.DrawPairText(_id, _pair, leftSide, () => rightSide - leftSide);
|
|
}
|
|
|
|
private string GetUserTooltip()
|
|
{
|
|
List<string>? groupDisplays = null;
|
|
if (_syncedGroups.Count > 0)
|
|
{
|
|
groupDisplays = new List<string>(_syncedGroups.Count);
|
|
foreach (var group in _syncedGroups)
|
|
{
|
|
var groupNote = _serverConfigurationManager.GetNoteForGid(group.GID);
|
|
groupDisplays.Add(string.IsNullOrEmpty(groupNote) ? group.GroupAliasOrGID : $"{groupNote} ({group.GroupAliasOrGID})");
|
|
}
|
|
}
|
|
|
|
var snapshot = new TooltipSnapshot(
|
|
_pair.IsPaused,
|
|
_pair.IsOnline,
|
|
_pair.IsVisible,
|
|
_pair.IndividualPairStatus,
|
|
_pair.UserData.AliasOrUID,
|
|
_pair.PlayerName ?? string.Empty,
|
|
_pair.LastAppliedDataBytes,
|
|
_pair.LastAppliedApproximateVRAMBytes,
|
|
_pair.LastAppliedApproximateEffectiveVRAMBytes,
|
|
_pair.LastAppliedDataTris,
|
|
_pair.IsPaired,
|
|
groupDisplays is null ? ImmutableArray<string>.Empty : ImmutableArray.CreateRange(groupDisplays));
|
|
|
|
if (!_tooltipSnapshot.Equals(snapshot))
|
|
{
|
|
_cachedTooltip = BuildTooltip(snapshot);
|
|
_tooltipSnapshot = snapshot;
|
|
}
|
|
|
|
return _cachedTooltip;
|
|
}
|
|
|
|
private static string BuildTooltip(in TooltipSnapshot snapshot)
|
|
{
|
|
var builder = new StringBuilder(256);
|
|
|
|
if (snapshot.IsPaused)
|
|
{
|
|
builder.Append(snapshot.AliasOrUid);
|
|
builder.Append(" is paused");
|
|
}
|
|
else if (!snapshot.IsOnline)
|
|
{
|
|
builder.Append(snapshot.AliasOrUid);
|
|
builder.Append(" is offline");
|
|
}
|
|
else if (snapshot.IsVisible)
|
|
{
|
|
builder.Append(snapshot.AliasOrUid);
|
|
builder.Append(" is visible: ");
|
|
builder.Append(snapshot.PlayerName);
|
|
builder.Append(Environment.NewLine);
|
|
builder.Append("Click to target this player");
|
|
}
|
|
else
|
|
{
|
|
builder.Append(snapshot.AliasOrUid);
|
|
builder.Append(" is online");
|
|
}
|
|
|
|
if (snapshot.PairStatus == IndividualPairStatus.OneSided)
|
|
{
|
|
builder.Append(UiSharedService.TooltipSeparator);
|
|
builder.Append("User has not added you back");
|
|
}
|
|
else if (snapshot.PairStatus == IndividualPairStatus.Bidirectional)
|
|
{
|
|
builder.Append(UiSharedService.TooltipSeparator);
|
|
builder.Append("You are directly Paired");
|
|
}
|
|
|
|
if (snapshot.LastAppliedDataBytes >= 0)
|
|
{
|
|
builder.Append(UiSharedService.TooltipSeparator);
|
|
if (!snapshot.IsPaired)
|
|
{
|
|
builder.Append("(Last) ");
|
|
}
|
|
builder.Append("Mods Info");
|
|
builder.Append(Environment.NewLine);
|
|
builder.Append("Files Size: ");
|
|
builder.Append(UiSharedService.ByteToString(snapshot.LastAppliedDataBytes, true));
|
|
|
|
if (snapshot.LastAppliedApproximateVRAMBytes >= 0)
|
|
{
|
|
builder.Append(Environment.NewLine);
|
|
builder.Append("Approx. VRAM Usage: ");
|
|
var originalText = UiSharedService.ByteToString(snapshot.LastAppliedApproximateVRAMBytes, true);
|
|
builder.Append(originalText);
|
|
if (snapshot.LastAppliedApproximateEffectiveVRAMBytes >= 0)
|
|
{
|
|
builder.Append(" (Effective: ");
|
|
builder.Append(UiSharedService.ByteToString(snapshot.LastAppliedApproximateEffectiveVRAMBytes, true));
|
|
builder.Append(')');
|
|
}
|
|
}
|
|
|
|
if (snapshot.LastAppliedDataTris >= 0)
|
|
{
|
|
builder.Append(Environment.NewLine);
|
|
builder.Append("Approx. Triangle Count (excl. Vanilla): ");
|
|
builder.Append(snapshot.LastAppliedDataTris > 1000
|
|
? (snapshot.LastAppliedDataTris / 1000d).ToString("0.0'k'")
|
|
: snapshot.LastAppliedDataTris);
|
|
}
|
|
}
|
|
|
|
if (!snapshot.GroupDisplays.IsEmpty)
|
|
{
|
|
builder.Append(UiSharedService.TooltipSeparator);
|
|
for (int i = 0; i < snapshot.GroupDisplays.Length; i++)
|
|
{
|
|
if (i > 0)
|
|
{
|
|
builder.Append(Environment.NewLine);
|
|
}
|
|
builder.Append("Paired through ");
|
|
builder.Append(snapshot.GroupDisplays[i]);
|
|
}
|
|
}
|
|
|
|
return builder.ToString();
|
|
}
|
|
|
|
private readonly record struct TooltipSnapshot(
|
|
bool IsPaused,
|
|
bool IsOnline,
|
|
bool IsVisible,
|
|
IndividualPairStatus PairStatus,
|
|
string AliasOrUid,
|
|
string PlayerName,
|
|
long LastAppliedDataBytes,
|
|
long LastAppliedApproximateVRAMBytes,
|
|
long LastAppliedApproximateEffectiveVRAMBytes,
|
|
long LastAppliedDataTris,
|
|
bool IsPaired,
|
|
ImmutableArray<string> GroupDisplays)
|
|
{
|
|
public static TooltipSnapshot Empty { get; } =
|
|
new(false, false, false, IndividualPairStatus.None, string.Empty, string.Empty, -1, -1, -1, -1, false, ImmutableArray<string>.Empty);
|
|
}
|
|
|
|
private void DrawPairedClientMenu()
|
|
{
|
|
DrawIndividualMenu();
|
|
|
|
if (_syncedGroups.Any()) ImGui.Separator();
|
|
foreach (var entry in _syncedGroups)
|
|
{
|
|
bool selfIsOwner = string.Equals(_apiController.UID, entry.Owner.UID, StringComparison.Ordinal);
|
|
bool selfIsModerator = entry.GroupUserInfo.IsModerator();
|
|
bool userIsModerator = entry.GroupPairUserInfos.TryGetValue(_pair.UserData.UID, out var modinfo) && modinfo.IsModerator();
|
|
bool userIsPinned = entry.GroupPairUserInfos.TryGetValue(_pair.UserData.UID, out var info) && info.IsPinned();
|
|
if (selfIsOwner || selfIsModerator)
|
|
{
|
|
var groupNote = _serverConfigurationManager.GetNoteForGid(entry.GID);
|
|
var groupString = string.IsNullOrEmpty(groupNote) ? entry.GroupAliasOrGID : $"{groupNote} ({entry.GroupAliasOrGID})";
|
|
|
|
if (ImGui.BeginMenu(groupString + " Moderation Functions"))
|
|
{
|
|
DrawSyncshellMenu(entry, selfIsOwner, selfIsModerator, userIsPinned, userIsModerator);
|
|
ImGui.EndMenu();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private float DrawRightSide()
|
|
{
|
|
var pauseIcon = _pair.UserPair!.OwnPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
|
|
var pauseButtonSize = _uiSharedService.GetIconButtonSize(pauseIcon);
|
|
var barButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.EllipsisV);
|
|
var spacingX = ImGui.GetStyle().ItemSpacing.X;
|
|
var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth();
|
|
float currentRightSide = windowEndX - barButtonSize.X;
|
|
|
|
ImGui.SameLine(currentRightSide);
|
|
ImGui.AlignTextToFramePadding();
|
|
if (_uiSharedService.IconButton(FontAwesomeIcon.EllipsisV))
|
|
{
|
|
ImGui.OpenPopup("User Flyout Menu");
|
|
}
|
|
|
|
currentRightSide -= (pauseButtonSize.X + spacingX);
|
|
ImGui.SameLine(currentRightSide);
|
|
if (_uiSharedService.IconButton(pauseIcon))
|
|
{
|
|
var perm = _pair.UserPair!.OwnPermissions;
|
|
|
|
if (UiSharedService.CtrlPressed() && !perm.IsPaused())
|
|
{
|
|
perm.SetSticky(true);
|
|
}
|
|
perm.SetPaused(!perm.IsPaused());
|
|
_ = _apiController.UserSetPairPermissions(new(_pair.UserData, perm));
|
|
}
|
|
UiSharedService.AttachToolTip(!_pair.UserPair!.OwnPermissions.IsPaused()
|
|
? ("Pause pairing with " + _pair.UserData.AliasOrUID
|
|
+ (_pair.UserPair!.OwnPermissions.IsSticky()
|
|
? string.Empty
|
|
: UiSharedService.TooltipSeparator + "Hold CTRL to enable preferred permissions while pausing." + Environment.NewLine + "This will leave this pair paused even if unpausing syncshells including this pair."))
|
|
: "Resume pairing with " + _pair.UserData.AliasOrUID);
|
|
|
|
if (_pair.IsPaired)
|
|
{
|
|
var individualSoundsDisabled = (_pair.UserPair?.OwnPermissions.IsDisableSounds() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableSounds() ?? false);
|
|
var individualAnimDisabled = (_pair.UserPair?.OwnPermissions.IsDisableAnimations() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableAnimations() ?? false);
|
|
var individualVFXDisabled = (_pair.UserPair?.OwnPermissions.IsDisableVFX() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableVFX() ?? false);
|
|
var individualIsSticky = _pair.UserPair!.OwnPermissions.IsSticky();
|
|
var individualIcon = individualIsSticky ? FontAwesomeIcon.ArrowCircleUp : FontAwesomeIcon.InfoCircle;
|
|
|
|
if (individualAnimDisabled || individualSoundsDisabled || individualVFXDisabled || individualIsSticky)
|
|
{
|
|
currentRightSide -= (_uiSharedService.GetIconSize(individualIcon).X + spacingX);
|
|
|
|
ImGui.SameLine(currentRightSide);
|
|
using (ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("LightlessYellow"), individualAnimDisabled || individualSoundsDisabled || individualVFXDisabled))
|
|
_uiSharedService.IconText(individualIcon);
|
|
if (ImGui.IsItemHovered())
|
|
{
|
|
ImGui.BeginTooltip();
|
|
|
|
ImGui.TextUnformatted("Individual User permissions");
|
|
ImGui.Separator();
|
|
|
|
if (individualIsSticky)
|
|
{
|
|
_uiSharedService.IconText(individualIcon);
|
|
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
|
ImGui.AlignTextToFramePadding();
|
|
ImGui.TextUnformatted("Preferred permissions enabled");
|
|
if (individualAnimDisabled || individualSoundsDisabled || individualVFXDisabled)
|
|
ImGui.Separator();
|
|
}
|
|
|
|
if (individualSoundsDisabled)
|
|
{
|
|
var userSoundsText = "Sound sync";
|
|
_uiSharedService.IconText(FontAwesomeIcon.VolumeOff);
|
|
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
|
ImGui.AlignTextToFramePadding();
|
|
ImGui.TextUnformatted(userSoundsText);
|
|
ImGui.NewLine();
|
|
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
|
ImGui.AlignTextToFramePadding();
|
|
ImGui.TextUnformatted("You");
|
|
_uiSharedService.BooleanToColoredIcon(!_pair.UserPair!.OwnPermissions.IsDisableSounds());
|
|
ImGui.SameLine();
|
|
ImGui.AlignTextToFramePadding();
|
|
ImGui.TextUnformatted("They");
|
|
_uiSharedService.BooleanToColoredIcon(!_pair.UserPair!.OtherPermissions.IsDisableSounds());
|
|
}
|
|
|
|
if (individualAnimDisabled)
|
|
{
|
|
var userAnimText = "Animation sync";
|
|
_uiSharedService.IconText(FontAwesomeIcon.Stop);
|
|
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
|
ImGui.AlignTextToFramePadding();
|
|
ImGui.TextUnformatted(userAnimText);
|
|
ImGui.NewLine();
|
|
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
|
ImGui.AlignTextToFramePadding();
|
|
ImGui.TextUnformatted("You");
|
|
_uiSharedService.BooleanToColoredIcon(!_pair.UserPair!.OwnPermissions.IsDisableAnimations());
|
|
ImGui.SameLine();
|
|
ImGui.AlignTextToFramePadding();
|
|
ImGui.TextUnformatted("They");
|
|
_uiSharedService.BooleanToColoredIcon(!_pair.UserPair!.OtherPermissions.IsDisableAnimations());
|
|
}
|
|
|
|
if (individualVFXDisabled)
|
|
{
|
|
var userVFXText = "VFX sync";
|
|
_uiSharedService.IconText(FontAwesomeIcon.Circle);
|
|
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
|
ImGui.AlignTextToFramePadding();
|
|
ImGui.TextUnformatted(userVFXText);
|
|
ImGui.NewLine();
|
|
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
|
ImGui.AlignTextToFramePadding();
|
|
ImGui.TextUnformatted("You");
|
|
_uiSharedService.BooleanToColoredIcon(!_pair.UserPair!.OwnPermissions.IsDisableVFX());
|
|
ImGui.SameLine();
|
|
ImGui.AlignTextToFramePadding();
|
|
ImGui.TextUnformatted("They");
|
|
_uiSharedService.BooleanToColoredIcon(!_pair.UserPair!.OtherPermissions.IsDisableVFX());
|
|
}
|
|
|
|
ImGui.EndTooltip();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_charaDataManager.SharedWithYouData.TryGetValue(_pair.UserData, out var sharedData))
|
|
{
|
|
currentRightSide -= (_uiSharedService.GetIconSize(FontAwesomeIcon.Running).X + (spacingX / 2f));
|
|
ImGui.SameLine(currentRightSide);
|
|
_uiSharedService.IconText(FontAwesomeIcon.Running);
|
|
UiSharedService.AttachToolTip($"This user has shared {sharedData.Count} Character Data Sets with you." + UiSharedService.TooltipSeparator
|
|
+ "Click to open the Character Data Hub and show the entries.");
|
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
|
{
|
|
_mediator.Publish(new OpenCharaDataHubWithFilterMessage(_pair.UserData));
|
|
}
|
|
}
|
|
|
|
if (_currentGroup != null)
|
|
{
|
|
var icon = FontAwesomeIcon.None;
|
|
var text = string.Empty;
|
|
if (string.Equals(_currentGroup.OwnerUID, _pair.UserData.UID, StringComparison.Ordinal))
|
|
{
|
|
icon = FontAwesomeIcon.Crown;
|
|
text = "User is owner of this syncshell";
|
|
}
|
|
else if (_currentGroup.GroupPairUserInfos.TryGetValue(_pair.UserData.UID, out var userinfo))
|
|
{
|
|
if (userinfo.IsModerator())
|
|
{
|
|
icon = FontAwesomeIcon.UserShield;
|
|
text = "User is moderator in this syncshell";
|
|
}
|
|
else if (userinfo.IsPinned())
|
|
{
|
|
icon = FontAwesomeIcon.Thumbtack;
|
|
text = "User is pinned in this syncshell";
|
|
}
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(text))
|
|
{
|
|
currentRightSide -= (_uiSharedService.GetIconSize(icon).X + spacingX);
|
|
ImGui.SameLine(currentRightSide);
|
|
_uiSharedService.IconText(icon);
|
|
UiSharedService.AttachToolTip(text);
|
|
}
|
|
}
|
|
|
|
if (ImGui.BeginPopup("User Flyout Menu"))
|
|
{
|
|
using (ImRaii.PushId($"buttons-{_pair.UserData.UID}"))
|
|
{
|
|
ImGui.TextUnformatted("Common Pair Functions");
|
|
DrawCommonClientMenu();
|
|
ImGui.Separator();
|
|
DrawPairedClientMenu();
|
|
if (_menuWidth <= 0)
|
|
{
|
|
_menuWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X;
|
|
}
|
|
}
|
|
|
|
ImGui.EndPopup();
|
|
}
|
|
|
|
return currentRightSide - spacingX;
|
|
}
|
|
|
|
private void DrawSyncshellMenu(GroupFullInfoDto group, bool selfIsOwner, bool selfIsModerator, bool userIsPinned, bool userIsModerator)
|
|
{
|
|
var showModeratorActions = selfIsOwner || (selfIsModerator && !userIsModerator);
|
|
var showOwnerActions = selfIsOwner;
|
|
|
|
if (showModeratorActions || showOwnerActions)
|
|
ImGui.Separator();
|
|
|
|
if (showModeratorActions)
|
|
{
|
|
ImGui.TextUnformatted("Syncshell Moderator Functions");
|
|
var pinText = userIsPinned ? "Unpin user" : "Pin user";
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Thumbtack, pinText, _menuWidth, true))
|
|
{
|
|
ImGui.CloseCurrentPopup();
|
|
if (!group.GroupPairUserInfos.TryGetValue(_pair.UserData.UID, out var userinfo))
|
|
{
|
|
userinfo = API.Data.Enum.GroupPairUserInfo.IsPinned;
|
|
}
|
|
else
|
|
{
|
|
userinfo.SetPinned(!userinfo.IsPinned());
|
|
}
|
|
_ = _apiController.GroupSetUserInfo(new GroupPairUserInfoDto(group.Group, _pair.UserData, userinfo));
|
|
}
|
|
UiSharedService.AttachToolTip("Pin this user to the Syncshell. Pinned users will not be deleted in case of a manually initiated Syncshell clean");
|
|
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Remove user", _menuWidth, true) && UiSharedService.CtrlPressed())
|
|
{
|
|
ImGui.CloseCurrentPopup();
|
|
_ = _apiController.GroupRemoveUser(new(group.Group, _pair.UserData));
|
|
}
|
|
UiSharedService.AttachToolTip("Hold CTRL and click to remove user " + (_pair.UserData.AliasOrUID) + " from Syncshell");
|
|
|
|
var banEnabled = UiSharedService.CtrlPressed();
|
|
var banLabel = banEnabled ? "Ban user" : "Ban user (Hold CTRL)";
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.UserSlash, banLabel, _menuWidth, true) && banEnabled)
|
|
{
|
|
_mediator.Publish(new OpenBanUserPopupMessage(_pair, group));
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
UiSharedService.AttachToolTip("Hold CTRL to ban user " + (_pair.UserData.AliasOrUID) + " from this Syncshell");
|
|
|
|
if (showOwnerActions)
|
|
{
|
|
ImGui.Separator();
|
|
}
|
|
}
|
|
|
|
if (showOwnerActions)
|
|
{
|
|
ImGui.TextUnformatted("Syncshell Owner Functions");
|
|
string modText = userIsModerator ? "Demod user" : "Mod user";
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.UserShield, modText, _menuWidth, true) && UiSharedService.CtrlPressed())
|
|
{
|
|
ImGui.CloseCurrentPopup();
|
|
if (!group.GroupPairUserInfos.TryGetValue(_pair.UserData.UID, out var userinfo))
|
|
{
|
|
userinfo = API.Data.Enum.GroupPairUserInfo.IsModerator;
|
|
}
|
|
else
|
|
{
|
|
userinfo.SetModerator(!userinfo.IsModerator());
|
|
}
|
|
|
|
_ = _apiController.GroupSetUserInfo(new GroupPairUserInfoDto(group.Group, _pair.UserData, userinfo));
|
|
}
|
|
UiSharedService.AttachToolTip("Hold CTRL to change the moderator status for " + (_pair.UserData.AliasOrUID) + Environment.NewLine +
|
|
"Moderators can kick, ban/unban, pin/unpin users and clear the Syncshell.");
|
|
|
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Crown, "Transfer Ownership", _menuWidth, true) && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed())
|
|
{
|
|
ImGui.CloseCurrentPopup();
|
|
_ = _apiController.GroupChangeOwnership(new(group.Group, _pair.UserData));
|
|
}
|
|
UiSharedService.AttachToolTip("Hold CTRL and SHIFT and click to transfer ownership of this Syncshell to "
|
|
+ (_pair.UserData.AliasOrUID) + Environment.NewLine + "WARNING: This action is irreversible.");
|
|
}
|
|
}
|
|
} |