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_utf8_string_literals = true:suggestion | ||||
| dotnet_diagnostic.S1075.severity = silent | ||||
| dotnet_diagnostic.SS3358.severity = true:suggestion | ||||
| dotnet_diagnostic.MA0007.severity = silent | ||||
| dotnet_diagnostic.MA0075.severity = silent | ||||
|  | ||||
| # 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(); | ||||
|         if (!fullName.Contains(_configService.Current.CacheFolder.ToLowerInvariant(), StringComparison.Ordinal)) return null; | ||||
|         string prefixedPath = fullName.Replace(_configService.Current.CacheFolder.ToLowerInvariant(), _cachePrefix + "\\", StringComparison.Ordinal).Replace("\\\\", "\\", StringComparison.Ordinal); | ||||
|         return CreateFileCacheEntity(fi, prefixedPath, fi.Name.ToUpper(CultureInfo.InvariantCulture)); | ||||
|         return CreateFileCacheEntity(fi, prefixedPath); | ||||
|     } | ||||
|  | ||||
|     public FileCacheEntity? CreateFileEntry(string path) | ||||
| @@ -122,11 +122,14 @@ public sealed class FileCacheManager : IDisposable | ||||
|         return validatedCacheEntry; | ||||
|     } | ||||
|  | ||||
|     public void RemoveHash(FileCacheEntity entity) | ||||
|     public void RemoveHash(FileCacheEntity? entity) | ||||
|     { | ||||
|         if (entity != null) | ||||
|         { | ||||
|             _logger.LogTrace("Removing {path}", entity.ResolvedFilepath); | ||||
|             _fileCaches.Remove(entity.PrefixedFilePath, out _); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public string ResolveFileReplacement(string gamePath) | ||||
|     { | ||||
|   | ||||
| @@ -15,7 +15,7 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase | ||||
|     private readonly PerformanceCollectorService _performanceCollector; | ||||
|     private long _currentFileProgress = 0; | ||||
|     private bool _fileScanWasRunning = false; | ||||
|     private CancellationTokenSource? _scanCancellationTokenSource; | ||||
|     private CancellationTokenSource _scanCancellationTokenSource = new(); | ||||
|     private TimeSpan _timeUntilNextScan = TimeSpan.Zero; | ||||
|  | ||||
|     public PeriodicFileScanner(ILogger<PeriodicFileScanner> logger, IpcManager ipcManager, MareConfigService configService, | ||||
| @@ -246,7 +246,7 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase | ||||
|             Logger.LogWarning(ex, "Error during enumerating FileCaches"); | ||||
|         } | ||||
|  | ||||
|         Task.WaitAll(dbTasks); | ||||
|         Task.WaitAll(dbTasks, _scanCancellationTokenSource.Token); | ||||
|  | ||||
|         if (!_ipcManager.CheckPenumbraApi()) | ||||
|         { | ||||
| @@ -308,7 +308,7 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase | ||||
|             if (ct.IsCancellationRequested) return; | ||||
|         } | ||||
|  | ||||
|         Task.WaitAll(dbTasks); | ||||
|         Task.WaitAll(dbTasks, _scanCancellationTokenSource.Token); | ||||
|  | ||||
|         Logger.LogTrace("Scanner added new files to db"); | ||||
|  | ||||
|   | ||||
| @@ -8,6 +8,7 @@ using Newtonsoft.Json; | ||||
|  | ||||
| namespace MareSynchronos.MareConfiguration; | ||||
| #pragma warning disable CS0618 // ignore Obsolete tag, the point of this migrator is to migrate obsolete configs to new ones | ||||
| #pragma warning disable CS0612 // ignore Obsolete tag, the point of this migrator is to migrate obsolete configs to new ones | ||||
|  | ||||
| public class ConfigurationMigrator : IHostedService | ||||
| { | ||||
| @@ -22,7 +23,7 @@ public class ConfigurationMigrator : IHostedService | ||||
|  | ||||
|     public void Migrate() | ||||
|     { | ||||
|         if (_pi.GetPluginConfig() is Configuration oldConfig) | ||||
|         if (_pi.GetPluginConfig() is Configurations.Obsolete.Configuration oldConfig) | ||||
|         { | ||||
|             _logger.LogInformation("Migrating Configuration from old config style to 1"); | ||||
|  | ||||
| @@ -52,13 +53,17 @@ public class ConfigurationMigrator : IHostedService | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 var serverConfig = JsonConvert.DeserializeObject<ServerConfigV0>(File.ReadAllText(ConfigurationPath(ServerConfigService.ConfigName)))!; | ||||
|                 var content = File.ReadAllText(ConfigurationPath(ServerConfigService.ConfigName)); | ||||
|                 if (!content.Contains("\"Version\": 1")) | ||||
|                 { | ||||
|                     var serverConfig = JsonConvert.DeserializeObject<ServerConfigV0>(content); | ||||
|  | ||||
|                 if (serverConfig.Version == 0) | ||||
|                     if (serverConfig != null && serverConfig.Version == 0) | ||||
|                     { | ||||
|                         MigrateServerConfigV0toV1(serverConfig); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 _logger.LogWarning("Failed to migrate ServerConfig", ex); | ||||
| @@ -92,7 +97,6 @@ public class ConfigurationMigrator : IHostedService | ||||
|  | ||||
|         MareConfig mareConfigV1 = mareConfigV0.ToV1(); | ||||
|  | ||||
|         int i = 0; | ||||
|         var serverConfig = new ServerConfig() | ||||
|         { | ||||
|             ServerStorage = mareConfigV0.ServerStorage.Select(p => p.Value.ToV1()).ToList() | ||||
| @@ -152,4 +156,5 @@ public class ConfigurationMigrator : IHostedService | ||||
|     } | ||||
| } | ||||
|  | ||||
| #pragma warning restore CS0612 // ignore Obsolete tag, the point of this migrator is to migrate obsolete configs to new ones | ||||
| #pragma warning restore CS0618 // ignore Obsolete tag, the point of this migrator is to migrate obsolete configs to new ones | ||||
| @@ -10,6 +10,7 @@ public class MareConfig : IMareConfiguration | ||||
|     public string CacheFolder { get; set; } = string.Empty; | ||||
|     public bool DisableOptionalPluginWarnings { get; set; } = false; | ||||
|     public NotificationLocation ErrorNotification { get; set; } = NotificationLocation.Both; | ||||
|     public string ExportFolder { get; set; } = string.Empty; | ||||
|     public bool FileScanPaused { get; set; } = false; | ||||
|     public NotificationLocation InfoNotification { get; set; } = NotificationLocation.Toast; | ||||
|     public bool InitialScanComplete { get; set; } = false; | ||||
| @@ -19,6 +20,9 @@ public class MareConfig : IMareConfiguration | ||||
|     public bool OpenGposeImportOnGposeStart { get; set; } = false; | ||||
|     public bool OpenPopupOnAdd { get; set; } = true; | ||||
|     public int ParallelDownloads { get; set; } = 10; | ||||
|     public float ProfileDelay { get; set; } = 2; | ||||
|     public bool ProfilesAllowNsfw { get; set; } = false; | ||||
|     public bool ProfilesShow { get; set; } = true; | ||||
|     public bool ReverseUserSort { get; set; } = false; | ||||
|     public bool ShowCharacterNameInsteadOfNotesForVisible { get; set; } = false; | ||||
|     public bool ShowOfflineUsersSeparately { get; set; } = true; | ||||
|   | ||||
| @@ -4,7 +4,7 @@ using MareSynchronos.WebAPI; | ||||
| namespace MareSynchronos.MareConfiguration.Configurations.Obsolete; | ||||
|  | ||||
| [Serializable] | ||||
| [Obsolete] | ||||
| [Obsolete("Replaced with ServerConfig")] | ||||
| public class ServerConfigV0 : IMareConfiguration | ||||
| { | ||||
|     public string CurrentServer { get; set; } = string.Empty; | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
|   <PropertyGroup> | ||||
|     <Authors></Authors> | ||||
|     <Company></Company> | ||||
|     <Version>0.8.8</Version> | ||||
|     <Version>0.8.10</Version> | ||||
|     <Description></Description> | ||||
|     <Copyright></Copyright> | ||||
|     <PackageProjectUrl>https://github.com/Penumbra-Sync/client</PackageProjectUrl> | ||||
| @@ -37,6 +37,7 @@ | ||||
|     <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" /> | ||||
|     <PackageReference Include="Penumbra.Api" Version="1.0.7" /> | ||||
|     <PackageReference Include="Penumbra.String" Version="1.0.3" /> | ||||
|     <PackageReference Include="SixLabors.ImageSharp" Version="3.0.0" /> | ||||
|     <PackageReference Include="SonarAnalyzer.CSharp" Version="8.54.0.64047"> | ||||
|       <PrivateAssets>all</PrivateAssets> | ||||
|       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
|   | ||||
| @@ -84,8 +84,8 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase | ||||
|  | ||||
|     public IntPtr Address { get; set; } | ||||
|     public unsafe Character* Character => (Character*)Address; | ||||
|     public Lazy<Dalamud.Game.ClientState.Objects.Types.GameObject?> GameObjectLazy { get; private set; } | ||||
|     public IntPtr CurrentAddress => _getAddress.Invoke(); | ||||
|     public Lazy<Dalamud.Game.ClientState.Objects.Types.GameObject?> GameObjectLazy { get; private set; } | ||||
|     public string Name { get; private set; } | ||||
|     public ObjectKind ObjectKind { get; } | ||||
|     private byte[] CustomizeData { get; set; } = new byte[26]; | ||||
| @@ -101,6 +101,8 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase | ||||
|             { | ||||
|                 curPtr = _getAddress.Invoke(); | ||||
|  | ||||
|                 if (curPtr == IntPtr.Zero) return true; | ||||
|  | ||||
|                 var drawObj = GetDrawObj(curPtr); | ||||
|                 return IsBeingDrawn(drawObj, curPtr); | ||||
|             } | ||||
|   | ||||
| @@ -158,7 +158,14 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase | ||||
|             _charaHandler?.Dispose(); | ||||
|             _charaHandler = null; | ||||
|  | ||||
|             if (!_lifetime.ApplicationStopping.IsCancellationRequested && !_dalamudUtil.IsZoning && !_dalamudUtil.IsInCutscene) | ||||
|             if (!_lifetime.ApplicationStopping.IsCancellationRequested) | ||||
|             { | ||||
|                 if (_dalamudUtil.IsZoning) | ||||
|                 { | ||||
|                     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); | ||||
| @@ -177,6 +184,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             Logger.LogWarning(ex, "Error on disposal of {name}", name); | ||||
| @@ -408,7 +416,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase | ||||
|             while ((!_applicationTask?.IsCompleted ?? false) && !downloadToken.IsCancellationRequested && !_applicationCancellationTokenSource.IsCancellationRequested) | ||||
|             { | ||||
|                 // block until current application is done | ||||
|                 Logger.LogDebug("Waiting for current data application (Id: {id}) to finish", _applicationId); | ||||
|                 Logger.LogDebug("Waiting for current data application (Id: {id}) for player ({handler}) to finish", _applicationId, PlayerName); | ||||
|                 await Task.Delay(250).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
| @@ -418,10 +426,17 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase | ||||
|             _applicationCancellationTokenSource = new(); | ||||
|             var token = _applicationCancellationTokenSource.Token; | ||||
|             _applicationTask = Task.Run(async () => | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     _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); | ||||
| @@ -435,6 +450,11 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase | ||||
|                     } | ||||
|  | ||||
|                     Logger.LogDebug("[{applicationId}] Application finished", _applicationId); | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     Logger.LogInformation("[{applicationId}] Cancelled, reason: {msg}", _applicationId, ex.Message); | ||||
|                 } | ||||
|             }, token); | ||||
|         }, downloadToken); | ||||
|     } | ||||
|   | ||||
| @@ -99,6 +99,7 @@ public sealed class PairManager : DisposableMediatorSubscriberBase | ||||
|     { | ||||
|         if (_allClientPairs.TryGetValue(user, out var pair)) | ||||
|         { | ||||
|             Mediator.Publish(new ClearProfileDataMessage(pair.UserData)); | ||||
|             pair.MarkOffline(); | ||||
|             RecreateLazy(); | ||||
|         } | ||||
| @@ -107,6 +108,9 @@ public sealed class PairManager : DisposableMediatorSubscriberBase | ||||
|     public void MarkPairOnline(OnlineUserIdentDto dto, bool sendNotif = true) | ||||
|     { | ||||
|         if (!_allClientPairs.ContainsKey(dto.User)) throw new InvalidOperationException("No user found for " + dto); | ||||
|  | ||||
|         Mediator.Publish(new ClearProfileDataMessage(dto.User)); | ||||
|  | ||||
|         var pair = _allClientPairs[dto.User]; | ||||
|         if (pair.HasCachedPlayer) return; | ||||
|  | ||||
| @@ -213,6 +217,12 @@ public sealed class PairManager : DisposableMediatorSubscriberBase | ||||
|  | ||||
|         if (pair.UserPair == null) throw new InvalidOperationException("No direct pair for " + dto); | ||||
|  | ||||
|         if (pair.UserPair.OtherPermissions.IsPaused() != dto.Permissions.IsPaused() | ||||
|             || pair.UserPair.OtherPermissions.IsPaired() != dto.Permissions.IsPaired()) | ||||
|         { | ||||
|             Mediator.Publish(new ClearProfileDataMessage(dto.User)); | ||||
|         } | ||||
|  | ||||
|         pair.UserPair.OtherPermissions = dto.Permissions; | ||||
|  | ||||
|         Logger.LogTrace("Paired: {synced}, Paused: {paused}, Anims: {anims}, Sounds: {sounds}", | ||||
| @@ -229,6 +239,12 @@ public sealed class PairManager : DisposableMediatorSubscriberBase | ||||
|  | ||||
|         if (pair.UserPair == null) throw new InvalidOperationException("No direct pair for " + dto); | ||||
|  | ||||
|         if (pair.UserPair.OwnPermissions.IsPaused() != dto.Permissions.IsPaused() | ||||
|             || pair.UserPair.OwnPermissions.IsPaired() != dto.Permissions.IsPaired()) | ||||
|         { | ||||
|             Mediator.Publish(new ClearProfileDataMessage(dto.User)); | ||||
|         } | ||||
|  | ||||
|         pair.UserPair.OwnPermissions = dto.Permissions; | ||||
|  | ||||
|         Logger.LogTrace("Paired: {synced}, Paused: {paused}, Anims: {anims}, Sounds: {sounds}", | ||||
| @@ -326,13 +342,14 @@ public sealed class PairManager : DisposableMediatorSubscriberBase | ||||
|     private void DisposePairs(bool recreate = false) | ||||
|     { | ||||
|         Logger.LogDebug("Disposing all Pairs"); | ||||
|         foreach (var item in _allClientPairs) | ||||
|         Parallel.ForEach(_allClientPairs, item => | ||||
|         { | ||||
|             if (recreate) | ||||
|                 item.Value.RecreateCachedPlayer(); | ||||
|             else | ||||
|                 item.Value.MarkOffline(); | ||||
|         } | ||||
|         }); | ||||
|  | ||||
|         RecreateLazy(); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -22,6 +22,7 @@ using MareSynchronos.Services; | ||||
| using MareSynchronos.Services.Mediator; | ||||
| using MareSynchronos.Services.ServerConfiguration; | ||||
| using MareSynchronos.UI; | ||||
| using MareSynchronos.UI.Handlers; | ||||
| using MareSynchronos.WebAPI; | ||||
| using MareSynchronos.WebAPI.Files; | ||||
| using MareSynchronos.WebAPI.SignalR; | ||||
| @@ -65,6 +66,11 @@ public sealed class Plugin : IDalamudPlugin | ||||
|             collection.AddSingleton<FileUploadManager>(); | ||||
|             collection.AddSingleton<FileTransferOrchestrator>(); | ||||
|             collection.AddSingleton<MarePlugin>(); | ||||
|             collection.AddSingleton<MareProfileManager>(); | ||||
|             collection.AddSingleton<UidDisplayHandler>((s) => new UidDisplayHandler(s.GetRequiredService<ILogger<UidDisplayHandler>>(), pluginInterface.UiBuilder, | ||||
|                 s.GetRequiredService<MareProfileManager>(), | ||||
|                 s.GetRequiredService<UiSharedService>(), s.GetRequiredService<PairManager>(), | ||||
|                 s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<MareConfigService>())); | ||||
|             collection.AddSingleton((s) => new DalamudUtilService(s.GetRequiredService<ILogger<DalamudUtilService>>(), | ||||
|                 clientState, objectTable, framework, gameGui, condition, gameData, | ||||
|                 s.GetRequiredService<MareMediator>(), s.GetRequiredService<PerformanceCollectorService>())); | ||||
| @@ -117,6 +123,9 @@ public sealed class Plugin : IDalamudPlugin | ||||
|             collection.AddScoped<WindowMediatorSubscriberBase, GposeUi>(); | ||||
|             collection.AddScoped<WindowMediatorSubscriberBase, IntroUi>(); | ||||
|             collection.AddScoped<WindowMediatorSubscriberBase, DownloadUi>(); | ||||
|             collection.AddScoped<WindowMediatorSubscriberBase, EditProfileUi>((s) => new EditProfileUi(s.GetRequiredService<ILogger<EditProfileUi>>(), | ||||
|                 s.GetRequiredService<MareMediator>(), s.GetRequiredService<ApiController>(), pluginInterface.UiBuilder, s.GetRequiredService<UiSharedService>(), | ||||
|                 s.GetRequiredService<FileDialogManager>(), s.GetRequiredService<MareProfileManager>())); | ||||
|             collection.AddScoped<CacheCreationService>(); | ||||
|             collection.AddScoped<TransientResourceManager>(); | ||||
|             collection.AddScoped<PlayerDataFactory>(); | ||||
|   | ||||
| @@ -55,6 +55,12 @@ public sealed class CommandManagerService : IDisposable | ||||
|  | ||||
|         if (string.Equals(splitArgs[0], "toggle", StringComparison.OrdinalIgnoreCase)) | ||||
|         { | ||||
|             if (_apiController.ServerState == WebAPI.SignalR.Utils.ServerState.Disconnecting) | ||||
|             { | ||||
|                 _mediator.Publish(new NotificationMessage("Mare disconnecting", "Cannot use /toggle while Mare Synchronos is still disconnecting", | ||||
|                     Dalamud.Interface.Internal.Notifications.NotificationType.Error)); | ||||
|             } | ||||
|  | ||||
|             if (_serverConfigurationManager.CurrentServer == null) return; | ||||
|             var fullPause = splitArgs.Length > 1 ? splitArgs[1] switch | ||||
|             { | ||||
|   | ||||
							
								
								
									
										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.Interface.Internal.Notifications; | ||||
| using MareSynchronos.API.Data; | ||||
| using MareSynchronos.API.Dto; | ||||
| using MareSynchronos.PlayerData.Handlers; | ||||
| using MareSynchronos.WebAPI.Files.Models; | ||||
| @@ -54,5 +55,6 @@ public record DownloadStartedMessage(GameObjectHandler DownloadId, Dictionary<st | ||||
| public record DownloadFinishedMessage(GameObjectHandler DownloadId) : IMessage; | ||||
| public record UiToggleMessage(Type UiType) : IMessage; | ||||
| public record PlayerUploadingMessage(GameObjectHandler Handler, bool IsUploading) : IMessage; | ||||
| public record ClearProfileDataMessage(UserData? UserData = null) : IMessage; | ||||
|  | ||||
| #pragma warning restore MA0048 // File name must match type name | ||||
| @@ -23,17 +23,29 @@ public class ServerConfigurationManager | ||||
|         _serverTagConfig = serverTagConfig; | ||||
|         _notesConfig = notesConfig; | ||||
|         _dalamudUtil = dalamudUtil; | ||||
|     } | ||||
|  | ||||
|     public string CurrentApiUrl => CurrentServer.ServerUri; | ||||
|     public ServerStorage CurrentServer => _configService.Current.ServerStorage[CurrentServerIndex]; | ||||
|  | ||||
|     public int CurrentServerIndex | ||||
|     { | ||||
|         set | ||||
|         { | ||||
|             _configService.Current.CurrentServer = value; | ||||
|             _configService.Save(); | ||||
|         } | ||||
|         get | ||||
|         { | ||||
|             if (_configService.Current.CurrentServer < 0) | ||||
|             { | ||||
|                 _configService.Current.CurrentServer = 0; | ||||
|                 _configService.Save(); | ||||
|             } | ||||
|     } | ||||
|  | ||||
|     public string CurrentApiUrl => CurrentServer.ServerUri; | ||||
|     public ServerStorage CurrentServer => _configService.Current.ServerStorage[CurrentServerIndex]; | ||||
|     public int CurrentServerIndex => _configService.Current.CurrentServer; | ||||
|             return _configService.Current.CurrentServer; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public string? GetSecretKey(int serverIdx = -1) | ||||
|     { | ||||
|   | ||||
| @@ -27,9 +27,6 @@ namespace MareSynchronos.UI; | ||||
|  | ||||
| public class CompactUi : WindowMediatorSubscriberBase | ||||
| { | ||||
|     public readonly Dictionary<string, bool> ShowUidForEntry = new(StringComparer.Ordinal); | ||||
|     public string EditNickEntry = string.Empty; | ||||
|     public string EditUserComment = string.Empty; | ||||
|     public float TransferPartHeight; | ||||
|     public float WindowContentWidth; | ||||
|     private readonly ApiController _apiController; | ||||
| @@ -43,6 +40,7 @@ public class CompactUi : WindowMediatorSubscriberBase | ||||
|     private readonly SelectPairForGroupUi _selectPairsForGroupUi; | ||||
|     private readonly ServerConfigurationManager _serverManager; | ||||
|     private readonly Stopwatch _timeout = new(); | ||||
|     private readonly UidDisplayHandler _uidDisplayHandler; | ||||
|     private readonly UiSharedService _uiShared; | ||||
|     private bool _buttonState; | ||||
|     private string _characterOrCommentFilter = string.Empty; | ||||
| @@ -55,7 +53,7 @@ public class CompactUi : WindowMediatorSubscriberBase | ||||
|     private bool _wasOpen; | ||||
|  | ||||
|     public CompactUi(ILogger<CompactUi> logger, UiSharedService uiShared, MareConfigService configService, ApiController apiController, PairManager pairManager, | ||||
|         ServerConfigurationManager serverManager, MareMediator mediator, FileUploadManager fileTransferManager) : base(logger, mediator, "###MareSynchronosMainUI") | ||||
|         ServerConfigurationManager serverManager, MareMediator mediator, FileUploadManager fileTransferManager, UidDisplayHandler uidDisplayHandler) : base(logger, mediator, "###MareSynchronosMainUI") | ||||
|     { | ||||
|         _uiShared = uiShared; | ||||
|         _configService = configService; | ||||
| @@ -63,12 +61,13 @@ public class CompactUi : WindowMediatorSubscriberBase | ||||
|         _pairManager = pairManager; | ||||
|         _serverManager = serverManager; | ||||
|         _fileTransferManager = fileTransferManager; | ||||
|         _uidDisplayHandler = uidDisplayHandler; | ||||
|         var tagHandler = new TagHandler(_serverManager); | ||||
|  | ||||
|         _groupPanel = new(this, uiShared, _pairManager, _serverManager, _configService); | ||||
|         _selectGroupForPairUi = new(tagHandler); | ||||
|         _selectPairsForGroupUi = new(tagHandler); | ||||
|         _pairGroupsUi = new(configService, tagHandler, DrawPairedClient, apiController, _selectPairsForGroupUi); | ||||
|         _groupPanel = new(this, uiShared, _pairManager, uidDisplayHandler, _serverManager); | ||||
|         _selectGroupForPairUi = new(tagHandler, uidDisplayHandler); | ||||
|         _selectPairsForGroupUi = new(tagHandler, uidDisplayHandler); | ||||
|         _pairGroupsUi = new(configService, tagHandler, apiController, _selectPairsForGroupUi); | ||||
|  | ||||
| #if DEBUG | ||||
|         string dev = "Dev Build"; | ||||
| @@ -164,8 +163,8 @@ public class CompactUi : WindowMediatorSubscriberBase | ||||
|             ImGui.Separator(); | ||||
|             UiSharedService.DrawWithID("transfers", DrawTransfers); | ||||
|             TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight; | ||||
|             UiSharedService.DrawWithID("group-user-popup", () => _selectPairsForGroupUi.Draw(_pairManager.DirectPairs, ShowUidForEntry)); | ||||
|             UiSharedService.DrawWithID("grouping-popup", () => _selectGroupForPairUi.Draw(ShowUidForEntry)); | ||||
|             UiSharedService.DrawWithID("group-user-popup", () => _selectPairsForGroupUi.Draw(_pairManager.DirectPairs)); | ||||
|             UiSharedService.DrawWithID("grouping-popup", () => _selectGroupForPairUi.Draw()); | ||||
|         } | ||||
|  | ||||
|         if (_configService.Current.OpenPopupOnAdd && _pairManager.LastAddedUser != null) | ||||
| @@ -202,8 +201,7 @@ public class CompactUi : WindowMediatorSubscriberBase | ||||
|  | ||||
|     public override void OnClose() | ||||
|     { | ||||
|         EditNickEntry = string.Empty; | ||||
|         EditUserComment = string.Empty; | ||||
|         _uidDisplayHandler.Clear(); | ||||
|         base.OnClose(); | ||||
|     } | ||||
|  | ||||
| @@ -213,8 +211,6 @@ public class CompactUi : WindowMediatorSubscriberBase | ||||
|         var keys = _serverManager.CurrentServer!.SecretKeys; | ||||
|         if (keys.TryGetValue(_secretKeyIdx, out var secretKey)) | ||||
|         { | ||||
|             var friendlyName = secretKey.FriendlyName; | ||||
|  | ||||
|             if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Add current character with secret key")) | ||||
|             { | ||||
|                 _serverManager.CurrentServer!.Authentications.Add(new MareConfiguration.Models.Authentication() | ||||
| @@ -350,210 +346,6 @@ public class CompactUi : WindowMediatorSubscriberBase | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void DrawPairedClient(Pair entry) | ||||
|     { | ||||
|         if (entry.UserPair == null) return; | ||||
|  | ||||
|         var pauseIcon = entry.UserPair!.OwnPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; | ||||
|         var pauseIconSize = UiSharedService.GetIconButtonSize(pauseIcon); | ||||
|         var barButtonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars); | ||||
|         var entryUID = entry.UserData.AliasOrUID; | ||||
|         var textSize = ImGui.CalcTextSize(entryUID); | ||||
|         var originalY = ImGui.GetCursorPosY(); | ||||
|         var buttonSizes = pauseIconSize.Y + barButtonSize.Y; | ||||
|         var spacingX = ImGui.GetStyle().ItemSpacing.X; | ||||
|         var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth(); | ||||
|  | ||||
|         var textPos = originalY + pauseIconSize.Y / 2 - textSize.Y / 2; | ||||
|         ImGui.SetCursorPosY(textPos); | ||||
|         FontAwesomeIcon connectionIcon; | ||||
|         string connectionText = string.Empty; | ||||
|         Vector4 connectionColor; | ||||
|         if (!(entry.UserPair!.OwnPermissions.IsPaired() && entry.UserPair!.OtherPermissions.IsPaired())) | ||||
|         { | ||||
|             connectionIcon = FontAwesomeIcon.ArrowUp; | ||||
|             connectionText = entryUID + " has not added you back"; | ||||
|             connectionColor = ImGuiColors.DalamudRed; | ||||
|         } | ||||
|         else if (entry.UserPair!.OwnPermissions.IsPaused() || entry.UserPair!.OtherPermissions.IsPaused()) | ||||
|         { | ||||
|             connectionIcon = FontAwesomeIcon.PauseCircle; | ||||
|             connectionText = "Pairing status with " + entryUID + " is paused"; | ||||
|             connectionColor = ImGuiColors.DalamudYellow; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             connectionIcon = FontAwesomeIcon.Check; | ||||
|             connectionText = "You are paired with " + entryUID; | ||||
|             connectionColor = ImGuiColors.ParsedGreen; | ||||
|         } | ||||
|  | ||||
|         ImGui.PushFont(UiBuilder.IconFont); | ||||
|         UiSharedService.ColorText(connectionIcon.ToIconString(), connectionColor); | ||||
|         ImGui.PopFont(); | ||||
|         UiSharedService.AttachToolTip(connectionText); | ||||
|         ImGui.SameLine(); | ||||
|         ImGui.SetCursorPosY(textPos); | ||||
|         if (entry is { IsOnline: true, IsVisible: true }) | ||||
|         { | ||||
|             ImGui.PushFont(UiBuilder.IconFont); | ||||
|             UiSharedService.ColorText(FontAwesomeIcon.Eye.ToIconString(), ImGuiColors.ParsedGreen); | ||||
|             ImGui.PopFont(); | ||||
|             UiSharedService.AttachToolTip(entryUID + " is visible: " + entry.PlayerName!); | ||||
|         } | ||||
|  | ||||
|         var textIsUid = true; | ||||
|         ShowUidForEntry.TryGetValue(entry.UserPair!.User.UID, out var showUidInsteadOfName); | ||||
|         string? playerText = _serverManager.GetNoteForUid(entry.UserPair!.User.UID); | ||||
|         if (!showUidInsteadOfName && playerText != null) | ||||
|         { | ||||
|             if (string.IsNullOrEmpty(playerText)) | ||||
|             { | ||||
|                 playerText = entryUID; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 textIsUid = false; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             playerText = entryUID; | ||||
|         } | ||||
|  | ||||
|         if (_configService.Current.ShowCharacterNameInsteadOfNotesForVisible && entry.IsVisible && !showUidInsteadOfName) | ||||
|         { | ||||
|             playerText = entry.PlayerName; | ||||
|             textIsUid = false; | ||||
|         } | ||||
|  | ||||
|         ImGui.SameLine(); | ||||
|         if (!string.Equals(EditNickEntry, entry.UserData.UID, StringComparison.Ordinal)) | ||||
|         { | ||||
|             ImGui.SetCursorPosY(textPos); | ||||
|             if (textIsUid) ImGui.PushFont(UiBuilder.MonoFont); | ||||
|             ImGui.TextUnformatted(playerText); | ||||
|             if (textIsUid) ImGui.PopFont(); | ||||
|             UiSharedService.AttachToolTip("Left click to switch between UID display and nick" + Environment.NewLine + | ||||
|                           "Right click to change nick for " + entryUID); | ||||
|             if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) | ||||
|             { | ||||
|                 var prevState = textIsUid; | ||||
|                 if (ShowUidForEntry.ContainsKey(entry.UserPair!.User.UID)) | ||||
|                 { | ||||
|                     prevState = ShowUidForEntry[entry.UserPair!.User.UID]; | ||||
|                 } | ||||
|  | ||||
|                 ShowUidForEntry[entry.UserPair!.User.UID] = !prevState; | ||||
|             } | ||||
|  | ||||
|             if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) | ||||
|             { | ||||
|                 var pair = _pairManager.DirectPairs.Find(p => string.Equals(p.UserData.UID, EditNickEntry, StringComparison.Ordinal)); | ||||
|                 if (pair != null) | ||||
|                 { | ||||
|                     pair.SetNote(EditUserComment); | ||||
|                     _configService.Save(); | ||||
|                 } | ||||
|                 EditUserComment = entry.GetNote() ?? string.Empty; | ||||
|                 EditNickEntry = entry.UserPair!.User.UID; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             ImGui.SetCursorPosY(originalY); | ||||
|  | ||||
|             ImGui.SetNextItemWidth(UiSharedService.GetWindowContentRegionWidth() - ImGui.GetCursorPosX() - buttonSizes - ImGui.GetStyle().ItemSpacing.X * 2); | ||||
|             if (ImGui.InputTextWithHint("", "Nick/Notes", ref EditUserComment, 255, ImGuiInputTextFlags.EnterReturnsTrue)) | ||||
|             { | ||||
|                 _serverManager.SetNoteForUid(entry.UserPair!.User.UID, EditUserComment); | ||||
|                 EditNickEntry = string.Empty; | ||||
|             } | ||||
|  | ||||
|             if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) | ||||
|             { | ||||
|                 EditNickEntry = string.Empty; | ||||
|             } | ||||
|             UiSharedService.AttachToolTip("Hit ENTER to save\nRight click to cancel"); | ||||
|         } | ||||
|  | ||||
|         // Pause Button && sound warnings | ||||
|         if (entry.UserPair!.OwnPermissions.IsPaired() && entry.UserPair!.OtherPermissions.IsPaired()) | ||||
|         { | ||||
|             var individualSoundsDisabled = (entry.UserPair?.OwnPermissions.IsDisableSounds() ?? false) || (entry.UserPair?.OtherPermissions.IsDisableSounds() ?? false); | ||||
|             var individualAnimDisabled = (entry.UserPair?.OwnPermissions.IsDisableAnimations() ?? false) || (entry.UserPair?.OtherPermissions.IsDisableAnimations() ?? false); | ||||
|  | ||||
|             if (individualAnimDisabled || individualSoundsDisabled) | ||||
|             { | ||||
|                 var infoIconPosDist = windowEndX - barButtonSize.X - spacingX - pauseIconSize.X - spacingX; | ||||
|                 var icon = FontAwesomeIcon.ExclamationTriangle; | ||||
|                 var iconwidth = UiSharedService.GetIconSize(icon); | ||||
|  | ||||
|                 ImGui.SameLine(infoIconPosDist - iconwidth.X); | ||||
|  | ||||
|                 ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); | ||||
|                 UiSharedService.FontText(icon.ToIconString(), UiBuilder.IconFont); | ||||
|                 ImGui.PopStyleColor(); | ||||
|                 if (ImGui.IsItemHovered()) | ||||
|                 { | ||||
|                     ImGui.BeginTooltip(); | ||||
|  | ||||
|                     ImGui.Text("Individual User permissions"); | ||||
|  | ||||
|                     if (individualSoundsDisabled) | ||||
|                     { | ||||
|                         var userSoundsText = "Sound sync disabled with " + entry.UserData.AliasOrUID; | ||||
|                         UiSharedService.FontText(FontAwesomeIcon.VolumeOff.ToIconString(), UiBuilder.IconFont); | ||||
|                         ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); | ||||
|                         ImGui.Text(userSoundsText); | ||||
|                         ImGui.NewLine(); | ||||
|                         ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); | ||||
|                         ImGui.Text("You: " + (entry.UserPair!.OwnPermissions.IsDisableSounds() ? "Disabled" : "Enabled") + ", They: " + (entry.UserPair!.OtherPermissions.IsDisableSounds() ? "Disabled" : "Enabled")); | ||||
|                     } | ||||
|  | ||||
|                     if (individualAnimDisabled) | ||||
|                     { | ||||
|                         var userAnimText = "Animation sync disabled with " + entry.UserData.AliasOrUID; | ||||
|                         UiSharedService.FontText(FontAwesomeIcon.Stop.ToIconString(), UiBuilder.IconFont); | ||||
|                         ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); | ||||
|                         ImGui.Text(userAnimText); | ||||
|                         ImGui.NewLine(); | ||||
|                         ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); | ||||
|                         ImGui.Text("You: " + (entry.UserPair!.OwnPermissions.IsDisableAnimations() ? "Disabled" : "Enabled") + ", They: " + (entry.UserPair!.OtherPermissions.IsDisableAnimations() ? "Disabled" : "Enabled")); | ||||
|                     } | ||||
|  | ||||
|                     ImGui.EndTooltip(); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             ImGui.SameLine(windowEndX - barButtonSize.X - spacingX - pauseIconSize.X); | ||||
|             ImGui.SetCursorPosY(originalY); | ||||
|             if (ImGuiComponents.IconButton(pauseIcon)) | ||||
|             { | ||||
|                 var perm = entry.UserPair!.OwnPermissions; | ||||
|                 perm.SetPaused(!perm.IsPaused()); | ||||
|                 _ = _apiController.UserSetPairPermissions(new(entry.UserData, perm)); | ||||
|             } | ||||
|             UiSharedService.AttachToolTip(!entry.UserPair!.OwnPermissions.IsPaused() | ||||
|                 ? "Pause pairing with " + entryUID | ||||
|                 : "Resume pairing with " + entryUID); | ||||
|         } | ||||
|  | ||||
|         // Flyout Menu | ||||
|         ImGui.SameLine(windowEndX - barButtonSize.X); | ||||
|         ImGui.SetCursorPosY(originalY); | ||||
|  | ||||
|         if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars)) | ||||
|         { | ||||
|             ImGui.OpenPopup("User Flyout Menu"); | ||||
|         } | ||||
|         if (ImGui.BeginPopup("User Flyout Menu")) | ||||
|         { | ||||
|             UiSharedService.DrawWithID($"buttons-{entry.UserPair!.User.UID}", () => DrawPairedClientMenu(entry)); | ||||
|             ImGui.EndPopup(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void DrawPairedClientMenu(Pair entry) | ||||
|     { | ||||
|         if (entry.IsVisible) | ||||
| @@ -624,9 +416,9 @@ public class CompactUi : WindowMediatorSubscriberBase | ||||
|             users.Reverse(); | ||||
|         } | ||||
|  | ||||
|         var onlineUsers = users.Where(u => u.IsOnline || u.UserPair.OwnPermissions.IsPaused()).ToList(); | ||||
|         var visibleUsers = onlineUsers.Where(u => u.IsVisible).ToList(); | ||||
|         var offlineUsers = users.Except(onlineUsers).ToList(); | ||||
|         var onlineUsers = users.Where(u => u.IsOnline || u.UserPair!.OwnPermissions.IsPaused()).Select(c => new DrawUserPair("Online" + c.UserData.UID, c, _uidDisplayHandler, _apiController, _selectGroupForPairUi)).ToList(); | ||||
|         var visibleUsers = users.Where(u => u.IsVisible).Select(c => new DrawUserPair("Visible" + c.UserData.UID, c, _uidDisplayHandler, _apiController, _selectGroupForPairUi)).ToList(); | ||||
|         var offlineUsers = users.Where(u => !u.IsOnline && !u.UserPair!.OwnPermissions.IsPaused()).Select(c => new DrawUserPair("Offline" + c.UserData.UID, c, _uidDisplayHandler, _apiController, _selectGroupForPairUi)).ToList(); | ||||
|  | ||||
|         ImGui.BeginChild("list", new Vector2(WindowContentWidth, ySize), border: false); | ||||
|  | ||||
| @@ -651,7 +443,7 @@ public class CompactUi : WindowMediatorSubscriberBase | ||||
|  | ||||
|         if (_apiController.ServerState is ServerState.Connected) | ||||
|         { | ||||
|             ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - buttonSize.X) / 2 - (userSize.X + textSize.X) / 2 - ImGui.GetStyle().ItemSpacing.X / 2); | ||||
|             ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth()) / 2 - (userSize.X + textSize.X) / 2 - ImGui.GetStyle().ItemSpacing.X / 2); | ||||
|             if (!printShard) ImGui.AlignTextToFramePadding(); | ||||
|             ImGui.TextColored(ImGuiColors.ParsedGreen, userCount); | ||||
|             ImGui.SameLine(); | ||||
| @@ -667,17 +459,33 @@ public class CompactUi : WindowMediatorSubscriberBase | ||||
|         if (printShard) | ||||
|         { | ||||
|             ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ImGui.GetStyle().ItemSpacing.Y); | ||||
|             ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - buttonSize.X) / 2 - shardTextSize.X / 2); | ||||
|             ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth()) / 2 - shardTextSize.X / 2); | ||||
|             ImGui.TextUnformatted(shardConnection); | ||||
|         } | ||||
|  | ||||
|         ImGui.SameLine(); | ||||
|         if (printShard) | ||||
|         { | ||||
|             ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ((userSize.Y + textSize.Y) / 2 + shardTextSize.Y) / 2 - ImGui.GetStyle().ItemSpacing.Y + buttonSize.Y / 2); | ||||
|         } | ||||
|         var color = UiSharedService.GetBoolColor(!_serverManager.CurrentServer!.FullPause); | ||||
|         var connectedIcon = !_serverManager.CurrentServer.FullPause ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink; | ||||
|  | ||||
|         if (_apiController.ServerState is ServerState.Connected) | ||||
|         { | ||||
|             ImGui.SetCursorPosX(0 + ImGui.GetStyle().ItemSpacing.X); | ||||
|             if (ImGuiComponents.IconButton(FontAwesomeIcon.UserCircle)) | ||||
|             { | ||||
|                 Mediator.Publish(new UiToggleMessage(typeof(EditProfileUi))); | ||||
|             } | ||||
|             UiSharedService.AttachToolTip("Edit your Mare Profile"); | ||||
|         } | ||||
|  | ||||
|         ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - buttonSize.X); | ||||
|         if (printShard) | ||||
|         { | ||||
|             ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ((userSize.Y + textSize.Y) / 2 + shardTextSize.Y) / 2 - ImGui.GetStyle().ItemSpacing.Y + buttonSize.Y / 2); | ||||
|         } | ||||
|         var color = UiSharedService.GetBoolColor(!_serverManager.CurrentServer!.FullPause); | ||||
|         var connectedIcon = !_serverManager.CurrentServer.FullPause ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink; | ||||
|  | ||||
|         if (_apiController.ServerState is not (ServerState.Reconnecting or ServerState.Disconnecting)) | ||||
|         { | ||||
|   | ||||
							
								
								
									
										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.Utility; | ||||
| using ImGuiNET; | ||||
| @@ -8,28 +7,26 @@ using System.Numerics; | ||||
| using System.Globalization; | ||||
| using MareSynchronos.API.Data; | ||||
| using MareSynchronos.API.Dto.Group; | ||||
| using MareSynchronos.API.Dto.User; | ||||
| using MareSynchronos.API.Data.Enum; | ||||
| using MareSynchronos.API.Data.Extensions; | ||||
| using MareSynchronos.API.Data.Comparer; | ||||
| using MareSynchronos.MareConfiguration; | ||||
| using MareSynchronos.PlayerData.Pairs; | ||||
| using MareSynchronos.Services.ServerConfiguration; | ||||
| using MareSynchronos.UI.Components; | ||||
| using MareSynchronos.UI.Handlers; | ||||
|  | ||||
| namespace MareSynchronos.UI; | ||||
|  | ||||
| internal sealed class GroupPanel | ||||
| { | ||||
|     private readonly MareConfigService _configService; | ||||
|     private readonly Dictionary<string, bool> _expandedGroupState = new(StringComparer.Ordinal); | ||||
|     private readonly CompactUi _mainUi; | ||||
|     private readonly PairManager _pairManager; | ||||
|     private readonly ServerConfigurationManager _serverConfigurationManager; | ||||
|     private readonly Dictionary<string, bool> _showGidForEntry = new(StringComparer.Ordinal); | ||||
|     private readonly UidDisplayHandler _uidDisplayHandler; | ||||
|     private readonly UiSharedService _uiShared; | ||||
|     private List<BannedGroupUserDto> _bannedUsers = new(); | ||||
|     private string _banReason = string.Empty; | ||||
|     private bool _banUserPopupOpen; | ||||
|     private int _bulkInviteCount = 10; | ||||
|     private List<string> _bulkOneTimeInvites = new(); | ||||
|     private string _editGroupComment = string.Empty; | ||||
| @@ -43,7 +40,6 @@ internal sealed class GroupPanel | ||||
|     private bool _modalChangePwOpened; | ||||
|     private string _newSyncShellPassword = string.Empty; | ||||
|     private bool _showModalBanList = false; | ||||
|     private bool _showModalBanUser; | ||||
|     private bool _showModalBulkOneTimeInvites = false; | ||||
|     private bool _showModalChangePassword; | ||||
|     private bool _showModalCreateGroup; | ||||
| @@ -51,13 +47,13 @@ internal sealed class GroupPanel | ||||
|     private string _syncShellPassword = string.Empty; | ||||
|     private string _syncShellToJoin = string.Empty; | ||||
|  | ||||
|     public GroupPanel(CompactUi mainUi, UiSharedService uiShared, PairManager pairManager, ServerConfigurationManager serverConfigurationManager, MareConfigService configurationService) | ||||
|     public GroupPanel(CompactUi mainUi, UiSharedService uiShared, PairManager pairManager, UidDisplayHandler uidDisplayHandler, ServerConfigurationManager serverConfigurationManager) | ||||
|     { | ||||
|         _mainUi = mainUi; | ||||
|         _uiShared = uiShared; | ||||
|         _pairManager = pairManager; | ||||
|         _uidDisplayHandler = uidDisplayHandler; | ||||
|         _serverConfigurationManager = serverConfigurationManager; | ||||
|         _configService = configurationService; | ||||
|     } | ||||
|  | ||||
|     private ApiController ApiController => _uiShared.ApiController; | ||||
| @@ -406,18 +402,24 @@ internal sealed class GroupPanel | ||||
|                 .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsModerator()) | ||||
|                 .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned()) | ||||
|                 .ThenBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase) | ||||
|                 .Select(c => new DrawGroupPair(groupDto.GID + c.UserData.UID, c, ApiController, groupDto, c.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value, | ||||
|                     _uidDisplayHandler)) | ||||
|                 .ToList(); | ||||
|             var onlineUsers = pairsInGroup.Where(u => u.IsOnline && !u.IsVisible) | ||||
|                 .OrderByDescending(u => string.Equals(u.UserData.UID, groupDto.OwnerUID, StringComparison.Ordinal)) | ||||
|                 .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsModerator()) | ||||
|                 .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned()) | ||||
|                 .ThenBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase) | ||||
|                 .Select(c => new DrawGroupPair(groupDto.GID + c.UserData.UID, c, ApiController, groupDto, c.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value, | ||||
|                     _uidDisplayHandler)) | ||||
|                 .ToList(); | ||||
|             var offlineUsers = pairsInGroup.Where(u => !u.IsOnline && !u.IsVisible) | ||||
|                 .OrderByDescending(u => string.Equals(u.UserData.UID, groupDto.OwnerUID, StringComparison.Ordinal)) | ||||
|                 .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsModerator()) | ||||
|                 .ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned()) | ||||
|                 .ThenBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase) | ||||
|                 .Select(c => new DrawGroupPair(groupDto.GID + c.UserData.UID, c, ApiController, groupDto, c.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value, | ||||
|                     _uidDisplayHandler)) | ||||
|                 .ToList(); | ||||
|  | ||||
|             if (visibleUsers.Any()) | ||||
| @@ -426,12 +428,7 @@ internal sealed class GroupPanel | ||||
|                 ImGui.Separator(); | ||||
|                 foreach (var entry in visibleUsers) | ||||
|                 { | ||||
|                     UiSharedService.DrawWithID(groupDto.GID + entry.UserData.UID, () => DrawSyncshellPairedClient( | ||||
|                         entry, | ||||
|                         entry.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value, | ||||
|                         groupDto.OwnerUID, | ||||
|                         string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal), | ||||
|                         groupDto.GroupUserInfo.IsModerator())); | ||||
|                     UiSharedService.DrawWithID(groupDto.GID + entry.UID, () => entry.DrawPairedClient()); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @@ -441,12 +438,7 @@ internal sealed class GroupPanel | ||||
|                 ImGui.Separator(); | ||||
|                 foreach (var entry in onlineUsers) | ||||
|                 { | ||||
|                     UiSharedService.DrawWithID(groupDto.GID + entry.UserData.UID, () => DrawSyncshellPairedClient( | ||||
|                         entry, | ||||
|                         entry.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value, | ||||
|                         groupDto.OwnerUID, | ||||
|                         string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal), | ||||
|                         groupDto.GroupUserInfo.IsModerator())); | ||||
|                     UiSharedService.DrawWithID(groupDto.GID + entry.UID, () => entry.DrawPairedClient()); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @@ -456,12 +448,7 @@ internal sealed class GroupPanel | ||||
|                 ImGui.Separator(); | ||||
|                 foreach (var entry in offlineUsers) | ||||
|                 { | ||||
|                     UiSharedService.DrawWithID(groupDto.GID + entry.UserData.UID, () => DrawSyncshellPairedClient( | ||||
|                         entry, | ||||
|                         entry.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value, | ||||
|                         groupDto.OwnerUID, | ||||
|                         string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal), | ||||
|                         groupDto.GroupUserInfo.IsModerator())); | ||||
|                     UiSharedService.DrawWithID(groupDto.GID + entry.UID, () => entry.DrawPairedClient()); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @@ -724,337 +711,4 @@ internal sealed class GroupPanel | ||||
|         } | ||||
|         ImGui.EndChild(); | ||||
|     } | ||||
|  | ||||
|     private void DrawSyncshellPairedClient(Pair pair, GroupPairFullInfoDto entry, string ownerUid, bool userIsOwner, bool userIsModerator) | ||||
|     { | ||||
|         var plusButtonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Plus); | ||||
|         var barButtonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars); | ||||
|         var entryUID = entry.UserAliasOrUID; | ||||
|         var textSize = ImGui.CalcTextSize(entryUID); | ||||
|         var originalY = ImGui.GetCursorPosY(); | ||||
|         var entryIsMod = entry.GroupPairStatusInfo.IsModerator(); | ||||
|         var entryIsOwner = string.Equals(pair.UserData.UID, ownerUid, StringComparison.Ordinal); | ||||
|         var entryIsPinned = entry.GroupPairStatusInfo.IsPinned(); | ||||
|         var presenceIcon = pair.IsVisible ? FontAwesomeIcon.Eye : (pair.IsOnline ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink); | ||||
|         var presenceColor = (pair.IsOnline || pair.IsVisible) ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; | ||||
|         var presenceText = entryUID + " is offline"; | ||||
|  | ||||
|         var soundsDisabled = entry.GroupUserPermissions.IsDisableSounds(); | ||||
|         var animDisabled = entry.GroupUserPermissions.IsDisableAnimations(); | ||||
|         var individualSoundsDisabled = (pair.UserPair?.OwnPermissions.IsDisableSounds() ?? false) || (pair.UserPair?.OtherPermissions.IsDisableSounds() ?? false); | ||||
|         var individualAnimDisabled = (pair.UserPair?.OwnPermissions.IsDisableAnimations() ?? false) || (pair.UserPair?.OtherPermissions.IsDisableAnimations() ?? false); | ||||
|  | ||||
|         var textPos = originalY + barButtonSize.Y / 2 - textSize.Y / 2; | ||||
|         ImGui.SetCursorPosY(textPos); | ||||
|         if (pair.IsPaused) | ||||
|         { | ||||
|             presenceIcon = FontAwesomeIcon.Question; | ||||
|             presenceColor = ImGuiColors.DalamudGrey; | ||||
|             presenceText = entryUID + " online status is unknown (paused)"; | ||||
|  | ||||
|             ImGui.PushFont(UiBuilder.IconFont); | ||||
|             UiSharedService.ColorText(FontAwesomeIcon.PauseCircle.ToIconString(), ImGuiColors.DalamudYellow); | ||||
|             ImGui.PopFont(); | ||||
|  | ||||
|             UiSharedService.AttachToolTip("Pairing status with " + entryUID + " is paused"); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             ImGui.PushFont(UiBuilder.IconFont); | ||||
|             UiSharedService.ColorText(FontAwesomeIcon.Check.ToIconString(), ImGuiColors.ParsedGreen); | ||||
|             ImGui.PopFont(); | ||||
|  | ||||
|             UiSharedService.AttachToolTip("You are paired with " + entryUID); | ||||
|         } | ||||
|  | ||||
|         if (pair.IsOnline && !pair.IsVisible) presenceText = entryUID + " is online"; | ||||
|         else if (pair.IsOnline && pair.IsVisible) presenceText = entryUID + " is visible: " + pair.PlayerName; | ||||
|  | ||||
|         ImGui.SameLine(); | ||||
|         ImGui.SetCursorPosY(textPos); | ||||
|         ImGui.PushFont(UiBuilder.IconFont); | ||||
|         UiSharedService.ColorText(presenceIcon.ToIconString(), presenceColor); | ||||
|         ImGui.PopFont(); | ||||
|         UiSharedService.AttachToolTip(presenceText); | ||||
|  | ||||
|         if (entryIsOwner) | ||||
|         { | ||||
|             ImGui.SameLine(); | ||||
|             ImGui.SetCursorPosY(textPos); | ||||
|             ImGui.PushFont(UiBuilder.IconFont); | ||||
|             ImGui.TextUnformatted(FontAwesomeIcon.Crown.ToIconString()); | ||||
|             ImGui.PopFont(); | ||||
|             UiSharedService.AttachToolTip("User is owner of this Syncshell"); | ||||
|         } | ||||
|         else if (entryIsMod) | ||||
|         { | ||||
|             ImGui.SameLine(); | ||||
|             ImGui.SetCursorPosY(textPos); | ||||
|             ImGui.PushFont(UiBuilder.IconFont); | ||||
|             ImGui.TextUnformatted(FontAwesomeIcon.UserShield.ToIconString()); | ||||
|             ImGui.PopFont(); | ||||
|             UiSharedService.AttachToolTip("User is moderator of this Syncshell"); | ||||
|         } | ||||
|         else if (entryIsPinned) | ||||
|         { | ||||
|             ImGui.SameLine(); | ||||
|             ImGui.SetCursorPosY(textPos); | ||||
|             ImGui.PushFont(UiBuilder.IconFont); | ||||
|             ImGui.TextUnformatted(FontAwesomeIcon.Thumbtack.ToIconString()); | ||||
|             ImGui.PopFont(); | ||||
|             UiSharedService.AttachToolTip("User is pinned in this Syncshell"); | ||||
|         } | ||||
|  | ||||
|         var textIsUid = true; | ||||
|         _mainUi.ShowUidForEntry.TryGetValue(entry.UID, out var showUidInsteadOfName); | ||||
|         var playerText = _serverConfigurationManager.GetNoteForUid(entry.UID); | ||||
|         if (showUidInsteadOfName || string.IsNullOrEmpty(playerText)) | ||||
|         { | ||||
|             playerText = entryUID; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             textIsUid = false; | ||||
|         } | ||||
|  | ||||
|         if (_configService.Current.ShowCharacterNameInsteadOfNotesForVisible && pair.IsVisible && !showUidInsteadOfName) | ||||
|         { | ||||
|             playerText = pair.PlayerName; | ||||
|             textIsUid = false; | ||||
|         } | ||||
|  | ||||
|         bool plusButtonShown = !_pairManager.DirectPairs.Any(p => string.Equals(p.UserData.UID, entry.UID, StringComparison.Ordinal)); | ||||
|  | ||||
|         ImGui.SameLine(); | ||||
|         if (!string.Equals(_mainUi.EditNickEntry, entry.UID, StringComparison.Ordinal)) | ||||
|         { | ||||
|             ImGui.SetCursorPosY(textPos); | ||||
|             if (textIsUid) ImGui.PushFont(UiBuilder.MonoFont); | ||||
|             ImGui.TextUnformatted(playerText); | ||||
|             if (textIsUid) ImGui.PopFont(); | ||||
|             UiSharedService.AttachToolTip("Left click to switch between UID display and nick" + Environment.NewLine + | ||||
|                           "Right click to change nick for " + entryUID); | ||||
|             if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) | ||||
|             { | ||||
|                 var prevState = textIsUid; | ||||
|                 if (_mainUi.ShowUidForEntry.ContainsKey(entry.UID)) | ||||
|                 { | ||||
|                     prevState = _mainUi.ShowUidForEntry[entry.UID]; | ||||
|                 } | ||||
|  | ||||
|                 _mainUi.ShowUidForEntry[entry.UID] = !prevState; | ||||
|             } | ||||
|  | ||||
|             if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) | ||||
|             { | ||||
|                 _serverConfigurationManager.SetNoteForUid(_mainUi.EditNickEntry, _mainUi.EditUserComment); | ||||
|                 _mainUi.EditUserComment = _serverConfigurationManager.GetNoteForUid(entry.UID) ?? string.Empty; | ||||
|                 _mainUi.EditNickEntry = entry.UID; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             ImGui.SetCursorPosY(originalY); | ||||
|             var buttonSizes = (plusButtonShown ? plusButtonSize.X : 0) + barButtonSize.X; | ||||
|             var buttons = plusButtonShown ? 2 : 1; | ||||
|  | ||||
|             ImGui.SetNextItemWidth(UiSharedService.GetWindowContentRegionWidth() - ImGui.GetCursorPosX() - buttonSizes - ImGui.GetStyle().ItemSpacing.X * buttons); | ||||
|             if (ImGui.InputTextWithHint("", "Nick/Notes", ref _mainUi.EditUserComment, 255, ImGuiInputTextFlags.EnterReturnsTrue)) | ||||
|             { | ||||
|                 _serverConfigurationManager.SetNoteForUid(entry.UID, _mainUi.EditUserComment); | ||||
|                 _mainUi.EditNickEntry = string.Empty; | ||||
|             } | ||||
|  | ||||
|             if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) | ||||
|             { | ||||
|                 _mainUi.EditNickEntry = string.Empty; | ||||
|             } | ||||
|             UiSharedService.AttachToolTip("Hit ENTER to save\nRight click to cancel"); | ||||
|         } | ||||
|  | ||||
|         if (plusButtonShown) | ||||
|         { | ||||
|             var barWidth = userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner) | ||||
|                 ? barButtonSize.X + ImGui.GetStyle().ItemSpacing.X | ||||
|                 : 0; | ||||
|             ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - plusButtonSize.X - barWidth); | ||||
|             ImGui.SetCursorPosY(originalY); | ||||
|  | ||||
|             if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus)) | ||||
|             { | ||||
|                 _ = ApiController.UserAddPair(new UserDto(entry.User)); | ||||
|             } | ||||
|             UiSharedService.AttachToolTip("Pair with " + entryUID + " individually"); | ||||
|         } | ||||
|  | ||||
|         if (userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner)) | ||||
|         { | ||||
|             ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - barButtonSize.X); | ||||
|             ImGui.SetCursorPosY(originalY); | ||||
|  | ||||
|             if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars)) | ||||
|             { | ||||
|                 ImGui.OpenPopup("Popup"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (individualAnimDisabled || individualSoundsDisabled) | ||||
|         { | ||||
|             var infoIconPosDist = (plusButtonShown ? plusButtonSize.X + ImGui.GetStyle().ItemSpacing.X : 0) | ||||
|                 + ((userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner)) ? barButtonSize.X + ImGui.GetStyle().ItemSpacing.X : 0); | ||||
|             var icon = FontAwesomeIcon.ExclamationTriangle; | ||||
|             var iconwidth = UiSharedService.GetIconSize(icon); | ||||
|  | ||||
|             ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - infoIconPosDist - iconwidth.X); | ||||
|  | ||||
|             ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); | ||||
|             UiSharedService.FontText(icon.ToIconString(), UiBuilder.IconFont); | ||||
|             ImGui.PopStyleColor(); | ||||
|             if (ImGui.IsItemHovered()) | ||||
|             { | ||||
|                 ImGui.BeginTooltip(); | ||||
|  | ||||
|                 ImGui.Text("Individual User permissions"); | ||||
|  | ||||
|                 if (individualSoundsDisabled) | ||||
|                 { | ||||
|                     var userSoundsText = "Sound sync disabled with " + pair.UserData.AliasOrUID; | ||||
|                     UiSharedService.FontText(FontAwesomeIcon.VolumeOff.ToIconString(), UiBuilder.IconFont); | ||||
|                     ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); | ||||
|                     ImGui.Text(userSoundsText); | ||||
|                     ImGui.NewLine(); | ||||
|                     ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); | ||||
|                     ImGui.Text("You: " + (pair.UserPair!.OwnPermissions.IsDisableSounds() ? "Disabled" : "Enabled") + ", They: " + (pair.UserPair!.OtherPermissions.IsDisableSounds() ? "Disabled" : "Enabled")); | ||||
|                 } | ||||
|  | ||||
|                 if (individualAnimDisabled) | ||||
|                 { | ||||
|                     var userAnimText = "Animation sync disabled with " + pair.UserData.AliasOrUID; | ||||
|                     UiSharedService.FontText(FontAwesomeIcon.Stop.ToIconString(), UiBuilder.IconFont); | ||||
|                     ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); | ||||
|                     ImGui.Text(userAnimText); | ||||
|                     ImGui.NewLine(); | ||||
|                     ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); | ||||
|                     ImGui.Text("You: " + (pair.UserPair!.OwnPermissions.IsDisableAnimations() ? "Disabled" : "Enabled") + ", They: " + (pair.UserPair!.OtherPermissions.IsDisableAnimations() ? "Disabled" : "Enabled")); | ||||
|                 } | ||||
|  | ||||
|                 ImGui.EndTooltip(); | ||||
|             } | ||||
|         } | ||||
|         else if ((animDisabled || soundsDisabled)) | ||||
|         { | ||||
|             var infoIconPosDist = (plusButtonShown ? plusButtonSize.X + ImGui.GetStyle().ItemSpacing.X : 0) | ||||
|                 + ((userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner)) ? barButtonSize.X + ImGui.GetStyle().ItemSpacing.X : 0); | ||||
|             var icon = FontAwesomeIcon.InfoCircle; | ||||
|             var iconwidth = UiSharedService.GetIconSize(icon); | ||||
|  | ||||
|             ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - infoIconPosDist - iconwidth.X); | ||||
|             ImGui.SetCursorPosY(originalY); | ||||
|  | ||||
|             UiSharedService.FontText(icon.ToIconString(), UiBuilder.IconFont); | ||||
|             if (ImGui.IsItemHovered()) | ||||
|             { | ||||
|                 ImGui.BeginTooltip(); | ||||
|  | ||||
|                 ImGui.Text("Sycnshell User permissions"); | ||||
|  | ||||
|                 if (soundsDisabled) | ||||
|                 { | ||||
|                     var userSoundsText = "Sound sync disabled by " + pair.UserData.AliasOrUID; | ||||
|                     UiSharedService.FontText(FontAwesomeIcon.VolumeOff.ToIconString(), UiBuilder.IconFont); | ||||
|                     ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); | ||||
|                     ImGui.Text(userSoundsText); | ||||
|                 } | ||||
|  | ||||
|                 if (animDisabled) | ||||
|                 { | ||||
|                     var userAnimText = "Animation sync disabled by " + pair.UserData.AliasOrUID; | ||||
|                     UiSharedService.FontText(FontAwesomeIcon.Stop.ToIconString(), UiBuilder.IconFont); | ||||
|                     ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); | ||||
|                     ImGui.Text(userAnimText); | ||||
|                 } | ||||
|  | ||||
|                 ImGui.EndTooltip(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (!plusButtonShown && !(userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner))) | ||||
|         { | ||||
|             ImGui.SameLine(); | ||||
|             ImGui.Dummy(barButtonSize with { X = 0 }); | ||||
|         } | ||||
|  | ||||
|         if (ImGui.BeginPopup("Popup")) | ||||
|         { | ||||
|             if ((userIsModerator || userIsOwner) && !(entryIsMod || entryIsOwner)) | ||||
|             { | ||||
|                 var pinText = entryIsPinned ? "Unpin user" : "Pin user"; | ||||
|                 if (UiSharedService.IconTextButton(FontAwesomeIcon.Thumbtack, pinText)) | ||||
|                 { | ||||
|                     ImGui.CloseCurrentPopup(); | ||||
|                     var userInfo = entry.GroupPairStatusInfo ^ GroupUserInfo.IsPinned; | ||||
|                     _ = ApiController.GroupSetUserInfo(new GroupPairUserInfoDto(entry.Group, entry.User, userInfo)); | ||||
|                 } | ||||
|                 UiSharedService.AttachToolTip("Pin this user to the Syncshell. Pinned users will not be deleted in case of a manually initiated Syncshell clean"); | ||||
|  | ||||
|                 if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Remove user") && UiSharedService.CtrlPressed()) | ||||
|                 { | ||||
|                     ImGui.CloseCurrentPopup(); | ||||
|                     _ = ApiController.GroupRemoveUser(entry); | ||||
|                 } | ||||
|  | ||||
|                 UiSharedService.AttachToolTip("Hold CTRL and click to remove user " + (entry.UserAliasOrUID) + " from Syncshell"); | ||||
|                 if (UiSharedService.IconTextButton(FontAwesomeIcon.UserSlash, "Ban User")) | ||||
|                 { | ||||
|                     _showModalBanUser = true; | ||||
|                     ImGui.CloseCurrentPopup(); | ||||
|                 } | ||||
|                 UiSharedService.AttachToolTip("Ban user from this Syncshell"); | ||||
|             } | ||||
|  | ||||
|             if (userIsOwner) | ||||
|             { | ||||
|                 string modText = entryIsMod ? "Demod user" : "Mod user"; | ||||
|                 if (UiSharedService.IconTextButton(FontAwesomeIcon.UserShield, modText) && UiSharedService.CtrlPressed()) | ||||
|                 { | ||||
|                     ImGui.CloseCurrentPopup(); | ||||
|                     var userInfo = entry.GroupPairStatusInfo ^ GroupUserInfo.IsModerator; | ||||
|                     _ = ApiController.GroupSetUserInfo(new GroupPairUserInfoDto(entry.Group, entry.User, userInfo)); | ||||
|                 } | ||||
|                 UiSharedService.AttachToolTip("Hold CTRL to change the moderator status for " + (entry.UserAliasOrUID) + Environment.NewLine + | ||||
|                     "Moderators can kick, ban/unban, pin/unpin users and clear the Syncshell."); | ||||
|                 if (UiSharedService.IconTextButton(FontAwesomeIcon.Crown, "Transfer Ownership") && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed()) | ||||
|                 { | ||||
|                     ImGui.CloseCurrentPopup(); | ||||
|                     _ = ApiController.GroupChangeOwnership(entry); | ||||
|                 } | ||||
|                 UiSharedService.AttachToolTip("Hold CTRL and SHIFT and click to transfer ownership of this Syncshell to " + (entry.UserAliasOrUID) + Environment.NewLine + "WARNING: This action is irreversible."); | ||||
|             } | ||||
|             ImGui.EndPopup(); | ||||
|         } | ||||
|  | ||||
|         if (_showModalBanUser && !_banUserPopupOpen) | ||||
|         { | ||||
|             ImGui.OpenPopup("Ban User"); | ||||
|             _banUserPopupOpen = true; | ||||
|         } | ||||
|  | ||||
|         if (!_showModalBanUser) _banUserPopupOpen = false; | ||||
|  | ||||
|         if (ImGui.BeginPopupModal("Ban User", ref _showModalBanUser, UiSharedService.PopupWindowFlags)) | ||||
|         { | ||||
|             UiSharedService.TextWrapped("User " + (entry.UserAliasOrUID) + " will be banned and removed from this Syncshell."); | ||||
|             ImGui.InputTextWithHint("##banreason", "Ban Reason", ref _banReason, 255); | ||||
|             if (ImGui.Button("Ban User")) | ||||
|             { | ||||
|                 ImGui.CloseCurrentPopup(); | ||||
|                 var reason = _banReason; | ||||
|                 _ = ApiController.GroupBanUser(entry, reason); | ||||
|                 _banReason = string.Empty; | ||||
|             } | ||||
|             UiSharedService.TextWrapped("The reason will be displayed in the banlist. The current server-side alias if present (Vanity ID) will automatically be attached to the reason."); | ||||
|             UiSharedService.SetScaledWindowSize(300); | ||||
|             ImGui.EndPopup(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -3,7 +3,6 @@ using Dalamud.Interface.Components; | ||||
| using ImGuiNET; | ||||
| using MareSynchronos.API.Data.Extensions; | ||||
| using MareSynchronos.MareConfiguration; | ||||
| using MareSynchronos.PlayerData.Pairs; | ||||
| using MareSynchronos.UI.Handlers; | ||||
| using MareSynchronos.WebAPI; | ||||
|  | ||||
| @@ -12,58 +11,31 @@ namespace MareSynchronos.UI.Components; | ||||
| public class PairGroupsUi | ||||
| { | ||||
|     private readonly ApiController _apiController; | ||||
|     private readonly Action<Pair> _clientRenderFn; | ||||
|     private readonly MareConfigService _mareConfig; | ||||
|     private readonly SelectPairForGroupUi _selectGroupForPairUi; | ||||
|     private readonly TagHandler _tagHandler; | ||||
|  | ||||
|     public PairGroupsUi(MareConfigService mareConfig, TagHandler tagHandler, Action<Pair> clientRenderFn, ApiController apiController, SelectPairForGroupUi selectGroupForPairUi) | ||||
|     public PairGroupsUi(MareConfigService mareConfig, TagHandler tagHandler, ApiController apiController, SelectPairForGroupUi selectGroupForPairUi) | ||||
|     { | ||||
|         _clientRenderFn = clientRenderFn; | ||||
|         _mareConfig = mareConfig; | ||||
|         _tagHandler = tagHandler; | ||||
|         _apiController = apiController; | ||||
|         _selectGroupForPairUi = selectGroupForPairUi; | ||||
|     } | ||||
|  | ||||
|     public void Draw(List<Pair> visibleUsers, List<Pair> onlineUsers, List<Pair> offlineUsers) | ||||
|     public void Draw<T>(List<T> visibleUsers, List<T> onlineUsers, List<T> offlineUsers) where T : DrawPairBase | ||||
|     { | ||||
|         // Only render those tags that actually have pairs in them, otherwise | ||||
|         // we can end up with a bunch of useless pair groups | ||||
|         var tagsWithPairsInThem = _tagHandler.GetAllTagsSorted(); | ||||
|         var allUsers = visibleUsers.Concat(onlineUsers).Concat(offlineUsers).ToList(); | ||||
|         if (_mareConfig.Current.ShowVisibleUsersSeparately) | ||||
|         if (typeof(T) == typeof(DrawUserPair)) | ||||
|         { | ||||
|             UiSharedService.DrawWithID("$group-VisibleCustomTag", () => DrawCategory(TagHandler.CustomVisibleTag, visibleUsers, allUsers)); | ||||
|             DrawUserPairs(tagsWithPairsInThem, allUsers.Cast<DrawUserPair>().ToList(), visibleUsers.Cast<DrawUserPair>(), onlineUsers.Cast<DrawUserPair>(), offlineUsers.Cast<DrawUserPair>()); | ||||
|         } | ||||
|         foreach (var tag in tagsWithPairsInThem) | ||||
|         { | ||||
|             if (_mareConfig.Current.ShowOfflineUsersSeparately) | ||||
|             { | ||||
|                 UiSharedService.DrawWithID($"group-{tag}", () => DrawCategory(tag, onlineUsers, allUsers, visibleUsers)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 UiSharedService.DrawWithID($"group-{tag}", () => DrawCategory(tag, onlineUsers.Concat(offlineUsers).ToList(), allUsers, visibleUsers)); | ||||
|             } | ||||
|         } | ||||
|         if (_mareConfig.Current.ShowOfflineUsersSeparately) | ||||
|         { | ||||
|             UiSharedService.DrawWithID($"group-OnlineCustomTag", () => DrawCategory(TagHandler.CustomOnlineTag, | ||||
|                 onlineUsers.Where(u => !_tagHandler.HasAnyTag(u.UserPair!)).ToList(), allUsers)); | ||||
|             UiSharedService.DrawWithID($"group-OfflineCustomTag", () => DrawCategory(TagHandler.CustomOfflineTag, | ||||
|                 offlineUsers.Where(u => u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers)); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             UiSharedService.DrawWithID($"group-OnlineCustomTag", () => DrawCategory(TagHandler.CustomOnlineTag, | ||||
|                 onlineUsers.Concat(offlineUsers).Where(u => u.UserPair!.OtherPermissions.IsPaired() && !_tagHandler.HasAnyTag(u.UserPair!)).ToList(), allUsers)); | ||||
|         } | ||||
|         UiSharedService.DrawWithID($"group-UnpairedCustomTag", () => DrawCategory(TagHandler.CustomUnpairedTag, | ||||
|             offlineUsers.Where(u => !u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers)); | ||||
|     } | ||||
|  | ||||
|     private void DrawButtons(string tag, List<Pair> availablePairsInThisTag) | ||||
|     private void DrawButtons(string tag, List<DrawUserPair> availablePairsInThisTag) | ||||
|     { | ||||
|         var allArePaused = availablePairsInThisTag.All(pair => pair.UserPair!.OwnPermissions.IsPaused()); | ||||
|         var pauseButton = allArePaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; | ||||
| @@ -114,9 +86,9 @@ public class PairGroupsUi | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void DrawCategory(string tag, List<Pair> onlineUsers, List<Pair> allUsers, List<Pair>? visibleUsers = null) | ||||
|     private void DrawCategory(string tag, IEnumerable<DrawPairBase> onlineUsers, IEnumerable<DrawPairBase> allUsers, IEnumerable<DrawPairBase>? visibleUsers = null) | ||||
|     { | ||||
|         List<Pair> usersInThisTag; | ||||
|         IEnumerable<DrawPairBase> usersInThisTag; | ||||
|         HashSet<string>? otherUidsTaggedWithTag = null; | ||||
|         bool isSpecialTag = false; | ||||
|         int visibleInThisTag = 0; | ||||
| @@ -129,16 +101,21 @@ public class PairGroupsUi | ||||
|         { | ||||
|             otherUidsTaggedWithTag = _tagHandler.GetOtherUidsForTag(tag); | ||||
|             usersInThisTag = onlineUsers | ||||
|                 .Where(pair => otherUidsTaggedWithTag.Contains(pair.UserData.UID)) | ||||
|                 .Where(pair => otherUidsTaggedWithTag.Contains(pair.UID)) | ||||
|                 .ToList(); | ||||
|             visibleInThisTag = visibleUsers?.Count(p => otherUidsTaggedWithTag.Contains(p.UserData.UID)) ?? 0; | ||||
|             visibleInThisTag = visibleUsers?.Count(p => otherUidsTaggedWithTag.Contains(p.UID)) ?? 0; | ||||
|         } | ||||
|  | ||||
|         if (isSpecialTag && !usersInThisTag.Any()) return; | ||||
|  | ||||
|         DrawName(tag, isSpecialTag, visibleInThisTag, usersInThisTag.Count, otherUidsTaggedWithTag?.Count); | ||||
|         DrawName(tag, isSpecialTag, visibleInThisTag, usersInThisTag.Count(), otherUidsTaggedWithTag?.Count); | ||||
|         if (!isSpecialTag) | ||||
|             UiSharedService.DrawWithID($"group-{tag}-buttons", () => DrawButtons(tag, allUsers.Where(p => otherUidsTaggedWithTag!.Contains(p.UserData.UID)).ToList())); | ||||
|         { | ||||
|             if (onlineUsers.Any() && onlineUsers.First() is DrawUserPair) | ||||
|             { | ||||
|                 UiSharedService.DrawWithID($"group-{tag}-buttons", () => DrawButtons(tag, allUsers.Cast<DrawUserPair>().Where(p => otherUidsTaggedWithTag!.Contains(p.UID)).ToList())); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (!_tagHandler.IsTagOpen(tag)) return; | ||||
|  | ||||
| @@ -201,31 +178,66 @@ public class PairGroupsUi | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void DrawPairs(string tag, List<Pair> availablePairsInThisCategory) | ||||
|     private void DrawPairs(string tag, IEnumerable<DrawPairBase> availablePairsInThisCategory) | ||||
|     { | ||||
|         // These are all the OtherUIDs that are tagged with this tag | ||||
|         availablePairsInThisCategory | ||||
|             .ForEach(pair => UiSharedService.DrawWithID($"tag-{tag}-pair-${pair.UserData.UID}", () => _clientRenderFn(pair))); | ||||
|         foreach (var pair in availablePairsInThisCategory) | ||||
|         { | ||||
|             UiSharedService.DrawWithID($"tag-{tag}-pair-${pair.UID}", () => pair.DrawPairedClient()); | ||||
|         } | ||||
|         ImGui.Separator(); | ||||
|     } | ||||
|  | ||||
|     private void PauseRemainingPairs(List<Pair> availablePairs) | ||||
|     private void DrawUserPairs(List<string> tagsWithPairsInThem, List<DrawUserPair> allUsers, IEnumerable<DrawUserPair> visibleUsers, IEnumerable<DrawUserPair> onlineUsers, IEnumerable<DrawUserPair> offlineUsers) | ||||
|     { | ||||
|         if (_mareConfig.Current.ShowVisibleUsersSeparately) | ||||
|         { | ||||
|             UiSharedService.DrawWithID("$group-VisibleCustomTag", () => DrawCategory(TagHandler.CustomVisibleTag, visibleUsers, allUsers)); | ||||
|         } | ||||
|         foreach (var tag in tagsWithPairsInThem) | ||||
|         { | ||||
|             if (_mareConfig.Current.ShowOfflineUsersSeparately) | ||||
|             { | ||||
|                 UiSharedService.DrawWithID($"group-{tag}", () => DrawCategory(tag, onlineUsers, allUsers, visibleUsers)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 UiSharedService.DrawWithID($"group-{tag}", () => DrawCategory(tag, onlineUsers.Concat(offlineUsers).ToList(), allUsers, visibleUsers)); | ||||
|             } | ||||
|         } | ||||
|         if (_mareConfig.Current.ShowOfflineUsersSeparately) | ||||
|         { | ||||
|             UiSharedService.DrawWithID($"group-OnlineCustomTag", () => DrawCategory(TagHandler.CustomOnlineTag, | ||||
|                 onlineUsers.Where(u => !_tagHandler.HasAnyTag(u.UID)).ToList(), allUsers)); | ||||
|             UiSharedService.DrawWithID($"group-OfflineCustomTag", () => DrawCategory(TagHandler.CustomOfflineTag, | ||||
|                 offlineUsers.Where(u => u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers)); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             UiSharedService.DrawWithID($"group-OnlineCustomTag", () => DrawCategory(TagHandler.CustomOnlineTag, | ||||
|                 onlineUsers.Concat(offlineUsers).Where(u => u.UserPair!.OtherPermissions.IsPaired() && !_tagHandler.HasAnyTag(u.UID)).ToList(), allUsers)); | ||||
|         } | ||||
|         UiSharedService.DrawWithID($"group-UnpairedCustomTag", () => DrawCategory(TagHandler.CustomUnpairedTag, | ||||
|             offlineUsers.Where(u => !u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers)); | ||||
|     } | ||||
|  | ||||
|     private void PauseRemainingPairs(List<DrawUserPair> availablePairs) | ||||
|     { | ||||
|         foreach (var pairToPause in availablePairs.Where(pair => !pair.UserPair!.OwnPermissions.IsPaused())) | ||||
|         { | ||||
|             var perm = pairToPause.UserPair!.OwnPermissions; | ||||
|             perm.SetPaused(paused: true); | ||||
|             _ = _apiController.UserSetPairPermissions(new(pairToPause.UserData, perm)); | ||||
|             _ = _apiController.UserSetPairPermissions(new(new(pairToPause.UID), perm)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void ResumeAllPairs(List<Pair> availablePairs) | ||||
|     private void ResumeAllPairs(List<DrawUserPair> availablePairs) | ||||
|     { | ||||
|         foreach (var pairToPause in availablePairs) | ||||
|         { | ||||
|             var perm = pairToPause.UserPair!.OwnPermissions; | ||||
|             perm.SetPaused(paused: false); | ||||
|             _ = _apiController.UserSetPairPermissions(new(pairToPause.UserData, perm)); | ||||
|             _ = _apiController.UserSetPairPermissions(new(new(pairToPause.UID), perm)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -11,6 +11,7 @@ namespace MareSynchronos.UI.Components; | ||||
| public class SelectGroupForPairUi | ||||
| { | ||||
|     private readonly TagHandler _tagHandler; | ||||
|     private readonly UidDisplayHandler _uidDisplayHandler; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// The group UI is always open for a specific pair. This defines which pair the UI is open for. | ||||
| @@ -28,21 +29,22 @@ public class SelectGroupForPairUi | ||||
|     /// </summary> | ||||
|     private string _tagNameToAdd = ""; | ||||
|  | ||||
|     public SelectGroupForPairUi(TagHandler tagHandler) | ||||
|     public SelectGroupForPairUi(TagHandler tagHandler, UidDisplayHandler uidDisplayHandler) | ||||
|     { | ||||
|         _show = false; | ||||
|         _pair = null; | ||||
|         _tagHandler = tagHandler; | ||||
|         _uidDisplayHandler = uidDisplayHandler; | ||||
|     } | ||||
|  | ||||
|     public void Draw(Dictionary<string, bool> showUidForEntry) | ||||
|     public void Draw() | ||||
|     { | ||||
|         if (_pair == null) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         var name = PairName(showUidForEntry, _pair); | ||||
|         var name = PairName(_pair); | ||||
|         var popupName = $"Choose Groups for {name}"; | ||||
|         // Is the popup supposed to show but did not open yet? Open it | ||||
|         if (_show) | ||||
| @@ -93,30 +95,19 @@ public class SelectGroupForPairUi | ||||
|         _show = true; | ||||
|     } | ||||
|  | ||||
|     private static string PairName(Dictionary<string, bool> showUidForEntry, Pair pair) | ||||
|     { | ||||
|         showUidForEntry.TryGetValue(pair.UserData.UID, out var showUidInsteadOfName); | ||||
|         var playerText = pair.GetNote(); | ||||
|         if (showUidInsteadOfName || string.IsNullOrEmpty(playerText)) | ||||
|         { | ||||
|             playerText = pair.UserData.AliasOrUID; | ||||
|         } | ||||
|         return playerText; | ||||
|     } | ||||
|  | ||||
|     private void DrawGroupName(Pair pair, string name) | ||||
|     { | ||||
|         var hasTagBefore = _tagHandler.HasTag(pair.UserPair!, name); | ||||
|         var hasTagBefore = _tagHandler.HasTag(pair.UserData.UID, name); | ||||
|         var hasTag = hasTagBefore; | ||||
|         if (ImGui.Checkbox(name, ref hasTag)) | ||||
|         { | ||||
|             if (hasTag) | ||||
|             { | ||||
|                 _tagHandler.AddTagToPairedUid(pair.UserPair!, name); | ||||
|                 _tagHandler.AddTagToPairedUid(pair.UserData.UID, name); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 _tagHandler.RemoveTagFromPairedUid(pair.UserPair!, name); | ||||
|                 _tagHandler.RemoveTagFromPairedUid(pair.UserData.UID, name); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -128,7 +119,7 @@ public class SelectGroupForPairUi | ||||
|             _tagHandler.AddTag(_tagNameToAdd); | ||||
|             if (_pair != null) | ||||
|             { | ||||
|                 _tagHandler.AddTagToPairedUid(_pair.UserPair!, _tagNameToAdd); | ||||
|                 _tagHandler.AddTagToPairedUid(_pair.UserData.UID, _tagNameToAdd); | ||||
|             } | ||||
|             _tagNameToAdd = string.Empty; | ||||
|         } | ||||
| @@ -137,4 +128,9 @@ public class SelectGroupForPairUi | ||||
|             _tagNameToAdd = string.Empty; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private string PairName(Pair pair) | ||||
|     { | ||||
|         return _uidDisplayHandler.GetPlayerText(pair).text; | ||||
|     } | ||||
| } | ||||
| @@ -9,18 +9,20 @@ namespace MareSynchronos.UI.Components; | ||||
| public class SelectPairForGroupUi | ||||
| { | ||||
|     private readonly TagHandler _tagHandler; | ||||
|     private readonly UidDisplayHandler _uidDisplayHandler; | ||||
|     private string _filter = string.Empty; | ||||
|     private bool _opened = false; | ||||
|     private HashSet<string> _peopleInGroup = new(StringComparer.Ordinal); | ||||
|     private bool _show = false; | ||||
|     private string _tag = string.Empty; | ||||
|  | ||||
|     public SelectPairForGroupUi(TagHandler tagHandler) | ||||
|     public SelectPairForGroupUi(TagHandler tagHandler, UidDisplayHandler uidDisplayHandler) | ||||
|     { | ||||
|         _tagHandler = tagHandler; | ||||
|         _uidDisplayHandler = uidDisplayHandler; | ||||
|     } | ||||
|  | ||||
|     public void Draw(List<Pair> pairs, Dictionary<string, bool> showUidForEntry) | ||||
|     public void Draw(List<Pair> pairs) | ||||
|     { | ||||
|         var workHeight = ImGui.GetMainViewport().WorkSize.Y / ImGuiHelpers.GlobalScale; | ||||
|         var minSize = new Vector2(300, workHeight < 400 ? workHeight : 400) * ImGuiHelpers.GlobalScale; | ||||
| @@ -47,21 +49,21 @@ public class SelectPairForGroupUi | ||||
|             UiSharedService.FontText($"Select users for group {_tag}", UiBuilder.DefaultFont); | ||||
|             ImGui.InputTextWithHint("##filter", "Filter", ref _filter, 255, ImGuiInputTextFlags.None); | ||||
|             foreach (var item in pairs | ||||
|                 .Where(p => string.IsNullOrEmpty(_filter) || PairName(showUidForEntry, p).Contains(_filter, StringComparison.OrdinalIgnoreCase)) | ||||
|                 .OrderBy(p => PairName(showUidForEntry, p), StringComparer.OrdinalIgnoreCase) | ||||
|                 .Where(p => string.IsNullOrEmpty(_filter) || PairName(p).Contains(_filter, StringComparison.OrdinalIgnoreCase)) | ||||
|                 .OrderBy(p => PairName(p), StringComparer.OrdinalIgnoreCase) | ||||
|                 .ToList()) | ||||
|             { | ||||
|                 var isInGroup = _peopleInGroup.Contains(item.UserData.UID); | ||||
|                 if (ImGui.Checkbox(PairName(showUidForEntry, item), ref isInGroup)) | ||||
|                 if (ImGui.Checkbox(PairName(item), ref isInGroup)) | ||||
|                 { | ||||
|                     if (isInGroup) | ||||
|                     { | ||||
|                         _tagHandler.AddTagToPairedUid(item.UserPair!, _tag); | ||||
|                         _tagHandler.AddTagToPairedUid(item.UserData.UID, _tag); | ||||
|                         _peopleInGroup.Add(item.UserData.UID); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         _tagHandler.RemoveTagFromPairedUid(item.UserPair!, _tag); | ||||
|                         _tagHandler.RemoveTagFromPairedUid(item.UserData.UID, _tag); | ||||
|                         _peopleInGroup.Remove(item.UserData.UID); | ||||
|                     } | ||||
|                 } | ||||
| @@ -82,14 +84,8 @@ public class SelectPairForGroupUi | ||||
|         _show = true; | ||||
|     } | ||||
|  | ||||
|     private static string PairName(Dictionary<string, bool> showUidForEntry, Pair pair) | ||||
|     private string PairName(Pair pair) | ||||
|     { | ||||
|         showUidForEntry.TryGetValue(pair.UserData.UID, out var showUidInsteadOfName); | ||||
|         var playerText = pair.GetNote(); | ||||
|         if (showUidInsteadOfName || string.IsNullOrEmpty(playerText)) | ||||
|         { | ||||
|             playerText = pair.UserData.AliasOrUID; | ||||
|         } | ||||
|         return playerText; | ||||
|         return _uidDisplayHandler.GetPlayerText(pair).text; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										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")) | ||||
|             { | ||||
|                 _fileDialogManager.OpenFileDialog("Pick MCDF file", ".mcdf", (success, path) => | ||||
|                 _fileDialogManager.OpenFileDialog("Pick MCDF file", ".mcdf", (success, paths) => | ||||
|                 { | ||||
|                     if (!success) return; | ||||
|                     if (paths.FirstOrDefault() is not string path) return; | ||||
|  | ||||
|                     _configService.Current.ExportFolder = Path.GetDirectoryName(path) ?? string.Empty; | ||||
|                     _configService.Save(); | ||||
|  | ||||
|                     Task.Run(() => _mareCharaFileManager.LoadMareCharaFile(path)); | ||||
|                 }); | ||||
|                 }, 1, Directory.Exists(_configService.Current.ExportFolder) ? _configService.Current.ExportFolder : null); | ||||
|             } | ||||
|             UiSharedService.AttachToolTip("Applies it to the currently selected GPose actor"); | ||||
|             if (_mareCharaFileManager.LoadedCharaFile != null) | ||||
|   | ||||
| @@ -21,9 +21,9 @@ public class TagHandler | ||||
|         _serverConfigurationManager.AddTag(tag); | ||||
|     } | ||||
|  | ||||
|     public void AddTagToPairedUid(UserPairDto pair, string tagName) | ||||
|     public void AddTagToPairedUid(string uid, string tagName) | ||||
|     { | ||||
|         _serverConfigurationManager.AddTagForUid(pair.User.UID, tagName); | ||||
|         _serverConfigurationManager.AddTagForUid(uid, tagName); | ||||
|     } | ||||
|  | ||||
|     public List<string> GetAllTagsSorted() | ||||
| @@ -38,14 +38,14 @@ public class TagHandler | ||||
|         return _serverConfigurationManager.GetUidsForTag(tag); | ||||
|     } | ||||
|  | ||||
|     public bool HasAnyTag(UserPairDto pair) | ||||
|     public bool HasAnyTag(string uid) | ||||
|     { | ||||
|         return _serverConfigurationManager.HasTags(pair.User.UID); | ||||
|         return _serverConfigurationManager.HasTags(uid); | ||||
|     } | ||||
|  | ||||
|     public bool HasTag(UserPairDto pair, string tagName) | ||||
|     public bool HasTag(string uid, string tagName) | ||||
|     { | ||||
|         return _serverConfigurationManager.ContainsTag(pair.User.UID, tagName); | ||||
|         return _serverConfigurationManager.ContainsTag(uid, tagName); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -64,9 +64,9 @@ public class TagHandler | ||||
|         _serverConfigurationManager.RemoveTag(tag); | ||||
|     } | ||||
|  | ||||
|     public void RemoveTagFromPairedUid(UserPairDto pair, string tagName) | ||||
|     public void RemoveTagFromPairedUid(string uid, string tagName) | ||||
|     { | ||||
|         _serverConfigurationManager.RemoveTagForUid(pair.User.UID, tagName); | ||||
|         _serverConfigurationManager.RemoveTagForUid(uid, tagName); | ||||
|     } | ||||
|  | ||||
|     public void SetTagOpen(string tag, bool open) | ||||
|   | ||||
							
								
								
									
										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); | ||||
|                 if (UiSharedService.IconTextButton(FontAwesomeIcon.Save, "Export Character as MCDF")) | ||||
|                 { | ||||
|                     _uiShared.FileDialogManager.SaveFileDialog("Export Character to file", ".mcdf", "export.mcdf", ".mcdf", (success, path) => | ||||
|                     string defaultFileName = string.IsNullOrEmpty(_exportDescription) | ||||
|                         ? "export.mcdf" | ||||
|                         : string.Join('_', $"{_exportDescription}.mcdf".Split(Path.GetInvalidFileNameChars())); | ||||
|                     _uiShared.FileDialogManager.SaveFileDialog("Export Character to file", ".mcdf", defaultFileName, ".mcdf", (success, path) => | ||||
|                     { | ||||
|                         if (!success) return; | ||||
|  | ||||
|                         _configService.Current.ExportFolder = Path.GetDirectoryName(path) ?? string.Empty; | ||||
|                         _configService.Save(); | ||||
|  | ||||
|                         Task.Run(() => | ||||
|                         { | ||||
|                             try | ||||
| @@ -378,7 +384,7 @@ public class SettingsUi : WindowMediatorSubscriberBase | ||||
|                                 _logger.LogCritical(ex, "Error saving data"); | ||||
|                             } | ||||
|                         }); | ||||
|                     }); | ||||
|                     }, Directory.Exists(_configService.Current.ExportFolder) ? _configService.Current.ExportFolder : null); | ||||
|                 } | ||||
|                 UiSharedService.ColorTextWrapped("Note: For best results make sure you have everything you want to be shared as well as the correct character appearance" + | ||||
|                     " equipped and redraw your character before exporting.", ImGuiColors.DalamudYellow); | ||||
| @@ -484,9 +490,11 @@ public class SettingsUi : WindowMediatorSubscriberBase | ||||
|         ImGui.Separator(); | ||||
|         UiSharedService.FontText("UI", _uiShared.UidFont); | ||||
|         var showNameInsteadOfNotes = _configService.Current.ShowCharacterNameInsteadOfNotesForVisible; | ||||
|         var reverseUserSort = _configService.Current.ReverseUserSort; | ||||
|         var showVisibleSeparate = _configService.Current.ShowVisibleUsersSeparately; | ||||
|         var showOfflineSeparate = _configService.Current.ShowOfflineUsersSeparately; | ||||
|         var showProfiles = _configService.Current.ProfilesShow; | ||||
|         var showNsfwProfiles = _configService.Current.ProfilesAllowNsfw; | ||||
|         var profileDelay = _configService.Current.ProfileDelay; | ||||
|  | ||||
|         if (ImGui.Checkbox("Show separate Visible group", ref showVisibleSeparate)) | ||||
|         { | ||||
| @@ -509,12 +517,30 @@ public class SettingsUi : WindowMediatorSubscriberBase | ||||
|         } | ||||
|         UiSharedService.DrawHelpText("This will show the character name instead of custom set note when a character is visible"); | ||||
|  | ||||
|         if (ImGui.Checkbox("Reverse user sort", ref reverseUserSort)) | ||||
|         if (ImGui.Checkbox("Show Mare Profiles on Hover", ref showProfiles)) | ||||
|         { | ||||
|             _configService.Current.ReverseUserSort = reverseUserSort; | ||||
|             Mediator.Publish(new ClearProfileDataMessage()); | ||||
|             _configService.Current.ProfilesShow = showProfiles; | ||||
|             _configService.Save(); | ||||
|         } | ||||
|         UiSharedService.DrawHelpText("This reverses the user sort from A->Z to Z->A"); | ||||
|         UiSharedService.DrawHelpText("This will show the configured user profile after a set delay"); | ||||
|         ImGui.Indent(); | ||||
|         if (!showProfiles) ImGui.BeginDisabled(); | ||||
|         if (ImGui.Checkbox("Show profiles marked as NSFW", ref showNsfwProfiles)) | ||||
|         { | ||||
|             Mediator.Publish(new ClearProfileDataMessage()); | ||||
|             _configService.Current.ProfilesAllowNsfw = showNsfwProfiles; | ||||
|             _configService.Save(); | ||||
|         } | ||||
|         UiSharedService.DrawHelpText("Will show profiles that have the NSFW tag enabled"); | ||||
|         if (ImGui.SliderFloat("Hover Delay", ref profileDelay, 1, 10)) | ||||
|         { | ||||
|             _configService.Current.ProfileDelay = profileDelay; | ||||
|             _configService.Save(); | ||||
|         } | ||||
|         UiSharedService.DrawHelpText("Delay until the profile should be displayed"); | ||||
|         if (!showProfiles) ImGui.EndDisabled(); | ||||
|         ImGui.Unindent(); | ||||
|  | ||||
|         ImGui.Separator(); | ||||
|  | ||||
|   | ||||
| @@ -161,13 +161,13 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase | ||||
|  | ||||
|     public static uint Color(Vector4 color) | ||||
|     { | ||||
|         uint ret = (byte)(color.W); | ||||
|         uint ret = (byte)(color.W * 255); | ||||
|         ret <<= 8; | ||||
|         ret += (byte)(color.X); | ||||
|         ret += (byte)(color.Z * 255); | ||||
|         ret <<= 8; | ||||
|         ret += (byte)(color.Y); | ||||
|         ret += (byte)(color.Y * 255); | ||||
|         ret <<= 8; | ||||
|         ret += (byte)(color.Z); | ||||
|         ret += (byte)(color.X * 255); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
| @@ -452,6 +452,13 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public void BigText(string text) | ||||
|     { | ||||
|         if (UidFontBuilt) ImGui.PushFont(UidFont); | ||||
|         ImGui.TextUnformatted(text); | ||||
|         if (UidFontBuilt) ImGui.PopFont(); | ||||
|     } | ||||
|  | ||||
|     public void DrawCacheDirectorySetting() | ||||
|     { | ||||
|         ColorTextWrapped("Note: The storage folder should be somewhere close to root (i.e. C:\\MareStorage) in a new empty folder. DO NOT point this to your game folder. DO NOT point this to your Penumbra folder.", ImGuiColors.DalamudYellow); | ||||
|   | ||||
| @@ -242,7 +242,13 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase | ||||
|                 fi.LastWriteTime = RandomDayInThePast().Invoke(); | ||||
|                 try | ||||
|                 { | ||||
|                     _ = _fileDbManager.CreateCacheEntry(filePath); | ||||
|                     var entry = _fileDbManager.CreateCacheEntry(filePath); | ||||
|                     if (!string.Equals(entry?.Hash, file.Hash, StringComparison.OrdinalIgnoreCase)) | ||||
|                     { | ||||
|                         Logger.LogError("Hash mismatch after extracting, got {hash}, expected {expectedHash}, deleting file", entry?.Hash, file.Hash); | ||||
|                         File.Delete(filePath); | ||||
|                         _fileDbManager.RemoveHash(entry); | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|   | ||||
| @@ -49,6 +49,12 @@ public partial class ApiController | ||||
|         return await _mareHub!.InvokeAsync<List<UserPairDto>>(nameof(UserGetPairedClients)).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     public async Task<UserProfileDto> UserGetProfile(UserDto dto) | ||||
|     { | ||||
|         if (!IsConnected) return new UserProfileDto(dto.User, false, null, null, null); | ||||
|         return await _mareHub!.InvokeAsync<UserProfileDto>(nameof(UserGetProfile), dto).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     public async Task UserPushData(UserCharaDataMessageDto dto) | ||||
|     { | ||||
|         try | ||||
| @@ -67,11 +73,23 @@ public partial class ApiController | ||||
|         await _mareHub!.SendAsync(nameof(UserRemovePair), userDto).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     public async Task UserReportProfile(UserProfileReportDto userDto) | ||||
|     { | ||||
|         if (!IsConnected) return; | ||||
|         await _mareHub!.SendAsync(nameof(UserReportProfile), userDto).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     public async Task UserSetPairPermissions(UserPermissionsDto userPermissions) | ||||
|     { | ||||
|         await _mareHub!.SendAsync(nameof(UserSetPairPermissions), userPermissions).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     public async Task UserSetProfile(UserProfileDto userDescription) | ||||
|     { | ||||
|         if (!IsConnected) return; | ||||
|         await _mareHub!.InvokeAsync(nameof(UserSetProfile), userDescription).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     private async Task PushCharacterDataInternal(CharacterData character, List<UserData> visibleCharacters) | ||||
|     { | ||||
|         Logger.LogInformation("Pushing character data for {hash} to {charas}", character.DataHash.Value, string.Join(", ", visibleCharacters.Select(c => c.AliasOrUID))); | ||||
|   | ||||
| @@ -162,6 +162,13 @@ public partial class ApiController | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     public Task Client_UserUpdateProfile(UserDto dto) | ||||
|     { | ||||
|         Logger.LogDebug("Client_UserUpdateProfile: {dto}", dto); | ||||
|         ExecuteSafely(() => Mediator.Publish(new ClearProfileDataMessage(dto.User))); | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     public Task Client_UserUpdateSelfPairPermissions(UserPermissionsDto dto) | ||||
|     { | ||||
|         Logger.LogDebug("Client_UserUpdateSelfPairPermissions: {dto}", dto); | ||||
| @@ -277,6 +284,12 @@ public partial class ApiController | ||||
|         _mareHub!.On(nameof(Client_UserUpdateOtherPairPermissions), act); | ||||
|     } | ||||
|  | ||||
|     public void OnUserUpdateProfile(Action<UserDto> act) | ||||
|     { | ||||
|         if (_initialized) return; | ||||
|         _mareHub!.On(nameof(Client_UserUpdateProfile), act); | ||||
|     } | ||||
|  | ||||
|     public void OnUserUpdateSelfPairPermissions(Action<UserPermissionsDto> act) | ||||
|     { | ||||
|         if (_initialized) return; | ||||
|   | ||||
| @@ -244,6 +244,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM | ||||
|         OnUserUpdateOtherPairPermissions(dto => Client_UserUpdateOtherPairPermissions(dto)); | ||||
|         OnUserUpdateSelfPairPermissions(dto => Client_UserUpdateSelfPairPermissions(dto)); | ||||
|         OnUserReceiveUploadStatus(dto => Client_UserReceiveUploadStatus(dto)); | ||||
|         OnUserUpdateProfile(dto => Client_UserUpdateProfile(dto)); | ||||
|  | ||||
|         OnGroupChangePermissions((dto) => Client_GroupChangePermissions(dto)); | ||||
|         OnGroupDelete((dto) => Client_GroupDelete(dto)); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Stanley Dimant
					Stanley Dimant