This commit is contained in:
Stanley Dimant
2023-03-20 11:06:25 +01:00
34 changed files with 1496 additions and 739 deletions

Submodule MareAPI updated: 0065dd3cda...22346739d2

View File

@@ -94,5 +94,9 @@ csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
csharp_style_prefer_tuple_swap = true:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion
dotnet_diagnostic.S1075.severity = silent
dotnet_diagnostic.SS3358.severity = true:suggestion
dotnet_diagnostic.MA0007.severity = silent
dotnet_diagnostic.MA0075.severity = silent
dotnet_diagnostic.MA0075.severity = silent
# S3358: Ternary operators should not be nested
dotnet_diagnostic.S3358.severity = suggestion

View File

@@ -66,7 +66,7 @@ public sealed class FileCacheManager : IDisposable
var fullName = fi.FullName.ToLowerInvariant();
if (!fullName.Contains(_configService.Current.CacheFolder.ToLowerInvariant(), StringComparison.Ordinal)) return null;
string prefixedPath = fullName.Replace(_configService.Current.CacheFolder.ToLowerInvariant(), _cachePrefix + "\\", StringComparison.Ordinal).Replace("\\\\", "\\", StringComparison.Ordinal);
return CreateFileCacheEntity(fi, prefixedPath, fi.Name.ToUpper(CultureInfo.InvariantCulture));
return CreateFileCacheEntity(fi, prefixedPath);
}
public FileCacheEntity? CreateFileEntry(string path)
@@ -122,10 +122,13 @@ public sealed class FileCacheManager : IDisposable
return validatedCacheEntry;
}
public void RemoveHash(FileCacheEntity entity)
public void RemoveHash(FileCacheEntity? entity)
{
_logger.LogTrace("Removing {path}", entity.ResolvedFilepath);
_fileCaches.Remove(entity.PrefixedFilePath, out _);
if (entity != null)
{
_logger.LogTrace("Removing {path}", entity.ResolvedFilepath);
_fileCaches.Remove(entity.PrefixedFilePath, out _);
}
}
public string ResolveFileReplacement(string gamePath)

View File

@@ -15,7 +15,7 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase
private readonly PerformanceCollectorService _performanceCollector;
private long _currentFileProgress = 0;
private bool _fileScanWasRunning = false;
private CancellationTokenSource? _scanCancellationTokenSource;
private CancellationTokenSource _scanCancellationTokenSource = new();
private TimeSpan _timeUntilNextScan = TimeSpan.Zero;
public PeriodicFileScanner(ILogger<PeriodicFileScanner> logger, IpcManager ipcManager, MareConfigService configService,
@@ -246,7 +246,7 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase
Logger.LogWarning(ex, "Error during enumerating FileCaches");
}
Task.WaitAll(dbTasks);
Task.WaitAll(dbTasks, _scanCancellationTokenSource.Token);
if (!_ipcManager.CheckPenumbraApi())
{
@@ -308,7 +308,7 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase
if (ct.IsCancellationRequested) return;
}
Task.WaitAll(dbTasks);
Task.WaitAll(dbTasks, _scanCancellationTokenSource.Token);
Logger.LogTrace("Scanner added new files to db");

View File

@@ -8,6 +8,7 @@ using Newtonsoft.Json;
namespace MareSynchronos.MareConfiguration;
#pragma warning disable CS0618 // ignore Obsolete tag, the point of this migrator is to migrate obsolete configs to new ones
#pragma warning disable CS0612 // ignore Obsolete tag, the point of this migrator is to migrate obsolete configs to new ones
public class ConfigurationMigrator : IHostedService
{
@@ -22,7 +23,7 @@ public class ConfigurationMigrator : IHostedService
public void Migrate()
{
if (_pi.GetPluginConfig() is Configuration oldConfig)
if (_pi.GetPluginConfig() is Configurations.Obsolete.Configuration oldConfig)
{
_logger.LogInformation("Migrating Configuration from old config style to 1");
@@ -52,11 +53,15 @@ public class ConfigurationMigrator : IHostedService
{
try
{
var serverConfig = JsonConvert.DeserializeObject<ServerConfigV0>(File.ReadAllText(ConfigurationPath(ServerConfigService.ConfigName)))!;
if (serverConfig.Version == 0)
var content = File.ReadAllText(ConfigurationPath(ServerConfigService.ConfigName));
if (!content.Contains("\"Version\": 1"))
{
MigrateServerConfigV0toV1(serverConfig);
var serverConfig = JsonConvert.DeserializeObject<ServerConfigV0>(content);
if (serverConfig != null && serverConfig.Version == 0)
{
MigrateServerConfigV0toV1(serverConfig);
}
}
}
catch (Exception ex)
@@ -92,7 +97,6 @@ public class ConfigurationMigrator : IHostedService
MareConfig mareConfigV1 = mareConfigV0.ToV1();
int i = 0;
var serverConfig = new ServerConfig()
{
ServerStorage = mareConfigV0.ServerStorage.Select(p => p.Value.ToV1()).ToList()
@@ -152,4 +156,5 @@ public class ConfigurationMigrator : IHostedService
}
}
#pragma warning restore CS0612 // ignore Obsolete tag, the point of this migrator is to migrate obsolete configs to new ones
#pragma warning restore CS0618 // ignore Obsolete tag, the point of this migrator is to migrate obsolete configs to new ones

View File

@@ -10,6 +10,7 @@ public class MareConfig : IMareConfiguration
public string CacheFolder { get; set; } = string.Empty;
public bool DisableOptionalPluginWarnings { get; set; } = false;
public NotificationLocation ErrorNotification { get; set; } = NotificationLocation.Both;
public string ExportFolder { get; set; } = string.Empty;
public bool FileScanPaused { get; set; } = false;
public NotificationLocation InfoNotification { get; set; } = NotificationLocation.Toast;
public bool InitialScanComplete { get; set; } = false;
@@ -19,6 +20,9 @@ public class MareConfig : IMareConfiguration
public bool OpenGposeImportOnGposeStart { get; set; } = false;
public bool OpenPopupOnAdd { get; set; } = true;
public int ParallelDownloads { get; set; } = 10;
public float ProfileDelay { get; set; } = 2;
public bool ProfilesAllowNsfw { get; set; } = false;
public bool ProfilesShow { get; set; } = true;
public bool ReverseUserSort { get; set; } = false;
public bool ShowCharacterNameInsteadOfNotesForVisible { get; set; } = false;
public bool ShowOfflineUsersSeparately { get; set; } = true;

View File

@@ -4,7 +4,7 @@ using MareSynchronos.WebAPI;
namespace MareSynchronos.MareConfiguration.Configurations.Obsolete;
[Serializable]
[Obsolete]
[Obsolete("Replaced with ServerConfig")]
public class ServerConfigV0 : IMareConfiguration
{
public string CurrentServer { get; set; } = string.Empty;
@@ -15,4 +15,4 @@ public class ServerConfigV0 : IMareConfiguration
};
public int Version { get; set; } = 0;
}
}

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<Authors></Authors>
<Company></Company>
<Version>0.8.8</Version>
<Version>0.8.10</Version>
<Description></Description>
<Copyright></Copyright>
<PackageProjectUrl>https://github.com/Penumbra-Sync/client</PackageProjectUrl>
@@ -37,6 +37,7 @@
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Penumbra.Api" Version="1.0.7" />
<PackageReference Include="Penumbra.String" Version="1.0.3" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.0" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.54.0.64047">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -84,8 +84,8 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
public IntPtr Address { get; set; }
public unsafe Character* Character => (Character*)Address;
public Lazy<Dalamud.Game.ClientState.Objects.Types.GameObject?> GameObjectLazy { get; private set; }
public IntPtr CurrentAddress => _getAddress.Invoke();
public Lazy<Dalamud.Game.ClientState.Objects.Types.GameObject?> GameObjectLazy { get; private set; }
public string Name { get; private set; }
public ObjectKind ObjectKind { get; }
private byte[] CustomizeData { get; set; } = new byte[26];
@@ -101,6 +101,8 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
{
curPtr = _getAddress.Invoke();
if (curPtr == IntPtr.Zero) return true;
var drawObj = GetDrawObj(curPtr);
return IsBeingDrawn(drawObj, curPtr);
}

View File

@@ -158,21 +158,29 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
_charaHandler?.Dispose();
_charaHandler = null;
if (!_lifetime.ApplicationStopping.IsCancellationRequested && !_dalamudUtil.IsZoning && !_dalamudUtil.IsInCutscene)
if (!_lifetime.ApplicationStopping.IsCancellationRequested)
{
Logger.LogTrace("[{applicationId}] Restoring state for {name} ({OnlineUser})", applicationId, name, OnlineUser);
_ipcManager.PenumbraRemoveTemporaryCollection(Logger, applicationId, name);
foreach (KeyValuePair<ObjectKind, List<FileReplacementData>> item in _cachedData.FileReplacements)
if (_dalamudUtil.IsZoning)
{
try
Logger.LogTrace("[{applicationId}] Removing temp collection for {name} ({OnlineUser})", applicationId, name, OnlineUser);
_ipcManager.PenumbraRemoveTemporaryCollection(Logger, applicationId, name);
}
else if (!_dalamudUtil.IsZoning && !_dalamudUtil.IsInCutscene)
{
Logger.LogTrace("[{applicationId}] Restoring state for {name} ({OnlineUser})", applicationId, name, OnlineUser);
_ipcManager.PenumbraRemoveTemporaryCollection(Logger, applicationId, name);
foreach (KeyValuePair<ObjectKind, List<FileReplacementData>> item in _cachedData.FileReplacements)
{
RevertCustomizationData(item.Key, name, applicationId).GetAwaiter().GetResult();
}
catch (InvalidOperationException ex)
{
Logger.LogWarning("Failed disposing player (not present anymore?)", ex);
break;
try
{
RevertCustomizationData(item.Key, name, applicationId).GetAwaiter().GetResult();
}
catch (InvalidOperationException ex)
{
Logger.LogWarning("Failed disposing player (not present anymore?)", ex);
break;
}
}
}
}
@@ -408,7 +416,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
while ((!_applicationTask?.IsCompleted ?? false) && !downloadToken.IsCancellationRequested && !_applicationCancellationTokenSource.IsCancellationRequested)
{
// block until current application is done
Logger.LogDebug("Waiting for current data application (Id: {id}) to finish", _applicationId);
Logger.LogDebug("Waiting for current data application (Id: {id}) for player ({handler}) to finish", _applicationId, PlayerName);
await Task.Delay(250).ConfigureAwait(false);
}
@@ -419,22 +427,34 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
var token = _applicationCancellationTokenSource.Token;
_applicationTask = Task.Run(async () =>
{
_applicationId = Guid.NewGuid();
Logger.LogDebug("[{applicationId}] Starting application task", _applicationId);
if (updateModdedPaths && (moddedPaths.Any() || !string.IsNullOrEmpty(charaData.ManipulationData)))
try
{
await ApplyBaseData(_applicationId, moddedPaths, charaData.ManipulationData, token).ConfigureAwait(false);
_applicationId = Guid.NewGuid();
Logger.LogDebug("[{applicationId}] Starting application task", _applicationId);
Logger.LogDebug("[{applicationId}] Waiting for initial draw for for {handler}", _applicationId, _charaHandler);
await _dalamudUtil.WaitWhileCharacterIsDrawing(Logger, _charaHandler, _applicationId, 30000, token).ConfigureAwait(false);
token.ThrowIfCancellationRequested();
if (updateModdedPaths && (moddedPaths.Any() || !string.IsNullOrEmpty(charaData.ManipulationData)))
{
await ApplyBaseData(_applicationId, moddedPaths, charaData.ManipulationData, token).ConfigureAwait(false);
}
token.ThrowIfCancellationRequested();
foreach (var kind in updatedData)
{
await ApplyCustomizationData(_applicationId, kind, charaData, token).ConfigureAwait(false);
}
Logger.LogDebug("[{applicationId}] Application finished", _applicationId);
}
token.ThrowIfCancellationRequested();
foreach (var kind in updatedData)
catch (Exception ex)
{
await ApplyCustomizationData(_applicationId, kind, charaData, token).ConfigureAwait(false);
Logger.LogInformation("[{applicationId}] Cancelled, reason: {msg}", _applicationId, ex.Message);
}
Logger.LogDebug("[{applicationId}] Application finished", _applicationId);
}, token);
}, downloadToken);
}

View File

@@ -99,6 +99,7 @@ public sealed class PairManager : DisposableMediatorSubscriberBase
{
if (_allClientPairs.TryGetValue(user, out var pair))
{
Mediator.Publish(new ClearProfileDataMessage(pair.UserData));
pair.MarkOffline();
RecreateLazy();
}
@@ -107,6 +108,9 @@ public sealed class PairManager : DisposableMediatorSubscriberBase
public void MarkPairOnline(OnlineUserIdentDto dto, bool sendNotif = true)
{
if (!_allClientPairs.ContainsKey(dto.User)) throw new InvalidOperationException("No user found for " + dto);
Mediator.Publish(new ClearProfileDataMessage(dto.User));
var pair = _allClientPairs[dto.User];
if (pair.HasCachedPlayer) return;
@@ -213,6 +217,12 @@ public sealed class PairManager : DisposableMediatorSubscriberBase
if (pair.UserPair == null) throw new InvalidOperationException("No direct pair for " + dto);
if (pair.UserPair.OtherPermissions.IsPaused() != dto.Permissions.IsPaused()
|| pair.UserPair.OtherPermissions.IsPaired() != dto.Permissions.IsPaired())
{
Mediator.Publish(new ClearProfileDataMessage(dto.User));
}
pair.UserPair.OtherPermissions = dto.Permissions;
Logger.LogTrace("Paired: {synced}, Paused: {paused}, Anims: {anims}, Sounds: {sounds}",
@@ -229,6 +239,12 @@ public sealed class PairManager : DisposableMediatorSubscriberBase
if (pair.UserPair == null) throw new InvalidOperationException("No direct pair for " + dto);
if (pair.UserPair.OwnPermissions.IsPaused() != dto.Permissions.IsPaused()
|| pair.UserPair.OwnPermissions.IsPaired() != dto.Permissions.IsPaired())
{
Mediator.Publish(new ClearProfileDataMessage(dto.User));
}
pair.UserPair.OwnPermissions = dto.Permissions;
Logger.LogTrace("Paired: {synced}, Paused: {paused}, Anims: {anims}, Sounds: {sounds}",
@@ -326,13 +342,14 @@ public sealed class PairManager : DisposableMediatorSubscriberBase
private void DisposePairs(bool recreate = false)
{
Logger.LogDebug("Disposing all Pairs");
foreach (var item in _allClientPairs)
Parallel.ForEach(_allClientPairs, item =>
{
if (recreate)
item.Value.RecreateCachedPlayer();
else
item.Value.MarkOffline();
}
});
RecreateLazy();
}

View File

@@ -22,6 +22,7 @@ using MareSynchronos.Services;
using MareSynchronos.Services.Mediator;
using MareSynchronos.Services.ServerConfiguration;
using MareSynchronos.UI;
using MareSynchronos.UI.Handlers;
using MareSynchronos.WebAPI;
using MareSynchronos.WebAPI.Files;
using MareSynchronos.WebAPI.SignalR;
@@ -65,6 +66,11 @@ public sealed class Plugin : IDalamudPlugin
collection.AddSingleton<FileUploadManager>();
collection.AddSingleton<FileTransferOrchestrator>();
collection.AddSingleton<MarePlugin>();
collection.AddSingleton<MareProfileManager>();
collection.AddSingleton<UidDisplayHandler>((s) => new UidDisplayHandler(s.GetRequiredService<ILogger<UidDisplayHandler>>(), pluginInterface.UiBuilder,
s.GetRequiredService<MareProfileManager>(),
s.GetRequiredService<UiSharedService>(), s.GetRequiredService<PairManager>(),
s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<MareConfigService>()));
collection.AddSingleton((s) => new DalamudUtilService(s.GetRequiredService<ILogger<DalamudUtilService>>(),
clientState, objectTable, framework, gameGui, condition, gameData,
s.GetRequiredService<MareMediator>(), s.GetRequiredService<PerformanceCollectorService>()));
@@ -117,6 +123,9 @@ public sealed class Plugin : IDalamudPlugin
collection.AddScoped<WindowMediatorSubscriberBase, GposeUi>();
collection.AddScoped<WindowMediatorSubscriberBase, IntroUi>();
collection.AddScoped<WindowMediatorSubscriberBase, DownloadUi>();
collection.AddScoped<WindowMediatorSubscriberBase, EditProfileUi>((s) => new EditProfileUi(s.GetRequiredService<ILogger<EditProfileUi>>(),
s.GetRequiredService<MareMediator>(), s.GetRequiredService<ApiController>(), pluginInterface.UiBuilder, s.GetRequiredService<UiSharedService>(),
s.GetRequiredService<FileDialogManager>(), s.GetRequiredService<MareProfileManager>()));
collection.AddScoped<CacheCreationService>();
collection.AddScoped<TransientResourceManager>();
collection.AddScoped<PlayerDataFactory>();

View File

@@ -55,6 +55,12 @@ public sealed class CommandManagerService : IDisposable
if (string.Equals(splitArgs[0], "toggle", StringComparison.OrdinalIgnoreCase))
{
if (_apiController.ServerState == WebAPI.SignalR.Utils.ServerState.Disconnecting)
{
_mediator.Publish(new NotificationMessage("Mare disconnecting", "Cannot use /toggle while Mare Synchronos is still disconnecting",
Dalamud.Interface.Internal.Notifications.NotificationType.Error));
}
if (_serverConfigurationManager.CurrentServer == null) return;
var fullPause = splitArgs.Length > 1 ? splitArgs[1] switch
{

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,6 @@
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Interface.Internal.Notifications;
using MareSynchronos.API.Data;
using MareSynchronos.API.Dto;
using MareSynchronos.PlayerData.Handlers;
using MareSynchronos.WebAPI.Files.Models;
@@ -54,5 +55,6 @@ public record DownloadStartedMessage(GameObjectHandler DownloadId, Dictionary<st
public record DownloadFinishedMessage(GameObjectHandler DownloadId) : IMessage;
public record UiToggleMessage(Type UiType) : IMessage;
public record PlayerUploadingMessage(GameObjectHandler Handler, bool IsUploading) : IMessage;
public record ClearProfileDataMessage(UserData? UserData = null) : IMessage;
#pragma warning restore MA0048 // File name must match type name

View File

@@ -23,17 +23,29 @@ public class ServerConfigurationManager
_serverTagConfig = serverTagConfig;
_notesConfig = notesConfig;
_dalamudUtil = dalamudUtil;
if (_configService.Current.CurrentServer < 0)
{
_configService.Current.CurrentServer = 0;
_configService.Save();
}
}
public string CurrentApiUrl => CurrentServer.ServerUri;
public ServerStorage CurrentServer => _configService.Current.ServerStorage[CurrentServerIndex];
public int CurrentServerIndex => _configService.Current.CurrentServer;
public int CurrentServerIndex
{
set
{
_configService.Current.CurrentServer = value;
_configService.Save();
}
get
{
if (_configService.Current.CurrentServer < 0)
{
_configService.Current.CurrentServer = 0;
_configService.Save();
}
return _configService.Current.CurrentServer;
}
}
public string? GetSecretKey(int serverIdx = -1)
{

View File

@@ -27,9 +27,6 @@ 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;
@@ -43,6 +40,7 @@ public class CompactUi : WindowMediatorSubscriberBase
private readonly SelectPairForGroupUi _selectPairsForGroupUi;
private readonly ServerConfigurationManager _serverManager;
private readonly Stopwatch _timeout = new();
private readonly UidDisplayHandler _uidDisplayHandler;
private readonly UiSharedService _uiShared;
private bool _buttonState;
private string _characterOrCommentFilter = string.Empty;
@@ -55,7 +53,7 @@ public class CompactUi : WindowMediatorSubscriberBase
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")
ServerConfigurationManager serverManager, MareMediator mediator, FileUploadManager fileTransferManager, UidDisplayHandler uidDisplayHandler) : base(logger, mediator, "###MareSynchronosMainUI")
{
_uiShared = uiShared;
_configService = configService;
@@ -63,12 +61,13 @@ public class CompactUi : WindowMediatorSubscriberBase
_pairManager = pairManager;
_serverManager = serverManager;
_fileTransferManager = fileTransferManager;
_uidDisplayHandler = uidDisplayHandler;
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);
_groupPanel = new(this, uiShared, _pairManager, uidDisplayHandler, _serverManager);
_selectGroupForPairUi = new(tagHandler, uidDisplayHandler);
_selectPairsForGroupUi = new(tagHandler, uidDisplayHandler);
_pairGroupsUi = new(configService, tagHandler, apiController, _selectPairsForGroupUi);
#if DEBUG
string dev = "Dev Build";
@@ -164,8 +163,8 @@ public class CompactUi : WindowMediatorSubscriberBase
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));
UiSharedService.DrawWithID("group-user-popup", () => _selectPairsForGroupUi.Draw(_pairManager.DirectPairs));
UiSharedService.DrawWithID("grouping-popup", () => _selectGroupForPairUi.Draw());
}
if (_configService.Current.OpenPopupOnAdd && _pairManager.LastAddedUser != null)
@@ -202,8 +201,7 @@ public class CompactUi : WindowMediatorSubscriberBase
public override void OnClose()
{
EditNickEntry = string.Empty;
EditUserComment = string.Empty;
_uidDisplayHandler.Clear();
base.OnClose();
}
@@ -213,8 +211,6 @@ public class CompactUi : WindowMediatorSubscriberBase
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()
@@ -350,210 +346,6 @@ public class CompactUi : WindowMediatorSubscriberBase
}
}
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)
@@ -624,9 +416,9 @@ public class CompactUi : WindowMediatorSubscriberBase
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();
var onlineUsers = users.Where(u => u.IsOnline || u.UserPair!.OwnPermissions.IsPaused()).Select(c => new DrawUserPair("Online" + c.UserData.UID, c, _uidDisplayHandler, _apiController, _selectGroupForPairUi)).ToList();
var visibleUsers = users.Where(u => u.IsVisible).Select(c => new DrawUserPair("Visible" + c.UserData.UID, c, _uidDisplayHandler, _apiController, _selectGroupForPairUi)).ToList();
var offlineUsers = users.Where(u => !u.IsOnline && !u.UserPair!.OwnPermissions.IsPaused()).Select(c => new DrawUserPair("Offline" + c.UserData.UID, c, _uidDisplayHandler, _apiController, _selectGroupForPairUi)).ToList();
ImGui.BeginChild("list", new Vector2(WindowContentWidth, ySize), border: false);
@@ -651,7 +443,7 @@ public class CompactUi : WindowMediatorSubscriberBase
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);
ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth()) / 2 - (userSize.X + textSize.X) / 2 - ImGui.GetStyle().ItemSpacing.X / 2);
if (!printShard) ImGui.AlignTextToFramePadding();
ImGui.TextColored(ImGuiColors.ParsedGreen, userCount);
ImGui.SameLine();
@@ -667,17 +459,33 @@ public class CompactUi : WindowMediatorSubscriberBase
if (printShard)
{
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ImGui.GetStyle().ItemSpacing.Y);
ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - buttonSize.X) / 2 - shardTextSize.X / 2);
ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth()) / 2 - shardTextSize.X / 2);
ImGui.TextUnformatted(shardConnection);
}
ImGui.SameLine();
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 is ServerState.Connected)
{
ImGui.SetCursorPosX(0 + ImGui.GetStyle().ItemSpacing.X);
if (ImGuiComponents.IconButton(FontAwesomeIcon.UserCircle))
{
Mediator.Publish(new UiToggleMessage(typeof(EditProfileUi)));
}
UiSharedService.AttachToolTip("Edit your Mare Profile");
}
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 is not (ServerState.Reconnecting or ServerState.Disconnecting))
{

View File

@@ -0,0 +1,308 @@
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface;
using ImGuiNET;
using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.API.Data.Extensions;
using MareSynchronos.WebAPI;
using MareSynchronos.API.Dto.User;
using MareSynchronos.UI.Handlers;
using MareSynchronos.API.Dto.Group;
using MareSynchronos.API.Data.Enum;
namespace MareSynchronos.UI.Components;
public class DrawGroupPair : DrawPairBase
{
private readonly GroupPairFullInfoDto _fullInfoDto;
private readonly GroupFullInfoDto _group;
private string _banReason = string.Empty;
private bool _banUserPopupOpen;
private bool _showModalBanUser;
public DrawGroupPair(string id, Pair entry, ApiController apiController, GroupFullInfoDto group, GroupPairFullInfoDto fullInfoDto, UidDisplayHandler handler) : base(id, entry, apiController, handler)
{
_group = group;
_fullInfoDto = fullInfoDto;
}
protected override void DrawLeftSide(float textPosY, float originalY)
{
var entryUID = _pair.UserData.AliasOrUID;
var entryIsMod = _fullInfoDto.GroupPairStatusInfo.IsModerator();
var entryIsOwner = string.Equals(_pair.UserData.UID, _group.OwnerUID, StringComparison.Ordinal);
var entryIsPinned = _fullInfoDto.GroupPairStatusInfo.IsPinned();
var presenceIcon = _pair.IsVisible ? FontAwesomeIcon.Eye : (_pair.IsOnline ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink);
var presenceColor = (_pair.IsOnline || _pair.IsVisible) ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed;
var presenceText = entryUID + " is offline";
ImGui.SetCursorPosY(textPosY);
if (_pair.IsPaused)
{
presenceIcon = FontAwesomeIcon.Question;
presenceColor = ImGuiColors.DalamudGrey;
presenceText = entryUID + " online status is unknown (paused)";
ImGui.PushFont(UiBuilder.IconFont);
UiSharedService.ColorText(FontAwesomeIcon.PauseCircle.ToIconString(), ImGuiColors.DalamudYellow);
ImGui.PopFont();
UiSharedService.AttachToolTip("Pairing status with " + entryUID + " is paused");
}
else
{
ImGui.PushFont(UiBuilder.IconFont);
UiSharedService.ColorText(FontAwesomeIcon.Check.ToIconString(), ImGuiColors.ParsedGreen);
ImGui.PopFont();
UiSharedService.AttachToolTip("You are paired with " + entryUID);
}
if (_pair.IsOnline && !_pair.IsVisible) presenceText = entryUID + " is online";
else if (_pair.IsOnline && _pair.IsVisible) presenceText = entryUID + " is visible: " + _pair.PlayerName;
ImGui.SameLine();
ImGui.SetCursorPosY(textPosY);
ImGui.PushFont(UiBuilder.IconFont);
UiSharedService.ColorText(presenceIcon.ToIconString(), presenceColor);
ImGui.PopFont();
UiSharedService.AttachToolTip(presenceText);
if (entryIsOwner)
{
ImGui.SameLine();
ImGui.SetCursorPosY(textPosY);
ImGui.PushFont(UiBuilder.IconFont);
ImGui.TextUnformatted(FontAwesomeIcon.Crown.ToIconString());
ImGui.PopFont();
UiSharedService.AttachToolTip("User is owner of this Syncshell");
}
else if (entryIsMod)
{
ImGui.SameLine();
ImGui.SetCursorPosY(textPosY);
ImGui.PushFont(UiBuilder.IconFont);
ImGui.TextUnformatted(FontAwesomeIcon.UserShield.ToIconString());
ImGui.PopFont();
UiSharedService.AttachToolTip("User is moderator of this Syncshell");
}
else if (entryIsPinned)
{
ImGui.SameLine();
ImGui.SetCursorPosY(textPosY);
ImGui.PushFont(UiBuilder.IconFont);
ImGui.TextUnformatted(FontAwesomeIcon.Thumbtack.ToIconString());
ImGui.PopFont();
UiSharedService.AttachToolTip("User is pinned in this Syncshell");
}
}
protected override float DrawRightSide(float textPosY, float originalY)
{
var entryUID = _fullInfoDto.UserAliasOrUID;
var entryIsMod = _fullInfoDto.GroupPairStatusInfo.IsModerator();
var entryIsOwner = string.Equals(_pair.UserData.UID, _group.OwnerUID, StringComparison.Ordinal);
var entryIsPinned = _fullInfoDto.GroupPairStatusInfo.IsPinned();
var userIsOwner = string.Equals(_group.OwnerUID, _apiController.UID, StringComparison.OrdinalIgnoreCase);
var userIsModerator = _group.GroupUserInfo.IsModerator();
var soundsDisabled = _fullInfoDto.GroupUserPermissions.IsDisableSounds();
var animDisabled = _fullInfoDto.GroupUserPermissions.IsDisableAnimations();
var individualSoundsDisabled = (_pair.UserPair?.OwnPermissions.IsDisableSounds() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableSounds() ?? false);
var individualAnimDisabled = (_pair.UserPair?.OwnPermissions.IsDisableAnimations() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableAnimations() ?? false);
bool showInfo = (individualAnimDisabled || individualSoundsDisabled || animDisabled || soundsDisabled);
bool showPlus = _pair.UserPair == null;
bool showBars = (userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner)) || !_pair.IsPaused;
var spacing = ImGui.GetStyle().ItemSpacing.X;
var permIcon = (individualAnimDisabled || individualSoundsDisabled) ? FontAwesomeIcon.ExclamationTriangle
: ((soundsDisabled || animDisabled) ? FontAwesomeIcon.InfoCircle : FontAwesomeIcon.None);
var infoIconWidth = UiSharedService.GetIconSize(permIcon).X;
var plusButtonWidth = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Plus).X;
var barButtonWidth = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars).X;
var pos = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() + spacing
- (showInfo ? (infoIconWidth + spacing) : 0)
- (showPlus ? (plusButtonWidth + spacing) : 0)
- (showBars ? (barButtonWidth + spacing) : 0);
ImGui.SameLine(pos);
if (individualAnimDisabled || individualSoundsDisabled)
{
ImGui.SetCursorPosY(textPosY);
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow);
UiSharedService.FontText(permIcon.ToIconString(), UiBuilder.IconFont);
ImGui.PopStyleColor();
if (ImGui.IsItemHovered())
{
ImGui.BeginTooltip();
ImGui.Text("Individual User permissions");
if (individualSoundsDisabled)
{
var userSoundsText = "Sound sync disabled with " + _pair.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: " + (_pair.UserPair!.OwnPermissions.IsDisableSounds() ? "Disabled" : "Enabled") + ", They: " + (_pair.UserPair!.OtherPermissions.IsDisableSounds() ? "Disabled" : "Enabled"));
}
if (individualAnimDisabled)
{
var userAnimText = "Animation sync disabled with " + _pair.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: " + (_pair.UserPair!.OwnPermissions.IsDisableAnimations() ? "Disabled" : "Enabled") + ", They: " + (_pair.UserPair!.OtherPermissions.IsDisableAnimations() ? "Disabled" : "Enabled"));
}
ImGui.EndTooltip();
}
ImGui.SameLine();
}
else if ((animDisabled || soundsDisabled))
{
ImGui.SetCursorPosY(textPosY);
UiSharedService.FontText(permIcon.ToIconString(), UiBuilder.IconFont);
if (ImGui.IsItemHovered())
{
ImGui.BeginTooltip();
ImGui.Text("Sycnshell User permissions");
if (soundsDisabled)
{
var userSoundsText = "Sound sync disabled by " + _pair.UserData.AliasOrUID;
UiSharedService.FontText(FontAwesomeIcon.VolumeOff.ToIconString(), UiBuilder.IconFont);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.Text(userSoundsText);
}
if (animDisabled)
{
var userAnimText = "Animation sync disabled by " + _pair.UserData.AliasOrUID;
UiSharedService.FontText(FontAwesomeIcon.Stop.ToIconString(), UiBuilder.IconFont);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.Text(userAnimText);
}
ImGui.EndTooltip();
}
ImGui.SameLine();
}
if (showPlus)
{
ImGui.SetCursorPosY(originalY);
if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
{
_ = _apiController.UserAddPair(new UserDto(new(_pair.UserData.UID)));
}
UiSharedService.AttachToolTip("Pair with " + entryUID + " individually");
ImGui.SameLine();
}
if (showBars)
{
ImGui.SetCursorPosY(originalY);
if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars))
{
ImGui.OpenPopup("Popup");
}
}
if (ImGui.BeginPopup("Popup"))
{
if ((userIsModerator || userIsOwner) && !(entryIsMod || entryIsOwner))
{
var pinText = entryIsPinned ? "Unpin user" : "Pin user";
if (UiSharedService.IconTextButton(FontAwesomeIcon.Thumbtack, pinText))
{
ImGui.CloseCurrentPopup();
var userInfo = _fullInfoDto.GroupPairStatusInfo ^ GroupUserInfo.IsPinned;
_ = _apiController.GroupSetUserInfo(new GroupPairUserInfoDto(_fullInfoDto.Group, _fullInfoDto.User, 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") && UiSharedService.CtrlPressed())
{
ImGui.CloseCurrentPopup();
_ = _apiController.GroupRemoveUser(_fullInfoDto);
}
UiSharedService.AttachToolTip("Hold CTRL and click to remove user " + (_pair.UserData.AliasOrUID) + " from Syncshell");
if (UiSharedService.IconTextButton(FontAwesomeIcon.UserSlash, "Ban User"))
{
_showModalBanUser = true;
ImGui.CloseCurrentPopup();
}
UiSharedService.AttachToolTip("Ban user from this Syncshell");
}
if (userIsOwner)
{
string modText = entryIsMod ? "Demod user" : "Mod user";
if (UiSharedService.IconTextButton(FontAwesomeIcon.UserShield, modText) && UiSharedService.CtrlPressed())
{
ImGui.CloseCurrentPopup();
var userInfo = _fullInfoDto.GroupPairStatusInfo ^ GroupUserInfo.IsModerator;
_ = _apiController.GroupSetUserInfo(new GroupPairUserInfoDto(_fullInfoDto.Group, _fullInfoDto.User, userInfo));
}
UiSharedService.AttachToolTip("Hold CTRL to change the moderator status for " + (_fullInfoDto.UserAliasOrUID) + Environment.NewLine +
"Moderators can kick, ban/unban, pin/unpin users and clear the Syncshell.");
if (UiSharedService.IconTextButton(FontAwesomeIcon.Crown, "Transfer Ownership") && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed())
{
ImGui.CloseCurrentPopup();
_ = _apiController.GroupChangeOwnership(_fullInfoDto);
}
UiSharedService.AttachToolTip("Hold CTRL and SHIFT and click to transfer ownership of this Syncshell to " + (_fullInfoDto.UserAliasOrUID) + Environment.NewLine + "WARNING: This action is irreversible.");
}
ImGui.Separator();
if (!_pair.IsPaused)
{
if (UiSharedService.IconTextButton(FontAwesomeIcon.ExclamationTriangle, "Report Mare Profile"))
{
ImGui.CloseCurrentPopup();
_showModalReport = true;
}
UiSharedService.AttachToolTip("Report this users Mare Profile to the administrative team");
}
ImGui.EndPopup();
}
if (_showModalBanUser && !_banUserPopupOpen)
{
ImGui.OpenPopup("Ban User");
_banUserPopupOpen = true;
}
if (!_showModalBanUser) _banUserPopupOpen = false;
if (ImGui.BeginPopupModal("Ban User", ref _showModalBanUser, UiSharedService.PopupWindowFlags))
{
UiSharedService.TextWrapped("User " + (_fullInfoDto.UserAliasOrUID) + " will be banned and removed from this Syncshell.");
ImGui.InputTextWithHint("##banreason", "Ban Reason", ref _banReason, 255);
if (ImGui.Button("Ban User"))
{
ImGui.CloseCurrentPopup();
var reason = _banReason;
_ = _apiController.GroupBanUser(new GroupPairDto(_group.Group, _fullInfoDto.User), 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.");
UiSharedService.SetScaledWindowSize(300);
ImGui.EndPopup();
}
return pos - spacing;
}
}

View File

@@ -0,0 +1,84 @@
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using ImGuiNET;
using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.UI.Handlers;
using MareSynchronos.WebAPI;
namespace MareSynchronos.UI.Components;
public abstract class DrawPairBase
{
protected static bool _showModalReport = false;
protected readonly ApiController _apiController;
protected Pair _pair;
private static bool _reportPopupOpen = false;
private static string _reportReason = string.Empty;
private readonly UidDisplayHandler _displayHandler;
private readonly string _id;
protected DrawPairBase(string id, Pair entry, ApiController apiController, UidDisplayHandler uIDDisplayHandler)
{
_id = id;
_pair = entry;
_apiController = apiController;
_displayHandler = uIDDisplayHandler;
}
public string UID => _pair.UserData.UID;
public void DrawPairedClient()
{
var originalY = ImGui.GetCursorPosY();
var pauseIconSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Play);
var textSize = ImGui.CalcTextSize(_pair.UserData.AliasOrUID);
var textPosY = originalY + pauseIconSize.Y / 2 - textSize.Y / 2;
DrawLeftSide(textPosY, originalY);
ImGui.SameLine();
var posX = ImGui.GetCursorPosX();
var rightSide = DrawRightSide(textPosY, originalY);
DrawName(originalY, posX, rightSide);
if (_showModalReport && !_reportPopupOpen)
{
ImGui.OpenPopup("Report Profile");
_reportPopupOpen = true;
}
if (!_showModalReport) _reportPopupOpen = false;
if (ImGui.BeginPopupModal("Report Profile", ref _showModalReport, UiSharedService.PopupWindowFlags))
{
UiSharedService.TextWrapped("Report " + (_pair.UserData.AliasOrUID) + " Mare Profile");
ImGui.InputTextMultiline("##reportReason", ref _reportReason, 500, new System.Numerics.Vector2(500 - ImGui.GetStyle().ItemSpacing.X * 2, 200));
UiSharedService.TextWrapped($"Note: Sending a report will disable the offending profile globally.{Environment.NewLine}" +
$"The report will be sent to the team of your currently connected Mare Synchronos Service.{Environment.NewLine}" +
$"The report will include your user and your contact info (Discord User).{Environment.NewLine}" +
$"Depending on the severity of the offense the users Mare profile or account can be permanently disabled or banned.");
UiSharedService.ColorTextWrapped("Report spam and wrong reports will not be tolerated and can lead to permanent account suspension.", ImGuiColors.DalamudRed);
if (string.IsNullOrEmpty(_reportReason)) ImGui.BeginDisabled();
if (ImGui.Button("Send Report"))
{
ImGui.CloseCurrentPopup();
var reason = _reportReason;
_ = _apiController.UserReportProfile(new(_pair.UserData, reason));
_reportReason = string.Empty;
_showModalReport = false;
_reportPopupOpen = false;
}
if (string.IsNullOrEmpty(_reportReason)) ImGui.EndDisabled();
UiSharedService.SetScaledWindowSize(500);
ImGui.EndPopup();
}
}
protected abstract void DrawLeftSide(float textPosY, float originalY);
protected abstract float DrawRightSide(float textPosY, float originalY);
private void DrawName(float originalY, float leftSide, float rightSide)
{
_displayHandler.DrawPairText(_id, _pair, leftSide, originalY, () => rightSide - leftSide);
}
}

View File

@@ -0,0 +1,222 @@
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface;
using ImGuiNET;
using MareSynchronos.PlayerData.Pairs;
using System.Numerics;
using MareSynchronos.API.Data.Extensions;
using MareSynchronos.WebAPI;
using MareSynchronos.API.Dto.User;
using MareSynchronos.UI.Handlers;
namespace MareSynchronos.UI.Components;
public class DrawUserPair : DrawPairBase
{
private readonly SelectGroupForPairUi _selectGroupForPairUi;
public DrawUserPair(string id, Pair entry, UidDisplayHandler displayHandler, ApiController apiController, SelectGroupForPairUi selectGroupForPairUi) : base(id, entry, apiController, displayHandler)
{
if (_pair.UserPair == null) throw new ArgumentException("Pair must be UserPair", nameof(entry));
_pair = entry;
_selectGroupForPairUi = selectGroupForPairUi;
}
public bool IsOnline => _pair.IsOnline;
public bool IsVisible => _pair.IsVisible;
public UserPairDto UserPair => _pair.UserPair!;
protected override void DrawLeftSide(float textPos, float originalY)
{
FontAwesomeIcon connectionIcon;
Vector4 connectionColor;
string connectionText;
if (!(_pair.UserPair!.OwnPermissions.IsPaired() && _pair.UserPair!.OtherPermissions.IsPaired()))
{
connectionIcon = FontAwesomeIcon.ArrowUp;
connectionText = _pair.UserData.AliasOrUID + " has not added you back";
connectionColor = ImGuiColors.DalamudRed;
}
else if (_pair.UserPair!.OwnPermissions.IsPaused() || _pair.UserPair!.OtherPermissions.IsPaused())
{
connectionIcon = FontAwesomeIcon.PauseCircle;
connectionText = "Pairing status with " + _pair.UserData.AliasOrUID + " is paused";
connectionColor = ImGuiColors.DalamudYellow;
}
else
{
connectionIcon = FontAwesomeIcon.Check;
connectionText = "You are paired with " + _pair.UserData.AliasOrUID;
connectionColor = ImGuiColors.ParsedGreen;
}
ImGui.SetCursorPosY(textPos);
ImGui.PushFont(UiBuilder.IconFont);
UiSharedService.ColorText(connectionIcon.ToIconString(), connectionColor);
ImGui.PopFont();
UiSharedService.AttachToolTip(connectionText);
if (_pair is { IsOnline: true, IsVisible: true })
{
ImGui.SameLine();
ImGui.SetCursorPosY(textPos);
ImGui.PushFont(UiBuilder.IconFont);
UiSharedService.ColorText(FontAwesomeIcon.Eye.ToIconString(), ImGuiColors.ParsedGreen);
ImGui.PopFont();
UiSharedService.AttachToolTip(_pair.UserData.AliasOrUID + " is visible: " + _pair.PlayerName!);
}
}
protected override float DrawRightSide(float textPosY, float originalY)
{
var pauseIcon = _pair.UserPair!.OwnPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
var pauseIconSize = UiSharedService.GetIconButtonSize(pauseIcon);
var barButtonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars);
var entryUID = _pair.UserData.AliasOrUID;
var spacingX = ImGui.GetStyle().ItemSpacing.X;
var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth();
var rightSideStart = 0f;
if (_pair.UserPair!.OwnPermissions.IsPaired() && _pair.UserPair!.OtherPermissions.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);
if (individualAnimDisabled || individualSoundsDisabled)
{
var infoIconPosDist = windowEndX - barButtonSize.X - spacingX - pauseIconSize.X - spacingX;
var icon = FontAwesomeIcon.ExclamationTriangle;
var iconwidth = UiSharedService.GetIconSize(icon);
rightSideStart = infoIconPosDist - iconwidth.X;
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 " + _pair.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: " + (_pair.UserPair!.OwnPermissions.IsDisableSounds() ? "Disabled" : "Enabled") + ", They: " + (_pair.UserPair!.OtherPermissions.IsDisableSounds() ? "Disabled" : "Enabled"));
}
if (individualAnimDisabled)
{
var userAnimText = "Animation sync disabled with " + _pair.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: " + (_pair.UserPair!.OwnPermissions.IsDisableAnimations() ? "Disabled" : "Enabled") + ", They: " + (_pair.UserPair!.OtherPermissions.IsDisableAnimations() ? "Disabled" : "Enabled"));
}
ImGui.EndTooltip();
}
}
if (rightSideStart == 0f)
{
rightSideStart = windowEndX - barButtonSize.X - spacingX * 2 - pauseIconSize.X;
}
ImGui.SameLine(windowEndX - barButtonSize.X - spacingX - pauseIconSize.X);
ImGui.SetCursorPosY(originalY);
if (ImGuiComponents.IconButton(pauseIcon))
{
var perm = _pair.UserPair!.OwnPermissions;
perm.SetPaused(!perm.IsPaused());
_ = _apiController.UserSetPairPermissions(new(_pair.UserData, perm));
}
UiSharedService.AttachToolTip(!_pair.UserPair!.OwnPermissions.IsPaused()
? "Pause pairing with " + entryUID
: "Resume pairing with " + entryUID);
}
// Flyout Menu
if (rightSideStart == 0f)
{
rightSideStart = windowEndX - barButtonSize.X;
}
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-{_pair.UserData.UID}", () => DrawPairedClientMenu(_pair));
ImGui.EndPopup();
}
return rightSideStart;
}
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);
ImGui.Separator();
if (!entry.IsPaused)
{
if (UiSharedService.IconTextButton(FontAwesomeIcon.ExclamationTriangle, "Report Mare Profile"))
{
ImGui.CloseCurrentPopup();
_showModalReport = true;
}
UiSharedService.AttachToolTip("Report this users Mare Profile to the administrative team");
}
}
}

View File

@@ -1,5 +1,4 @@
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Components;
using Dalamud.Interface;
using Dalamud.Utility;
using ImGuiNET;
@@ -8,28 +7,26 @@ using System.Numerics;
using System.Globalization;
using MareSynchronos.API.Data;
using MareSynchronos.API.Dto.Group;
using MareSynchronos.API.Dto.User;
using MareSynchronos.API.Data.Enum;
using MareSynchronos.API.Data.Extensions;
using MareSynchronos.API.Data.Comparer;
using MareSynchronos.MareConfiguration;
using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.Services.ServerConfiguration;
using MareSynchronos.UI.Components;
using MareSynchronos.UI.Handlers;
namespace MareSynchronos.UI;
internal sealed class GroupPanel
{
private readonly MareConfigService _configService;
private readonly Dictionary<string, bool> _expandedGroupState = new(StringComparer.Ordinal);
private readonly CompactUi _mainUi;
private readonly PairManager _pairManager;
private readonly ServerConfigurationManager _serverConfigurationManager;
private readonly Dictionary<string, bool> _showGidForEntry = new(StringComparer.Ordinal);
private readonly UidDisplayHandler _uidDisplayHandler;
private readonly UiSharedService _uiShared;
private List<BannedGroupUserDto> _bannedUsers = new();
private string _banReason = string.Empty;
private bool _banUserPopupOpen;
private int _bulkInviteCount = 10;
private List<string> _bulkOneTimeInvites = new();
private string _editGroupComment = string.Empty;
@@ -43,7 +40,6 @@ internal sealed class GroupPanel
private bool _modalChangePwOpened;
private string _newSyncShellPassword = string.Empty;
private bool _showModalBanList = false;
private bool _showModalBanUser;
private bool _showModalBulkOneTimeInvites = false;
private bool _showModalChangePassword;
private bool _showModalCreateGroup;
@@ -51,13 +47,13 @@ internal sealed class GroupPanel
private string _syncShellPassword = string.Empty;
private string _syncShellToJoin = string.Empty;
public GroupPanel(CompactUi mainUi, UiSharedService uiShared, PairManager pairManager, ServerConfigurationManager serverConfigurationManager, MareConfigService configurationService)
public GroupPanel(CompactUi mainUi, UiSharedService uiShared, PairManager pairManager, UidDisplayHandler uidDisplayHandler, ServerConfigurationManager serverConfigurationManager)
{
_mainUi = mainUi;
_uiShared = uiShared;
_pairManager = pairManager;
_uidDisplayHandler = uidDisplayHandler;
_serverConfigurationManager = serverConfigurationManager;
_configService = configurationService;
}
private ApiController ApiController => _uiShared.ApiController;
@@ -406,18 +402,24 @@ internal sealed class GroupPanel
.ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsModerator())
.ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned())
.ThenBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase)
.Select(c => new DrawGroupPair(groupDto.GID + c.UserData.UID, c, ApiController, groupDto, c.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value,
_uidDisplayHandler))
.ToList();
var onlineUsers = pairsInGroup.Where(u => u.IsOnline && !u.IsVisible)
.OrderByDescending(u => string.Equals(u.UserData.UID, groupDto.OwnerUID, StringComparison.Ordinal))
.ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsModerator())
.ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned())
.ThenBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase)
.Select(c => new DrawGroupPair(groupDto.GID + c.UserData.UID, c, ApiController, groupDto, c.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value,
_uidDisplayHandler))
.ToList();
var offlineUsers = pairsInGroup.Where(u => !u.IsOnline && !u.IsVisible)
.OrderByDescending(u => string.Equals(u.UserData.UID, groupDto.OwnerUID, StringComparison.Ordinal))
.ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsModerator())
.ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned())
.ThenBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase)
.Select(c => new DrawGroupPair(groupDto.GID + c.UserData.UID, c, ApiController, groupDto, c.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value,
_uidDisplayHandler))
.ToList();
if (visibleUsers.Any())
@@ -426,12 +428,7 @@ internal sealed class GroupPanel
ImGui.Separator();
foreach (var entry in visibleUsers)
{
UiSharedService.DrawWithID(groupDto.GID + entry.UserData.UID, () => DrawSyncshellPairedClient(
entry,
entry.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value,
groupDto.OwnerUID,
string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal),
groupDto.GroupUserInfo.IsModerator()));
UiSharedService.DrawWithID(groupDto.GID + entry.UID, () => entry.DrawPairedClient());
}
}
@@ -441,12 +438,7 @@ internal sealed class GroupPanel
ImGui.Separator();
foreach (var entry in onlineUsers)
{
UiSharedService.DrawWithID(groupDto.GID + entry.UserData.UID, () => DrawSyncshellPairedClient(
entry,
entry.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value,
groupDto.OwnerUID,
string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal),
groupDto.GroupUserInfo.IsModerator()));
UiSharedService.DrawWithID(groupDto.GID + entry.UID, () => entry.DrawPairedClient());
}
}
@@ -456,12 +448,7 @@ internal sealed class GroupPanel
ImGui.Separator();
foreach (var entry in offlineUsers)
{
UiSharedService.DrawWithID(groupDto.GID + entry.UserData.UID, () => DrawSyncshellPairedClient(
entry,
entry.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value,
groupDto.OwnerUID,
string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal),
groupDto.GroupUserInfo.IsModerator()));
UiSharedService.DrawWithID(groupDto.GID + entry.UID, () => entry.DrawPairedClient());
}
}
@@ -724,337 +711,4 @@ internal sealed class GroupPanel
}
ImGui.EndChild();
}
private void DrawSyncshellPairedClient(Pair pair, GroupPairFullInfoDto entry, string ownerUid, bool userIsOwner, bool userIsModerator)
{
var plusButtonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Plus);
var barButtonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars);
var entryUID = entry.UserAliasOrUID;
var textSize = ImGui.CalcTextSize(entryUID);
var originalY = ImGui.GetCursorPosY();
var entryIsMod = entry.GroupPairStatusInfo.IsModerator();
var entryIsOwner = string.Equals(pair.UserData.UID, ownerUid, StringComparison.Ordinal);
var entryIsPinned = entry.GroupPairStatusInfo.IsPinned();
var presenceIcon = pair.IsVisible ? FontAwesomeIcon.Eye : (pair.IsOnline ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink);
var presenceColor = (pair.IsOnline || pair.IsVisible) ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed;
var presenceText = entryUID + " is offline";
var soundsDisabled = entry.GroupUserPermissions.IsDisableSounds();
var animDisabled = entry.GroupUserPermissions.IsDisableAnimations();
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 textPos = originalY + barButtonSize.Y / 2 - textSize.Y / 2;
ImGui.SetCursorPosY(textPos);
if (pair.IsPaused)
{
presenceIcon = FontAwesomeIcon.Question;
presenceColor = ImGuiColors.DalamudGrey;
presenceText = entryUID + " online status is unknown (paused)";
ImGui.PushFont(UiBuilder.IconFont);
UiSharedService.ColorText(FontAwesomeIcon.PauseCircle.ToIconString(), ImGuiColors.DalamudYellow);
ImGui.PopFont();
UiSharedService.AttachToolTip("Pairing status with " + entryUID + " is paused");
}
else
{
ImGui.PushFont(UiBuilder.IconFont);
UiSharedService.ColorText(FontAwesomeIcon.Check.ToIconString(), ImGuiColors.ParsedGreen);
ImGui.PopFont();
UiSharedService.AttachToolTip("You are paired with " + entryUID);
}
if (pair.IsOnline && !pair.IsVisible) presenceText = entryUID + " is online";
else if (pair.IsOnline && pair.IsVisible) presenceText = entryUID + " is visible: " + pair.PlayerName;
ImGui.SameLine();
ImGui.SetCursorPosY(textPos);
ImGui.PushFont(UiBuilder.IconFont);
UiSharedService.ColorText(presenceIcon.ToIconString(), presenceColor);
ImGui.PopFont();
UiSharedService.AttachToolTip(presenceText);
if (entryIsOwner)
{
ImGui.SameLine();
ImGui.SetCursorPosY(textPos);
ImGui.PushFont(UiBuilder.IconFont);
ImGui.TextUnformatted(FontAwesomeIcon.Crown.ToIconString());
ImGui.PopFont();
UiSharedService.AttachToolTip("User is owner of this Syncshell");
}
else if (entryIsMod)
{
ImGui.SameLine();
ImGui.SetCursorPosY(textPos);
ImGui.PushFont(UiBuilder.IconFont);
ImGui.TextUnformatted(FontAwesomeIcon.UserShield.ToIconString());
ImGui.PopFont();
UiSharedService.AttachToolTip("User is moderator of this Syncshell");
}
else if (entryIsPinned)
{
ImGui.SameLine();
ImGui.SetCursorPosY(textPos);
ImGui.PushFont(UiBuilder.IconFont);
ImGui.TextUnformatted(FontAwesomeIcon.Thumbtack.ToIconString());
ImGui.PopFont();
UiSharedService.AttachToolTip("User is pinned in this Syncshell");
}
var textIsUid = true;
_mainUi.ShowUidForEntry.TryGetValue(entry.UID, out var showUidInsteadOfName);
var playerText = _serverConfigurationManager.GetNoteForUid(entry.UID);
if (showUidInsteadOfName || string.IsNullOrEmpty(playerText))
{
playerText = entryUID;
}
else
{
textIsUid = false;
}
if (_configService.Current.ShowCharacterNameInsteadOfNotesForVisible && pair.IsVisible && !showUidInsteadOfName)
{
playerText = pair.PlayerName;
textIsUid = false;
}
bool plusButtonShown = !_pairManager.DirectPairs.Any(p => string.Equals(p.UserData.UID, entry.UID, StringComparison.Ordinal));
ImGui.SameLine();
if (!string.Equals(_mainUi.EditNickEntry, entry.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 (_mainUi.ShowUidForEntry.ContainsKey(entry.UID))
{
prevState = _mainUi.ShowUidForEntry[entry.UID];
}
_mainUi.ShowUidForEntry[entry.UID] = !prevState;
}
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
_serverConfigurationManager.SetNoteForUid(_mainUi.EditNickEntry, _mainUi.EditUserComment);
_mainUi.EditUserComment = _serverConfigurationManager.GetNoteForUid(entry.UID) ?? string.Empty;
_mainUi.EditNickEntry = entry.UID;
}
}
else
{
ImGui.SetCursorPosY(originalY);
var buttonSizes = (plusButtonShown ? plusButtonSize.X : 0) + barButtonSize.X;
var buttons = plusButtonShown ? 2 : 1;
ImGui.SetNextItemWidth(UiSharedService.GetWindowContentRegionWidth() - ImGui.GetCursorPosX() - buttonSizes - ImGui.GetStyle().ItemSpacing.X * buttons);
if (ImGui.InputTextWithHint("", "Nick/Notes", ref _mainUi.EditUserComment, 255, ImGuiInputTextFlags.EnterReturnsTrue))
{
_serverConfigurationManager.SetNoteForUid(entry.UID, _mainUi.EditUserComment);
_mainUi.EditNickEntry = string.Empty;
}
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
_mainUi.EditNickEntry = string.Empty;
}
UiSharedService.AttachToolTip("Hit ENTER to save\nRight click to cancel");
}
if (plusButtonShown)
{
var barWidth = userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner)
? barButtonSize.X + ImGui.GetStyle().ItemSpacing.X
: 0;
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - plusButtonSize.X - barWidth);
ImGui.SetCursorPosY(originalY);
if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
{
_ = ApiController.UserAddPair(new UserDto(entry.User));
}
UiSharedService.AttachToolTip("Pair with " + entryUID + " individually");
}
if (userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner))
{
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - barButtonSize.X);
ImGui.SetCursorPosY(originalY);
if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars))
{
ImGui.OpenPopup("Popup");
}
}
if (individualAnimDisabled || individualSoundsDisabled)
{
var infoIconPosDist = (plusButtonShown ? plusButtonSize.X + ImGui.GetStyle().ItemSpacing.X : 0)
+ ((userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner)) ? barButtonSize.X + ImGui.GetStyle().ItemSpacing.X : 0);
var icon = FontAwesomeIcon.ExclamationTriangle;
var iconwidth = UiSharedService.GetIconSize(icon);
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - 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 " + pair.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: " + (pair.UserPair!.OwnPermissions.IsDisableSounds() ? "Disabled" : "Enabled") + ", They: " + (pair.UserPair!.OtherPermissions.IsDisableSounds() ? "Disabled" : "Enabled"));
}
if (individualAnimDisabled)
{
var userAnimText = "Animation sync disabled with " + pair.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: " + (pair.UserPair!.OwnPermissions.IsDisableAnimations() ? "Disabled" : "Enabled") + ", They: " + (pair.UserPair!.OtherPermissions.IsDisableAnimations() ? "Disabled" : "Enabled"));
}
ImGui.EndTooltip();
}
}
else if ((animDisabled || soundsDisabled))
{
var infoIconPosDist = (plusButtonShown ? plusButtonSize.X + ImGui.GetStyle().ItemSpacing.X : 0)
+ ((userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner)) ? barButtonSize.X + ImGui.GetStyle().ItemSpacing.X : 0);
var icon = FontAwesomeIcon.InfoCircle;
var iconwidth = UiSharedService.GetIconSize(icon);
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - infoIconPosDist - iconwidth.X);
ImGui.SetCursorPosY(originalY);
UiSharedService.FontText(icon.ToIconString(), UiBuilder.IconFont);
if (ImGui.IsItemHovered())
{
ImGui.BeginTooltip();
ImGui.Text("Sycnshell User permissions");
if (soundsDisabled)
{
var userSoundsText = "Sound sync disabled by " + pair.UserData.AliasOrUID;
UiSharedService.FontText(FontAwesomeIcon.VolumeOff.ToIconString(), UiBuilder.IconFont);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.Text(userSoundsText);
}
if (animDisabled)
{
var userAnimText = "Animation sync disabled by " + pair.UserData.AliasOrUID;
UiSharedService.FontText(FontAwesomeIcon.Stop.ToIconString(), UiBuilder.IconFont);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.Text(userAnimText);
}
ImGui.EndTooltip();
}
}
if (!plusButtonShown && !(userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner)))
{
ImGui.SameLine();
ImGui.Dummy(barButtonSize with { X = 0 });
}
if (ImGui.BeginPopup("Popup"))
{
if ((userIsModerator || userIsOwner) && !(entryIsMod || entryIsOwner))
{
var pinText = entryIsPinned ? "Unpin user" : "Pin user";
if (UiSharedService.IconTextButton(FontAwesomeIcon.Thumbtack, pinText))
{
ImGui.CloseCurrentPopup();
var userInfo = entry.GroupPairStatusInfo ^ GroupUserInfo.IsPinned;
_ = ApiController.GroupSetUserInfo(new GroupPairUserInfoDto(entry.Group, entry.User, 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") && UiSharedService.CtrlPressed())
{
ImGui.CloseCurrentPopup();
_ = ApiController.GroupRemoveUser(entry);
}
UiSharedService.AttachToolTip("Hold CTRL and click to remove user " + (entry.UserAliasOrUID) + " from Syncshell");
if (UiSharedService.IconTextButton(FontAwesomeIcon.UserSlash, "Ban User"))
{
_showModalBanUser = true;
ImGui.CloseCurrentPopup();
}
UiSharedService.AttachToolTip("Ban user from this Syncshell");
}
if (userIsOwner)
{
string modText = entryIsMod ? "Demod user" : "Mod user";
if (UiSharedService.IconTextButton(FontAwesomeIcon.UserShield, modText) && UiSharedService.CtrlPressed())
{
ImGui.CloseCurrentPopup();
var userInfo = entry.GroupPairStatusInfo ^ GroupUserInfo.IsModerator;
_ = ApiController.GroupSetUserInfo(new GroupPairUserInfoDto(entry.Group, entry.User, userInfo));
}
UiSharedService.AttachToolTip("Hold CTRL to change the moderator status for " + (entry.UserAliasOrUID) + Environment.NewLine +
"Moderators can kick, ban/unban, pin/unpin users and clear the Syncshell.");
if (UiSharedService.IconTextButton(FontAwesomeIcon.Crown, "Transfer Ownership") && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed())
{
ImGui.CloseCurrentPopup();
_ = ApiController.GroupChangeOwnership(entry);
}
UiSharedService.AttachToolTip("Hold CTRL and SHIFT and click to transfer ownership of this Syncshell to " + (entry.UserAliasOrUID) + Environment.NewLine + "WARNING: This action is irreversible.");
}
ImGui.EndPopup();
}
if (_showModalBanUser && !_banUserPopupOpen)
{
ImGui.OpenPopup("Ban User");
_banUserPopupOpen = true;
}
if (!_showModalBanUser) _banUserPopupOpen = false;
if (ImGui.BeginPopupModal("Ban User", ref _showModalBanUser, UiSharedService.PopupWindowFlags))
{
UiSharedService.TextWrapped("User " + (entry.UserAliasOrUID) + " will be banned and removed from this Syncshell.");
ImGui.InputTextWithHint("##banreason", "Ban Reason", ref _banReason, 255);
if (ImGui.Button("Ban User"))
{
ImGui.CloseCurrentPopup();
var reason = _banReason;
_ = ApiController.GroupBanUser(entry, 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.");
UiSharedService.SetScaledWindowSize(300);
ImGui.EndPopup();
}
}
}

View File

@@ -3,7 +3,6 @@ using Dalamud.Interface.Components;
using ImGuiNET;
using MareSynchronos.API.Data.Extensions;
using MareSynchronos.MareConfiguration;
using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.UI.Handlers;
using MareSynchronos.WebAPI;
@@ -12,58 +11,31 @@ namespace MareSynchronos.UI.Components;
public class PairGroupsUi
{
private readonly ApiController _apiController;
private readonly Action<Pair> _clientRenderFn;
private readonly MareConfigService _mareConfig;
private readonly SelectPairForGroupUi _selectGroupForPairUi;
private readonly TagHandler _tagHandler;
public PairGroupsUi(MareConfigService mareConfig, TagHandler tagHandler, Action<Pair> clientRenderFn, ApiController apiController, SelectPairForGroupUi selectGroupForPairUi)
public PairGroupsUi(MareConfigService mareConfig, TagHandler tagHandler, ApiController apiController, SelectPairForGroupUi selectGroupForPairUi)
{
_clientRenderFn = clientRenderFn;
_mareConfig = mareConfig;
_tagHandler = tagHandler;
_apiController = apiController;
_selectGroupForPairUi = selectGroupForPairUi;
}
public void Draw(List<Pair> visibleUsers, List<Pair> onlineUsers, List<Pair> offlineUsers)
public void Draw<T>(List<T> visibleUsers, List<T> onlineUsers, List<T> offlineUsers) where T : DrawPairBase
{
// Only render those tags that actually have pairs in them, otherwise
// we can end up with a bunch of useless pair groups
var tagsWithPairsInThem = _tagHandler.GetAllTagsSorted();
var allUsers = visibleUsers.Concat(onlineUsers).Concat(offlineUsers).ToList();
if (_mareConfig.Current.ShowVisibleUsersSeparately)
if (typeof(T) == typeof(DrawUserPair))
{
UiSharedService.DrawWithID("$group-VisibleCustomTag", () => DrawCategory(TagHandler.CustomVisibleTag, visibleUsers, allUsers));
DrawUserPairs(tagsWithPairsInThem, allUsers.Cast<DrawUserPair>().ToList(), visibleUsers.Cast<DrawUserPair>(), onlineUsers.Cast<DrawUserPair>(), offlineUsers.Cast<DrawUserPair>());
}
foreach (var tag in tagsWithPairsInThem)
{
if (_mareConfig.Current.ShowOfflineUsersSeparately)
{
UiSharedService.DrawWithID($"group-{tag}", () => DrawCategory(tag, onlineUsers, allUsers, visibleUsers));
}
else
{
UiSharedService.DrawWithID($"group-{tag}", () => DrawCategory(tag, onlineUsers.Concat(offlineUsers).ToList(), allUsers, visibleUsers));
}
}
if (_mareConfig.Current.ShowOfflineUsersSeparately)
{
UiSharedService.DrawWithID($"group-OnlineCustomTag", () => DrawCategory(TagHandler.CustomOnlineTag,
onlineUsers.Where(u => !_tagHandler.HasAnyTag(u.UserPair!)).ToList(), allUsers));
UiSharedService.DrawWithID($"group-OfflineCustomTag", () => DrawCategory(TagHandler.CustomOfflineTag,
offlineUsers.Where(u => u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers));
}
else
{
UiSharedService.DrawWithID($"group-OnlineCustomTag", () => DrawCategory(TagHandler.CustomOnlineTag,
onlineUsers.Concat(offlineUsers).Where(u => u.UserPair!.OtherPermissions.IsPaired() && !_tagHandler.HasAnyTag(u.UserPair!)).ToList(), allUsers));
}
UiSharedService.DrawWithID($"group-UnpairedCustomTag", () => DrawCategory(TagHandler.CustomUnpairedTag,
offlineUsers.Where(u => !u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers));
}
private void DrawButtons(string tag, List<Pair> availablePairsInThisTag)
private void DrawButtons(string tag, List<DrawUserPair> availablePairsInThisTag)
{
var allArePaused = availablePairsInThisTag.All(pair => pair.UserPair!.OwnPermissions.IsPaused());
var pauseButton = allArePaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
@@ -114,9 +86,9 @@ public class PairGroupsUi
}
}
private void DrawCategory(string tag, List<Pair> onlineUsers, List<Pair> allUsers, List<Pair>? visibleUsers = null)
private void DrawCategory(string tag, IEnumerable<DrawPairBase> onlineUsers, IEnumerable<DrawPairBase> allUsers, IEnumerable<DrawPairBase>? visibleUsers = null)
{
List<Pair> usersInThisTag;
IEnumerable<DrawPairBase> usersInThisTag;
HashSet<string>? otherUidsTaggedWithTag = null;
bool isSpecialTag = false;
int visibleInThisTag = 0;
@@ -129,16 +101,21 @@ public class PairGroupsUi
{
otherUidsTaggedWithTag = _tagHandler.GetOtherUidsForTag(tag);
usersInThisTag = onlineUsers
.Where(pair => otherUidsTaggedWithTag.Contains(pair.UserData.UID))
.Where(pair => otherUidsTaggedWithTag.Contains(pair.UID))
.ToList();
visibleInThisTag = visibleUsers?.Count(p => otherUidsTaggedWithTag.Contains(p.UserData.UID)) ?? 0;
visibleInThisTag = visibleUsers?.Count(p => otherUidsTaggedWithTag.Contains(p.UID)) ?? 0;
}
if (isSpecialTag && !usersInThisTag.Any()) return;
DrawName(tag, isSpecialTag, visibleInThisTag, usersInThisTag.Count, otherUidsTaggedWithTag?.Count);
DrawName(tag, isSpecialTag, visibleInThisTag, usersInThisTag.Count(), otherUidsTaggedWithTag?.Count);
if (!isSpecialTag)
UiSharedService.DrawWithID($"group-{tag}-buttons", () => DrawButtons(tag, allUsers.Where(p => otherUidsTaggedWithTag!.Contains(p.UserData.UID)).ToList()));
{
if (onlineUsers.Any() && onlineUsers.First() is DrawUserPair)
{
UiSharedService.DrawWithID($"group-{tag}-buttons", () => DrawButtons(tag, allUsers.Cast<DrawUserPair>().Where(p => otherUidsTaggedWithTag!.Contains(p.UID)).ToList()));
}
}
if (!_tagHandler.IsTagOpen(tag)) return;
@@ -201,31 +178,66 @@ public class PairGroupsUi
}
}
private void DrawPairs(string tag, List<Pair> availablePairsInThisCategory)
private void DrawPairs(string tag, IEnumerable<DrawPairBase> availablePairsInThisCategory)
{
// These are all the OtherUIDs that are tagged with this tag
availablePairsInThisCategory
.ForEach(pair => UiSharedService.DrawWithID($"tag-{tag}-pair-${pair.UserData.UID}", () => _clientRenderFn(pair)));
foreach (var pair in availablePairsInThisCategory)
{
UiSharedService.DrawWithID($"tag-{tag}-pair-${pair.UID}", () => pair.DrawPairedClient());
}
ImGui.Separator();
}
private void PauseRemainingPairs(List<Pair> availablePairs)
private void DrawUserPairs(List<string> tagsWithPairsInThem, List<DrawUserPair> allUsers, IEnumerable<DrawUserPair> visibleUsers, IEnumerable<DrawUserPair> onlineUsers, IEnumerable<DrawUserPair> offlineUsers)
{
if (_mareConfig.Current.ShowVisibleUsersSeparately)
{
UiSharedService.DrawWithID("$group-VisibleCustomTag", () => DrawCategory(TagHandler.CustomVisibleTag, visibleUsers, allUsers));
}
foreach (var tag in tagsWithPairsInThem)
{
if (_mareConfig.Current.ShowOfflineUsersSeparately)
{
UiSharedService.DrawWithID($"group-{tag}", () => DrawCategory(tag, onlineUsers, allUsers, visibleUsers));
}
else
{
UiSharedService.DrawWithID($"group-{tag}", () => DrawCategory(tag, onlineUsers.Concat(offlineUsers).ToList(), allUsers, visibleUsers));
}
}
if (_mareConfig.Current.ShowOfflineUsersSeparately)
{
UiSharedService.DrawWithID($"group-OnlineCustomTag", () => DrawCategory(TagHandler.CustomOnlineTag,
onlineUsers.Where(u => !_tagHandler.HasAnyTag(u.UID)).ToList(), allUsers));
UiSharedService.DrawWithID($"group-OfflineCustomTag", () => DrawCategory(TagHandler.CustomOfflineTag,
offlineUsers.Where(u => u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers));
}
else
{
UiSharedService.DrawWithID($"group-OnlineCustomTag", () => DrawCategory(TagHandler.CustomOnlineTag,
onlineUsers.Concat(offlineUsers).Where(u => u.UserPair!.OtherPermissions.IsPaired() && !_tagHandler.HasAnyTag(u.UID)).ToList(), allUsers));
}
UiSharedService.DrawWithID($"group-UnpairedCustomTag", () => DrawCategory(TagHandler.CustomUnpairedTag,
offlineUsers.Where(u => !u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers));
}
private void PauseRemainingPairs(List<DrawUserPair> availablePairs)
{
foreach (var pairToPause in availablePairs.Where(pair => !pair.UserPair!.OwnPermissions.IsPaused()))
{
var perm = pairToPause.UserPair!.OwnPermissions;
perm.SetPaused(paused: true);
_ = _apiController.UserSetPairPermissions(new(pairToPause.UserData, perm));
_ = _apiController.UserSetPairPermissions(new(new(pairToPause.UID), perm));
}
}
private void ResumeAllPairs(List<Pair> availablePairs)
private void ResumeAllPairs(List<DrawUserPair> availablePairs)
{
foreach (var pairToPause in availablePairs)
{
var perm = pairToPause.UserPair!.OwnPermissions;
perm.SetPaused(paused: false);
_ = _apiController.UserSetPairPermissions(new(pairToPause.UserData, perm));
_ = _apiController.UserSetPairPermissions(new(new(pairToPause.UID), perm));
}
}

View File

@@ -11,6 +11,7 @@ namespace MareSynchronos.UI.Components;
public class SelectGroupForPairUi
{
private readonly TagHandler _tagHandler;
private readonly UidDisplayHandler _uidDisplayHandler;
/// <summary>
/// The group UI is always open for a specific pair. This defines which pair the UI is open for.
@@ -28,21 +29,22 @@ public class SelectGroupForPairUi
/// </summary>
private string _tagNameToAdd = "";
public SelectGroupForPairUi(TagHandler tagHandler)
public SelectGroupForPairUi(TagHandler tagHandler, UidDisplayHandler uidDisplayHandler)
{
_show = false;
_pair = null;
_tagHandler = tagHandler;
_uidDisplayHandler = uidDisplayHandler;
}
public void Draw(Dictionary<string, bool> showUidForEntry)
public void Draw()
{
if (_pair == null)
{
return;
}
var name = PairName(showUidForEntry, _pair);
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)
@@ -93,30 +95,19 @@ public class SelectGroupForPairUi
_show = true;
}
private static string PairName(Dictionary<string, bool> showUidForEntry, Pair pair)
{
showUidForEntry.TryGetValue(pair.UserData.UID, out var showUidInsteadOfName);
var playerText = pair.GetNote();
if (showUidInsteadOfName || string.IsNullOrEmpty(playerText))
{
playerText = pair.UserData.AliasOrUID;
}
return playerText;
}
private void DrawGroupName(Pair pair, string name)
{
var hasTagBefore = _tagHandler.HasTag(pair.UserPair!, name);
var hasTagBefore = _tagHandler.HasTag(pair.UserData.UID, name);
var hasTag = hasTagBefore;
if (ImGui.Checkbox(name, ref hasTag))
{
if (hasTag)
{
_tagHandler.AddTagToPairedUid(pair.UserPair!, name);
_tagHandler.AddTagToPairedUid(pair.UserData.UID, name);
}
else
{
_tagHandler.RemoveTagFromPairedUid(pair.UserPair!, name);
_tagHandler.RemoveTagFromPairedUid(pair.UserData.UID, name);
}
}
}
@@ -128,7 +119,7 @@ public class SelectGroupForPairUi
_tagHandler.AddTag(_tagNameToAdd);
if (_pair != null)
{
_tagHandler.AddTagToPairedUid(_pair.UserPair!, _tagNameToAdd);
_tagHandler.AddTagToPairedUid(_pair.UserData.UID, _tagNameToAdd);
}
_tagNameToAdd = string.Empty;
}
@@ -137,4 +128,9 @@ public class SelectGroupForPairUi
_tagNameToAdd = string.Empty;
}
}
private string PairName(Pair pair)
{
return _uidDisplayHandler.GetPlayerText(pair).text;
}
}

View File

@@ -9,18 +9,20 @@ namespace MareSynchronos.UI.Components;
public class SelectPairForGroupUi
{
private readonly TagHandler _tagHandler;
private readonly UidDisplayHandler _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 SelectPairForGroupUi(TagHandler tagHandler)
public SelectPairForGroupUi(TagHandler tagHandler, UidDisplayHandler uidDisplayHandler)
{
_tagHandler = tagHandler;
_uidDisplayHandler = uidDisplayHandler;
}
public void Draw(List<Pair> pairs, Dictionary<string, bool> showUidForEntry)
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;
@@ -47,21 +49,21 @@ public class SelectPairForGroupUi
UiSharedService.FontText($"Select users for group {_tag}", UiBuilder.DefaultFont);
ImGui.InputTextWithHint("##filter", "Filter", ref _filter, 255, ImGuiInputTextFlags.None);
foreach (var item in pairs
.Where(p => string.IsNullOrEmpty(_filter) || PairName(showUidForEntry, p).Contains(_filter, StringComparison.OrdinalIgnoreCase))
.OrderBy(p => PairName(showUidForEntry, p), StringComparer.OrdinalIgnoreCase)
.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(showUidForEntry, item), ref isInGroup))
if (ImGui.Checkbox(PairName(item), ref isInGroup))
{
if (isInGroup)
{
_tagHandler.AddTagToPairedUid(item.UserPair!, _tag);
_tagHandler.AddTagToPairedUid(item.UserData.UID, _tag);
_peopleInGroup.Add(item.UserData.UID);
}
else
{
_tagHandler.RemoveTagFromPairedUid(item.UserPair!, _tag);
_tagHandler.RemoveTagFromPairedUid(item.UserData.UID, _tag);
_peopleInGroup.Remove(item.UserData.UID);
}
}
@@ -82,14 +84,8 @@ public class SelectPairForGroupUi
_show = true;
}
private static string PairName(Dictionary<string, bool> showUidForEntry, Pair pair)
private string PairName(Pair pair)
{
showUidForEntry.TryGetValue(pair.UserData.UID, out var showUidInsteadOfName);
var playerText = pair.GetNote();
if (showUidInsteadOfName || string.IsNullOrEmpty(playerText))
{
playerText = pair.UserData.AliasOrUID;
}
return playerText;
return _uidDisplayHandler.GetPlayerText(pair).text;
}
}

View File

@@ -0,0 +1,187 @@
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.ImGuiFileDialog;
using ImGuiNET;
using ImGuiScene;
using MareSynchronos.API.Data;
using MareSynchronos.API.Data.Enum;
using MareSynchronos.API.Dto.User;
using MareSynchronos.Services;
using MareSynchronos.Services.Mediator;
using MareSynchronos.WebAPI;
using Microsoft.Extensions.Logging;
namespace MareSynchronos.UI;
public class EditProfileUi : WindowMediatorSubscriberBase
{
private readonly ApiController _apiController;
private readonly FileDialogManager _fileDialogManager;
private readonly MareProfileManager _mareProfileManager;
private readonly UiBuilder _uiBuilder;
private readonly UiSharedService _uiSharedService;
private string _descriptionText;
private bool _loadedPrior = false;
private TextureWrap? _pfpTextureWrap;
private bool _showFileDialogError = false;
private bool _wasOpen;
public EditProfileUi(ILogger<EditProfileUi> logger, MareMediator mediator,
ApiController apiController, UiBuilder uiBuilder, UiSharedService uiSharedService,
FileDialogManager fileDialogManager, MareProfileManager mareProfileManager) : base(logger, mediator, "Mare Synchronos Edit Profile###MareSynchronosEditProfileUI")
{
IsOpen = false;
this.SizeConstraints = new()
{
MinimumSize = new(768, 512),
MaximumSize = new(2000, 2000)
};
_apiController = apiController;
_uiBuilder = uiBuilder;
_uiSharedService = uiSharedService;
_fileDialogManager = fileDialogManager;
_mareProfileManager = mareProfileManager;
Mediator.Subscribe<GposeStartMessage>(this, (_) => { _wasOpen = IsOpen; IsOpen = false; });
Mediator.Subscribe<GposeEndMessage>(this, (_) => IsOpen = _wasOpen);
Mediator.Subscribe<DisconnectedMessage>(this, (_) => IsOpen = false);
Mediator.Subscribe<ClearProfileDataMessage>(this, (msg) =>
{
if (string.Equals(msg.UserData.UID, _apiController.UID, StringComparison.Ordinal))
{
_pfpTextureWrap?.Dispose();
_pfpTextureWrap = null;
}
});
}
public override void Draw()
{
_uiSharedService.BigText("Current Profile");
var (loaded, profile) = _mareProfileManager.GetMareProfile(new API.Data.UserData(_apiController.UID));
if (!loaded)
{
_loadedPrior = false;
_descriptionText = string.Empty;
ImGui.TextUnformatted("Loading profile...");
return;
}
else
{
if (profile.IsFlagged)
{
UiSharedService.ColorTextWrapped(profile.Description, ImGuiColors.DalamudRed);
return;
}
if (!_loadedPrior)
{
_descriptionText = profile.Description;
_loadedPrior = true;
_pfpTextureWrap?.Dispose();
_pfpTextureWrap = _uiBuilder.LoadImage(Convert.FromBase64String(profile.Base64ProfilePicture));
}
}
if (_pfpTextureWrap != null)
{
ImGui.Image(_pfpTextureWrap.ImGuiHandle, new System.Numerics.Vector2(_pfpTextureWrap.Width, _pfpTextureWrap.Height));
}
ImGui.SameLine(256);
var posX = ImGui.GetCursorPosX();
var spacing = ImGui.GetStyle().ItemSpacing.X;
var width = UiSharedService.GetWindowContentRegionWidth() - posX + spacing;
if (ImGui.BeginChildFrame(100, new System.Numerics.Vector2(width, 256)))
{
var nsfw = profile.IsNSFW;
ImGui.BeginDisabled();
ImGui.Checkbox("Is NSFW", ref nsfw);
ImGui.EndDisabled();
UiSharedService.TextWrapped("Description:" + Environment.NewLine + profile.Description);
}
ImGui.EndChildFrame();
ImGui.Separator();
_uiSharedService.BigText("Notes and Rules for Profiles");
ImGui.TextWrapped($"- All users that are paired and unpaused with you will be able to see your profile picture and description.{Environment.NewLine}" +
$"- Other users have the possibility to report your profile for breaking the rules.{Environment.NewLine}" +
$"- !!! AVOID: anything as profile image that can be considered highly illegal or obscene (bestiality, anything that could be considered a sexual act with a minor (that includes Lalafells), etc.){Environment.NewLine}" +
$"- !!! AVOID: slurs of any kind in the description that can be considered highly offensive{Environment.NewLine}" +
$"- In case of valid reports from other users this can lead to disabling your profile forever or terminating your Mare account indefinitely.{Environment.NewLine}" +
$"- Judgement of your profile validity from reports through staff is not up to debate and decisions to disable your profile/account permanent.{Environment.NewLine}" +
$"- If your profile picture or profile description could be considered NSFW, enable the toggle below.");
ImGui.Separator();
_uiSharedService.BigText("Profile Settings");
if (UiSharedService.IconTextButton(FontAwesomeIcon.FileUpload, "Upload new profile image"))
{
_fileDialogManager.OpenFileDialog("Select new Profile picture", ".png", (success, file) =>
{
if (!success) return;
Task.Run(async () =>
{
var fileContent = File.ReadAllBytes(file);
using MemoryStream ms = new(fileContent);
var format = await Image.DetectFormatAsync(ms).ConfigureAwait(false);
if (!format.FileExtensions.Contains("png", StringComparer.OrdinalIgnoreCase))
{
_showFileDialogError = true;
return;
}
using var image = Image.Load<Rgba32>(fileContent);
if (image.Width > 256 || image.Height > 256 || (fileContent.Length > 250 * 1024))
{
_showFileDialogError = true;
return;
}
_showFileDialogError = false;
await _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), false, null, Convert.ToBase64String(fileContent), null))
.ConfigureAwait(false);
});
});
}
UiSharedService.AttachToolTip("Select and upload a new profile picture");
ImGui.SameLine();
if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear uploaded profile picture"))
{
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), false, null, "", null));
}
UiSharedService.AttachToolTip("Clear your currently uploaded profile picture");
if (_showFileDialogError)
{
UiSharedService.ColorTextWrapped("The profile picture must be a PNG file with a maximum height and width of 256px and 250KiB size", ImGuiColors.DalamudRed);
}
var isNsfw = profile.IsNSFW;
if (ImGui.Checkbox("Profile is NSFW", ref isNsfw))
{
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), false, isNsfw, null, null));
}
UiSharedService.DrawHelpText("If your profile description or image can be considered NSFW, toggle this to ON");
var widthTextBox = UiSharedService.GetWindowContentRegionWidth() - posX + spacing;
ImGui.TextUnformatted($"Description {_descriptionText.Length}/750");
ImGui.InputTextMultiline("##description", ref _descriptionText, 750, new System.Numerics.Vector2(widthTextBox, 200));
if (UiSharedService.IconTextButton(FontAwesomeIcon.Save, "Save Description"))
{
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), false, null, null, _descriptionText));
}
UiSharedService.AttachToolTip("Sets your profile description text");
ImGui.SameLine();
if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear Description"))
{
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), false, null, null, ""));
}
UiSharedService.AttachToolTip("Clears your profile description text");
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
_pfpTextureWrap?.Dispose();
}
}

View File

@@ -43,12 +43,16 @@ public class GposeUi : WindowMediatorSubscriberBase
{
if (UiSharedService.IconTextButton(FontAwesomeIcon.FolderOpen, "Load MCDF"))
{
_fileDialogManager.OpenFileDialog("Pick MCDF file", ".mcdf", (success, path) =>
_fileDialogManager.OpenFileDialog("Pick MCDF file", ".mcdf", (success, paths) =>
{
if (!success) return;
if (paths.FirstOrDefault() is not string path) return;
_configService.Current.ExportFolder = Path.GetDirectoryName(path) ?? string.Empty;
_configService.Save();
Task.Run(() => _mareCharaFileManager.LoadMareCharaFile(path));
});
}, 1, Directory.Exists(_configService.Current.ExportFolder) ? _configService.Current.ExportFolder : null);
}
UiSharedService.AttachToolTip("Applies it to the currently selected GPose actor");
if (_mareCharaFileManager.LoadedCharaFile != null)

View File

@@ -21,9 +21,9 @@ public class TagHandler
_serverConfigurationManager.AddTag(tag);
}
public void AddTagToPairedUid(UserPairDto pair, string tagName)
public void AddTagToPairedUid(string uid, string tagName)
{
_serverConfigurationManager.AddTagForUid(pair.User.UID, tagName);
_serverConfigurationManager.AddTagForUid(uid, tagName);
}
public List<string> GetAllTagsSorted()
@@ -38,14 +38,14 @@ public class TagHandler
return _serverConfigurationManager.GetUidsForTag(tag);
}
public bool HasAnyTag(UserPairDto pair)
public bool HasAnyTag(string uid)
{
return _serverConfigurationManager.HasTags(pair.User.UID);
return _serverConfigurationManager.HasTags(uid);
}
public bool HasTag(UserPairDto pair, string tagName)
public bool HasTag(string uid, string tagName)
{
return _serverConfigurationManager.ContainsTag(pair.User.UID, tagName);
return _serverConfigurationManager.ContainsTag(uid, tagName);
}
/// <summary>
@@ -64,9 +64,9 @@ public class TagHandler
_serverConfigurationManager.RemoveTag(tag);
}
public void RemoveTagFromPairedUid(UserPairDto pair, string tagName)
public void RemoveTagFromPairedUid(string uid, string tagName)
{
_serverConfigurationManager.RemoveTagForUid(pair.User.UID, tagName);
_serverConfigurationManager.RemoveTagForUid(uid, tagName);
}
public void SetTagOpen(string tag, bool open)

View File

@@ -0,0 +1,250 @@
using Dalamud.Interface;
using ImGuiNET;
using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.Services.ServerConfiguration;
using MareSynchronos.MareConfiguration;
using Dalamud.Interface.Colors;
using MareSynchronos.API.Data.Extensions;
using ImGuiScene;
using System.Numerics;
using Microsoft.Extensions.Logging;
using MareSynchronos.Services;
namespace MareSynchronos.UI.Handlers;
public class UidDisplayHandler
{
private readonly ILogger<UidDisplayHandler> _logger;
private readonly MareConfigService _mareConfigService;
private readonly MareProfileManager _mareProfileManager;
private readonly PairManager _pairManager;
private readonly ServerConfigurationManager _serverManager;
private readonly Dictionary<string, bool> _showUidForEntry = new(StringComparer.Ordinal);
private readonly UiBuilder _uiBuilder;
private readonly UiSharedService _uiSharedService;
private string _editNickEntry = string.Empty;
private string _editUserComment = string.Empty;
private string _lastMouseOverUid = string.Empty;
private byte[] _lastProfilePicture = Array.Empty<byte>();
private DateTime? _popupTime;
private TextureWrap? _textureWrap;
public UidDisplayHandler(ILogger<UidDisplayHandler> logger, UiBuilder uiBuilder, MareProfileManager mareProfileManager,
UiSharedService uiSharedService, PairManager pairManager, ServerConfigurationManager serverManager, MareConfigService mareConfigService)
{
_logger = logger;
_uiBuilder = uiBuilder;
_mareProfileManager = mareProfileManager;
_uiSharedService = uiSharedService;
_pairManager = pairManager;
_serverManager = serverManager;
_mareConfigService = mareConfigService;
}
public void DrawPairText(string id, Pair pair, float textPosX, float originalY, Func<float> editBoxWidth)
{
ImGui.SameLine(textPosX);
(bool textIsUid, string playerText) = GetPlayerText(pair);
if (!string.Equals(_editNickEntry, pair.UserData.UID, StringComparison.Ordinal))
{
ImGui.SetCursorPosY(originalY);
if (textIsUid) ImGui.PushFont(UiBuilder.MonoFont);
ImGui.TextUnformatted(playerText);
if (textIsUid) ImGui.PopFont();
if (ImGui.IsItemHovered())
{
if (!string.Equals(_lastMouseOverUid, id))
{
_popupTime = DateTime.UtcNow.AddSeconds(_mareConfigService.Current.ProfileDelay);
}
_lastMouseOverUid = id;
if (_popupTime > DateTime.UtcNow || !_mareConfigService.Current.ProfilesShow)
{
ImGui.SetTooltip("Left click to switch between UID display and nick" + Environment.NewLine + "Right click to change nick for " + pair.UserData.AliasOrUID);
}
else
{
try
{
var spacing = ImGui.GetStyle().ItemSpacing;
ImGui.SetNextWindowSizeConstraints(new Vector2(512 + spacing.X * 4, 256 + spacing.Y * 3), new Vector2(512 + spacing.X * 4, 512 + spacing.Y * 3));
ImGui.BeginTooltip();
var mareProfile = _mareProfileManager.GetMareProfile(pair.UserData);
if (_textureWrap == null || !mareProfile.Profile.ImageData.Value.SequenceEqual(_lastProfilePicture))
{
_textureWrap?.Dispose();
_lastProfilePicture = mareProfile.Profile.ImageData.Value;
_textureWrap = _uiBuilder.LoadImage(_lastProfilePicture);
}
var drawList = ImGui.GetWindowDrawList();
var rect = drawList.GetClipRectMin();
var rectMax = drawList.GetClipRectMax();
ImGui.Indent(256 + spacing.X * 2);
if (_uiSharedService.UidFontBuilt) ImGui.PushFont(_uiSharedService.UidFont);
UiSharedService.ColorText(pair.UserData.AliasOrUID, ImGuiColors.HealerGreen);
if (_uiSharedService.UidFontBuilt) ImGui.PopFont();
var pos = ImGui.GetCursorPos();
var note = _serverManager.GetNoteForUid(pair.UserData.UID);
if (!string.IsNullOrEmpty(note))
{
UiSharedService.ColorText(note, ImGuiColors.DalamudGrey);
}
string status = pair.IsVisible ? "Visible" : (pair.IsOnline ? "Online" : "Offline");
UiSharedService.ColorText(status, (pair.IsVisible || pair.IsOnline) ? ImGuiColors.HealerGreen : ImGuiColors.DalamudRed);
if (pair.IsVisible)
{
ImGui.SameLine();
ImGui.TextUnformatted($"({pair.PlayerName})");
}
if (pair.UserPair != null)
{
ImGui.TextUnformatted("Directly paired");
if (pair.UserPair.OwnPermissions.IsPaused())
{
ImGui.SameLine();
UiSharedService.ColorText("You: paused", ImGuiColors.DalamudYellow);
}
if (pair.UserPair.OtherPermissions.IsPaused())
{
ImGui.SameLine();
UiSharedService.ColorText("They: paused", ImGuiColors.DalamudYellow);
}
}
if (pair.GroupPair.Any())
{
ImGui.TextUnformatted("Paired through Syncshells:");
foreach (var groupPair in pair.GroupPair)
{
ImGui.TextUnformatted("- " + groupPair.Key.GroupAliasOrGID);
}
}
var posDone = ImGui.GetCursorPos();
UiSharedService.TextWrapped(mareProfile.Profile.Description);
ImGui.Unindent();
var sepColor = ImGui.GetStyle().Colors[(int)ImGuiCol.Separator];
bool tallerThanWide = _textureWrap.Height >= _textureWrap.Width;
var stretchFactor = tallerThanWide ? 256f / _textureWrap.Height : 256f / _textureWrap.Width;
var newWidth = _textureWrap.Width * stretchFactor;
var newHeight = _textureWrap.Height * stretchFactor;
var remainingWidth = (256f - newWidth) / 2f;
var remainingHeight = (256f - newHeight) / 2f;
drawList.AddImage(_textureWrap.ImGuiHandle, new Vector2(rect.X + spacing.X + remainingWidth, rect.Y + spacing.Y + remainingHeight),
new Vector2(rect.X + spacing.X + remainingWidth + newWidth, rect.Y + spacing.Y + remainingHeight + newHeight));
drawList.AddLine(new Vector2(rect.X + 256 + spacing.X * 2, rect.Y + pos.Y - spacing.Y),
new Vector2(rectMax.X - spacing.X, rect.Y + pos.Y - spacing.Y),
UiSharedService.Color((byte)(sepColor.X * 255), (byte)(sepColor.Y * 255), (byte)(sepColor.Z * 255), (byte)(sepColor.W * 255)));
drawList.AddLine(new Vector2(rect.X + 256 + spacing.X * 2, rect.Y + posDone.Y - spacing.Y),
new Vector2(rectMax.X - spacing.X, rect.Y + posDone.Y - spacing.Y),
UiSharedService.Color((byte)(sepColor.X * 255), (byte)(sepColor.Y * 255), (byte)(sepColor.Z * 255), (byte)(sepColor.W * 255)));
ImGui.EndTooltip();
}
catch (Exception ex)
{
_logger.LogWarning("Error during draw tooltip", ex);
}
}
}
else
{
if (string.Equals(_lastMouseOverUid, id))
{
_lastProfilePicture = Array.Empty<byte>();
_lastMouseOverUid = string.Empty;
_textureWrap?.Dispose();
_textureWrap = null;
}
}
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
{
var prevState = textIsUid;
if (_showUidForEntry.ContainsKey(pair.UserData.UID))
{
prevState = _showUidForEntry[pair.UserData.UID];
}
_showUidForEntry[pair.UserData.UID] = !prevState;
}
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
var nickEntryPair = _pairManager.DirectPairs.Find(p => string.Equals(p.UserData.UID, _editNickEntry, StringComparison.Ordinal));
nickEntryPair?.SetNote(_editUserComment);
_editUserComment = pair.GetNote() ?? string.Empty;
_editNickEntry = pair.UserData.UID;
}
}
else
{
ImGui.SetCursorPosY(originalY);
ImGui.SetNextItemWidth(editBoxWidth.Invoke());
if (ImGui.InputTextWithHint("", "Nick/Notes", ref _editUserComment, 255, ImGuiInputTextFlags.EnterReturnsTrue))
{
_serverManager.SetNoteForUid(pair.UserData.UID, _editUserComment);
_serverManager.SaveNotes();
_editNickEntry = string.Empty;
}
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
_editNickEntry = string.Empty;
}
UiSharedService.AttachToolTip("Hit ENTER to save\nRight click to cancel");
}
}
public (bool isUid, string text) GetPlayerText(Pair pair)
{
var textIsUid = true;
bool showUidInsteadOfName = ShowUidInsteadOfName(pair);
string? playerText = _serverManager.GetNoteForUid(pair.UserData.UID);
if (!showUidInsteadOfName && playerText != null)
{
if (string.IsNullOrEmpty(playerText))
{
playerText = pair.UserData.AliasOrUID;
}
else
{
textIsUid = false;
}
}
else
{
playerText = pair.UserData.AliasOrUID;
}
if (_mareConfigService.Current.ShowCharacterNameInsteadOfNotesForVisible && pair.IsVisible && !showUidInsteadOfName)
{
playerText = pair.PlayerName;
textIsUid = false;
}
return (textIsUid, playerText!);
}
internal void Clear()
{
_editNickEntry = string.Empty;
_editUserComment = string.Empty;
}
private bool ShowUidInsteadOfName(Pair pair)
{
_showUidForEntry.TryGetValue(pair.UserData.UID, out var showUidInsteadOfName);
return showUidInsteadOfName;
}
}

View File

@@ -362,10 +362,16 @@ public class SettingsUi : WindowMediatorSubscriberBase
ImGui.InputTextWithHint("Export Descriptor", "This description will be shown on loading the data", ref _exportDescription, 255);
if (UiSharedService.IconTextButton(FontAwesomeIcon.Save, "Export Character as MCDF"))
{
_uiShared.FileDialogManager.SaveFileDialog("Export Character to file", ".mcdf", "export.mcdf", ".mcdf", (success, path) =>
string defaultFileName = string.IsNullOrEmpty(_exportDescription)
? "export.mcdf"
: string.Join('_', $"{_exportDescription}.mcdf".Split(Path.GetInvalidFileNameChars()));
_uiShared.FileDialogManager.SaveFileDialog("Export Character to file", ".mcdf", defaultFileName, ".mcdf", (success, path) =>
{
if (!success) return;
_configService.Current.ExportFolder = Path.GetDirectoryName(path) ?? string.Empty;
_configService.Save();
Task.Run(() =>
{
try
@@ -378,7 +384,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
_logger.LogCritical(ex, "Error saving data");
}
});
});
}, Directory.Exists(_configService.Current.ExportFolder) ? _configService.Current.ExportFolder : null);
}
UiSharedService.ColorTextWrapped("Note: For best results make sure you have everything you want to be shared as well as the correct character appearance" +
" equipped and redraw your character before exporting.", ImGuiColors.DalamudYellow);
@@ -484,9 +490,11 @@ public class SettingsUi : WindowMediatorSubscriberBase
ImGui.Separator();
UiSharedService.FontText("UI", _uiShared.UidFont);
var showNameInsteadOfNotes = _configService.Current.ShowCharacterNameInsteadOfNotesForVisible;
var reverseUserSort = _configService.Current.ReverseUserSort;
var showVisibleSeparate = _configService.Current.ShowVisibleUsersSeparately;
var showOfflineSeparate = _configService.Current.ShowOfflineUsersSeparately;
var showProfiles = _configService.Current.ProfilesShow;
var showNsfwProfiles = _configService.Current.ProfilesAllowNsfw;
var profileDelay = _configService.Current.ProfileDelay;
if (ImGui.Checkbox("Show separate Visible group", ref showVisibleSeparate))
{
@@ -509,12 +517,30 @@ public class SettingsUi : WindowMediatorSubscriberBase
}
UiSharedService.DrawHelpText("This will show the character name instead of custom set note when a character is visible");
if (ImGui.Checkbox("Reverse user sort", ref reverseUserSort))
if (ImGui.Checkbox("Show Mare Profiles on Hover", ref showProfiles))
{
_configService.Current.ReverseUserSort = reverseUserSort;
Mediator.Publish(new ClearProfileDataMessage());
_configService.Current.ProfilesShow = showProfiles;
_configService.Save();
}
UiSharedService.DrawHelpText("This reverses the user sort from A->Z to Z->A");
UiSharedService.DrawHelpText("This will show the configured user profile after a set delay");
ImGui.Indent();
if (!showProfiles) ImGui.BeginDisabled();
if (ImGui.Checkbox("Show profiles marked as NSFW", ref showNsfwProfiles))
{
Mediator.Publish(new ClearProfileDataMessage());
_configService.Current.ProfilesAllowNsfw = showNsfwProfiles;
_configService.Save();
}
UiSharedService.DrawHelpText("Will show profiles that have the NSFW tag enabled");
if (ImGui.SliderFloat("Hover Delay", ref profileDelay, 1, 10))
{
_configService.Current.ProfileDelay = profileDelay;
_configService.Save();
}
UiSharedService.DrawHelpText("Delay until the profile should be displayed");
if (!showProfiles) ImGui.EndDisabled();
ImGui.Unindent();
ImGui.Separator();

View File

@@ -161,13 +161,13 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
public static uint Color(Vector4 color)
{
uint ret = (byte)(color.W);
uint ret = (byte)(color.W * 255);
ret <<= 8;
ret += (byte)(color.X);
ret += (byte)(color.Z * 255);
ret <<= 8;
ret += (byte)(color.Y);
ret += (byte)(color.Y * 255);
ret <<= 8;
ret += (byte)(color.Z);
ret += (byte)(color.X * 255);
return ret;
}
@@ -452,6 +452,13 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
return true;
}
public void BigText(string text)
{
if (UidFontBuilt) ImGui.PushFont(UidFont);
ImGui.TextUnformatted(text);
if (UidFontBuilt) ImGui.PopFont();
}
public void DrawCacheDirectorySetting()
{
ColorTextWrapped("Note: The storage folder should be somewhere close to root (i.e. C:\\MareStorage) in a new empty folder. DO NOT point this to your game folder. DO NOT point this to your Penumbra folder.", ImGuiColors.DalamudYellow);

View File

@@ -242,7 +242,13 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
fi.LastWriteTime = RandomDayInThePast().Invoke();
try
{
_ = _fileDbManager.CreateCacheEntry(filePath);
var entry = _fileDbManager.CreateCacheEntry(filePath);
if (!string.Equals(entry?.Hash, file.Hash, StringComparison.OrdinalIgnoreCase))
{
Logger.LogError("Hash mismatch after extracting, got {hash}, expected {expectedHash}, deleting file", entry?.Hash, file.Hash);
File.Delete(filePath);
_fileDbManager.RemoveHash(entry);
}
}
catch (Exception ex)
{

View File

@@ -49,6 +49,12 @@ public partial class ApiController
return await _mareHub!.InvokeAsync<List<UserPairDto>>(nameof(UserGetPairedClients)).ConfigureAwait(false);
}
public async Task<UserProfileDto> UserGetProfile(UserDto dto)
{
if (!IsConnected) return new UserProfileDto(dto.User, false, null, null, null);
return await _mareHub!.InvokeAsync<UserProfileDto>(nameof(UserGetProfile), dto).ConfigureAwait(false);
}
public async Task UserPushData(UserCharaDataMessageDto dto)
{
try
@@ -67,11 +73,23 @@ public partial class ApiController
await _mareHub!.SendAsync(nameof(UserRemovePair), userDto).ConfigureAwait(false);
}
public async Task UserReportProfile(UserProfileReportDto userDto)
{
if (!IsConnected) return;
await _mareHub!.SendAsync(nameof(UserReportProfile), userDto).ConfigureAwait(false);
}
public async Task UserSetPairPermissions(UserPermissionsDto userPermissions)
{
await _mareHub!.SendAsync(nameof(UserSetPairPermissions), userPermissions).ConfigureAwait(false);
}
public async Task UserSetProfile(UserProfileDto userDescription)
{
if (!IsConnected) return;
await _mareHub!.InvokeAsync(nameof(UserSetProfile), userDescription).ConfigureAwait(false);
}
private async Task PushCharacterDataInternal(CharacterData character, List<UserData> visibleCharacters)
{
Logger.LogInformation("Pushing character data for {hash} to {charas}", character.DataHash.Value, string.Join(", ", visibleCharacters.Select(c => c.AliasOrUID)));

View File

@@ -162,6 +162,13 @@ public partial class ApiController
return Task.CompletedTask;
}
public Task Client_UserUpdateProfile(UserDto dto)
{
Logger.LogDebug("Client_UserUpdateProfile: {dto}", dto);
ExecuteSafely(() => Mediator.Publish(new ClearProfileDataMessage(dto.User)));
return Task.CompletedTask;
}
public Task Client_UserUpdateSelfPairPermissions(UserPermissionsDto dto)
{
Logger.LogDebug("Client_UserUpdateSelfPairPermissions: {dto}", dto);
@@ -277,6 +284,12 @@ public partial class ApiController
_mareHub!.On(nameof(Client_UserUpdateOtherPairPermissions), act);
}
public void OnUserUpdateProfile(Action<UserDto> act)
{
if (_initialized) return;
_mareHub!.On(nameof(Client_UserUpdateProfile), act);
}
public void OnUserUpdateSelfPairPermissions(Action<UserPermissionsDto> act)
{
if (_initialized) return;

View File

@@ -244,6 +244,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
OnUserUpdateOtherPairPermissions(dto => Client_UserUpdateOtherPairPermissions(dto));
OnUserUpdateSelfPairPermissions(dto => Client_UserUpdateSelfPairPermissions(dto));
OnUserReceiveUploadStatus(dto => Client_UserReceiveUploadStatus(dto));
OnUserUpdateProfile(dto => Client_UserUpdateProfile(dto));
OnGroupChangePermissions((dto) => Client_GroupChangePermissions(dto));
OnGroupDelete((dto) => Client_GroupDelete(dto));