rebuild project & update internal names

This commit is contained in:
Abelfreyja
2025-08-22 13:07:48 +09:00
parent 3d9bf49d7f
commit 7d3de5361a
190 changed files with 851 additions and 851 deletions

View File

@@ -0,0 +1,129 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.UI.Handlers;
using System.Collections.Immutable;
namespace LightlessSync.UI.Components;
public abstract class DrawFolderBase : IDrawFolder
{
public IImmutableList<DrawUserPair> DrawPairs { get; init; }
protected readonly string _id;
protected readonly IImmutableList<Pair> _allPairs;
protected readonly TagHandler _tagHandler;
protected readonly UiSharedService _uiSharedService;
private float _menuWidth = -1;
public int OnlinePairs => DrawPairs.Count(u => u.Pair.IsOnline);
public int TotalPairs => _allPairs.Count;
private bool _wasHovered = false;
protected DrawFolderBase(string id, IImmutableList<DrawUserPair> drawPairs,
IImmutableList<Pair> allPairs, TagHandler tagHandler, UiSharedService uiSharedService)
{
_id = id;
DrawPairs = drawPairs;
_allPairs = allPairs;
_tagHandler = tagHandler;
_uiSharedService = uiSharedService;
}
protected abstract bool RenderIfEmpty { get; }
protected abstract bool RenderMenu { get; }
public void Draw()
{
if (!RenderIfEmpty && !DrawPairs.Any()) return;
using var id = ImRaii.PushId("folder_" + _id);
var color = ImRaii.PushColor(ImGuiCol.ChildBg, ImGui.GetColorU32(ImGuiCol.FrameBgHovered), _wasHovered);
using (ImRaii.Child("folder__" + _id, new System.Numerics.Vector2(UiSharedService.GetWindowContentRegionWidth() - ImGui.GetCursorPosX(), ImGui.GetFrameHeight())))
{
// draw opener
var icon = _tagHandler.IsTagOpen(_id) ? FontAwesomeIcon.CaretDown : FontAwesomeIcon.CaretRight;
ImGui.AlignTextToFramePadding();
_uiSharedService.IconText(icon);
if (ImGui.IsItemClicked())
{
_tagHandler.SetTagOpen(_id, !_tagHandler.IsTagOpen(_id));
}
ImGui.SameLine();
var leftSideEnd = DrawIcon();
ImGui.SameLine();
var rightSideStart = DrawRightSideInternal();
// draw name
ImGui.SameLine(leftSideEnd);
DrawName(rightSideStart - leftSideEnd);
}
_wasHovered = ImGui.IsItemHovered();
color.Dispose();
ImGui.Separator();
// if opened draw content
if (_tagHandler.IsTagOpen(_id))
{
using var indent = ImRaii.PushIndent(_uiSharedService.GetIconSize(FontAwesomeIcon.EllipsisV).X + ImGui.GetStyle().ItemSpacing.X, false);
if (DrawPairs.Any())
{
foreach (var item in DrawPairs)
{
item.DrawPairedClient();
}
}
else
{
ImGui.TextUnformatted("No users (online)");
}
ImGui.Separator();
}
}
protected abstract float DrawIcon();
protected abstract void DrawMenu(float menuWidth);
protected abstract void DrawName(float width);
protected abstract float DrawRightSide(float currentRightSideX);
private float DrawRightSideInternal()
{
var barButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.EllipsisV);
var spacingX = ImGui.GetStyle().ItemSpacing.X;
var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth();
// Flyout Menu
var rightSideStart = windowEndX - (RenderMenu ? (barButtonSize.X + spacingX) : spacingX);
if (RenderMenu)
{
ImGui.SameLine(windowEndX - barButtonSize.X);
if (_uiSharedService.IconButton(FontAwesomeIcon.EllipsisV))
{
ImGui.OpenPopup("User Flyout Menu");
}
if (ImGui.BeginPopup("User Flyout Menu"))
{
using (ImRaii.PushId($"buttons-{_id}")) DrawMenu(_menuWidth);
_menuWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X;
ImGui.EndPopup();
}
else
{
_menuWidth = 0;
}
}
return DrawRightSide(rightSideStart);
}
}

View File

@@ -0,0 +1,244 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using LightlessSync.API.Data.Extensions;
using LightlessSync.API.Dto.Group;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services.Mediator;
using LightlessSync.UI.Handlers;
using LightlessSync.WebAPI;
using System.Collections.Immutable;
namespace LightlessSync.UI.Components;
public class DrawFolderGroup : DrawFolderBase
{
private readonly ApiController _apiController;
private readonly GroupFullInfoDto _groupFullInfoDto;
private readonly IdDisplayHandler _idDisplayHandler;
private readonly MareMediator _mareMediator;
public DrawFolderGroup(string id, GroupFullInfoDto groupFullInfoDto, ApiController apiController,
IImmutableList<DrawUserPair> drawPairs, IImmutableList<Pair> allPairs, TagHandler tagHandler, IdDisplayHandler idDisplayHandler,
MareMediator mareMediator, UiSharedService uiSharedService) :
base(id, drawPairs, allPairs, tagHandler, uiSharedService)
{
_groupFullInfoDto = groupFullInfoDto;
_apiController = apiController;
_idDisplayHandler = idDisplayHandler;
_mareMediator = mareMediator;
}
protected override bool RenderIfEmpty => true;
protected override bool RenderMenu => true;
private bool IsModerator => IsOwner || _groupFullInfoDto.GroupUserInfo.IsModerator();
private bool IsOwner => string.Equals(_groupFullInfoDto.OwnerUID, _apiController.UID, StringComparison.Ordinal);
private bool IsPinned => _groupFullInfoDto.GroupUserInfo.IsPinned();
protected override float DrawIcon()
{
ImGui.AlignTextToFramePadding();
_uiSharedService.IconText(_groupFullInfoDto.GroupPermissions.IsDisableInvites() ? FontAwesomeIcon.Lock : FontAwesomeIcon.Users);
if (_groupFullInfoDto.GroupPermissions.IsDisableInvites())
{
UiSharedService.AttachToolTip("Syncshell " + _groupFullInfoDto.GroupAliasOrGID + " is closed for invites");
}
using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X / 2f }))
{
ImGui.SameLine();
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("[" + OnlinePairs.ToString() + "]");
}
UiSharedService.AttachToolTip(OnlinePairs + " online" + Environment.NewLine + TotalPairs + " total");
ImGui.SameLine();
if (IsOwner)
{
ImGui.AlignTextToFramePadding();
_uiSharedService.IconText(FontAwesomeIcon.Crown);
UiSharedService.AttachToolTip("You are the owner of " + _groupFullInfoDto.GroupAliasOrGID);
}
else if (IsModerator)
{
ImGui.AlignTextToFramePadding();
_uiSharedService.IconText(FontAwesomeIcon.UserShield);
UiSharedService.AttachToolTip("You are a moderator in " + _groupFullInfoDto.GroupAliasOrGID);
}
else if (IsPinned)
{
ImGui.AlignTextToFramePadding();
_uiSharedService.IconText(FontAwesomeIcon.Thumbtack);
UiSharedService.AttachToolTip("You are pinned in " + _groupFullInfoDto.GroupAliasOrGID);
}
ImGui.SameLine();
return ImGui.GetCursorPosX();
}
protected override void DrawMenu(float menuWidth)
{
ImGui.TextUnformatted("Syncshell Menu (" + _groupFullInfoDto.GroupAliasOrGID + ")");
ImGui.Separator();
ImGui.TextUnformatted("General Syncshell Actions");
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Copy, "Copy ID", menuWidth, true))
{
ImGui.CloseCurrentPopup();
ImGui.SetClipboardText(_groupFullInfoDto.GroupAliasOrGID);
}
UiSharedService.AttachToolTip("Copy Syncshell ID to Clipboard");
if (_uiSharedService.IconTextButton(FontAwesomeIcon.StickyNote, "Copy Notes", menuWidth, true))
{
ImGui.CloseCurrentPopup();
ImGui.SetClipboardText(UiSharedService.GetNotes(DrawPairs.Select(k => k.Pair).ToList()));
}
UiSharedService.AttachToolTip("Copies all your notes for all users in this Syncshell to the clipboard." + Environment.NewLine + "They can be imported via Settings -> General -> Notes -> Import notes from clipboard");
if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowCircleLeft, "Leave Syncshell", menuWidth, true) && UiSharedService.CtrlPressed())
{
_ = _apiController.GroupLeave(_groupFullInfoDto);
ImGui.CloseCurrentPopup();
}
UiSharedService.AttachToolTip("Hold CTRL and click to leave this Syncshell" + (!string.Equals(_groupFullInfoDto.OwnerUID, _apiController.UID, StringComparison.Ordinal)
? string.Empty : Environment.NewLine + "WARNING: This action is irreversible" + Environment.NewLine + "Leaving an owned Syncshell will transfer the ownership to a random person in the Syncshell."));
ImGui.Separator();
ImGui.TextUnformatted("Permission Settings");
var perm = _groupFullInfoDto.GroupUserPermissions;
bool disableSounds = perm.IsDisableSounds();
bool disableAnims = perm.IsDisableAnimations();
bool disableVfx = perm.IsDisableVFX();
if ((_groupFullInfoDto.GroupPermissions.IsPreferDisableAnimations() != disableAnims
|| _groupFullInfoDto.GroupPermissions.IsPreferDisableSounds() != disableSounds
|| _groupFullInfoDto.GroupPermissions.IsPreferDisableVFX() != disableVfx)
&& _uiSharedService.IconTextButton(FontAwesomeIcon.Check, "Align with suggested permissions", menuWidth, true))
{
perm.SetDisableVFX(_groupFullInfoDto.GroupPermissions.IsPreferDisableVFX());
perm.SetDisableSounds(_groupFullInfoDto.GroupPermissions.IsPreferDisableSounds());
perm.SetDisableAnimations(_groupFullInfoDto.GroupPermissions.IsPreferDisableAnimations());
_ = _apiController.GroupChangeIndividualPermissionState(new(_groupFullInfoDto.Group, new(_apiController.UID), perm));
ImGui.CloseCurrentPopup();
}
if (_uiSharedService.IconTextButton(disableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeOff, disableSounds ? "Enable Sound Sync" : "Disable Sound Sync", menuWidth, true))
{
perm.SetDisableSounds(!disableSounds);
_ = _apiController.GroupChangeIndividualPermissionState(new(_groupFullInfoDto.Group, new(_apiController.UID), perm));
ImGui.CloseCurrentPopup();
}
if (_uiSharedService.IconTextButton(disableAnims ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop, disableAnims ? "Enable Animation Sync" : "Disable Animation Sync", menuWidth, true))
{
perm.SetDisableAnimations(!disableAnims);
_ = _apiController.GroupChangeIndividualPermissionState(new(_groupFullInfoDto.Group, new(_apiController.UID), perm));
ImGui.CloseCurrentPopup();
}
if (_uiSharedService.IconTextButton(disableVfx ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle, disableVfx ? "Enable VFX Sync" : "Disable VFX Sync", menuWidth, true))
{
perm.SetDisableVFX(!disableVfx);
_ = _apiController.GroupChangeIndividualPermissionState(new(_groupFullInfoDto.Group, new(_apiController.UID), perm));
ImGui.CloseCurrentPopup();
}
if (IsModerator || IsOwner)
{
ImGui.Separator();
ImGui.TextUnformatted("Syncshell Admin Functions");
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Cog, "Open Admin Panel", menuWidth, true))
{
ImGui.CloseCurrentPopup();
_mareMediator.Publish(new OpenSyncshellAdminPanel(_groupFullInfoDto));
}
}
}
protected override void DrawName(float width)
{
_idDisplayHandler.DrawGroupText(_id, _groupFullInfoDto, ImGui.GetCursorPosX(), () => width);
}
protected override float DrawRightSide(float currentRightSideX)
{
var spacingX = ImGui.GetStyle().ItemSpacing.X;
FontAwesomeIcon pauseIcon = _groupFullInfoDto.GroupUserPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
var pauseButtonSize = _uiSharedService.GetIconButtonSize(pauseIcon);
var userCogButtonSize = _uiSharedService.GetIconSize(FontAwesomeIcon.UsersCog);
var individualSoundsDisabled = _groupFullInfoDto.GroupUserPermissions.IsDisableSounds();
var individualAnimDisabled = _groupFullInfoDto.GroupUserPermissions.IsDisableAnimations();
var individualVFXDisabled = _groupFullInfoDto.GroupUserPermissions.IsDisableVFX();
var infoIconPosDist = currentRightSideX - pauseButtonSize.X - spacingX;
ImGui.SameLine(infoIconPosDist - userCogButtonSize.X);
ImGui.AlignTextToFramePadding();
_uiSharedService.IconText(FontAwesomeIcon.UsersCog, (_groupFullInfoDto.GroupPermissions.IsPreferDisableAnimations() != individualAnimDisabled
|| _groupFullInfoDto.GroupPermissions.IsPreferDisableSounds() != individualSoundsDisabled
|| _groupFullInfoDto.GroupPermissions.IsPreferDisableVFX() != individualVFXDisabled) ? ImGuiColors.DalamudYellow : null);
if (ImGui.IsItemHovered())
{
ImGui.BeginTooltip();
ImGui.TextUnformatted("Syncshell Permissions");
ImGuiHelpers.ScaledDummy(2f);
_uiSharedService.BooleanToColoredIcon(!individualSoundsDisabled, inline: false);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Sound Sync");
_uiSharedService.BooleanToColoredIcon(!individualAnimDisabled, inline: false);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Animation Sync");
_uiSharedService.BooleanToColoredIcon(!individualVFXDisabled, inline: false);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("VFX Sync");
ImGui.Separator();
ImGuiHelpers.ScaledDummy(2f);
ImGui.TextUnformatted("Suggested Permissions");
ImGuiHelpers.ScaledDummy(2f);
_uiSharedService.BooleanToColoredIcon(!_groupFullInfoDto.GroupPermissions.IsPreferDisableSounds(), inline: false);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Sound Sync");
_uiSharedService.BooleanToColoredIcon(!_groupFullInfoDto.GroupPermissions.IsPreferDisableAnimations(), inline: false);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Animation Sync");
_uiSharedService.BooleanToColoredIcon(!_groupFullInfoDto.GroupPermissions.IsPreferDisableVFX(), inline: false);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("VFX Sync");
ImGui.EndTooltip();
}
ImGui.SameLine();
if (_uiSharedService.IconButton(pauseIcon))
{
var perm = _groupFullInfoDto.GroupUserPermissions;
perm.SetPaused(!perm.IsPaused());
_ = _apiController.GroupChangeIndividualPermissionState(new GroupPairUserPermissionDto(_groupFullInfoDto.Group, new(_apiController.UID), perm));
}
return currentRightSideX;
}
}

View File

@@ -0,0 +1,190 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using LightlessSync.API.Data.Extensions;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.UI.Handlers;
using LightlessSync.WebAPI;
using System.Collections.Immutable;
namespace LightlessSync.UI.Components;
public class DrawFolderTag : DrawFolderBase
{
private readonly ApiController _apiController;
private readonly SelectPairForTagUi _selectPairForTagUi;
public DrawFolderTag(string id, IImmutableList<DrawUserPair> drawPairs, IImmutableList<Pair> allPairs,
TagHandler tagHandler, ApiController apiController, SelectPairForTagUi selectPairForTagUi, UiSharedService uiSharedService)
: base(id, drawPairs, allPairs, tagHandler, uiSharedService)
{
_apiController = apiController;
_selectPairForTagUi = selectPairForTagUi;
}
protected override bool RenderIfEmpty => _id switch
{
TagHandler.CustomUnpairedTag => false,
TagHandler.CustomOnlineTag => false,
TagHandler.CustomOfflineTag => false,
TagHandler.CustomVisibleTag => false,
TagHandler.CustomAllTag => true,
TagHandler.CustomOfflineSyncshellTag => false,
_ => true,
};
protected override bool RenderMenu => _id switch
{
TagHandler.CustomUnpairedTag => false,
TagHandler.CustomOnlineTag => false,
TagHandler.CustomOfflineTag => false,
TagHandler.CustomVisibleTag => false,
TagHandler.CustomAllTag => false,
TagHandler.CustomOfflineSyncshellTag => false,
_ => true,
};
private bool RenderPause => _id switch
{
TagHandler.CustomUnpairedTag => false,
TagHandler.CustomOnlineTag => false,
TagHandler.CustomOfflineTag => false,
TagHandler.CustomVisibleTag => false,
TagHandler.CustomAllTag => false,
TagHandler.CustomOfflineSyncshellTag => false,
_ => true,
} && _allPairs.Any();
private bool RenderCount => _id switch
{
TagHandler.CustomUnpairedTag => false,
TagHandler.CustomOnlineTag => false,
TagHandler.CustomOfflineTag => false,
TagHandler.CustomVisibleTag => false,
TagHandler.CustomAllTag => false,
TagHandler.CustomOfflineSyncshellTag => false,
_ => true
};
protected override float DrawIcon()
{
var icon = _id switch
{
TagHandler.CustomUnpairedTag => FontAwesomeIcon.ArrowsLeftRight,
TagHandler.CustomOnlineTag => FontAwesomeIcon.Link,
TagHandler.CustomOfflineTag => FontAwesomeIcon.Unlink,
TagHandler.CustomOfflineSyncshellTag => FontAwesomeIcon.Unlink,
TagHandler.CustomVisibleTag => FontAwesomeIcon.Eye,
TagHandler.CustomAllTag => FontAwesomeIcon.User,
_ => FontAwesomeIcon.Folder
};
ImGui.AlignTextToFramePadding();
_uiSharedService.IconText(icon);
if (RenderCount)
{
using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X / 2f }))
{
ImGui.SameLine();
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("[" + OnlinePairs.ToString() + "]");
}
UiSharedService.AttachToolTip(OnlinePairs + " online" + Environment.NewLine + TotalPairs + " total");
}
ImGui.SameLine();
return ImGui.GetCursorPosX();
}
protected override void DrawMenu(float menuWidth)
{
ImGui.TextUnformatted("Group Menu");
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Users, "Select Pairs", menuWidth, true))
{
_selectPairForTagUi.Open(_id);
}
UiSharedService.AttachToolTip("Select Individual Pairs for this Pair Group");
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Pair Group", menuWidth, true) && UiSharedService.CtrlPressed())
{
_tagHandler.RemoveTag(_id);
}
UiSharedService.AttachToolTip("Hold CTRL to remove this Group permanently." + Environment.NewLine +
"Note: this will not unpair with users in this Group.");
}
protected override void DrawName(float width)
{
ImGui.AlignTextToFramePadding();
string name = _id switch
{
TagHandler.CustomUnpairedTag => "One-sided Individual Pairs",
TagHandler.CustomOnlineTag => "Online / Paused by you",
TagHandler.CustomOfflineTag => "Offline / Paused by other",
TagHandler.CustomOfflineSyncshellTag => "Offline Syncshell Users",
TagHandler.CustomVisibleTag => "Visible",
TagHandler.CustomAllTag => "Users",
_ => _id
};
ImGui.TextUnformatted(name);
}
protected override float DrawRightSide(float currentRightSideX)
{
if (!RenderPause) return currentRightSideX;
var allArePaused = _allPairs.All(pair => pair.UserPair!.OwnPermissions.IsPaused());
var pauseButton = allArePaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
var pauseButtonX = _uiSharedService.GetIconButtonSize(pauseButton).X;
var buttonPauseOffset = currentRightSideX - pauseButtonX;
ImGui.SameLine(buttonPauseOffset);
if (_uiSharedService.IconButton(pauseButton))
{
if (allArePaused)
{
ResumeAllPairs(_allPairs);
}
else
{
PauseRemainingPairs(_allPairs);
}
}
if (allArePaused)
{
UiSharedService.AttachToolTip($"Resume pairing with all pairs in {_id}");
}
else
{
UiSharedService.AttachToolTip($"Pause pairing with all pairs in {_id}");
}
return currentRightSideX;
}
private void PauseRemainingPairs(IEnumerable<Pair> availablePairs)
{
_ = _apiController.SetBulkPermissions(new(availablePairs
.ToDictionary(g => g.UserData.UID, g =>
{
var perm = g.UserPair.OwnPermissions;
perm.SetPaused(paused: true);
return perm;
}, StringComparer.Ordinal), new(StringComparer.Ordinal)))
.ConfigureAwait(false);
}
private void ResumeAllPairs(IEnumerable<Pair> availablePairs)
{
_ = _apiController.SetBulkPermissions(new(availablePairs
.ToDictionary(g => g.UserData.UID, g =>
{
var perm = g.UserPair.OwnPermissions;
perm.SetPaused(paused: false);
return perm;
}, StringComparer.Ordinal), new(StringComparer.Ordinal)))
.ConfigureAwait(false);
}
}

View File

@@ -0,0 +1,79 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using LightlessSync.UI.Handlers;
using System.Collections.Immutable;
using System.Numerics;
namespace LightlessSync.UI.Components;
public class DrawGroupedGroupFolder : IDrawFolder
{
private readonly IEnumerable<IDrawFolder> _groups;
private readonly TagHandler _tagHandler;
private readonly UiSharedService _uiSharedService;
private bool _wasHovered = false;
public IImmutableList<DrawUserPair> DrawPairs => throw new NotSupportedException();
public int OnlinePairs => _groups.SelectMany(g => g.DrawPairs).Where(g => g.Pair.IsOnline).DistinctBy(g => g.Pair.UserData.UID).Count();
public int TotalPairs => _groups.Sum(g => g.TotalPairs);
public DrawGroupedGroupFolder(IEnumerable<IDrawFolder> groups, TagHandler tagHandler, UiSharedService uiSharedService)
{
_groups = groups;
_tagHandler = tagHandler;
_uiSharedService = uiSharedService;
}
public void Draw()
{
if (!_groups.Any()) return;
string _id = "__folder_syncshells";
using var id = ImRaii.PushId(_id);
var color = ImRaii.PushColor(ImGuiCol.ChildBg, ImGui.GetColorU32(ImGuiCol.FrameBgHovered), _wasHovered);
using (ImRaii.Child("folder__" + _id, new System.Numerics.Vector2(UiSharedService.GetWindowContentRegionWidth() - ImGui.GetCursorPosX(), ImGui.GetFrameHeight())))
{
ImGui.Dummy(new Vector2(0f, ImGui.GetFrameHeight()));
using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(0f, 0f)))
ImGui.SameLine();
var icon = _tagHandler.IsTagOpen(_id) ? FontAwesomeIcon.CaretDown : FontAwesomeIcon.CaretRight;
ImGui.AlignTextToFramePadding();
_uiSharedService.IconText(icon);
if (ImGui.IsItemClicked())
{
_tagHandler.SetTagOpen(_id, !_tagHandler.IsTagOpen(_id));
}
ImGui.SameLine();
ImGui.AlignTextToFramePadding();
_uiSharedService.IconText(FontAwesomeIcon.UsersRectangle);
using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X / 2f }))
{
ImGui.SameLine();
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("[" + OnlinePairs.ToString() + "]");
}
UiSharedService.AttachToolTip(OnlinePairs + " online in all of your joined syncshells" + Environment.NewLine +
TotalPairs + " pairs combined in all of your joined syncshells");
ImGui.SameLine();
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("All Syncshells");
}
color.Dispose();
_wasHovered = ImGui.IsItemHovered();
ImGui.Separator();
if (_tagHandler.IsTagOpen(_id))
{
using var indent = ImRaii.PushIndent(20f);
foreach (var entry in _groups)
{
entry.Draw();
}
}
}
}

View File

@@ -0,0 +1,590 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using LightlessSync.API.Data.Extensions;
using LightlessSync.API.Dto.Group;
using LightlessSync.API.Dto.User;
using LightlessSync.MareConfiguration;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services;
using LightlessSync.Services.Mediator;
using LightlessSync.Services.ServerConfiguration;
using LightlessSync.UI.Handlers;
using LightlessSync.WebAPI;
namespace LightlessSync.UI.Components;
public class DrawUserPair
{
protected readonly ApiController _apiController;
protected readonly IdDisplayHandler _displayHandler;
protected readonly MareMediator _mediator;
protected readonly List<GroupFullInfoDto> _syncedGroups;
private readonly GroupFullInfoDto? _currentGroup;
protected Pair _pair;
private readonly string _id;
private readonly SelectTagForPairUi _selectTagForPairUi;
private readonly ServerConfigurationManager _serverConfigurationManager;
private readonly UiSharedService _uiSharedService;
private readonly PlayerPerformanceConfigService _performanceConfigService;
private readonly CharaDataManager _charaDataManager;
private float _menuWidth = -1;
private bool _wasHovered = false;
public DrawUserPair(string id, Pair entry, List<GroupFullInfoDto> syncedGroups,
GroupFullInfoDto? currentGroup,
ApiController apiController, IdDisplayHandler uIDDisplayHandler,
MareMediator mareMediator, SelectTagForPairUi selectTagForPairUi,
ServerConfigurationManager serverConfigurationManager,
UiSharedService uiSharedService, PlayerPerformanceConfigService performanceConfigService,
CharaDataManager charaDataManager)
{
_id = id;
_pair = entry;
_syncedGroups = syncedGroups;
_currentGroup = currentGroup;
_apiController = apiController;
_displayHandler = uIDDisplayHandler;
_mediator = mareMediator;
_selectTagForPairUi = selectTagForPairUi;
_serverConfigurationManager = serverConfigurationManager;
_uiSharedService = uiSharedService;
_performanceConfigService = performanceConfigService;
_charaDataManager = charaDataManager;
}
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();
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");
}
if (_uiSharedService.IconTextButton(FontAwesomeIcon.PlayCircle, "Cycle pause state", _menuWidth, true))
{
_ = _apiController.CyclePauseAsync(_pair.UserData);
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()
{
string userPairText = string.Empty;
ImGui.AlignTextToFramePadding();
if (_pair.IsPaused)
{
using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow);
_uiSharedService.IconText(FontAwesomeIcon.PauseCircle);
userPairText = _pair.UserData.AliasOrUID + " is paused";
}
else if (!_pair.IsOnline)
{
using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed);
_uiSharedService.IconText(_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.OneSided
? FontAwesomeIcon.ArrowsLeftRight
: (_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional
? FontAwesomeIcon.User : FontAwesomeIcon.Users));
userPairText = _pair.UserData.AliasOrUID + " is offline";
}
else if (_pair.IsVisible)
{
_uiSharedService.IconText(FontAwesomeIcon.Eye, ImGuiColors.ParsedGreen);
userPairText = _pair.UserData.AliasOrUID + " is visible: " + _pair.PlayerName + Environment.NewLine + "Click to target this player";
if (ImGui.IsItemClicked())
{
_mediator.Publish(new TargetPairMessage(_pair));
}
}
else
{
using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.HealerGreen);
_uiSharedService.IconText(_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional
? FontAwesomeIcon.User : FontAwesomeIcon.Users);
userPairText = _pair.UserData.AliasOrUID + " is online";
}
if (_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.OneSided)
{
userPairText += UiSharedService.TooltipSeparator + "User has not added you back";
}
else if (_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional)
{
userPairText += UiSharedService.TooltipSeparator + "You are directly Paired";
}
if (_pair.LastAppliedDataBytes >= 0)
{
userPairText += UiSharedService.TooltipSeparator;
userPairText += ((!_pair.IsPaired) ? "(Last) " : string.Empty) + "Mods Info" + Environment.NewLine;
userPairText += "Files Size: " + UiSharedService.ByteToString(_pair.LastAppliedDataBytes, true);
if (_pair.LastAppliedApproximateVRAMBytes >= 0)
{
userPairText += Environment.NewLine + "Approx. VRAM Usage: " + UiSharedService.ByteToString(_pair.LastAppliedApproximateVRAMBytes, true);
}
if (_pair.LastAppliedDataTris >= 0)
{
userPairText += Environment.NewLine + "Approx. Triangle Count (excl. Vanilla): "
+ (_pair.LastAppliedDataTris > 1000 ? (_pair.LastAppliedDataTris / 1000d).ToString("0.0'k'") : _pair.LastAppliedDataTris);
}
}
if (_syncedGroups.Any())
{
userPairText += UiSharedService.TooltipSeparator + string.Join(Environment.NewLine,
_syncedGroups.Select(g =>
{
var groupNote = _serverConfigurationManager.GetNoteForGid(g.GID);
var groupString = string.IsNullOrEmpty(groupNote) ? g.GroupAliasOrGID : $"{groupNote} ({g.GroupAliasOrGID})";
return "Paired through " + groupString;
}));
}
UiSharedService.AttachToolTip(userPairText);
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, ImGuiColors.DalamudYellow);
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();
}
private void DrawName(float leftSide, float rightSide)
{
_displayHandler.DrawPairText(_id, _pair, leftSide, () => rightSide - leftSide);
}
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, ImGuiColors.DalamudYellow, 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)
{
if (selfIsOwner || ((selfIsModerator) && (!userIsModerator)))
{
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");
if (_uiSharedService.IconTextButton(FontAwesomeIcon.UserSlash, "Ban User", _menuWidth, true))
{
_mediator.Publish(new OpenBanUserPopupMessage(_pair, group));
ImGui.CloseCurrentPopup();
}
UiSharedService.AttachToolTip("Ban user from this Syncshell");
ImGui.Separator();
}
if (selfIsOwner)
{
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.");
}
}
}

View File

@@ -0,0 +1,12 @@

using System.Collections.Immutable;
namespace LightlessSync.UI.Components;
public interface IDrawFolder
{
int TotalPairs { get; }
int OnlinePairs { get; }
IImmutableList<DrawUserPair> DrawPairs { get; }
void Draw();
}

View File

@@ -0,0 +1,50 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using LightlessSync.API.Dto.Group;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services.Mediator;
using LightlessSync.WebAPI;
using System.Numerics;
namespace LightlessSync.UI.Components.Popup;
public class BanUserPopupHandler : IPopupHandler
{
private readonly ApiController _apiController;
private readonly UiSharedService _uiSharedService;
private string _banReason = string.Empty;
private GroupFullInfoDto _group = null!;
private Pair _reportedPair = null!;
public BanUserPopupHandler(ApiController apiController, UiSharedService uiSharedService)
{
_apiController = apiController;
_uiSharedService = uiSharedService;
}
public Vector2 PopupSize => new(500, 250);
public bool ShowClose => true;
public void DrawContent()
{
UiSharedService.TextWrapped("User " + (_reportedPair.UserData.AliasOrUID) + " will be banned and removed from this Syncshell.");
ImGui.InputTextWithHint("##banreason", "Ban Reason", ref _banReason, 255);
if (_uiSharedService.IconTextButton(FontAwesomeIcon.UserSlash, "Ban User"))
{
ImGui.CloseCurrentPopup();
var reason = _banReason;
_ = _apiController.GroupBanUser(new GroupPairDto(_group.Group, _reportedPair.UserData), reason);
_banReason = string.Empty;
}
UiSharedService.TextWrapped("The reason will be displayed in the banlist. The current server-side alias if present (Vanity ID) will automatically be attached to the reason.");
}
public void Open(OpenBanUserPopupMessage message)
{
_reportedPair = message.PairToBan;
_group = message.GroupFullInfoDto;
_banReason = string.Empty;
}
}

View File

@@ -0,0 +1,64 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility;
using LightlessSync.Services.ServerConfiguration;
using System.Numerics;
namespace LightlessSync.UI.Components.Popup;
public class CensusPopupHandler : IPopupHandler
{
private readonly ServerConfigurationManager _serverConfigurationManager;
private readonly UiSharedService _uiSharedService;
public CensusPopupHandler(ServerConfigurationManager serverConfigurationManager, UiSharedService uiSharedService)
{
_serverConfigurationManager = serverConfigurationManager;
_uiSharedService = uiSharedService;
}
private Vector2 _size = new(600, 450);
public Vector2 PopupSize => _size;
public bool ShowClose => false;
public void DrawContent()
{
var start = 0f;
using (_uiSharedService.UidFont.Push())
{
start = ImGui.GetCursorPosY() - ImGui.CalcTextSize("Mare Census Data").Y;
UiSharedService.TextWrapped("Mare Census Participation");
}
ImGuiHelpers.ScaledDummy(5f);
UiSharedService.TextWrapped("If you are seeing this popup you are updating from a Mare version that did not collect census data. Please read the following carefully.");
ImGui.Separator();
UiSharedService.TextWrapped("Mare Census is a data collecting service that can be used for statistical purposes. " +
"All data collected through Mare Census is temporary and will be stored associated with your UID on the connected service as long as you are connected. " +
"The data cannot be used for long term tracking of individuals.");
UiSharedService.TextWrapped("If enabled, Mare Census will collect following data:" + Environment.NewLine
+ "- Currently connected World" + Environment.NewLine
+ "- Current Gender (reflecting Glamourer changes)" + Environment.NewLine
+ "- Current Race (reflecting Glamourer changes)" + Environment.NewLine
+ "- Current Clan (i.e. Seeker of the Sun, Keeper of the Moon, etc., reflecting Glamourer changes)");
UiSharedService.TextWrapped("To consent to collecting census data press the appropriate button below.");
UiSharedService.TextWrapped("This setting can be changed anytime in the Mare Settings.");
var width = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X;
var buttonSize = ImGuiHelpers.GetButtonSize("I consent to send my census data");
ImGuiHelpers.ScaledDummy(5f);
if (ImGui.Button("I consent to send my census data", new Vector2(width, buttonSize.Y * 2.5f)))
{
_serverConfigurationManager.SendCensusData = true;
_serverConfigurationManager.ShownCensusPopup = true;
ImGui.CloseCurrentPopup();
}
ImGuiHelpers.ScaledDummy(1f);
if (ImGui.Button("I do not consent to send my census data", new Vector2(width, buttonSize.Y)))
{
_serverConfigurationManager.SendCensusData = false;
_serverConfigurationManager.ShownCensusPopup = true;
ImGui.CloseCurrentPopup();
}
var height = ImGui.GetCursorPosY() - start;
_size = _size with { Y = height };
}
}

View File

@@ -0,0 +1,11 @@
using System.Numerics;
namespace LightlessSync.UI.Components.Popup;
public interface IPopupHandler
{
Vector2 PopupSize { get; }
bool ShowClose { get; }
void DrawContent();
}

View File

@@ -0,0 +1,80 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using LightlessSync.Services;
using LightlessSync.Services.Mediator;
using Microsoft.Extensions.Logging;
using System.Numerics;
namespace LightlessSync.UI.Components.Popup;
public class PopupHandler : WindowMediatorSubscriberBase
{
protected bool _openPopup = false;
private readonly HashSet<IPopupHandler> _handlers;
private readonly UiSharedService _uiSharedService;
private IPopupHandler? _currentHandler = null;
public PopupHandler(ILogger<PopupHandler> logger, MareMediator mediator, IEnumerable<IPopupHandler> popupHandlers,
PerformanceCollectorService performanceCollectorService, UiSharedService uiSharedService)
: base(logger, mediator, "MarePopupHandler", performanceCollectorService)
{
Flags = ImGuiWindowFlags.NoBringToFrontOnFocus
| ImGuiWindowFlags.NoDecoration
| ImGuiWindowFlags.NoInputs
| ImGuiWindowFlags.NoSavedSettings
| ImGuiWindowFlags.NoBackground
| ImGuiWindowFlags.NoMove
| ImGuiWindowFlags.NoNav
| ImGuiWindowFlags.NoTitleBar
| ImGuiWindowFlags.NoFocusOnAppearing;
IsOpen = true;
_handlers = popupHandlers.ToHashSet();
Mediator.Subscribe<OpenBanUserPopupMessage>(this, (msg) =>
{
_openPopup = true;
_currentHandler = _handlers.OfType<BanUserPopupHandler>().Single();
((BanUserPopupHandler)_currentHandler).Open(msg);
IsOpen = true;
});
Mediator.Subscribe<OpenCensusPopupMessage>(this, (msg) =>
{
_openPopup = true;
_currentHandler = _handlers.OfType<CensusPopupHandler>().Single();
IsOpen = true;
});
_uiSharedService = uiSharedService;
DisableWindowSounds = true;
}
protected override void DrawInternal()
{
if (_currentHandler == null) return;
if (_openPopup)
{
ImGui.OpenPopup(WindowName);
_openPopup = false;
}
var viewportSize = ImGui.GetWindowViewport().Size;
ImGui.SetNextWindowSize(_currentHandler!.PopupSize * ImGuiHelpers.GlobalScale);
ImGui.SetNextWindowPos(viewportSize / 2, ImGuiCond.Always, new Vector2(0.5f));
using var popup = ImRaii.Popup(WindowName, ImGuiWindowFlags.Modal);
if (!popup) return;
_currentHandler.DrawContent();
if (_currentHandler.ShowClose)
{
ImGui.Separator();
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Times, "Close"))
{
ImGui.CloseCurrentPopup();
}
}
}
}

View File

@@ -0,0 +1,93 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.UI.Handlers;
using System.Numerics;
namespace LightlessSync.UI.Components;
public class SelectPairForTagUi
{
private readonly TagHandler _tagHandler;
private readonly IdDisplayHandler _uidDisplayHandler;
private string _filter = string.Empty;
private bool _opened = false;
private HashSet<string> _peopleInGroup = new(StringComparer.Ordinal);
private bool _show = false;
private string _tag = string.Empty;
public SelectPairForTagUi(TagHandler tagHandler, IdDisplayHandler uidDisplayHandler)
{
_tagHandler = tagHandler;
_uidDisplayHandler = uidDisplayHandler;
}
public void Draw(List<Pair> pairs)
{
var workHeight = ImGui.GetMainViewport().WorkSize.Y / ImGuiHelpers.GlobalScale;
var minSize = new Vector2(300, workHeight < 400 ? workHeight : 400) * ImGuiHelpers.GlobalScale;
var maxSize = new Vector2(300, 1000) * ImGuiHelpers.GlobalScale;
var popupName = $"Choose Users for Group {_tag}";
if (!_show)
{
_opened = false;
}
if (_show && !_opened)
{
ImGui.SetNextWindowSize(minSize);
UiSharedService.CenterNextWindow(minSize.X, minSize.Y, ImGuiCond.Always);
ImGui.OpenPopup(popupName);
_opened = true;
}
ImGui.SetNextWindowSizeConstraints(minSize, maxSize);
if (ImGui.BeginPopupModal(popupName, ref _show, ImGuiWindowFlags.Popup | ImGuiWindowFlags.Modal))
{
ImGui.TextUnformatted($"Select users for group {_tag}");
ImGui.InputTextWithHint("##filter", "Filter", ref _filter, 255, ImGuiInputTextFlags.None);
foreach (var item in pairs
.Where(p => string.IsNullOrEmpty(_filter) || PairName(p).Contains(_filter, StringComparison.OrdinalIgnoreCase))
.OrderBy(p => PairName(p), StringComparer.OrdinalIgnoreCase)
.ToList())
{
var isInGroup = _peopleInGroup.Contains(item.UserData.UID);
if (ImGui.Checkbox(PairName(item), ref isInGroup))
{
if (isInGroup)
{
_tagHandler.AddTagToPairedUid(item.UserData.UID, _tag);
_peopleInGroup.Add(item.UserData.UID);
}
else
{
_tagHandler.RemoveTagFromPairedUid(item.UserData.UID, _tag);
_peopleInGroup.Remove(item.UserData.UID);
}
}
}
ImGui.EndPopup();
}
else
{
_filter = string.Empty;
_show = false;
}
}
public void Open(string tag)
{
_peopleInGroup = _tagHandler.GetOtherUidsForTag(tag);
_tag = tag;
_show = true;
}
private string PairName(Pair pair)
{
return _uidDisplayHandler.GetPlayerText(pair).text;
}
}

View File

@@ -0,0 +1,140 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Utility;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.UI.Handlers;
using System.Numerics;
namespace LightlessSync.UI.Components;
public class SelectTagForPairUi
{
private readonly TagHandler _tagHandler;
private readonly IdDisplayHandler _uidDisplayHandler;
private readonly UiSharedService _uiSharedService;
/// <summary>
/// The group UI is always open for a specific pair. This defines which pair the UI is open for.
/// </summary>
/// <returns></returns>
private Pair? _pair;
/// <summary>
/// Should the panel show, yes/no
/// </summary>
private bool _show;
/// <summary>
/// For the add category option, this stores the currently typed in tag name
/// </summary>
private string _tagNameToAdd = "";
public SelectTagForPairUi(TagHandler tagHandler, IdDisplayHandler uidDisplayHandler, UiSharedService uiSharedService)
{
_show = false;
_pair = null;
_tagHandler = tagHandler;
_uidDisplayHandler = uidDisplayHandler;
_uiSharedService = uiSharedService;
}
public void Draw()
{
if (_pair == null)
{
return;
}
var name = PairName(_pair);
var popupName = $"Choose Groups for {name}";
// Is the popup supposed to show but did not open yet? Open it
if (_show)
{
ImGui.OpenPopup(popupName);
_show = false;
}
if (ImGui.BeginPopup(popupName))
{
var tags = _tagHandler.GetAllTagsSorted();
var childHeight = tags.Count != 0 ? tags.Count * 25 : 1;
var childSize = new Vector2(0, childHeight > 100 ? 100 : childHeight) * ImGuiHelpers.GlobalScale;
ImGui.TextUnformatted($"Select the groups you want {name} to be in.");
if (ImGui.BeginChild(name + "##listGroups", childSize))
{
foreach (var tag in tags)
{
using (ImRaii.PushId($"groups-pair-{_pair.UserData.UID}-{tag}")) DrawGroupName(_pair, tag);
}
ImGui.EndChild();
}
ImGui.Separator();
ImGui.TextUnformatted($"Create a new group for {name}.");
if (_uiSharedService.IconButton(FontAwesomeIcon.Plus))
{
HandleAddTag();
}
ImGui.SameLine();
ImGui.InputTextWithHint("##category_name", "New Group", ref _tagNameToAdd, 40);
if (ImGui.IsKeyDown(ImGuiKey.Enter))
{
HandleAddTag();
}
ImGui.EndPopup();
}
}
public void Open(Pair pair)
{
_pair = pair;
// Using "_show" here to de-couple the opening of the popup
// The popup name is derived from the name the user currently sees, which is
// based on the showUidForEntry dictionary.
// We'd have to derive the name here to open it popup modal here, when the Open() is called
_show = true;
}
private void DrawGroupName(Pair pair, string name)
{
var hasTagBefore = _tagHandler.HasTag(pair.UserData.UID, name);
var hasTag = hasTagBefore;
if (ImGui.Checkbox(name, ref hasTag))
{
if (hasTag)
{
_tagHandler.AddTagToPairedUid(pair.UserData.UID, name);
}
else
{
_tagHandler.RemoveTagFromPairedUid(pair.UserData.UID, name);
}
}
}
private void HandleAddTag()
{
if (!_tagNameToAdd.IsNullOrWhitespace() && _tagNameToAdd is not (TagHandler.CustomOfflineTag or TagHandler.CustomOnlineTag or TagHandler.CustomVisibleTag))
{
_tagHandler.AddTag(_tagNameToAdd);
if (_pair != null)
{
_tagHandler.AddTagToPairedUid(_pair.UserData.UID, _tagNameToAdd);
}
_tagNameToAdd = string.Empty;
}
else
{
_tagNameToAdd = string.Empty;
}
}
private string PairName(Pair pair)
{
return _uidDisplayHandler.GetPlayerText(pair).text;
}
}