* move stuff out into file transfer manager * obnoxious unsupported version text, adjustments to filetransfermanager * add back file upload transfer progress * restructure code * cleanup some more stuff I guess * downloadids by playername * individual anim/sound bs * fix migration stuff, finalize impl of individual sound/anim pause * fixes with logging stuff * move download manager to transient * rework dl ui first iteration * some refactoring and cleanup * more code cleanup * refactoring * switch to hostbuilder * some more rework I guess * more refactoring * clean up mediator calls and disposal * fun code cleanup * push error message when log level is set to anything but information in non-debug builds * remove notificationservice * move message to after login * add download bars to gameworld * fixes download progress bar * set gpose ui min and max size * remove unnecessary usings * adjustments to reconnection logic * add options to set visible/offline groups visibility * add impl of uploading display, transfer list in settings ui * attempt to fix issues with server selection * add back download status to compact ui * make dl bar fixed size based * some fixes for upload/download handling * adjust text from Syncing back to Uploading --------- Co-authored-by: rootdarkarchon <root.darkarchon@outlook.com> Co-authored-by: Stanley Dimant <stanley.dimant@varian.com>
877 lines
38 KiB
C#
877 lines
38 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.Numerics;
|
|
using System.Reflection;
|
|
using Dalamud.Interface;
|
|
using Dalamud.Interface.Colors;
|
|
using Dalamud.Interface.Components;
|
|
using Dalamud.Utility;
|
|
using ImGuiNET;
|
|
using MareSynchronos.API.Data.Extensions;
|
|
using MareSynchronos.API.Dto.User;
|
|
using MareSynchronos.MareConfiguration;
|
|
using MareSynchronos.PlayerData.Handlers;
|
|
using MareSynchronos.PlayerData.Pairs;
|
|
using MareSynchronos.Services.Mediator;
|
|
using MareSynchronos.Services.ServerConfiguration;
|
|
using MareSynchronos.UI.Components;
|
|
using MareSynchronos.UI.Handlers;
|
|
using MareSynchronos.WebAPI;
|
|
using MareSynchronos.WebAPI.Files;
|
|
using MareSynchronos.WebAPI.Files.Models;
|
|
using MareSynchronos.WebAPI.SignalR.Utils;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace MareSynchronos.UI;
|
|
|
|
public class CompactUi : WindowMediatorSubscriberBase
|
|
{
|
|
public readonly Dictionary<string, bool> ShowUidForEntry = new(StringComparer.Ordinal);
|
|
public string EditNickEntry = string.Empty;
|
|
public string EditUserComment = string.Empty;
|
|
public float TransferPartHeight;
|
|
public float WindowContentWidth;
|
|
private readonly ApiController _apiController;
|
|
private readonly MareConfigService _configService;
|
|
private readonly ConcurrentDictionary<GameObjectHandler, Dictionary<string, FileDownloadStatus>> _currentDownloads = new();
|
|
private readonly FileUploadManager _fileTransferManager;
|
|
private readonly GroupPanel _groupPanel;
|
|
private readonly PairGroupsUi _pairGroupsUi;
|
|
private readonly PairManager _pairManager;
|
|
private readonly SelectGroupForPairUi _selectGroupForPairUi;
|
|
private readonly SelectPairForGroupUi _selectPairsForGroupUi;
|
|
private readonly ServerConfigurationManager _serverManager;
|
|
private readonly Stopwatch _timeout = new();
|
|
private readonly UiSharedService _uiShared;
|
|
private bool _buttonState;
|
|
private string _characterOrCommentFilter = string.Empty;
|
|
private Pair? _lastAddedUser;
|
|
private string _lastAddedUserComment = string.Empty;
|
|
private string _pairToAdd = string.Empty;
|
|
private int _secretKeyIdx = 0;
|
|
private bool _showModalForUserAddition;
|
|
private bool _showSyncShells;
|
|
private bool _wasOpen;
|
|
|
|
public CompactUi(ILogger<CompactUi> logger, UiSharedService uiShared, MareConfigService configService, ApiController apiController, PairManager pairManager,
|
|
ServerConfigurationManager serverManager, MareMediator mediator, FileUploadManager fileTransferManager) : base(logger, mediator, "###MareSynchronosMainUI")
|
|
{
|
|
_uiShared = uiShared;
|
|
_configService = configService;
|
|
_apiController = apiController;
|
|
_pairManager = pairManager;
|
|
_serverManager = serverManager;
|
|
_fileTransferManager = fileTransferManager;
|
|
var tagHandler = new TagHandler(_serverManager);
|
|
|
|
_groupPanel = new(this, uiShared, _pairManager, _serverManager, _configService);
|
|
_selectGroupForPairUi = new(tagHandler);
|
|
_selectPairsForGroupUi = new(tagHandler);
|
|
_pairGroupsUi = new(configService, tagHandler, DrawPairedClient, apiController, _selectPairsForGroupUi);
|
|
|
|
#if DEBUG
|
|
string dev = "Dev Build";
|
|
var ver = Assembly.GetExecutingAssembly().GetName().Version!;
|
|
WindowName = $"Mare Synchronos {dev} ({ver.Major}.{ver.Minor}.{ver.Build})###MareSynchronosMainUI";
|
|
Toggle();
|
|
#else
|
|
var ver = Assembly.GetExecutingAssembly().GetName().Version;
|
|
WindowName = "Mare Synchronos " + ver.Major + "." + ver.Minor + "." + ver.Build + "###MareSynchronosMainUI";
|
|
#endif
|
|
Mediator.Subscribe<SwitchToMainUiMessage>(this, (_) => IsOpen = true);
|
|
Mediator.Subscribe<SwitchToIntroUiMessage>(this, (_) => IsOpen = false);
|
|
Mediator.Subscribe<CutsceneStartMessage>(this, (_) => UiSharedService_GposeStart());
|
|
Mediator.Subscribe<CutsceneEndMessage>(this, (_) => UiSharedService_GposeEnd());
|
|
Mediator.Subscribe<DownloadStartedMessage>(this, (msg) => _currentDownloads[msg.DownloadId] = msg.DownloadStatus);
|
|
Mediator.Subscribe<DownloadFinishedMessage>(this, (msg) => _currentDownloads.TryRemove(msg.DownloadId, out _));
|
|
|
|
SizeConstraints = new WindowSizeConstraints()
|
|
{
|
|
MinimumSize = new Vector2(350, 400),
|
|
MaximumSize = new Vector2(350, 2000),
|
|
};
|
|
}
|
|
|
|
public override void Draw()
|
|
{
|
|
WindowContentWidth = UiSharedService.GetWindowContentRegionWidth();
|
|
if (!_apiController.IsCurrentVersion)
|
|
{
|
|
var ver = _apiController.CurrentClientVersion;
|
|
if (_uiShared.UidFontBuilt) ImGui.PushFont(_uiShared.UidFont);
|
|
var unsupported = "UNSUPPORTED VERSION";
|
|
var uidTextSize = ImGui.CalcTextSize(unsupported);
|
|
ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMax().X + ImGui.GetWindowContentRegionMin().X) / 2 - uidTextSize.X / 2);
|
|
ImGui.TextColored(ImGuiColors.DalamudRed, unsupported);
|
|
if (_uiShared.UidFontBuilt) ImGui.PopFont();
|
|
UiSharedService.ColorTextWrapped($"Your Mare Synchronos installation is out of date, the current version is {ver.Major}.{ver.Minor}.{ver.Build}. " +
|
|
$"It is highly recommended to keep Mare Synchronos up to date. Open /xlplugins and update the plugin.", ImGuiColors.DalamudRed);
|
|
}
|
|
|
|
UiSharedService.DrawWithID("header", DrawUIDHeader);
|
|
ImGui.Separator();
|
|
UiSharedService.DrawWithID("serverstatus", DrawServerStatus);
|
|
|
|
if (_apiController.ServerState is ServerState.Connected)
|
|
{
|
|
var hasShownSyncShells = _showSyncShells;
|
|
|
|
ImGui.PushFont(UiBuilder.IconFont);
|
|
if (!hasShownSyncShells)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Button, ImGui.GetStyle().Colors[(int)ImGuiCol.ButtonHovered]);
|
|
}
|
|
if (ImGui.Button(FontAwesomeIcon.User.ToIconString(), new Vector2((UiSharedService.GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X) / 2, 30 * ImGuiHelpers.GlobalScale)))
|
|
{
|
|
_showSyncShells = false;
|
|
}
|
|
if (!hasShownSyncShells)
|
|
{
|
|
ImGui.PopStyleColor();
|
|
}
|
|
ImGui.PopFont();
|
|
UiSharedService.AttachToolTip("Individual pairs");
|
|
|
|
ImGui.SameLine();
|
|
|
|
ImGui.PushFont(UiBuilder.IconFont);
|
|
if (hasShownSyncShells)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Button, ImGui.GetStyle().Colors[(int)ImGuiCol.ButtonHovered]);
|
|
}
|
|
if (ImGui.Button(FontAwesomeIcon.UserFriends.ToIconString(), new Vector2((UiSharedService.GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X) / 2, 30 * ImGuiHelpers.GlobalScale)))
|
|
{
|
|
_showSyncShells = true;
|
|
}
|
|
if (hasShownSyncShells)
|
|
{
|
|
ImGui.PopStyleColor();
|
|
}
|
|
ImGui.PopFont();
|
|
|
|
UiSharedService.AttachToolTip("Syncshells");
|
|
|
|
ImGui.Separator();
|
|
if (!hasShownSyncShells)
|
|
{
|
|
UiSharedService.DrawWithID("pairlist", DrawPairList);
|
|
}
|
|
else
|
|
{
|
|
UiSharedService.DrawWithID("syncshells", _groupPanel.DrawSyncshells);
|
|
}
|
|
ImGui.Separator();
|
|
UiSharedService.DrawWithID("transfers", DrawTransfers);
|
|
TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight;
|
|
UiSharedService.DrawWithID("group-user-popup", () => _selectPairsForGroupUi.Draw(_pairManager.DirectPairs, ShowUidForEntry));
|
|
UiSharedService.DrawWithID("grouping-popup", () => _selectGroupForPairUi.Draw(ShowUidForEntry));
|
|
}
|
|
|
|
if (_configService.Current.OpenPopupOnAdd && _pairManager.LastAddedUser != null)
|
|
{
|
|
_lastAddedUser = _pairManager.LastAddedUser;
|
|
_pairManager.LastAddedUser = null;
|
|
ImGui.OpenPopup("Set Notes for New User");
|
|
_showModalForUserAddition = true;
|
|
_lastAddedUserComment = string.Empty;
|
|
}
|
|
|
|
if (ImGui.BeginPopupModal("Set Notes for New User", ref _showModalForUserAddition, UiSharedService.PopupWindowFlags))
|
|
{
|
|
if (_lastAddedUser == null)
|
|
{
|
|
_showModalForUserAddition = false;
|
|
}
|
|
else
|
|
{
|
|
UiSharedService.TextWrapped($"You have successfully added {_lastAddedUser.UserData.AliasOrUID}. Set a local note for the user in the field below:");
|
|
ImGui.InputTextWithHint("##noteforuser", $"Note for {_lastAddedUser.UserData.AliasOrUID}", ref _lastAddedUserComment, 100);
|
|
if (UiSharedService.IconTextButton(FontAwesomeIcon.Save, "Save Note"))
|
|
{
|
|
_serverManager.SetNoteForUid(_lastAddedUser.UserData.UID, _lastAddedUserComment);
|
|
_lastAddedUser = null;
|
|
_lastAddedUserComment = string.Empty;
|
|
_showModalForUserAddition = false;
|
|
}
|
|
}
|
|
UiSharedService.SetScaledWindowSize(275);
|
|
ImGui.EndPopup();
|
|
}
|
|
}
|
|
|
|
public override void OnClose()
|
|
{
|
|
EditNickEntry = string.Empty;
|
|
EditUserComment = string.Empty;
|
|
base.OnClose();
|
|
}
|
|
|
|
private void DrawAddCharacter()
|
|
{
|
|
ImGui.Dummy(new(10));
|
|
var keys = _serverManager.CurrentServer!.SecretKeys;
|
|
if (keys.TryGetValue(_secretKeyIdx, out var secretKey))
|
|
{
|
|
var friendlyName = secretKey.FriendlyName;
|
|
|
|
if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Add current character with secret key"))
|
|
{
|
|
_serverManager.CurrentServer!.Authentications.Add(new MareConfiguration.Models.Authentication()
|
|
{
|
|
CharacterName = _uiShared.PlayerName,
|
|
WorldId = _uiShared.WorldId,
|
|
SecretKeyIdx = _secretKeyIdx
|
|
});
|
|
|
|
_serverManager.Save();
|
|
|
|
_ = _apiController.CreateConnections(forceGetToken: true);
|
|
}
|
|
|
|
_uiShared.DrawCombo("Secret Key##addCharacterSecretKey", keys, (f) => f.Value.FriendlyName, (f) => _secretKeyIdx = f.Key);
|
|
}
|
|
else
|
|
{
|
|
UiSharedService.ColorTextWrapped("No secret keys are configured for the current server.", ImGuiColors.DalamudYellow);
|
|
}
|
|
}
|
|
|
|
private void DrawAddPair()
|
|
{
|
|
var buttonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Plus);
|
|
ImGui.SetNextItemWidth(UiSharedService.GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X - buttonSize.X);
|
|
ImGui.InputTextWithHint("##otheruid", "Other players UID/Alias", ref _pairToAdd, 20);
|
|
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - buttonSize.X);
|
|
var canAdd = !_pairManager.DirectPairs.Any(p => string.Equals(p.UserData.UID, _pairToAdd, StringComparison.Ordinal) || string.Equals(p.UserData.Alias, _pairToAdd, StringComparison.Ordinal));
|
|
if (!canAdd)
|
|
{
|
|
ImGuiComponents.DisabledButton(FontAwesomeIcon.Plus);
|
|
}
|
|
else
|
|
{
|
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
|
|
{
|
|
_ = _apiController.UserAddPair(new(new(_pairToAdd)));
|
|
_pairToAdd = string.Empty;
|
|
}
|
|
UiSharedService.AttachToolTip("Pair with " + (_pairToAdd.IsNullOrEmpty() ? "other user" : _pairToAdd));
|
|
}
|
|
|
|
ImGuiHelpers.ScaledDummy(2);
|
|
}
|
|
|
|
private void DrawFilter()
|
|
{
|
|
var buttonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.ArrowUp);
|
|
var playButtonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Play);
|
|
if (!_configService.Current.ReverseUserSort)
|
|
{
|
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.ArrowDown))
|
|
{
|
|
_configService.Current.ReverseUserSort = true;
|
|
_configService.Save();
|
|
}
|
|
UiSharedService.AttachToolTip("Sort by name descending");
|
|
}
|
|
else
|
|
{
|
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.ArrowUp))
|
|
{
|
|
_configService.Current.ReverseUserSort = false;
|
|
_configService.Save();
|
|
}
|
|
UiSharedService.AttachToolTip("Sort by name ascending");
|
|
}
|
|
ImGui.SameLine();
|
|
|
|
var users = GetFilteredUsers();
|
|
var userCount = users.Count;
|
|
|
|
var spacing = userCount > 0
|
|
? playButtonSize.X + ImGui.GetStyle().ItemSpacing.X * 2
|
|
: ImGui.GetStyle().ItemSpacing.X;
|
|
|
|
ImGui.SetNextItemWidth(WindowContentWidth - buttonSize.X - spacing);
|
|
ImGui.InputTextWithHint("##filter", "Filter for UID/notes", ref _characterOrCommentFilter, 255);
|
|
|
|
if (userCount == 0) return;
|
|
|
|
var pausedUsers = users.Where(u => u.UserPair!.OwnPermissions.IsPaused() && u.UserPair.OtherPermissions.IsPaired()).ToList();
|
|
var resumedUsers = users.Where(u => !u.UserPair!.OwnPermissions.IsPaused() && u.UserPair.OtherPermissions.IsPaired()).ToList();
|
|
|
|
if (!pausedUsers.Any() && !resumedUsers.Any()) return;
|
|
ImGui.SameLine();
|
|
|
|
switch (_buttonState)
|
|
{
|
|
case true when !pausedUsers.Any():
|
|
_buttonState = false;
|
|
break;
|
|
|
|
case false when !resumedUsers.Any():
|
|
_buttonState = true;
|
|
break;
|
|
|
|
case true:
|
|
users = pausedUsers;
|
|
break;
|
|
|
|
case false:
|
|
users = resumedUsers;
|
|
break;
|
|
}
|
|
|
|
var button = _buttonState ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
|
|
|
|
if (!_timeout.IsRunning || _timeout.ElapsedMilliseconds > 15000)
|
|
{
|
|
_timeout.Reset();
|
|
|
|
if (ImGuiComponents.IconButton(button) && UiSharedService.CtrlPressed())
|
|
{
|
|
foreach (var entry in users)
|
|
{
|
|
var perm = entry.UserPair!.OwnPermissions;
|
|
perm.SetPaused(!perm.IsPaused());
|
|
_ = _apiController.UserSetPairPermissions(new UserPermissionsDto(entry.UserData, perm));
|
|
}
|
|
|
|
_timeout.Start();
|
|
_buttonState = !_buttonState;
|
|
}
|
|
UiSharedService.AttachToolTip($"Hold Control to {(button == FontAwesomeIcon.Play ? "resume" : "pause")} pairing with {users.Count} out of {userCount} displayed users.");
|
|
}
|
|
else
|
|
{
|
|
var availableAt = (15000 - _timeout.ElapsedMilliseconds) / 1000;
|
|
ImGuiComponents.DisabledButton(button);
|
|
UiSharedService.AttachToolTip($"Next execution is available at {availableAt} seconds");
|
|
}
|
|
}
|
|
|
|
private void DrawPairedClient(Pair entry)
|
|
{
|
|
if (entry.UserPair == null) return;
|
|
|
|
var pauseIcon = entry.UserPair!.OwnPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
|
|
var pauseIconSize = UiSharedService.GetIconButtonSize(pauseIcon);
|
|
var barButtonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars);
|
|
var entryUID = entry.UserData.AliasOrUID;
|
|
var textSize = ImGui.CalcTextSize(entryUID);
|
|
var originalY = ImGui.GetCursorPosY();
|
|
var buttonSizes = pauseIconSize.Y + barButtonSize.Y;
|
|
var spacingX = ImGui.GetStyle().ItemSpacing.X;
|
|
var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth();
|
|
|
|
var textPos = originalY + pauseIconSize.Y / 2 - textSize.Y / 2;
|
|
ImGui.SetCursorPosY(textPos);
|
|
FontAwesomeIcon connectionIcon;
|
|
string connectionText = string.Empty;
|
|
Vector4 connectionColor;
|
|
if (!(entry.UserPair!.OwnPermissions.IsPaired() && entry.UserPair!.OtherPermissions.IsPaired()))
|
|
{
|
|
connectionIcon = FontAwesomeIcon.ArrowUp;
|
|
connectionText = entryUID + " has not added you back";
|
|
connectionColor = ImGuiColors.DalamudRed;
|
|
}
|
|
else if (entry.UserPair!.OwnPermissions.IsPaused() || entry.UserPair!.OtherPermissions.IsPaused())
|
|
{
|
|
connectionIcon = FontAwesomeIcon.PauseCircle;
|
|
connectionText = "Pairing status with " + entryUID + " is paused";
|
|
connectionColor = ImGuiColors.DalamudYellow;
|
|
}
|
|
else
|
|
{
|
|
connectionIcon = FontAwesomeIcon.Check;
|
|
connectionText = "You are paired with " + entryUID;
|
|
connectionColor = ImGuiColors.ParsedGreen;
|
|
}
|
|
|
|
ImGui.PushFont(UiBuilder.IconFont);
|
|
UiSharedService.ColorText(connectionIcon.ToIconString(), connectionColor);
|
|
ImGui.PopFont();
|
|
UiSharedService.AttachToolTip(connectionText);
|
|
ImGui.SameLine();
|
|
ImGui.SetCursorPosY(textPos);
|
|
if (entry is { IsOnline: true, IsVisible: true })
|
|
{
|
|
ImGui.PushFont(UiBuilder.IconFont);
|
|
UiSharedService.ColorText(FontAwesomeIcon.Eye.ToIconString(), ImGuiColors.ParsedGreen);
|
|
ImGui.PopFont();
|
|
UiSharedService.AttachToolTip(entryUID + " is visible: " + entry.PlayerName!);
|
|
}
|
|
|
|
var textIsUid = true;
|
|
ShowUidForEntry.TryGetValue(entry.UserPair!.User.UID, out var showUidInsteadOfName);
|
|
string? playerText = _serverManager.GetNoteForUid(entry.UserPair!.User.UID);
|
|
if (!showUidInsteadOfName && playerText != null)
|
|
{
|
|
if (string.IsNullOrEmpty(playerText))
|
|
{
|
|
playerText = entryUID;
|
|
}
|
|
else
|
|
{
|
|
textIsUid = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
playerText = entryUID;
|
|
}
|
|
|
|
if (_configService.Current.ShowCharacterNameInsteadOfNotesForVisible && entry.IsVisible && !showUidInsteadOfName)
|
|
{
|
|
playerText = entry.PlayerName;
|
|
textIsUid = false;
|
|
}
|
|
|
|
ImGui.SameLine();
|
|
if (!string.Equals(EditNickEntry, entry.UserData.UID, StringComparison.Ordinal))
|
|
{
|
|
ImGui.SetCursorPosY(textPos);
|
|
if (textIsUid) ImGui.PushFont(UiBuilder.MonoFont);
|
|
ImGui.TextUnformatted(playerText);
|
|
if (textIsUid) ImGui.PopFont();
|
|
UiSharedService.AttachToolTip("Left click to switch between UID display and nick" + Environment.NewLine +
|
|
"Right click to change nick for " + entryUID);
|
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
|
{
|
|
var prevState = textIsUid;
|
|
if (ShowUidForEntry.ContainsKey(entry.UserPair!.User.UID))
|
|
{
|
|
prevState = ShowUidForEntry[entry.UserPair!.User.UID];
|
|
}
|
|
|
|
ShowUidForEntry[entry.UserPair!.User.UID] = !prevState;
|
|
}
|
|
|
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
|
{
|
|
var pair = _pairManager.DirectPairs.Find(p => string.Equals(p.UserData.UID, EditNickEntry, StringComparison.Ordinal));
|
|
if (pair != null)
|
|
{
|
|
pair.SetNote(EditUserComment);
|
|
_configService.Save();
|
|
}
|
|
EditUserComment = entry.GetNote() ?? string.Empty;
|
|
EditNickEntry = entry.UserPair!.User.UID;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ImGui.SetCursorPosY(originalY);
|
|
|
|
ImGui.SetNextItemWidth(UiSharedService.GetWindowContentRegionWidth() - ImGui.GetCursorPosX() - buttonSizes - ImGui.GetStyle().ItemSpacing.X * 2);
|
|
if (ImGui.InputTextWithHint("", "Nick/Notes", ref EditUserComment, 255, ImGuiInputTextFlags.EnterReturnsTrue))
|
|
{
|
|
_serverManager.SetNoteForUid(entry.UserPair!.User.UID, EditUserComment);
|
|
EditNickEntry = string.Empty;
|
|
}
|
|
|
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
|
{
|
|
EditNickEntry = string.Empty;
|
|
}
|
|
UiSharedService.AttachToolTip("Hit ENTER to save\nRight click to cancel");
|
|
}
|
|
|
|
// Pause Button && sound warnings
|
|
if (entry.UserPair!.OwnPermissions.IsPaired() && entry.UserPair!.OtherPermissions.IsPaired())
|
|
{
|
|
var individualSoundsDisabled = (entry.UserPair?.OwnPermissions.IsDisableSounds() ?? false) || (entry.UserPair?.OtherPermissions.IsDisableSounds() ?? false);
|
|
var individualAnimDisabled = (entry.UserPair?.OwnPermissions.IsDisableAnimations() ?? false) || (entry.UserPair?.OtherPermissions.IsDisableAnimations() ?? false);
|
|
|
|
if (individualAnimDisabled || individualSoundsDisabled)
|
|
{
|
|
var infoIconPosDist = windowEndX - barButtonSize.X - spacingX - pauseIconSize.X - spacingX;
|
|
var icon = FontAwesomeIcon.ExclamationTriangle;
|
|
var iconwidth = UiSharedService.GetIconSize(icon);
|
|
|
|
ImGui.SameLine(infoIconPosDist - iconwidth.X);
|
|
|
|
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow);
|
|
UiSharedService.FontText(icon.ToIconString(), UiBuilder.IconFont);
|
|
ImGui.PopStyleColor();
|
|
if (ImGui.IsItemHovered())
|
|
{
|
|
ImGui.BeginTooltip();
|
|
|
|
ImGui.Text("Individual User permissions");
|
|
|
|
if (individualSoundsDisabled)
|
|
{
|
|
var userSoundsText = "Sound sync disabled with " + entry.UserData.AliasOrUID;
|
|
UiSharedService.FontText(FontAwesomeIcon.VolumeOff.ToIconString(), UiBuilder.IconFont);
|
|
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
|
ImGui.Text(userSoundsText);
|
|
ImGui.NewLine();
|
|
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
|
ImGui.Text("You: " + (entry.UserPair!.OwnPermissions.IsDisableSounds() ? "Disabled" : "Enabled") + ", They: " + (entry.UserPair!.OtherPermissions.IsDisableSounds() ? "Disabled" : "Enabled"));
|
|
}
|
|
|
|
if (individualAnimDisabled)
|
|
{
|
|
var userAnimText = "Animation sync disabled with " + entry.UserData.AliasOrUID;
|
|
UiSharedService.FontText(FontAwesomeIcon.Stop.ToIconString(), UiBuilder.IconFont);
|
|
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
|
ImGui.Text(userAnimText);
|
|
ImGui.NewLine();
|
|
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
|
ImGui.Text("You: " + (entry.UserPair!.OwnPermissions.IsDisableAnimations() ? "Disabled" : "Enabled") + ", They: " + (entry.UserPair!.OtherPermissions.IsDisableAnimations() ? "Disabled" : "Enabled"));
|
|
}
|
|
|
|
ImGui.EndTooltip();
|
|
}
|
|
}
|
|
|
|
ImGui.SameLine(windowEndX - barButtonSize.X - spacingX - pauseIconSize.X);
|
|
ImGui.SetCursorPosY(originalY);
|
|
if (ImGuiComponents.IconButton(pauseIcon))
|
|
{
|
|
var perm = entry.UserPair!.OwnPermissions;
|
|
perm.SetPaused(!perm.IsPaused());
|
|
_ = _apiController.UserSetPairPermissions(new(entry.UserData, perm));
|
|
}
|
|
UiSharedService.AttachToolTip(!entry.UserPair!.OwnPermissions.IsPaused()
|
|
? "Pause pairing with " + entryUID
|
|
: "Resume pairing with " + entryUID);
|
|
}
|
|
|
|
// Flyout Menu
|
|
ImGui.SameLine(windowEndX - barButtonSize.X);
|
|
ImGui.SetCursorPosY(originalY);
|
|
|
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars))
|
|
{
|
|
ImGui.OpenPopup("User Flyout Menu");
|
|
}
|
|
if (ImGui.BeginPopup("User Flyout Menu"))
|
|
{
|
|
UiSharedService.DrawWithID($"buttons-{entry.UserPair!.User.UID}", () => DrawPairedClientMenu(entry));
|
|
ImGui.EndPopup();
|
|
}
|
|
}
|
|
|
|
private void DrawPairedClientMenu(Pair entry)
|
|
{
|
|
if (entry.IsVisible)
|
|
{
|
|
if (UiSharedService.IconTextButton(FontAwesomeIcon.Sync, "Reload last data"))
|
|
{
|
|
entry.ApplyLastReceivedData(forced: true);
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
UiSharedService.AttachToolTip("This reapplies the last received character data to this character");
|
|
}
|
|
|
|
var entryUID = entry.UserData.AliasOrUID;
|
|
if (UiSharedService.IconTextButton(FontAwesomeIcon.Folder, "Pair Groups"))
|
|
{
|
|
_selectGroupForPairUi.Open(entry);
|
|
}
|
|
UiSharedService.AttachToolTip("Choose pair groups for " + entryUID);
|
|
|
|
var isDisableSounds = entry.UserPair!.OwnPermissions.IsDisableSounds();
|
|
string disableSoundsText = isDisableSounds ? "Enable sound sync" : "Disable sound sync";
|
|
var disableSoundsIcon = isDisableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeMute;
|
|
if (UiSharedService.IconTextButton(disableSoundsIcon, disableSoundsText))
|
|
{
|
|
var permissions = entry.UserPair.OwnPermissions;
|
|
permissions.SetDisableSounds(!isDisableSounds);
|
|
_ = _apiController.UserSetPairPermissions(new UserPermissionsDto(entry.UserData, permissions));
|
|
}
|
|
|
|
var isDisableAnims = entry.UserPair!.OwnPermissions.IsDisableAnimations();
|
|
string disableAnimsText = isDisableAnims ? "Enable animation sync" : "Disable animation sync";
|
|
var disableAnimsIcon = isDisableAnims ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop;
|
|
if (UiSharedService.IconTextButton(disableAnimsIcon, disableAnimsText))
|
|
{
|
|
var permissions = entry.UserPair.OwnPermissions;
|
|
permissions.SetDisableAnimations(!isDisableAnims);
|
|
_ = _apiController.UserSetPairPermissions(new UserPermissionsDto(entry.UserData, permissions));
|
|
}
|
|
|
|
if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Unpair Permanently") && UiSharedService.CtrlPressed())
|
|
{
|
|
_ = _apiController.UserRemovePair(new(entry.UserData));
|
|
}
|
|
UiSharedService.AttachToolTip("Hold CTRL and click to unpair permanently from " + entryUID);
|
|
}
|
|
|
|
private void DrawPairList()
|
|
{
|
|
UiSharedService.DrawWithID("addpair", DrawAddPair);
|
|
UiSharedService.DrawWithID("pairs", DrawPairs);
|
|
TransferPartHeight = ImGui.GetCursorPosY();
|
|
UiSharedService.DrawWithID("filter", DrawFilter);
|
|
}
|
|
|
|
private void DrawPairs()
|
|
{
|
|
var ySize = TransferPartHeight == 0
|
|
? 1
|
|
: (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y) - TransferPartHeight - ImGui.GetCursorPosY();
|
|
var users = GetFilteredUsers()
|
|
.OrderBy(
|
|
u => _configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(u.PlayerName)
|
|
? u.PlayerName
|
|
: (u.GetNote() ?? u.UserData.AliasOrUID), StringComparer.OrdinalIgnoreCase).ToList();
|
|
|
|
if (_configService.Current.ReverseUserSort)
|
|
{
|
|
users.Reverse();
|
|
}
|
|
|
|
var onlineUsers = users.Where(u => u.IsOnline || u.UserPair.OwnPermissions.IsPaused()).ToList();
|
|
var visibleUsers = onlineUsers.Where(u => u.IsVisible).ToList();
|
|
var offlineUsers = users.Except(onlineUsers).ToList();
|
|
|
|
ImGui.BeginChild("list", new Vector2(WindowContentWidth, ySize), border: false);
|
|
|
|
_pairGroupsUi.Draw(visibleUsers, onlineUsers, offlineUsers);
|
|
|
|
ImGui.EndChild();
|
|
}
|
|
|
|
private void DrawServerStatus()
|
|
{
|
|
var buttonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Link);
|
|
var userCount = _apiController.OnlineUsers.ToString(CultureInfo.InvariantCulture);
|
|
var userSize = ImGui.CalcTextSize(userCount);
|
|
var textSize = ImGui.CalcTextSize("Users Online");
|
|
#if DEBUG
|
|
string shardConnection = $"Shard: {_apiController.ServerInfo.ShardName}";
|
|
#else
|
|
string shardConnection = string.Equals(_apiController.ServerInfo.ShardName, "Main", StringComparison.OrdinalIgnoreCase) ? string.Empty : $"Shard: {_apiController.ServerInfo.ShardName}";
|
|
#endif
|
|
var shardTextSize = ImGui.CalcTextSize(shardConnection);
|
|
var printShard = !string.IsNullOrEmpty(_apiController.ServerInfo.ShardName) && shardConnection != string.Empty;
|
|
|
|
if (_apiController.ServerState is ServerState.Connected)
|
|
{
|
|
ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - buttonSize.X) / 2 - (userSize.X + textSize.X) / 2 - ImGui.GetStyle().ItemSpacing.X / 2);
|
|
if (!printShard) ImGui.AlignTextToFramePadding();
|
|
ImGui.TextColored(ImGuiColors.ParsedGreen, userCount);
|
|
ImGui.SameLine();
|
|
if (!printShard) ImGui.AlignTextToFramePadding();
|
|
ImGui.Text("Users Online");
|
|
}
|
|
else
|
|
{
|
|
ImGui.AlignTextToFramePadding();
|
|
ImGui.TextColored(ImGuiColors.DalamudRed, "Not connected to any server");
|
|
}
|
|
|
|
if (printShard)
|
|
{
|
|
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ImGui.GetStyle().ItemSpacing.Y);
|
|
ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - buttonSize.X) / 2 - shardTextSize.X / 2);
|
|
ImGui.TextUnformatted(shardConnection);
|
|
}
|
|
|
|
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - buttonSize.X);
|
|
if (printShard)
|
|
{
|
|
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ((userSize.Y + textSize.Y) / 2 + shardTextSize.Y) / 2 - ImGui.GetStyle().ItemSpacing.Y + buttonSize.Y / 2);
|
|
}
|
|
var color = UiSharedService.GetBoolColor(!_serverManager.CurrentServer!.FullPause);
|
|
var connectedIcon = !_serverManager.CurrentServer.FullPause ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink;
|
|
|
|
if (_apiController.ServerState != ServerState.Reconnecting)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, color);
|
|
if (ImGuiComponents.IconButton(connectedIcon))
|
|
{
|
|
_serverManager.CurrentServer.FullPause = !_serverManager.CurrentServer.FullPause;
|
|
_serverManager.Save();
|
|
_ = _apiController.CreateConnections();
|
|
}
|
|
ImGui.PopStyleColor();
|
|
UiSharedService.AttachToolTip(!_serverManager.CurrentServer.FullPause ? "Disconnect from " + _serverManager.CurrentServer.ServerName : "Connect to " + _serverManager.CurrentServer.ServerName);
|
|
}
|
|
}
|
|
|
|
private void DrawTransfers()
|
|
{
|
|
var currentUploads = _fileTransferManager.CurrentUploads.ToList();
|
|
ImGui.PushFont(UiBuilder.IconFont);
|
|
ImGui.Text(FontAwesomeIcon.Upload.ToIconString());
|
|
ImGui.PopFont();
|
|
ImGui.SameLine(35 * ImGuiHelpers.GlobalScale);
|
|
|
|
if (currentUploads.Any())
|
|
{
|
|
var totalUploads = currentUploads.Count;
|
|
|
|
var doneUploads = currentUploads.Count(c => c.IsTransferred);
|
|
var totalUploaded = currentUploads.Sum(c => c.Transferred);
|
|
var totalToUpload = currentUploads.Sum(c => c.Total);
|
|
|
|
ImGui.Text($"{doneUploads}/{totalUploads}");
|
|
var uploadText = $"({UiSharedService.ByteToString(totalUploaded)}/{UiSharedService.ByteToString(totalToUpload)})";
|
|
var textSize = ImGui.CalcTextSize(uploadText);
|
|
ImGui.SameLine(WindowContentWidth - textSize.X);
|
|
ImGui.Text(uploadText);
|
|
}
|
|
else
|
|
{
|
|
ImGui.Text("No uploads in progress");
|
|
}
|
|
|
|
var currentDownloads = _currentDownloads.SelectMany(d => d.Value.Values).ToList();
|
|
ImGui.PushFont(UiBuilder.IconFont);
|
|
ImGui.Text(FontAwesomeIcon.Download.ToIconString());
|
|
ImGui.PopFont();
|
|
ImGui.SameLine(35 * ImGuiHelpers.GlobalScale);
|
|
|
|
if (currentDownloads.Any())
|
|
{
|
|
var totalDownloads = currentDownloads.Sum(c => c.TotalFiles);
|
|
var doneDownloads = currentDownloads.Sum(c => c.TransferredFiles);
|
|
var totalDownloaded = currentDownloads.Sum(c => c.TransferredBytes);
|
|
var totalToDownload = currentDownloads.Sum(c => c.TotalBytes);
|
|
|
|
ImGui.Text($"{doneDownloads}/{totalDownloads}");
|
|
var downloadText =
|
|
$"({UiSharedService.ByteToString(totalDownloaded)}/{UiSharedService.ByteToString(totalToDownload)})";
|
|
var textSize = ImGui.CalcTextSize(downloadText);
|
|
ImGui.SameLine(WindowContentWidth - textSize.X);
|
|
ImGui.Text(downloadText);
|
|
}
|
|
else
|
|
{
|
|
ImGui.Text("No downloads in progress");
|
|
}
|
|
|
|
ImGui.SameLine();
|
|
}
|
|
|
|
private void DrawUIDHeader()
|
|
{
|
|
var uidText = GetUidText();
|
|
var buttonSizeX = 0f;
|
|
|
|
if (_uiShared.UidFontBuilt) ImGui.PushFont(_uiShared.UidFont);
|
|
var uidTextSize = ImGui.CalcTextSize(uidText);
|
|
if (_uiShared.UidFontBuilt) ImGui.PopFont();
|
|
|
|
var originalPos = ImGui.GetCursorPos();
|
|
ImGui.SetWindowFontScale(1.5f);
|
|
var buttonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Cog);
|
|
buttonSizeX -= buttonSize.X - ImGui.GetStyle().ItemSpacing.X * 2;
|
|
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - buttonSize.X);
|
|
ImGui.SetCursorPosY(originalPos.Y + uidTextSize.Y / 2 - buttonSize.Y / 2);
|
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Cog))
|
|
{
|
|
Mediator.Publish(new OpenSettingsUiMessage());
|
|
}
|
|
UiSharedService.AttachToolTip("Open the Mare Synchronos Settings");
|
|
|
|
ImGui.SameLine(); //Important to draw the uidText consistently
|
|
ImGui.SetCursorPos(originalPos);
|
|
|
|
if (_apiController.ServerState is ServerState.Connected)
|
|
{
|
|
buttonSizeX += UiSharedService.GetIconButtonSize(FontAwesomeIcon.Copy).X - ImGui.GetStyle().ItemSpacing.X * 2;
|
|
ImGui.SetCursorPosY(originalPos.Y + uidTextSize.Y / 2 - buttonSize.Y / 2);
|
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Copy))
|
|
{
|
|
ImGui.SetClipboardText(_apiController.DisplayName);
|
|
}
|
|
UiSharedService.AttachToolTip("Copy your UID to clipboard");
|
|
ImGui.SameLine();
|
|
}
|
|
ImGui.SetWindowFontScale(1f);
|
|
|
|
ImGui.SetCursorPosY(originalPos.Y + buttonSize.Y / 2 - uidTextSize.Y / 2 - ImGui.GetStyle().ItemSpacing.Y / 2);
|
|
ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMax().X + ImGui.GetWindowContentRegionMin().X) / 2 + buttonSizeX - uidTextSize.X / 2);
|
|
if (_uiShared.UidFontBuilt) ImGui.PushFont(_uiShared.UidFont);
|
|
ImGui.TextColored(GetUidColor(), uidText);
|
|
if (_uiShared.UidFontBuilt) ImGui.PopFont();
|
|
|
|
if (_apiController.ServerState is not ServerState.Connected)
|
|
{
|
|
UiSharedService.ColorTextWrapped(GetServerError(), GetUidColor());
|
|
if (_apiController.ServerState is ServerState.NoSecretKey)
|
|
{
|
|
DrawAddCharacter();
|
|
}
|
|
}
|
|
}
|
|
|
|
private List<Pair> GetFilteredUsers()
|
|
{
|
|
return _pairManager.DirectPairs.Where(p =>
|
|
{
|
|
if (_characterOrCommentFilter.IsNullOrEmpty()) return true;
|
|
return p.UserData.AliasOrUID.Contains(_characterOrCommentFilter, StringComparison.OrdinalIgnoreCase) ||
|
|
(p.GetNote()?.Contains(_characterOrCommentFilter, StringComparison.OrdinalIgnoreCase) ?? false) ||
|
|
(p.PlayerName?.Contains(_characterOrCommentFilter, StringComparison.OrdinalIgnoreCase) ?? false);
|
|
}).ToList();
|
|
}
|
|
|
|
private string GetServerError()
|
|
{
|
|
return _apiController.ServerState switch
|
|
{
|
|
ServerState.Connecting => "Attempting to connect to the server.",
|
|
ServerState.Reconnecting => "Connection to server interrupted, attempting to reconnect to the server.",
|
|
ServerState.Disconnected => "You are currently disconnected from the Mare Synchronos server.",
|
|
ServerState.Unauthorized => "Server Response: " + _apiController.AuthFailureMessage,
|
|
ServerState.Offline => "Your selected Mare Synchronos server is currently offline.",
|
|
ServerState.VersionMisMatch =>
|
|
"Your plugin or the server you are connecting to is out of date. Please update your plugin now. If you already did so, contact the server provider to update their server to the latest version.",
|
|
ServerState.RateLimited => "You are rate limited for (re)connecting too often. Disconnect, wait 10 minutes and try again.",
|
|
ServerState.Connected => string.Empty,
|
|
ServerState.NoSecretKey => "You have no secret key set for this current character. Use the button below or open the settings and set a secret key for the current character. You can reuse the same secret key for multiple characters.",
|
|
_ => string.Empty
|
|
};
|
|
}
|
|
|
|
private Vector4 GetUidColor()
|
|
{
|
|
return _apiController.ServerState switch
|
|
{
|
|
ServerState.Connecting => ImGuiColors.DalamudYellow,
|
|
ServerState.Reconnecting => ImGuiColors.DalamudRed,
|
|
ServerState.Connected => ImGuiColors.ParsedGreen,
|
|
ServerState.Disconnected => ImGuiColors.DalamudYellow,
|
|
ServerState.Unauthorized => ImGuiColors.DalamudRed,
|
|
ServerState.VersionMisMatch => ImGuiColors.DalamudRed,
|
|
ServerState.Offline => ImGuiColors.DalamudRed,
|
|
ServerState.RateLimited => ImGuiColors.DalamudYellow,
|
|
ServerState.NoSecretKey => ImGuiColors.DalamudYellow,
|
|
_ => ImGuiColors.DalamudRed
|
|
};
|
|
}
|
|
|
|
private string GetUidText()
|
|
{
|
|
return _apiController.ServerState switch
|
|
{
|
|
ServerState.Reconnecting => "Reconnecting",
|
|
ServerState.Connecting => "Connecting",
|
|
ServerState.Disconnected => "Disconnected",
|
|
ServerState.Unauthorized => "Unauthorized",
|
|
ServerState.VersionMisMatch => "Version mismatch",
|
|
ServerState.Offline => "Unavailable",
|
|
ServerState.RateLimited => "Rate Limited",
|
|
ServerState.NoSecretKey => "No Secret Key",
|
|
ServerState.Connected => _apiController.DisplayName,
|
|
_ => string.Empty
|
|
};
|
|
}
|
|
|
|
private void UiSharedService_GposeEnd()
|
|
{
|
|
IsOpen = _wasOpen;
|
|
}
|
|
|
|
private void UiSharedService_GposeStart()
|
|
{
|
|
_wasOpen = IsOpen;
|
|
IsOpen = false;
|
|
}
|
|
} |