Merge branch 'main' of https://github.com/Penumbra-Sync/client
This commit is contained in:
		
							
								
								
									
										2
									
								
								MareAPI
									
									
									
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								MareAPI
									
									
									
									
									
								
							 Submodule MareAPI updated: 0065dd3cda...22346739d2
									
								
							| @@ -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_tuple_swap = true:suggestion | ||||||
| csharp_style_prefer_utf8_string_literals = true:suggestion | csharp_style_prefer_utf8_string_literals = true:suggestion | ||||||
| dotnet_diagnostic.S1075.severity = silent | dotnet_diagnostic.S1075.severity = silent | ||||||
|  | dotnet_diagnostic.SS3358.severity = true:suggestion | ||||||
| dotnet_diagnostic.MA0007.severity = silent | 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 | ||||||
|   | |||||||
| @@ -66,7 +66,7 @@ public sealed class FileCacheManager : IDisposable | |||||||
|         var fullName = fi.FullName.ToLowerInvariant(); |         var fullName = fi.FullName.ToLowerInvariant(); | ||||||
|         if (!fullName.Contains(_configService.Current.CacheFolder.ToLowerInvariant(), StringComparison.Ordinal)) return null; |         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); |         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) |     public FileCacheEntity? CreateFileEntry(string path) | ||||||
| @@ -122,10 +122,13 @@ public sealed class FileCacheManager : IDisposable | |||||||
|         return validatedCacheEntry; |         return validatedCacheEntry; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void RemoveHash(FileCacheEntity entity) |     public void RemoveHash(FileCacheEntity? entity) | ||||||
|     { |     { | ||||||
|         _logger.LogTrace("Removing {path}", entity.ResolvedFilepath); |         if (entity != null) | ||||||
|         _fileCaches.Remove(entity.PrefixedFilePath, out _); |         { | ||||||
|  |             _logger.LogTrace("Removing {path}", entity.ResolvedFilepath); | ||||||
|  |             _fileCaches.Remove(entity.PrefixedFilePath, out _); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public string ResolveFileReplacement(string gamePath) |     public string ResolveFileReplacement(string gamePath) | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase | |||||||
|     private readonly PerformanceCollectorService _performanceCollector; |     private readonly PerformanceCollectorService _performanceCollector; | ||||||
|     private long _currentFileProgress = 0; |     private long _currentFileProgress = 0; | ||||||
|     private bool _fileScanWasRunning = false; |     private bool _fileScanWasRunning = false; | ||||||
|     private CancellationTokenSource? _scanCancellationTokenSource; |     private CancellationTokenSource _scanCancellationTokenSource = new(); | ||||||
|     private TimeSpan _timeUntilNextScan = TimeSpan.Zero; |     private TimeSpan _timeUntilNextScan = TimeSpan.Zero; | ||||||
|  |  | ||||||
|     public PeriodicFileScanner(ILogger<PeriodicFileScanner> logger, IpcManager ipcManager, MareConfigService configService, |     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"); |             Logger.LogWarning(ex, "Error during enumerating FileCaches"); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         Task.WaitAll(dbTasks); |         Task.WaitAll(dbTasks, _scanCancellationTokenSource.Token); | ||||||
|  |  | ||||||
|         if (!_ipcManager.CheckPenumbraApi()) |         if (!_ipcManager.CheckPenumbraApi()) | ||||||
|         { |         { | ||||||
| @@ -308,7 +308,7 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase | |||||||
|             if (ct.IsCancellationRequested) return; |             if (ct.IsCancellationRequested) return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         Task.WaitAll(dbTasks); |         Task.WaitAll(dbTasks, _scanCancellationTokenSource.Token); | ||||||
|  |  | ||||||
|         Logger.LogTrace("Scanner added new files to db"); |         Logger.LogTrace("Scanner added new files to db"); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ using Newtonsoft.Json; | |||||||
|  |  | ||||||
| namespace MareSynchronos.MareConfiguration; | 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 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 | public class ConfigurationMigrator : IHostedService | ||||||
| { | { | ||||||
| @@ -22,7 +23,7 @@ public class ConfigurationMigrator : IHostedService | |||||||
|  |  | ||||||
|     public void Migrate() |     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"); |             _logger.LogInformation("Migrating Configuration from old config style to 1"); | ||||||
|  |  | ||||||
| @@ -52,11 +53,15 @@ public class ConfigurationMigrator : IHostedService | |||||||
|         { |         { | ||||||
|             try |             try | ||||||
|             { |             { | ||||||
|                 var serverConfig = JsonConvert.DeserializeObject<ServerConfigV0>(File.ReadAllText(ConfigurationPath(ServerConfigService.ConfigName)))!; |                 var content = File.ReadAllText(ConfigurationPath(ServerConfigService.ConfigName)); | ||||||
|  |                 if (!content.Contains("\"Version\": 1")) | ||||||
|                 if (serverConfig.Version == 0) |  | ||||||
|                 { |                 { | ||||||
|                     MigrateServerConfigV0toV1(serverConfig); |                     var serverConfig = JsonConvert.DeserializeObject<ServerConfigV0>(content); | ||||||
|  |  | ||||||
|  |                     if (serverConfig != null && serverConfig.Version == 0) | ||||||
|  |                     { | ||||||
|  |                         MigrateServerConfigV0toV1(serverConfig); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             catch (Exception ex) |             catch (Exception ex) | ||||||
| @@ -92,7 +97,6 @@ public class ConfigurationMigrator : IHostedService | |||||||
|  |  | ||||||
|         MareConfig mareConfigV1 = mareConfigV0.ToV1(); |         MareConfig mareConfigV1 = mareConfigV0.ToV1(); | ||||||
|  |  | ||||||
|         int i = 0; |  | ||||||
|         var serverConfig = new ServerConfig() |         var serverConfig = new ServerConfig() | ||||||
|         { |         { | ||||||
|             ServerStorage = mareConfigV0.ServerStorage.Select(p => p.Value.ToV1()).ToList() |             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 | #pragma warning restore CS0618 // ignore Obsolete tag, the point of this migrator is to migrate obsolete configs to new ones | ||||||
| @@ -10,6 +10,7 @@ public class MareConfig : IMareConfiguration | |||||||
|     public string CacheFolder { get; set; } = string.Empty; |     public string CacheFolder { get; set; } = string.Empty; | ||||||
|     public bool DisableOptionalPluginWarnings { get; set; } = false; |     public bool DisableOptionalPluginWarnings { get; set; } = false; | ||||||
|     public NotificationLocation ErrorNotification { get; set; } = NotificationLocation.Both; |     public NotificationLocation ErrorNotification { get; set; } = NotificationLocation.Both; | ||||||
|  |     public string ExportFolder { get; set; } = string.Empty; | ||||||
|     public bool FileScanPaused { get; set; } = false; |     public bool FileScanPaused { get; set; } = false; | ||||||
|     public NotificationLocation InfoNotification { get; set; } = NotificationLocation.Toast; |     public NotificationLocation InfoNotification { get; set; } = NotificationLocation.Toast; | ||||||
|     public bool InitialScanComplete { get; set; } = false; |     public bool InitialScanComplete { get; set; } = false; | ||||||
| @@ -19,6 +20,9 @@ public class MareConfig : IMareConfiguration | |||||||
|     public bool OpenGposeImportOnGposeStart { get; set; } = false; |     public bool OpenGposeImportOnGposeStart { get; set; } = false; | ||||||
|     public bool OpenPopupOnAdd { get; set; } = true; |     public bool OpenPopupOnAdd { get; set; } = true; | ||||||
|     public int ParallelDownloads { get; set; } = 10; |     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 ReverseUserSort { get; set; } = false; | ||||||
|     public bool ShowCharacterNameInsteadOfNotesForVisible { get; set; } = false; |     public bool ShowCharacterNameInsteadOfNotesForVisible { get; set; } = false; | ||||||
|     public bool ShowOfflineUsersSeparately { get; set; } = true; |     public bool ShowOfflineUsersSeparately { get; set; } = true; | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ using MareSynchronos.WebAPI; | |||||||
| namespace MareSynchronos.MareConfiguration.Configurations.Obsolete; | namespace MareSynchronos.MareConfiguration.Configurations.Obsolete; | ||||||
|  |  | ||||||
| [Serializable] | [Serializable] | ||||||
| [Obsolete] | [Obsolete("Replaced with ServerConfig")] | ||||||
| public class ServerConfigV0 : IMareConfiguration | public class ServerConfigV0 : IMareConfiguration | ||||||
| { | { | ||||||
|     public string CurrentServer { get; set; } = string.Empty; |     public string CurrentServer { get; set; } = string.Empty; | ||||||
| @@ -15,4 +15,4 @@ public class ServerConfigV0 : IMareConfiguration | |||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     public int Version { get; set; } = 0; |     public int Version { get; set; } = 0; | ||||||
| } | } | ||||||
| @@ -3,7 +3,7 @@ | |||||||
|   <PropertyGroup> |   <PropertyGroup> | ||||||
|     <Authors></Authors> |     <Authors></Authors> | ||||||
|     <Company></Company> |     <Company></Company> | ||||||
|     <Version>0.8.8</Version> |     <Version>0.8.10</Version> | ||||||
|     <Description></Description> |     <Description></Description> | ||||||
|     <Copyright></Copyright> |     <Copyright></Copyright> | ||||||
|     <PackageProjectUrl>https://github.com/Penumbra-Sync/client</PackageProjectUrl> |     <PackageProjectUrl>https://github.com/Penumbra-Sync/client</PackageProjectUrl> | ||||||
| @@ -37,6 +37,7 @@ | |||||||
|     <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" /> |     <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" /> | ||||||
|     <PackageReference Include="Penumbra.Api" Version="1.0.7" /> |     <PackageReference Include="Penumbra.Api" Version="1.0.7" /> | ||||||
|     <PackageReference Include="Penumbra.String" Version="1.0.3" /> |     <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"> |     <PackageReference Include="SonarAnalyzer.CSharp" Version="8.54.0.64047"> | ||||||
|       <PrivateAssets>all</PrivateAssets> |       <PrivateAssets>all</PrivateAssets> | ||||||
|       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||||
|   | |||||||
| @@ -84,8 +84,8 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase | |||||||
|  |  | ||||||
|     public IntPtr Address { get; set; } |     public IntPtr Address { get; set; } | ||||||
|     public unsafe Character* Character => (Character*)Address; |     public unsafe Character* Character => (Character*)Address; | ||||||
|     public Lazy<Dalamud.Game.ClientState.Objects.Types.GameObject?> GameObjectLazy { get; private set; } |  | ||||||
|     public IntPtr CurrentAddress => _getAddress.Invoke(); |     public IntPtr CurrentAddress => _getAddress.Invoke(); | ||||||
|  |     public Lazy<Dalamud.Game.ClientState.Objects.Types.GameObject?> GameObjectLazy { get; private set; } | ||||||
|     public string Name { get; private set; } |     public string Name { get; private set; } | ||||||
|     public ObjectKind ObjectKind { get; } |     public ObjectKind ObjectKind { get; } | ||||||
|     private byte[] CustomizeData { get; set; } = new byte[26]; |     private byte[] CustomizeData { get; set; } = new byte[26]; | ||||||
| @@ -101,6 +101,8 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase | |||||||
|             { |             { | ||||||
|                 curPtr = _getAddress.Invoke(); |                 curPtr = _getAddress.Invoke(); | ||||||
|  |  | ||||||
|  |                 if (curPtr == IntPtr.Zero) return true; | ||||||
|  |  | ||||||
|                 var drawObj = GetDrawObj(curPtr); |                 var drawObj = GetDrawObj(curPtr); | ||||||
|                 return IsBeingDrawn(drawObj, curPtr); |                 return IsBeingDrawn(drawObj, curPtr); | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -158,21 +158,29 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase | |||||||
|             _charaHandler?.Dispose(); |             _charaHandler?.Dispose(); | ||||||
|             _charaHandler = null; |             _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); |                 if (_dalamudUtil.IsZoning) | ||||||
|                 _ipcManager.PenumbraRemoveTemporaryCollection(Logger, applicationId, name); |  | ||||||
|  |  | ||||||
|                 foreach (KeyValuePair<ObjectKind, List<FileReplacementData>> item in _cachedData.FileReplacements) |  | ||||||
|                 { |                 { | ||||||
|                     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(); |                         try | ||||||
|                     } |                         { | ||||||
|                     catch (InvalidOperationException ex) |                             RevertCustomizationData(item.Key, name, applicationId).GetAwaiter().GetResult(); | ||||||
|                     { |                         } | ||||||
|                         Logger.LogWarning("Failed disposing player (not present anymore?)", ex); |                         catch (InvalidOperationException ex) | ||||||
|                         break; |                         { | ||||||
|  |                             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) |             while ((!_applicationTask?.IsCompleted ?? false) && !downloadToken.IsCancellationRequested && !_applicationCancellationTokenSource.IsCancellationRequested) | ||||||
|             { |             { | ||||||
|                 // block until current application is done |                 // 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); |                 await Task.Delay(250).ConfigureAwait(false); | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -419,22 +427,34 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase | |||||||
|             var token = _applicationCancellationTokenSource.Token; |             var token = _applicationCancellationTokenSource.Token; | ||||||
|             _applicationTask = Task.Run(async () => |             _applicationTask = Task.Run(async () => | ||||||
|             { |             { | ||||||
|                 _applicationId = Guid.NewGuid(); |                 try | ||||||
|                 Logger.LogDebug("[{applicationId}] Starting application task", _applicationId); |  | ||||||
|  |  | ||||||
|                 if (updateModdedPaths && (moddedPaths.Any() || !string.IsNullOrEmpty(charaData.ManipulationData))) |  | ||||||
|                 { |                 { | ||||||
|                     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); | ||||||
|                 } |                 } | ||||||
|  |                 catch (Exception ex) | ||||||
|                 token.ThrowIfCancellationRequested(); |  | ||||||
|  |  | ||||||
|                 foreach (var kind in updatedData) |  | ||||||
|                 { |                 { | ||||||
|                     await ApplyCustomizationData(_applicationId, kind, charaData, token).ConfigureAwait(false); |                     Logger.LogInformation("[{applicationId}] Cancelled, reason: {msg}", _applicationId, ex.Message); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 Logger.LogDebug("[{applicationId}] Application finished", _applicationId); |  | ||||||
|             }, token); |             }, token); | ||||||
|         }, downloadToken); |         }, downloadToken); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -99,6 +99,7 @@ public sealed class PairManager : DisposableMediatorSubscriberBase | |||||||
|     { |     { | ||||||
|         if (_allClientPairs.TryGetValue(user, out var pair)) |         if (_allClientPairs.TryGetValue(user, out var pair)) | ||||||
|         { |         { | ||||||
|  |             Mediator.Publish(new ClearProfileDataMessage(pair.UserData)); | ||||||
|             pair.MarkOffline(); |             pair.MarkOffline(); | ||||||
|             RecreateLazy(); |             RecreateLazy(); | ||||||
|         } |         } | ||||||
| @@ -107,6 +108,9 @@ public sealed class PairManager : DisposableMediatorSubscriberBase | |||||||
|     public void MarkPairOnline(OnlineUserIdentDto dto, bool sendNotif = true) |     public void MarkPairOnline(OnlineUserIdentDto dto, bool sendNotif = true) | ||||||
|     { |     { | ||||||
|         if (!_allClientPairs.ContainsKey(dto.User)) throw new InvalidOperationException("No user found for " + dto); |         if (!_allClientPairs.ContainsKey(dto.User)) throw new InvalidOperationException("No user found for " + dto); | ||||||
|  |  | ||||||
|  |         Mediator.Publish(new ClearProfileDataMessage(dto.User)); | ||||||
|  |  | ||||||
|         var pair = _allClientPairs[dto.User]; |         var pair = _allClientPairs[dto.User]; | ||||||
|         if (pair.HasCachedPlayer) return; |         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 == 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; |         pair.UserPair.OtherPermissions = dto.Permissions; | ||||||
|  |  | ||||||
|         Logger.LogTrace("Paired: {synced}, Paused: {paused}, Anims: {anims}, Sounds: {sounds}", |         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 == 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; |         pair.UserPair.OwnPermissions = dto.Permissions; | ||||||
|  |  | ||||||
|         Logger.LogTrace("Paired: {synced}, Paused: {paused}, Anims: {anims}, Sounds: {sounds}", |         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) |     private void DisposePairs(bool recreate = false) | ||||||
|     { |     { | ||||||
|         Logger.LogDebug("Disposing all Pairs"); |         Logger.LogDebug("Disposing all Pairs"); | ||||||
|         foreach (var item in _allClientPairs) |         Parallel.ForEach(_allClientPairs, item => | ||||||
|         { |         { | ||||||
|             if (recreate) |             if (recreate) | ||||||
|                 item.Value.RecreateCachedPlayer(); |                 item.Value.RecreateCachedPlayer(); | ||||||
|             else |             else | ||||||
|                 item.Value.MarkOffline(); |                 item.Value.MarkOffline(); | ||||||
|         } |         }); | ||||||
|  |  | ||||||
|         RecreateLazy(); |         RecreateLazy(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,6 +22,7 @@ using MareSynchronos.Services; | |||||||
| using MareSynchronos.Services.Mediator; | using MareSynchronos.Services.Mediator; | ||||||
| using MareSynchronos.Services.ServerConfiguration; | using MareSynchronos.Services.ServerConfiguration; | ||||||
| using MareSynchronos.UI; | using MareSynchronos.UI; | ||||||
|  | using MareSynchronos.UI.Handlers; | ||||||
| using MareSynchronos.WebAPI; | using MareSynchronos.WebAPI; | ||||||
| using MareSynchronos.WebAPI.Files; | using MareSynchronos.WebAPI.Files; | ||||||
| using MareSynchronos.WebAPI.SignalR; | using MareSynchronos.WebAPI.SignalR; | ||||||
| @@ -65,6 +66,11 @@ public sealed class Plugin : IDalamudPlugin | |||||||
|             collection.AddSingleton<FileUploadManager>(); |             collection.AddSingleton<FileUploadManager>(); | ||||||
|             collection.AddSingleton<FileTransferOrchestrator>(); |             collection.AddSingleton<FileTransferOrchestrator>(); | ||||||
|             collection.AddSingleton<MarePlugin>(); |             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>>(), |             collection.AddSingleton((s) => new DalamudUtilService(s.GetRequiredService<ILogger<DalamudUtilService>>(), | ||||||
|                 clientState, objectTable, framework, gameGui, condition, gameData, |                 clientState, objectTable, framework, gameGui, condition, gameData, | ||||||
|                 s.GetRequiredService<MareMediator>(), s.GetRequiredService<PerformanceCollectorService>())); |                 s.GetRequiredService<MareMediator>(), s.GetRequiredService<PerformanceCollectorService>())); | ||||||
| @@ -117,6 +123,9 @@ public sealed class Plugin : IDalamudPlugin | |||||||
|             collection.AddScoped<WindowMediatorSubscriberBase, GposeUi>(); |             collection.AddScoped<WindowMediatorSubscriberBase, GposeUi>(); | ||||||
|             collection.AddScoped<WindowMediatorSubscriberBase, IntroUi>(); |             collection.AddScoped<WindowMediatorSubscriberBase, IntroUi>(); | ||||||
|             collection.AddScoped<WindowMediatorSubscriberBase, DownloadUi>(); |             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<CacheCreationService>(); | ||||||
|             collection.AddScoped<TransientResourceManager>(); |             collection.AddScoped<TransientResourceManager>(); | ||||||
|             collection.AddScoped<PlayerDataFactory>(); |             collection.AddScoped<PlayerDataFactory>(); | ||||||
|   | |||||||
| @@ -55,6 +55,12 @@ public sealed class CommandManagerService : IDisposable | |||||||
|  |  | ||||||
|         if (string.Equals(splitArgs[0], "toggle", StringComparison.OrdinalIgnoreCase)) |         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; |             if (_serverConfigurationManager.CurrentServer == null) return; | ||||||
|             var fullPause = splitArgs.Length > 1 ? splitArgs[1] switch |             var fullPause = splitArgs.Length > 1 ? splitArgs[1] switch | ||||||
|             { |             { | ||||||
|   | |||||||
							
								
								
									
										80
									
								
								MareSynchronos/Services/MareProfileManager.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								MareSynchronos/Services/MareProfileManager.cs
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -1,5 +1,6 @@ | |||||||
| using Dalamud.Game.ClientState.Objects.Types; | using Dalamud.Game.ClientState.Objects.Types; | ||||||
| using Dalamud.Interface.Internal.Notifications; | using Dalamud.Interface.Internal.Notifications; | ||||||
|  | using MareSynchronos.API.Data; | ||||||
| using MareSynchronos.API.Dto; | using MareSynchronos.API.Dto; | ||||||
| using MareSynchronos.PlayerData.Handlers; | using MareSynchronos.PlayerData.Handlers; | ||||||
| using MareSynchronos.WebAPI.Files.Models; | using MareSynchronos.WebAPI.Files.Models; | ||||||
| @@ -54,5 +55,6 @@ public record DownloadStartedMessage(GameObjectHandler DownloadId, Dictionary<st | |||||||
| public record DownloadFinishedMessage(GameObjectHandler DownloadId) : IMessage; | public record DownloadFinishedMessage(GameObjectHandler DownloadId) : IMessage; | ||||||
| public record UiToggleMessage(Type UiType) : IMessage; | public record UiToggleMessage(Type UiType) : IMessage; | ||||||
| public record PlayerUploadingMessage(GameObjectHandler Handler, bool IsUploading) : 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 | #pragma warning restore MA0048 // File name must match type name | ||||||
| @@ -23,17 +23,29 @@ public class ServerConfigurationManager | |||||||
|         _serverTagConfig = serverTagConfig; |         _serverTagConfig = serverTagConfig; | ||||||
|         _notesConfig = notesConfig; |         _notesConfig = notesConfig; | ||||||
|         _dalamudUtil = dalamudUtil; |         _dalamudUtil = dalamudUtil; | ||||||
|  |  | ||||||
|         if (_configService.Current.CurrentServer < 0) |  | ||||||
|         { |  | ||||||
|             _configService.Current.CurrentServer = 0; |  | ||||||
|             _configService.Save(); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public string CurrentApiUrl => CurrentServer.ServerUri; |     public string CurrentApiUrl => CurrentServer.ServerUri; | ||||||
|     public ServerStorage CurrentServer => _configService.Current.ServerStorage[CurrentServerIndex]; |     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) |     public string? GetSecretKey(int serverIdx = -1) | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -27,9 +27,6 @@ namespace MareSynchronos.UI; | |||||||
|  |  | ||||||
| public class CompactUi : WindowMediatorSubscriberBase | 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 TransferPartHeight; | ||||||
|     public float WindowContentWidth; |     public float WindowContentWidth; | ||||||
|     private readonly ApiController _apiController; |     private readonly ApiController _apiController; | ||||||
| @@ -43,6 +40,7 @@ public class CompactUi : WindowMediatorSubscriberBase | |||||||
|     private readonly SelectPairForGroupUi _selectPairsForGroupUi; |     private readonly SelectPairForGroupUi _selectPairsForGroupUi; | ||||||
|     private readonly ServerConfigurationManager _serverManager; |     private readonly ServerConfigurationManager _serverManager; | ||||||
|     private readonly Stopwatch _timeout = new(); |     private readonly Stopwatch _timeout = new(); | ||||||
|  |     private readonly UidDisplayHandler _uidDisplayHandler; | ||||||
|     private readonly UiSharedService _uiShared; |     private readonly UiSharedService _uiShared; | ||||||
|     private bool _buttonState; |     private bool _buttonState; | ||||||
|     private string _characterOrCommentFilter = string.Empty; |     private string _characterOrCommentFilter = string.Empty; | ||||||
| @@ -55,7 +53,7 @@ public class CompactUi : WindowMediatorSubscriberBase | |||||||
|     private bool _wasOpen; |     private bool _wasOpen; | ||||||
|  |  | ||||||
|     public CompactUi(ILogger<CompactUi> logger, UiSharedService uiShared, MareConfigService configService, ApiController apiController, PairManager pairManager, |     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; |         _uiShared = uiShared; | ||||||
|         _configService = configService; |         _configService = configService; | ||||||
| @@ -63,12 +61,13 @@ public class CompactUi : WindowMediatorSubscriberBase | |||||||
|         _pairManager = pairManager; |         _pairManager = pairManager; | ||||||
|         _serverManager = serverManager; |         _serverManager = serverManager; | ||||||
|         _fileTransferManager = fileTransferManager; |         _fileTransferManager = fileTransferManager; | ||||||
|  |         _uidDisplayHandler = uidDisplayHandler; | ||||||
|         var tagHandler = new TagHandler(_serverManager); |         var tagHandler = new TagHandler(_serverManager); | ||||||
|  |  | ||||||
|         _groupPanel = new(this, uiShared, _pairManager, _serverManager, _configService); |         _groupPanel = new(this, uiShared, _pairManager, uidDisplayHandler, _serverManager); | ||||||
|         _selectGroupForPairUi = new(tagHandler); |         _selectGroupForPairUi = new(tagHandler, uidDisplayHandler); | ||||||
|         _selectPairsForGroupUi = new(tagHandler); |         _selectPairsForGroupUi = new(tagHandler, uidDisplayHandler); | ||||||
|         _pairGroupsUi = new(configService, tagHandler, DrawPairedClient, apiController, _selectPairsForGroupUi); |         _pairGroupsUi = new(configService, tagHandler, apiController, _selectPairsForGroupUi); | ||||||
|  |  | ||||||
| #if DEBUG | #if DEBUG | ||||||
|         string dev = "Dev Build"; |         string dev = "Dev Build"; | ||||||
| @@ -164,8 +163,8 @@ public class CompactUi : WindowMediatorSubscriberBase | |||||||
|             ImGui.Separator(); |             ImGui.Separator(); | ||||||
|             UiSharedService.DrawWithID("transfers", DrawTransfers); |             UiSharedService.DrawWithID("transfers", DrawTransfers); | ||||||
|             TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight; |             TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight; | ||||||
|             UiSharedService.DrawWithID("group-user-popup", () => _selectPairsForGroupUi.Draw(_pairManager.DirectPairs, ShowUidForEntry)); |             UiSharedService.DrawWithID("group-user-popup", () => _selectPairsForGroupUi.Draw(_pairManager.DirectPairs)); | ||||||
|             UiSharedService.DrawWithID("grouping-popup", () => _selectGroupForPairUi.Draw(ShowUidForEntry)); |             UiSharedService.DrawWithID("grouping-popup", () => _selectGroupForPairUi.Draw()); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (_configService.Current.OpenPopupOnAdd && _pairManager.LastAddedUser != null) |         if (_configService.Current.OpenPopupOnAdd && _pairManager.LastAddedUser != null) | ||||||
| @@ -202,8 +201,7 @@ public class CompactUi : WindowMediatorSubscriberBase | |||||||
|  |  | ||||||
|     public override void OnClose() |     public override void OnClose() | ||||||
|     { |     { | ||||||
|         EditNickEntry = string.Empty; |         _uidDisplayHandler.Clear(); | ||||||
|         EditUserComment = string.Empty; |  | ||||||
|         base.OnClose(); |         base.OnClose(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -213,8 +211,6 @@ public class CompactUi : WindowMediatorSubscriberBase | |||||||
|         var keys = _serverManager.CurrentServer!.SecretKeys; |         var keys = _serverManager.CurrentServer!.SecretKeys; | ||||||
|         if (keys.TryGetValue(_secretKeyIdx, out var secretKey)) |         if (keys.TryGetValue(_secretKeyIdx, out var secretKey)) | ||||||
|         { |         { | ||||||
|             var friendlyName = secretKey.FriendlyName; |  | ||||||
|  |  | ||||||
|             if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Add current character with secret key")) |             if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Add current character with secret key")) | ||||||
|             { |             { | ||||||
|                 _serverManager.CurrentServer!.Authentications.Add(new MareConfiguration.Models.Authentication() |                 _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) |     private void DrawPairedClientMenu(Pair entry) | ||||||
|     { |     { | ||||||
|         if (entry.IsVisible) |         if (entry.IsVisible) | ||||||
| @@ -624,9 +416,9 @@ public class CompactUi : WindowMediatorSubscriberBase | |||||||
|             users.Reverse(); |             users.Reverse(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         var onlineUsers = users.Where(u => u.IsOnline || u.UserPair.OwnPermissions.IsPaused()).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 = onlineUsers.Where(u => u.IsVisible).ToList(); |         var visibleUsers = users.Where(u => u.IsVisible).Select(c => new DrawUserPair("Visible" + c.UserData.UID, c, _uidDisplayHandler, _apiController, _selectGroupForPairUi)).ToList(); | ||||||
|         var offlineUsers = users.Except(onlineUsers).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); |         ImGui.BeginChild("list", new Vector2(WindowContentWidth, ySize), border: false); | ||||||
|  |  | ||||||
| @@ -651,7 +443,7 @@ public class CompactUi : WindowMediatorSubscriberBase | |||||||
|  |  | ||||||
|         if (_apiController.ServerState is ServerState.Connected) |         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(); |             if (!printShard) ImGui.AlignTextToFramePadding(); | ||||||
|             ImGui.TextColored(ImGuiColors.ParsedGreen, userCount); |             ImGui.TextColored(ImGuiColors.ParsedGreen, userCount); | ||||||
|             ImGui.SameLine(); |             ImGui.SameLine(); | ||||||
| @@ -667,17 +459,33 @@ public class CompactUi : WindowMediatorSubscriberBase | |||||||
|         if (printShard) |         if (printShard) | ||||||
|         { |         { | ||||||
|             ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ImGui.GetStyle().ItemSpacing.Y); |             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.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); |         ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - buttonSize.X); | ||||||
|         if (printShard) |         if (printShard) | ||||||
|         { |         { | ||||||
|             ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ((userSize.Y + textSize.Y) / 2 + shardTextSize.Y) / 2 - ImGui.GetStyle().ItemSpacing.Y + buttonSize.Y / 2); |             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)) |         if (_apiController.ServerState is not (ServerState.Reconnecting or ServerState.Disconnecting)) | ||||||
|         { |         { | ||||||
|   | |||||||
							
								
								
									
										308
									
								
								MareSynchronos/UI/Components/DrawGroupPair.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										308
									
								
								MareSynchronos/UI/Components/DrawGroupPair.cs
									
									
									
									
									
										Normal 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; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										84
									
								
								MareSynchronos/UI/Components/DrawPairBase.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								MareSynchronos/UI/Components/DrawPairBase.cs
									
									
									
									
									
										Normal 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); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										222
									
								
								MareSynchronos/UI/Components/DrawUserPair.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								MareSynchronos/UI/Components/DrawUserPair.cs
									
									
									
									
									
										Normal 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"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,5 +1,4 @@ | |||||||
| using Dalamud.Interface.Colors; | using Dalamud.Interface.Components; | ||||||
| using Dalamud.Interface.Components; |  | ||||||
| using Dalamud.Interface; | using Dalamud.Interface; | ||||||
| using Dalamud.Utility; | using Dalamud.Utility; | ||||||
| using ImGuiNET; | using ImGuiNET; | ||||||
| @@ -8,28 +7,26 @@ using System.Numerics; | |||||||
| using System.Globalization; | using System.Globalization; | ||||||
| using MareSynchronos.API.Data; | using MareSynchronos.API.Data; | ||||||
| using MareSynchronos.API.Dto.Group; | using MareSynchronos.API.Dto.Group; | ||||||
| using MareSynchronos.API.Dto.User; |  | ||||||
| using MareSynchronos.API.Data.Enum; | using MareSynchronos.API.Data.Enum; | ||||||
| using MareSynchronos.API.Data.Extensions; | using MareSynchronos.API.Data.Extensions; | ||||||
| using MareSynchronos.API.Data.Comparer; | using MareSynchronos.API.Data.Comparer; | ||||||
| using MareSynchronos.MareConfiguration; |  | ||||||
| using MareSynchronos.PlayerData.Pairs; | using MareSynchronos.PlayerData.Pairs; | ||||||
| using MareSynchronos.Services.ServerConfiguration; | using MareSynchronos.Services.ServerConfiguration; | ||||||
|  | using MareSynchronos.UI.Components; | ||||||
|  | using MareSynchronos.UI.Handlers; | ||||||
|  |  | ||||||
| namespace MareSynchronos.UI; | namespace MareSynchronos.UI; | ||||||
|  |  | ||||||
| internal sealed class GroupPanel | internal sealed class GroupPanel | ||||||
| { | { | ||||||
|     private readonly MareConfigService _configService; |  | ||||||
|     private readonly Dictionary<string, bool> _expandedGroupState = new(StringComparer.Ordinal); |     private readonly Dictionary<string, bool> _expandedGroupState = new(StringComparer.Ordinal); | ||||||
|     private readonly CompactUi _mainUi; |     private readonly CompactUi _mainUi; | ||||||
|     private readonly PairManager _pairManager; |     private readonly PairManager _pairManager; | ||||||
|     private readonly ServerConfigurationManager _serverConfigurationManager; |     private readonly ServerConfigurationManager _serverConfigurationManager; | ||||||
|     private readonly Dictionary<string, bool> _showGidForEntry = new(StringComparer.Ordinal); |     private readonly Dictionary<string, bool> _showGidForEntry = new(StringComparer.Ordinal); | ||||||
|  |     private readonly UidDisplayHandler _uidDisplayHandler; | ||||||
|     private readonly UiSharedService _uiShared; |     private readonly UiSharedService _uiShared; | ||||||
|     private List<BannedGroupUserDto> _bannedUsers = new(); |     private List<BannedGroupUserDto> _bannedUsers = new(); | ||||||
|     private string _banReason = string.Empty; |  | ||||||
|     private bool _banUserPopupOpen; |  | ||||||
|     private int _bulkInviteCount = 10; |     private int _bulkInviteCount = 10; | ||||||
|     private List<string> _bulkOneTimeInvites = new(); |     private List<string> _bulkOneTimeInvites = new(); | ||||||
|     private string _editGroupComment = string.Empty; |     private string _editGroupComment = string.Empty; | ||||||
| @@ -43,7 +40,6 @@ internal sealed class GroupPanel | |||||||
|     private bool _modalChangePwOpened; |     private bool _modalChangePwOpened; | ||||||
|     private string _newSyncShellPassword = string.Empty; |     private string _newSyncShellPassword = string.Empty; | ||||||
|     private bool _showModalBanList = false; |     private bool _showModalBanList = false; | ||||||
|     private bool _showModalBanUser; |  | ||||||
|     private bool _showModalBulkOneTimeInvites = false; |     private bool _showModalBulkOneTimeInvites = false; | ||||||
|     private bool _showModalChangePassword; |     private bool _showModalChangePassword; | ||||||
|     private bool _showModalCreateGroup; |     private bool _showModalCreateGroup; | ||||||
| @@ -51,13 +47,13 @@ internal sealed class GroupPanel | |||||||
|     private string _syncShellPassword = string.Empty; |     private string _syncShellPassword = string.Empty; | ||||||
|     private string _syncShellToJoin = 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; |         _mainUi = mainUi; | ||||||
|         _uiShared = uiShared; |         _uiShared = uiShared; | ||||||
|         _pairManager = pairManager; |         _pairManager = pairManager; | ||||||
|  |         _uidDisplayHandler = uidDisplayHandler; | ||||||
|         _serverConfigurationManager = serverConfigurationManager; |         _serverConfigurationManager = serverConfigurationManager; | ||||||
|         _configService = configurationService; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private ApiController ApiController => _uiShared.ApiController; |     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.IsModerator()) | ||||||
|                 .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned()) |                 .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned()) | ||||||
|                 .ThenBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase) |                 .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(); |                 .ToList(); | ||||||
|             var onlineUsers = pairsInGroup.Where(u => u.IsOnline && !u.IsVisible) |             var onlineUsers = pairsInGroup.Where(u => u.IsOnline && !u.IsVisible) | ||||||
|                 .OrderByDescending(u => string.Equals(u.UserData.UID, groupDto.OwnerUID, StringComparison.Ordinal)) |                 .OrderByDescending(u => string.Equals(u.UserData.UID, groupDto.OwnerUID, StringComparison.Ordinal)) | ||||||
|                 .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsModerator()) |                 .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsModerator()) | ||||||
|                 .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned()) |                 .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned()) | ||||||
|                 .ThenBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase) |                 .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(); |                 .ToList(); | ||||||
|             var offlineUsers = pairsInGroup.Where(u => !u.IsOnline && !u.IsVisible) |             var offlineUsers = pairsInGroup.Where(u => !u.IsOnline && !u.IsVisible) | ||||||
|                 .OrderByDescending(u => string.Equals(u.UserData.UID, groupDto.OwnerUID, StringComparison.Ordinal)) |                 .OrderByDescending(u => string.Equals(u.UserData.UID, groupDto.OwnerUID, StringComparison.Ordinal)) | ||||||
|                 .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsModerator()) |                 .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsModerator()) | ||||||
|                 .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned()) |                 .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned()) | ||||||
|                 .ThenBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase) |                 .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(); |                 .ToList(); | ||||||
|  |  | ||||||
|             if (visibleUsers.Any()) |             if (visibleUsers.Any()) | ||||||
| @@ -426,12 +428,7 @@ internal sealed class GroupPanel | |||||||
|                 ImGui.Separator(); |                 ImGui.Separator(); | ||||||
|                 foreach (var entry in visibleUsers) |                 foreach (var entry in visibleUsers) | ||||||
|                 { |                 { | ||||||
|                     UiSharedService.DrawWithID(groupDto.GID + entry.UserData.UID, () => DrawSyncshellPairedClient( |                     UiSharedService.DrawWithID(groupDto.GID + entry.UID, () => entry.DrawPairedClient()); | ||||||
|                         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())); |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -441,12 +438,7 @@ internal sealed class GroupPanel | |||||||
|                 ImGui.Separator(); |                 ImGui.Separator(); | ||||||
|                 foreach (var entry in onlineUsers) |                 foreach (var entry in onlineUsers) | ||||||
|                 { |                 { | ||||||
|                     UiSharedService.DrawWithID(groupDto.GID + entry.UserData.UID, () => DrawSyncshellPairedClient( |                     UiSharedService.DrawWithID(groupDto.GID + entry.UID, () => entry.DrawPairedClient()); | ||||||
|                         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())); |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -456,12 +448,7 @@ internal sealed class GroupPanel | |||||||
|                 ImGui.Separator(); |                 ImGui.Separator(); | ||||||
|                 foreach (var entry in offlineUsers) |                 foreach (var entry in offlineUsers) | ||||||
|                 { |                 { | ||||||
|                     UiSharedService.DrawWithID(groupDto.GID + entry.UserData.UID, () => DrawSyncshellPairedClient( |                     UiSharedService.DrawWithID(groupDto.GID + entry.UID, () => entry.DrawPairedClient()); | ||||||
|                         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())); |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -724,337 +711,4 @@ internal sealed class GroupPanel | |||||||
|         } |         } | ||||||
|         ImGui.EndChild(); |         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(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| @@ -3,7 +3,6 @@ using Dalamud.Interface.Components; | |||||||
| using ImGuiNET; | using ImGuiNET; | ||||||
| using MareSynchronos.API.Data.Extensions; | using MareSynchronos.API.Data.Extensions; | ||||||
| using MareSynchronos.MareConfiguration; | using MareSynchronos.MareConfiguration; | ||||||
| using MareSynchronos.PlayerData.Pairs; |  | ||||||
| using MareSynchronos.UI.Handlers; | using MareSynchronos.UI.Handlers; | ||||||
| using MareSynchronos.WebAPI; | using MareSynchronos.WebAPI; | ||||||
|  |  | ||||||
| @@ -12,58 +11,31 @@ namespace MareSynchronos.UI.Components; | |||||||
| public class PairGroupsUi | public class PairGroupsUi | ||||||
| { | { | ||||||
|     private readonly ApiController _apiController; |     private readonly ApiController _apiController; | ||||||
|     private readonly Action<Pair> _clientRenderFn; |  | ||||||
|     private readonly MareConfigService _mareConfig; |     private readonly MareConfigService _mareConfig; | ||||||
|     private readonly SelectPairForGroupUi _selectGroupForPairUi; |     private readonly SelectPairForGroupUi _selectGroupForPairUi; | ||||||
|     private readonly TagHandler _tagHandler; |     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; |         _mareConfig = mareConfig; | ||||||
|         _tagHandler = tagHandler; |         _tagHandler = tagHandler; | ||||||
|         _apiController = apiController; |         _apiController = apiController; | ||||||
|         _selectGroupForPairUi = selectGroupForPairUi; |         _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 |         // Only render those tags that actually have pairs in them, otherwise | ||||||
|         // we can end up with a bunch of useless pair groups |         // we can end up with a bunch of useless pair groups | ||||||
|         var tagsWithPairsInThem = _tagHandler.GetAllTagsSorted(); |         var tagsWithPairsInThem = _tagHandler.GetAllTagsSorted(); | ||||||
|         var allUsers = visibleUsers.Concat(onlineUsers).Concat(offlineUsers).ToList(); |         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 allArePaused = availablePairsInThisTag.All(pair => pair.UserPair!.OwnPermissions.IsPaused()); | ||||||
|         var pauseButton = allArePaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; |         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; |         HashSet<string>? otherUidsTaggedWithTag = null; | ||||||
|         bool isSpecialTag = false; |         bool isSpecialTag = false; | ||||||
|         int visibleInThisTag = 0; |         int visibleInThisTag = 0; | ||||||
| @@ -129,16 +101,21 @@ public class PairGroupsUi | |||||||
|         { |         { | ||||||
|             otherUidsTaggedWithTag = _tagHandler.GetOtherUidsForTag(tag); |             otherUidsTaggedWithTag = _tagHandler.GetOtherUidsForTag(tag); | ||||||
|             usersInThisTag = onlineUsers |             usersInThisTag = onlineUsers | ||||||
|                 .Where(pair => otherUidsTaggedWithTag.Contains(pair.UserData.UID)) |                 .Where(pair => otherUidsTaggedWithTag.Contains(pair.UID)) | ||||||
|                 .ToList(); |                 .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; |         if (isSpecialTag && !usersInThisTag.Any()) return; | ||||||
|  |  | ||||||
|         DrawName(tag, isSpecialTag, visibleInThisTag, usersInThisTag.Count, otherUidsTaggedWithTag?.Count); |         DrawName(tag, isSpecialTag, visibleInThisTag, usersInThisTag.Count(), otherUidsTaggedWithTag?.Count); | ||||||
|         if (!isSpecialTag) |         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; |         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 |         // These are all the OtherUIDs that are tagged with this tag | ||||||
|         availablePairsInThisCategory |         foreach (var pair in availablePairsInThisCategory) | ||||||
|             .ForEach(pair => UiSharedService.DrawWithID($"tag-{tag}-pair-${pair.UserData.UID}", () => _clientRenderFn(pair))); |         { | ||||||
|  |             UiSharedService.DrawWithID($"tag-{tag}-pair-${pair.UID}", () => pair.DrawPairedClient()); | ||||||
|  |         } | ||||||
|         ImGui.Separator(); |         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())) |         foreach (var pairToPause in availablePairs.Where(pair => !pair.UserPair!.OwnPermissions.IsPaused())) | ||||||
|         { |         { | ||||||
|             var perm = pairToPause.UserPair!.OwnPermissions; |             var perm = pairToPause.UserPair!.OwnPermissions; | ||||||
|             perm.SetPaused(paused: true); |             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) |         foreach (var pairToPause in availablePairs) | ||||||
|         { |         { | ||||||
|             var perm = pairToPause.UserPair!.OwnPermissions; |             var perm = pairToPause.UserPair!.OwnPermissions; | ||||||
|             perm.SetPaused(paused: false); |             perm.SetPaused(paused: false); | ||||||
|             _ = _apiController.UserSetPairPermissions(new(pairToPause.UserData, perm)); |             _ = _apiController.UserSetPairPermissions(new(new(pairToPause.UID), perm)); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ namespace MareSynchronos.UI.Components; | |||||||
| public class SelectGroupForPairUi | public class SelectGroupForPairUi | ||||||
| { | { | ||||||
|     private readonly TagHandler _tagHandler; |     private readonly TagHandler _tagHandler; | ||||||
|  |     private readonly UidDisplayHandler _uidDisplayHandler; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// The group UI is always open for a specific pair. This defines which pair the UI is open for. |     /// 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> |     /// </summary> | ||||||
|     private string _tagNameToAdd = ""; |     private string _tagNameToAdd = ""; | ||||||
|  |  | ||||||
|     public SelectGroupForPairUi(TagHandler tagHandler) |     public SelectGroupForPairUi(TagHandler tagHandler, UidDisplayHandler uidDisplayHandler) | ||||||
|     { |     { | ||||||
|         _show = false; |         _show = false; | ||||||
|         _pair = null; |         _pair = null; | ||||||
|         _tagHandler = tagHandler; |         _tagHandler = tagHandler; | ||||||
|  |         _uidDisplayHandler = uidDisplayHandler; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void Draw(Dictionary<string, bool> showUidForEntry) |     public void Draw() | ||||||
|     { |     { | ||||||
|         if (_pair == null) |         if (_pair == null) | ||||||
|         { |         { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         var name = PairName(showUidForEntry, _pair); |         var name = PairName(_pair); | ||||||
|         var popupName = $"Choose Groups for {name}"; |         var popupName = $"Choose Groups for {name}"; | ||||||
|         // Is the popup supposed to show but did not open yet? Open it |         // Is the popup supposed to show but did not open yet? Open it | ||||||
|         if (_show) |         if (_show) | ||||||
| @@ -93,30 +95,19 @@ public class SelectGroupForPairUi | |||||||
|         _show = true; |         _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) |     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; |         var hasTag = hasTagBefore; | ||||||
|         if (ImGui.Checkbox(name, ref hasTag)) |         if (ImGui.Checkbox(name, ref hasTag)) | ||||||
|         { |         { | ||||||
|             if (hasTag) |             if (hasTag) | ||||||
|             { |             { | ||||||
|                 _tagHandler.AddTagToPairedUid(pair.UserPair!, name); |                 _tagHandler.AddTagToPairedUid(pair.UserData.UID, name); | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
|             { |             { | ||||||
|                 _tagHandler.RemoveTagFromPairedUid(pair.UserPair!, name); |                 _tagHandler.RemoveTagFromPairedUid(pair.UserData.UID, name); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -128,7 +119,7 @@ public class SelectGroupForPairUi | |||||||
|             _tagHandler.AddTag(_tagNameToAdd); |             _tagHandler.AddTag(_tagNameToAdd); | ||||||
|             if (_pair != null) |             if (_pair != null) | ||||||
|             { |             { | ||||||
|                 _tagHandler.AddTagToPairedUid(_pair.UserPair!, _tagNameToAdd); |                 _tagHandler.AddTagToPairedUid(_pair.UserData.UID, _tagNameToAdd); | ||||||
|             } |             } | ||||||
|             _tagNameToAdd = string.Empty; |             _tagNameToAdd = string.Empty; | ||||||
|         } |         } | ||||||
| @@ -137,4 +128,9 @@ public class SelectGroupForPairUi | |||||||
|             _tagNameToAdd = string.Empty; |             _tagNameToAdd = string.Empty; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private string PairName(Pair pair) | ||||||
|  |     { | ||||||
|  |         return _uidDisplayHandler.GetPlayerText(pair).text; | ||||||
|  |     } | ||||||
| } | } | ||||||
| @@ -9,18 +9,20 @@ namespace MareSynchronos.UI.Components; | |||||||
| public class SelectPairForGroupUi | public class SelectPairForGroupUi | ||||||
| { | { | ||||||
|     private readonly TagHandler _tagHandler; |     private readonly TagHandler _tagHandler; | ||||||
|  |     private readonly UidDisplayHandler _uidDisplayHandler; | ||||||
|     private string _filter = string.Empty; |     private string _filter = string.Empty; | ||||||
|     private bool _opened = false; |     private bool _opened = false; | ||||||
|     private HashSet<string> _peopleInGroup = new(StringComparer.Ordinal); |     private HashSet<string> _peopleInGroup = new(StringComparer.Ordinal); | ||||||
|     private bool _show = false; |     private bool _show = false; | ||||||
|     private string _tag = string.Empty; |     private string _tag = string.Empty; | ||||||
|  |  | ||||||
|     public SelectPairForGroupUi(TagHandler tagHandler) |     public SelectPairForGroupUi(TagHandler tagHandler, UidDisplayHandler uidDisplayHandler) | ||||||
|     { |     { | ||||||
|         _tagHandler = tagHandler; |         _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 workHeight = ImGui.GetMainViewport().WorkSize.Y / ImGuiHelpers.GlobalScale; | ||||||
|         var minSize = new Vector2(300, workHeight < 400 ? workHeight : 400) * 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); |             UiSharedService.FontText($"Select users for group {_tag}", UiBuilder.DefaultFont); | ||||||
|             ImGui.InputTextWithHint("##filter", "Filter", ref _filter, 255, ImGuiInputTextFlags.None); |             ImGui.InputTextWithHint("##filter", "Filter", ref _filter, 255, ImGuiInputTextFlags.None); | ||||||
|             foreach (var item in pairs |             foreach (var item in pairs | ||||||
|                 .Where(p => string.IsNullOrEmpty(_filter) || PairName(showUidForEntry, p).Contains(_filter, StringComparison.OrdinalIgnoreCase)) |                 .Where(p => string.IsNullOrEmpty(_filter) || PairName(p).Contains(_filter, StringComparison.OrdinalIgnoreCase)) | ||||||
|                 .OrderBy(p => PairName(showUidForEntry, p), StringComparer.OrdinalIgnoreCase) |                 .OrderBy(p => PairName(p), StringComparer.OrdinalIgnoreCase) | ||||||
|                 .ToList()) |                 .ToList()) | ||||||
|             { |             { | ||||||
|                 var isInGroup = _peopleInGroup.Contains(item.UserData.UID); |                 var isInGroup = _peopleInGroup.Contains(item.UserData.UID); | ||||||
|                 if (ImGui.Checkbox(PairName(showUidForEntry, item), ref isInGroup)) |                 if (ImGui.Checkbox(PairName(item), ref isInGroup)) | ||||||
|                 { |                 { | ||||||
|                     if (isInGroup) |                     if (isInGroup) | ||||||
|                     { |                     { | ||||||
|                         _tagHandler.AddTagToPairedUid(item.UserPair!, _tag); |                         _tagHandler.AddTagToPairedUid(item.UserData.UID, _tag); | ||||||
|                         _peopleInGroup.Add(item.UserData.UID); |                         _peopleInGroup.Add(item.UserData.UID); | ||||||
|                     } |                     } | ||||||
|                     else |                     else | ||||||
|                     { |                     { | ||||||
|                         _tagHandler.RemoveTagFromPairedUid(item.UserPair!, _tag); |                         _tagHandler.RemoveTagFromPairedUid(item.UserData.UID, _tag); | ||||||
|                         _peopleInGroup.Remove(item.UserData.UID); |                         _peopleInGroup.Remove(item.UserData.UID); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| @@ -82,14 +84,8 @@ public class SelectPairForGroupUi | |||||||
|         _show = true; |         _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); |         return _uidDisplayHandler.GetPlayerText(pair).text; | ||||||
|         var playerText = pair.GetNote(); |  | ||||||
|         if (showUidInsteadOfName || string.IsNullOrEmpty(playerText)) |  | ||||||
|         { |  | ||||||
|             playerText = pair.UserData.AliasOrUID; |  | ||||||
|         } |  | ||||||
|         return playerText; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
							
								
								
									
										187
									
								
								MareSynchronos/UI/EditProfileUi.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								MareSynchronos/UI/EditProfileUi.cs
									
									
									
									
									
										Normal 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(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -43,12 +43,16 @@ public class GposeUi : WindowMediatorSubscriberBase | |||||||
|         { |         { | ||||||
|             if (UiSharedService.IconTextButton(FontAwesomeIcon.FolderOpen, "Load MCDF")) |             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 (!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)); |                     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"); |             UiSharedService.AttachToolTip("Applies it to the currently selected GPose actor"); | ||||||
|             if (_mareCharaFileManager.LoadedCharaFile != null) |             if (_mareCharaFileManager.LoadedCharaFile != null) | ||||||
|   | |||||||
| @@ -21,9 +21,9 @@ public class TagHandler | |||||||
|         _serverConfigurationManager.AddTag(tag); |         _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() |     public List<string> GetAllTagsSorted() | ||||||
| @@ -38,14 +38,14 @@ public class TagHandler | |||||||
|         return _serverConfigurationManager.GetUidsForTag(tag); |         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> |     /// <summary> | ||||||
| @@ -64,9 +64,9 @@ public class TagHandler | |||||||
|         _serverConfigurationManager.RemoveTag(tag); |         _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) |     public void SetTagOpen(string tag, bool open) | ||||||
|   | |||||||
							
								
								
									
										250
									
								
								MareSynchronos/UI/Handlers/UidDisplayHandler.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								MareSynchronos/UI/Handlers/UidDisplayHandler.cs
									
									
									
									
									
										Normal 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; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -362,10 +362,16 @@ public class SettingsUi : WindowMediatorSubscriberBase | |||||||
|                 ImGui.InputTextWithHint("Export Descriptor", "This description will be shown on loading the data", ref _exportDescription, 255); |                 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")) |                 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; |                         if (!success) return; | ||||||
|  |  | ||||||
|  |                         _configService.Current.ExportFolder = Path.GetDirectoryName(path) ?? string.Empty; | ||||||
|  |                         _configService.Save(); | ||||||
|  |  | ||||||
|                         Task.Run(() => |                         Task.Run(() => | ||||||
|                         { |                         { | ||||||
|                             try |                             try | ||||||
| @@ -378,7 +384,7 @@ public class SettingsUi : WindowMediatorSubscriberBase | |||||||
|                                 _logger.LogCritical(ex, "Error saving data"); |                                 _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" + |                 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); |                     " equipped and redraw your character before exporting.", ImGuiColors.DalamudYellow); | ||||||
| @@ -484,9 +490,11 @@ public class SettingsUi : WindowMediatorSubscriberBase | |||||||
|         ImGui.Separator(); |         ImGui.Separator(); | ||||||
|         UiSharedService.FontText("UI", _uiShared.UidFont); |         UiSharedService.FontText("UI", _uiShared.UidFont); | ||||||
|         var showNameInsteadOfNotes = _configService.Current.ShowCharacterNameInsteadOfNotesForVisible; |         var showNameInsteadOfNotes = _configService.Current.ShowCharacterNameInsteadOfNotesForVisible; | ||||||
|         var reverseUserSort = _configService.Current.ReverseUserSort; |  | ||||||
|         var showVisibleSeparate = _configService.Current.ShowVisibleUsersSeparately; |         var showVisibleSeparate = _configService.Current.ShowVisibleUsersSeparately; | ||||||
|         var showOfflineSeparate = _configService.Current.ShowOfflineUsersSeparately; |         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)) |         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"); |         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(); |             _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(); |         ImGui.Separator(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -161,13 +161,13 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase | |||||||
|  |  | ||||||
|     public static uint Color(Vector4 color) |     public static uint Color(Vector4 color) | ||||||
|     { |     { | ||||||
|         uint ret = (byte)(color.W); |         uint ret = (byte)(color.W * 255); | ||||||
|         ret <<= 8; |         ret <<= 8; | ||||||
|         ret += (byte)(color.X); |         ret += (byte)(color.Z * 255); | ||||||
|         ret <<= 8; |         ret <<= 8; | ||||||
|         ret += (byte)(color.Y); |         ret += (byte)(color.Y * 255); | ||||||
|         ret <<= 8; |         ret <<= 8; | ||||||
|         ret += (byte)(color.Z); |         ret += (byte)(color.X * 255); | ||||||
|         return ret; |         return ret; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -452,6 +452,13 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase | |||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public void BigText(string text) | ||||||
|  |     { | ||||||
|  |         if (UidFontBuilt) ImGui.PushFont(UidFont); | ||||||
|  |         ImGui.TextUnformatted(text); | ||||||
|  |         if (UidFontBuilt) ImGui.PopFont(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public void DrawCacheDirectorySetting() |     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); |         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); | ||||||
|   | |||||||
| @@ -242,7 +242,13 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase | |||||||
|                 fi.LastWriteTime = RandomDayInThePast().Invoke(); |                 fi.LastWriteTime = RandomDayInThePast().Invoke(); | ||||||
|                 try |                 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) |                 catch (Exception ex) | ||||||
|                 { |                 { | ||||||
|   | |||||||
| @@ -49,6 +49,12 @@ public partial class ApiController | |||||||
|         return await _mareHub!.InvokeAsync<List<UserPairDto>>(nameof(UserGetPairedClients)).ConfigureAwait(false); |         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) |     public async Task UserPushData(UserCharaDataMessageDto dto) | ||||||
|     { |     { | ||||||
|         try |         try | ||||||
| @@ -67,11 +73,23 @@ public partial class ApiController | |||||||
|         await _mareHub!.SendAsync(nameof(UserRemovePair), userDto).ConfigureAwait(false); |         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) |     public async Task UserSetPairPermissions(UserPermissionsDto userPermissions) | ||||||
|     { |     { | ||||||
|         await _mareHub!.SendAsync(nameof(UserSetPairPermissions), userPermissions).ConfigureAwait(false); |         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) |     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))); |         Logger.LogInformation("Pushing character data for {hash} to {charas}", character.DataHash.Value, string.Join(", ", visibleCharacters.Select(c => c.AliasOrUID))); | ||||||
|   | |||||||
| @@ -162,6 +162,13 @@ public partial class ApiController | |||||||
|         return Task.CompletedTask; |         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) |     public Task Client_UserUpdateSelfPairPermissions(UserPermissionsDto dto) | ||||||
|     { |     { | ||||||
|         Logger.LogDebug("Client_UserUpdateSelfPairPermissions: {dto}", dto); |         Logger.LogDebug("Client_UserUpdateSelfPairPermissions: {dto}", dto); | ||||||
| @@ -277,6 +284,12 @@ public partial class ApiController | |||||||
|         _mareHub!.On(nameof(Client_UserUpdateOtherPairPermissions), act); |         _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) |     public void OnUserUpdateSelfPairPermissions(Action<UserPermissionsDto> act) | ||||||
|     { |     { | ||||||
|         if (_initialized) return; |         if (_initialized) return; | ||||||
|   | |||||||
| @@ -244,6 +244,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM | |||||||
|         OnUserUpdateOtherPairPermissions(dto => Client_UserUpdateOtherPairPermissions(dto)); |         OnUserUpdateOtherPairPermissions(dto => Client_UserUpdateOtherPairPermissions(dto)); | ||||||
|         OnUserUpdateSelfPairPermissions(dto => Client_UserUpdateSelfPairPermissions(dto)); |         OnUserUpdateSelfPairPermissions(dto => Client_UserUpdateSelfPairPermissions(dto)); | ||||||
|         OnUserReceiveUploadStatus(dto => Client_UserReceiveUploadStatus(dto)); |         OnUserReceiveUploadStatus(dto => Client_UserReceiveUploadStatus(dto)); | ||||||
|  |         OnUserUpdateProfile(dto => Client_UserUpdateProfile(dto)); | ||||||
|  |  | ||||||
|         OnGroupChangePermissions((dto) => Client_GroupChangePermissions(dto)); |         OnGroupChangePermissions((dto) => Client_GroupChangePermissions(dto)); | ||||||
|         OnGroupDelete((dto) => Client_GroupDelete(dto)); |         OnGroupDelete((dto) => Client_GroupDelete(dto)); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Stanley Dimant
					Stanley Dimant