Update 0.1.9 - Correctif UI + Default Synchronisation settings + Detect TypeChat
This commit is contained in:
2
MareAPI
2
MareAPI
Submodule MareAPI updated: fa9b7bce43...3b175900c1
@@ -1,4 +1,5 @@
|
|||||||
using MareSynchronos.MareConfiguration.Models;
|
using System.Collections.Generic;
|
||||||
|
using MareSynchronos.MareConfiguration.Models;
|
||||||
using MareSynchronos.UI;
|
using MareSynchronos.UI;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
@@ -60,6 +61,11 @@ public class MareConfig : IMareConfiguration
|
|||||||
public bool ShowUploadingBigText { get; set; } = true;
|
public bool ShowUploadingBigText { get; set; } = true;
|
||||||
public bool ShowVisibleUsersSeparately { get; set; } = true;
|
public bool ShowVisibleUsersSeparately { get; set; } = true;
|
||||||
public string LastChangelogVersionSeen { get; set; } = string.Empty;
|
public string LastChangelogVersionSeen { get; set; } = string.Empty;
|
||||||
|
public bool DefaultDisableSounds { get; set; } = false;
|
||||||
|
public bool DefaultDisableAnimations { get; set; } = false;
|
||||||
|
public bool DefaultDisableVfx { get; set; } = false;
|
||||||
|
public Dictionary<string, SyncOverrideEntry> PairSyncOverrides { get; set; } = new(StringComparer.Ordinal);
|
||||||
|
public Dictionary<string, SyncOverrideEntry> GroupSyncOverrides { get; set; } = new(StringComparer.Ordinal);
|
||||||
public bool EnableAutoDetectDiscovery { get; set; } = false;
|
public bool EnableAutoDetectDiscovery { get; set; } = false;
|
||||||
public bool AllowAutoDetectPairRequests { get; set; } = false;
|
public bool AllowAutoDetectPairRequests { get; set; } = false;
|
||||||
public int AutoDetectMaxDistanceMeters { get; set; } = 40;
|
public int AutoDetectMaxDistanceMeters { get; set; } = 40;
|
||||||
@@ -78,6 +84,8 @@ public class MareConfig : IMareConfiguration
|
|||||||
public int ChatLogKind { get; set; } = 1; // XivChatType.Debug
|
public int ChatLogKind { get; set; } = 1; // XivChatType.Debug
|
||||||
public bool ExtraChatAPI { get; set; } = false;
|
public bool ExtraChatAPI { get; set; } = false;
|
||||||
public bool ExtraChatTags { get; set; } = false;
|
public bool ExtraChatTags { get; set; } = false;
|
||||||
|
public bool TypingIndicatorShowOnNameplates { get; set; } = true;
|
||||||
|
public bool TypingIndicatorShowOnPartyList { get; set; } = true;
|
||||||
|
|
||||||
public bool MareAPI { get; set; } = true;
|
public bool MareAPI { get; set; } = true;
|
||||||
}
|
}
|
||||||
|
|||||||
13
MareSynchronos/MareConfiguration/Models/SyncOverrideEntry.cs
Normal file
13
MareSynchronos/MareConfiguration/Models/SyncOverrideEntry.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MareSynchronos.MareConfiguration.Models;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class SyncOverrideEntry
|
||||||
|
{
|
||||||
|
public bool? DisableSounds { get; set; }
|
||||||
|
public bool? DisableAnimations { get; set; }
|
||||||
|
public bool? DisableVfx { get; set; }
|
||||||
|
|
||||||
|
public bool IsEmpty => DisableSounds is null && DisableAnimations is null && DisableVfx is null;
|
||||||
|
}
|
||||||
@@ -150,6 +150,7 @@ public class MarePlugin : MediatorSubscriberBase, IHostedService
|
|||||||
_runtimeServiceScope.ServiceProvider.GetRequiredService<TransientResourceManager>();
|
_runtimeServiceScope.ServiceProvider.GetRequiredService<TransientResourceManager>();
|
||||||
_runtimeServiceScope.ServiceProvider.GetRequiredService<OnlinePlayerManager>();
|
_runtimeServiceScope.ServiceProvider.GetRequiredService<OnlinePlayerManager>();
|
||||||
_runtimeServiceScope.ServiceProvider.GetRequiredService<NotificationService>();
|
_runtimeServiceScope.ServiceProvider.GetRequiredService<NotificationService>();
|
||||||
|
_runtimeServiceScope.ServiceProvider.GetRequiredService<SyncDefaultsService>();
|
||||||
_runtimeServiceScope.ServiceProvider.GetRequiredService<ChatService>();
|
_runtimeServiceScope.ServiceProvider.GetRequiredService<ChatService>();
|
||||||
_runtimeServiceScope.ServiceProvider.GetRequiredService<GuiHookService>();
|
_runtimeServiceScope.ServiceProvider.GetRequiredService<GuiHookService>();
|
||||||
|
|
||||||
@@ -167,4 +168,4 @@ public class MarePlugin : MediatorSubscriberBase, IHostedService
|
|||||||
Logger?.LogCritical(ex, "Error during launch of managers");
|
Logger?.LogCritical(ex, "Error during launch of managers");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AssemblyName>UmbraSync</AssemblyName>
|
<AssemblyName>UmbraSync</AssemblyName>
|
||||||
<RootNamespace>UmbraSync</RootNamespace>
|
<RootNamespace>UmbraSync</RootNamespace>
|
||||||
<Version>0.1.8.2</Version>
|
<Version>0.1.9.0</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public sealed class PairManager : DisposableMediatorSubscriberBase
|
|||||||
RecreateLazy();
|
RecreateLazy();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddGroupPair(GroupPairFullInfoDto dto)
|
public void AddGroupPair(GroupPairFullInfoDto dto, bool isInitialLoad = false)
|
||||||
{
|
{
|
||||||
if (!_allClientPairs.ContainsKey(dto.User))
|
if (!_allClientPairs.ContainsKey(dto.User))
|
||||||
_allClientPairs[dto.User] = _pairFactory.Create(dto.User);
|
_allClientPairs[dto.User] = _pairFactory.Create(dto.User);
|
||||||
@@ -59,6 +59,11 @@ public sealed class PairManager : DisposableMediatorSubscriberBase
|
|||||||
var group = _allGroups[dto.Group];
|
var group = _allGroups[dto.Group];
|
||||||
_allClientPairs[dto.User].GroupPair[group] = dto;
|
_allClientPairs[dto.User].GroupPair[group] = dto;
|
||||||
RecreateLazy();
|
RecreateLazy();
|
||||||
|
|
||||||
|
if (!isInitialLoad)
|
||||||
|
{
|
||||||
|
Mediator.Publish(new ApplyDefaultGroupPermissionsMessage(dto));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair? GetPairByUID(string uid)
|
public Pair? GetPairByUID(string uid)
|
||||||
@@ -88,6 +93,11 @@ public sealed class PairManager : DisposableMediatorSubscriberBase
|
|||||||
LastAddedUser = _allClientPairs[dto.User];
|
LastAddedUser = _allClientPairs[dto.User];
|
||||||
_allClientPairs[dto.User].ApplyLastReceivedData();
|
_allClientPairs[dto.User].ApplyLastReceivedData();
|
||||||
RecreateLazy();
|
RecreateLazy();
|
||||||
|
|
||||||
|
if (addToLastAddedUser)
|
||||||
|
{
|
||||||
|
Mediator.Publish(new ApplyDefaultPairPermissionsMessage(dto));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearPairs()
|
public void ClearPairs()
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddSingleton<PluginWarningNotificationService>();
|
collection.AddSingleton<PluginWarningNotificationService>();
|
||||||
collection.AddSingleton<FileCompactor>();
|
collection.AddSingleton<FileCompactor>();
|
||||||
collection.AddSingleton<TagHandler>();
|
collection.AddSingleton<TagHandler>();
|
||||||
|
collection.AddSingleton<SyncDefaultsService>();
|
||||||
collection.AddSingleton<UidDisplayHandler>();
|
collection.AddSingleton<UidDisplayHandler>();
|
||||||
collection.AddSingleton<PluginWatcherService>();
|
collection.AddSingleton<PluginWatcherService>();
|
||||||
collection.AddSingleton<PlayerPerformanceService>();
|
collection.AddSingleton<PlayerPerformanceService>();
|
||||||
@@ -146,6 +147,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddSingleton<IpcManager>();
|
collection.AddSingleton<IpcManager>();
|
||||||
collection.AddSingleton<NotificationService>();
|
collection.AddSingleton<NotificationService>();
|
||||||
collection.AddSingleton<TemporarySyncshellNotificationService>();
|
collection.AddSingleton<TemporarySyncshellNotificationService>();
|
||||||
|
collection.AddSingleton<PartyListTypingService>();
|
||||||
|
|
||||||
collection.AddSingleton((s) => new MareConfigService(pluginInterface.ConfigDirectory.FullName));
|
collection.AddSingleton((s) => new MareConfigService(pluginInterface.ConfigDirectory.FullName));
|
||||||
collection.AddSingleton((s) => new ServerConfigService(pluginInterface.ConfigDirectory.FullName));
|
collection.AddSingleton((s) => new ServerConfigService(pluginInterface.ConfigDirectory.FullName));
|
||||||
@@ -218,6 +220,16 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
})
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var partyListTypingService = _host.Services.GetRequiredService<PartyListTypingService>();
|
||||||
|
pluginInterface.UiBuilder.Draw += partyListTypingService.Draw;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
pluginLog.Warning(e, "Failed to initialize PartyListTypingService draw hook");
|
||||||
|
}
|
||||||
|
|
||||||
_ = Task.Run(async () => {
|
_ = Task.Run(async () => {
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Threading;
|
||||||
using Dalamud.Game.Text;
|
using Dalamud.Game.Text;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
@@ -30,6 +31,11 @@ public class ChatService : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
private readonly Lazy<GameChatHooks> _gameChatHooks;
|
private readonly Lazy<GameChatHooks> _gameChatHooks;
|
||||||
|
|
||||||
|
private readonly object _typingLock = new();
|
||||||
|
private CancellationTokenSource? _typingCts;
|
||||||
|
private bool _isTypingAnnounced;
|
||||||
|
private static readonly TimeSpan TypingIdle = TimeSpan.FromSeconds(2);
|
||||||
|
|
||||||
public ChatService(ILogger<ChatService> logger, DalamudUtilService dalamudUtil, MareMediator mediator, ApiController apiController,
|
public ChatService(ILogger<ChatService> logger, DalamudUtilService dalamudUtil, MareMediator mediator, ApiController apiController,
|
||||||
PairManager pairManager, ILoggerFactory loggerFactory, IGameInteropProvider gameInteropProvider, IChatGui chatGui,
|
PairManager pairManager, ILoggerFactory loggerFactory, IGameInteropProvider gameInteropProvider, IChatGui chatGui,
|
||||||
MareConfigService mareConfig, ServerConfigurationManager serverConfigurationManager) : base(logger, mediator)
|
MareConfigService mareConfig, ServerConfigurationManager serverConfigurationManager) : base(logger, mediator)
|
||||||
@@ -46,13 +52,12 @@ public class ChatService : DisposableMediatorSubscriberBase
|
|||||||
Mediator.Subscribe<GroupChatMsgMessage>(this, HandleGroupChat);
|
Mediator.Subscribe<GroupChatMsgMessage>(this, HandleGroupChat);
|
||||||
|
|
||||||
_gameChatHooks = new(() => new GameChatHooks(loggerFactory.CreateLogger<GameChatHooks>(), gameInteropProvider, SendChatShell));
|
_gameChatHooks = new(() => new GameChatHooks(loggerFactory.CreateLogger<GameChatHooks>(), gameInteropProvider, SendChatShell));
|
||||||
|
|
||||||
// Initialize chat hooks in advance
|
|
||||||
_ = Task.Run(() =>
|
_ = Task.Run(() =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_ = _gameChatHooks.Value;
|
_ = _gameChatHooks.Value;
|
||||||
|
_isTypingAnnounced = false;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -64,9 +69,74 @@ public class ChatService : DisposableMediatorSubscriberBase
|
|||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
|
_typingCts?.Cancel();
|
||||||
|
_typingCts?.Dispose();
|
||||||
if (_gameChatHooks.IsValueCreated)
|
if (_gameChatHooks.IsValueCreated)
|
||||||
_gameChatHooks.Value!.Dispose();
|
_gameChatHooks.Value!.Dispose();
|
||||||
}
|
}
|
||||||
|
public void NotifyTypingKeystroke()
|
||||||
|
{
|
||||||
|
lock (_typingLock)
|
||||||
|
{
|
||||||
|
if (!_isTypingAnnounced)
|
||||||
|
{
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try { await _apiController.UserSetTypingState(true).ConfigureAwait(false); }
|
||||||
|
catch (Exception ex) { _logger.LogDebug(ex, "NotifyTypingKeystroke: failed to send typing=true"); }
|
||||||
|
});
|
||||||
|
_isTypingAnnounced = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_typingCts?.Cancel();
|
||||||
|
_typingCts?.Dispose();
|
||||||
|
_typingCts = new CancellationTokenSource();
|
||||||
|
var token = _typingCts.Token;
|
||||||
|
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(TypingIdle, token).ConfigureAwait(false);
|
||||||
|
await _apiController.UserSetTypingState(false).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
// reset timer
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogDebug(ex, "NotifyTypingKeystroke: failed to send typing=false");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
lock (_typingLock)
|
||||||
|
{
|
||||||
|
if (!token.IsCancellationRequested)
|
||||||
|
_isTypingAnnounced = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void ClearTypingState()
|
||||||
|
{
|
||||||
|
lock (_typingLock)
|
||||||
|
{
|
||||||
|
_typingCts?.Cancel();
|
||||||
|
_typingCts?.Dispose();
|
||||||
|
_typingCts = null;
|
||||||
|
if (_isTypingAnnounced)
|
||||||
|
{
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try { await _apiController.UserSetTypingState(false).ConfigureAwait(false); }
|
||||||
|
catch (Exception ex) { _logger.LogDebug(ex, "ClearTypingState: failed to send typing=false"); }
|
||||||
|
});
|
||||||
|
_isTypingAnnounced = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void HandleUserChat(UserChatMsgMessage message)
|
private void HandleUserChat(UserChatMsgMessage message)
|
||||||
{
|
{
|
||||||
@@ -124,7 +194,6 @@ public class ChatService : DisposableMediatorSubscriberBase
|
|||||||
msg.AddText($"[SS{shellNumber}]<");
|
msg.AddText($"[SS{shellNumber}]<");
|
||||||
if (message.ChatMsg.Sender.UID.Equals(_apiController.UID, StringComparison.Ordinal))
|
if (message.ChatMsg.Sender.UID.Equals(_apiController.UID, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
// Don't link to your own character
|
|
||||||
msg.AddText(chatMsg.SenderName);
|
msg.AddText(chatMsg.SenderName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -142,8 +211,6 @@ public class ChatService : DisposableMediatorSubscriberBase
|
|||||||
Type = logKind
|
Type = logKind
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print an example message to the configured global chat channel
|
|
||||||
public void PrintChannelExample(string message, string gid = "")
|
public void PrintChannelExample(string message, string gid = "")
|
||||||
{
|
{
|
||||||
int chatType = _mareConfig.Current.ChatLogKind;
|
int chatType = _mareConfig.Current.ChatLogKind;
|
||||||
@@ -164,8 +231,6 @@ public class ChatService : DisposableMediatorSubscriberBase
|
|||||||
Type = (XivChatType)chatType
|
Type = (XivChatType)chatType
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called to update the active chat shell name if its renamed
|
|
||||||
public void MaybeUpdateShellName(int shellNumber)
|
public void MaybeUpdateShellName(int shellNumber)
|
||||||
{
|
{
|
||||||
if (_mareConfig.Current.DisableSyncshellChat)
|
if (_mareConfig.Current.DisableSyncshellChat)
|
||||||
@@ -178,7 +243,6 @@ public class ChatService : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
if (_gameChatHooks.IsValueCreated && _gameChatHooks.Value.ChatChannelOverride != null)
|
if (_gameChatHooks.IsValueCreated && _gameChatHooks.Value.ChatChannelOverride != null)
|
||||||
{
|
{
|
||||||
// Very dumb and won't handle re-numbering -- need to identify the active chat channel more reliably later
|
|
||||||
if (_gameChatHooks.Value.ChatChannelOverride.ChannelName.StartsWith($"SS [{shellNumber}]", StringComparison.Ordinal))
|
if (_gameChatHooks.Value.ChatChannelOverride.ChannelName.StartsWith($"SS [{shellNumber}]", StringComparison.Ordinal))
|
||||||
SwitchChatShell(shellNumber);
|
SwitchChatShell(shellNumber);
|
||||||
}
|
}
|
||||||
@@ -197,7 +261,6 @@ public class ChatService : DisposableMediatorSubscriberBase
|
|||||||
if (shellConfig.Enabled && shellConfig.ShellNumber == shellNumber)
|
if (shellConfig.Enabled && shellConfig.ShellNumber == shellNumber)
|
||||||
{
|
{
|
||||||
var name = _serverConfigurationManager.GetNoteForGid(group.Key.GID) ?? group.Key.AliasOrGID;
|
var name = _serverConfigurationManager.GetNoteForGid(group.Key.GID) ?? group.Key.AliasOrGID;
|
||||||
// BUG: This doesn't always update the chat window e.g. when renaming a group
|
|
||||||
_gameChatHooks.Value.ChatChannelOverride = new()
|
_gameChatHooks.Value.ChatChannelOverride = new()
|
||||||
{
|
{
|
||||||
ChannelName = $"SS [{shellNumber}]: {name}",
|
ChannelName = $"SS [{shellNumber}]: {name}",
|
||||||
@@ -221,7 +284,6 @@ public class ChatService : DisposableMediatorSubscriberBase
|
|||||||
if (shellConfig.Enabled && shellConfig.ShellNumber == shellNumber)
|
if (shellConfig.Enabled && shellConfig.ShellNumber == shellNumber)
|
||||||
{
|
{
|
||||||
_ = Task.Run(async () => {
|
_ = Task.Run(async () => {
|
||||||
// Should cache the name and home world instead of fetching it every time
|
|
||||||
var chatMsg = await _dalamudUtil.RunOnFrameworkThread(() => {
|
var chatMsg = await _dalamudUtil.RunOnFrameworkThread(() => {
|
||||||
return new ChatMessage()
|
return new ChatMessage()
|
||||||
{
|
{
|
||||||
@@ -230,6 +292,7 @@ public class ChatService : DisposableMediatorSubscriberBase
|
|||||||
PayloadContent = chatBytes
|
PayloadContent = chatBytes
|
||||||
};
|
};
|
||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
|
ClearTypingState();
|
||||||
await _apiController.GroupChatSendMsg(new(group.Key), chatMsg).ConfigureAwait(false);
|
await _apiController.GroupChatSendMsg(new(group.Key), chatMsg).ConfigureAwait(false);
|
||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ using MareSynchronos.PlayerData.Pairs;
|
|||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.UI;
|
using MareSynchronos.UI;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MareSynchronos.API.Dto.User;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using Dalamud.Game.Text;
|
||||||
|
|
||||||
namespace MareSynchronos.Services;
|
namespace MareSynchronos.Services;
|
||||||
|
|
||||||
@@ -20,6 +23,9 @@ public class GuiHookService : DisposableMediatorSubscriberBase
|
|||||||
private readonly IPartyList _partyList;
|
private readonly IPartyList _partyList;
|
||||||
private readonly PairManager _pairManager;
|
private readonly PairManager _pairManager;
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<string, DateTime> _typingUsers = new();
|
||||||
|
private static readonly TimeSpan TypingDisplayTime = TimeSpan.FromSeconds(2);
|
||||||
|
|
||||||
private bool _isModified = false;
|
private bool _isModified = false;
|
||||||
private bool _namePlateRoleColorsEnabled = false;
|
private bool _namePlateRoleColorsEnabled = false;
|
||||||
|
|
||||||
@@ -41,6 +47,18 @@ public class GuiHookService : DisposableMediatorSubscriberBase
|
|||||||
Mediator.Subscribe<DelayedFrameworkUpdateMessage>(this, (_) => GameSettingsCheck());
|
Mediator.Subscribe<DelayedFrameworkUpdateMessage>(this, (_) => GameSettingsCheck());
|
||||||
Mediator.Subscribe<PairHandlerVisibleMessage>(this, (_) => RequestRedraw());
|
Mediator.Subscribe<PairHandlerVisibleMessage>(this, (_) => RequestRedraw());
|
||||||
Mediator.Subscribe<NameplateRedrawMessage>(this, (_) => RequestRedraw());
|
Mediator.Subscribe<NameplateRedrawMessage>(this, (_) => RequestRedraw());
|
||||||
|
Mediator.Subscribe<UserTypingStateMessage>(this, (msg) =>
|
||||||
|
{
|
||||||
|
if (msg.Typing.IsTyping)
|
||||||
|
{
|
||||||
|
_typingUsers[msg.Typing.User.UID] = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_typingUsers.TryRemove(msg.Typing.User.UID, out _);
|
||||||
|
}
|
||||||
|
RequestRedraw();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RequestRedraw(bool force = false)
|
public void RequestRedraw(bool force = false)
|
||||||
@@ -72,6 +90,7 @@ public class GuiHookService : DisposableMediatorSubscriberBase
|
|||||||
if (!_configService.Current.UseNameColors)
|
if (!_configService.Current.UseNameColors)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var showTypingIndicator = _configService.Current.TypingIndicatorShowOnNameplates;
|
||||||
var visibleUsers = _pairManager.GetOnlineUserPairs().Where(u => u.IsVisible && u.PlayerCharacterId != uint.MaxValue);
|
var visibleUsers = _pairManager.GetOnlineUserPairs().Where(u => u.IsVisible && u.PlayerCharacterId != uint.MaxValue);
|
||||||
var visibleUsersIds = visibleUsers.Select(u => (ulong)u.PlayerCharacterId).ToHashSet();
|
var visibleUsersIds = visibleUsers.Select(u => (ulong)u.PlayerCharacterId).ToHashSet();
|
||||||
|
|
||||||
@@ -95,6 +114,17 @@ public class GuiHookService : DisposableMediatorSubscriberBase
|
|||||||
BuildColorEndSeString(colors)
|
BuildColorEndSeString(colors)
|
||||||
);
|
);
|
||||||
_isModified = true;
|
_isModified = true;
|
||||||
|
if (showTypingIndicator
|
||||||
|
&& _typingUsers.TryGetValue(pair.UserData.UID, out var lastTyping)
|
||||||
|
&& (DateTime.UtcNow - lastTyping) < TypingDisplayTime)
|
||||||
|
{
|
||||||
|
var ssb = new SeStringBuilder();
|
||||||
|
ssb.Append(handler.Name);
|
||||||
|
ssb.Add(new IconPayload(BitmapFontIcon.AutoTranslateBegin));
|
||||||
|
ssb.AddText("...");
|
||||||
|
ssb.Add(new IconPayload(BitmapFontIcon.AutoTranslateEnd));
|
||||||
|
handler.Name = ssb.Build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using MareSynchronos.API.Data;
|
|||||||
using MareSynchronos.API.Dto;
|
using MareSynchronos.API.Dto;
|
||||||
using MareSynchronos.API.Dto.CharaData;
|
using MareSynchronos.API.Dto.CharaData;
|
||||||
using MareSynchronos.API.Dto.Group;
|
using MareSynchronos.API.Dto.Group;
|
||||||
|
using MareSynchronos.API.Dto.User;
|
||||||
using MareSynchronos.MareConfiguration.Models;
|
using MareSynchronos.MareConfiguration.Models;
|
||||||
using MareSynchronos.PlayerData.Handlers;
|
using MareSynchronos.PlayerData.Handlers;
|
||||||
using MareSynchronos.PlayerData.Pairs;
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
@@ -52,6 +53,7 @@ public record HaltScanMessage(string Source) : MessageBase;
|
|||||||
public record ResumeScanMessage(string Source) : MessageBase;
|
public record ResumeScanMessage(string Source) : MessageBase;
|
||||||
public record NotificationMessage
|
public record NotificationMessage
|
||||||
(string Title, string Message, NotificationType Type, TimeSpan? TimeShownOnScreen = null) : MessageBase;
|
(string Title, string Message, NotificationType Type, TimeSpan? TimeShownOnScreen = null) : MessageBase;
|
||||||
|
public record DualNotificationMessage(string Title, string Message, NotificationType Type, TimeSpan? ToastDuration = null) : MessageBase;
|
||||||
public record CreateCacheForObjectMessage(GameObjectHandler ObjectToCreateFor) : MessageBase;
|
public record CreateCacheForObjectMessage(GameObjectHandler ObjectToCreateFor) : MessageBase;
|
||||||
public record ClearCacheForObjectMessage(GameObjectHandler ObjectToCreateFor) : MessageBase;
|
public record ClearCacheForObjectMessage(GameObjectHandler ObjectToCreateFor) : MessageBase;
|
||||||
public record CharacterDataCreatedMessage(CharacterData CharacterData) : SameThreadMessage;
|
public record CharacterDataCreatedMessage(CharacterData CharacterData) : SameThreadMessage;
|
||||||
@@ -90,6 +92,7 @@ public record PenumbraDirectoryChangedMessage(string? ModDirectory) : MessageBas
|
|||||||
public record PenumbraRedrawCharacterMessage(ICharacter Character) : SameThreadMessage;
|
public record PenumbraRedrawCharacterMessage(ICharacter Character) : SameThreadMessage;
|
||||||
public record UserChatMsgMessage(SignedChatMessage ChatMsg) : MessageBase;
|
public record UserChatMsgMessage(SignedChatMessage ChatMsg) : MessageBase;
|
||||||
public record GroupChatMsgMessage(GroupDto GroupInfo, SignedChatMessage ChatMsg) : MessageBase;
|
public record GroupChatMsgMessage(GroupDto GroupInfo, SignedChatMessage ChatMsg) : MessageBase;
|
||||||
|
public record UserTypingStateMessage(TypingStateDto Typing) : MessageBase;
|
||||||
public record RecalculatePerformanceMessage(string? UID) : MessageBase;
|
public record RecalculatePerformanceMessage(string? UID) : MessageBase;
|
||||||
public record NameplateRedrawMessage : MessageBase;
|
public record NameplateRedrawMessage : MessageBase;
|
||||||
public record HoldPairApplicationMessage(string UID, string Source) : KeyedMessage(UID);
|
public record HoldPairApplicationMessage(string UID, string Source) : KeyedMessage(UID);
|
||||||
@@ -112,6 +115,11 @@ public record NearbyEntry(string Name, ushort WorldId, float Distance, bool IsMa
|
|||||||
public record DiscoveryListUpdated(List<NearbyEntry> Entries) : MessageBase;
|
public record DiscoveryListUpdated(List<NearbyEntry> Entries) : MessageBase;
|
||||||
public record NearbyDetectionToggled(bool Enabled) : MessageBase;
|
public record NearbyDetectionToggled(bool Enabled) : MessageBase;
|
||||||
public record AllowPairRequestsToggled(bool Enabled) : MessageBase;
|
public record AllowPairRequestsToggled(bool Enabled) : MessageBase;
|
||||||
|
public record ApplyDefaultPairPermissionsMessage(UserPairDto Pair) : MessageBase;
|
||||||
|
public record ApplyDefaultGroupPermissionsMessage(GroupPairFullInfoDto GroupPair) : MessageBase;
|
||||||
|
public record ApplyDefaultsToAllSyncsMessage : MessageBase;
|
||||||
|
public record PairSyncOverrideChanged(string Uid, bool? DisableSounds, bool? DisableAnimations, bool? DisableVfx) : MessageBase;
|
||||||
|
public record GroupSyncOverrideChanged(string Gid, bool? DisableSounds, bool? DisableAnimations, bool? DisableVfx) : MessageBase;
|
||||||
|
|
||||||
public record PluginChangeMessage(string InternalName, Version Version, bool IsLoaded) : KeyedMessage(InternalName);
|
public record PluginChangeMessage(string InternalName, Version Version, bool IsLoaded) : KeyedMessage(InternalName);
|
||||||
#pragma warning restore S2094
|
#pragma warning restore S2094
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
|||||||
public Task StartAsync(CancellationToken cancellationToken)
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
Mediator.Subscribe<NotificationMessage>(this, ShowNotification);
|
Mediator.Subscribe<NotificationMessage>(this, ShowNotification);
|
||||||
|
Mediator.Subscribe<DualNotificationMessage>(this, ShowDualNotification);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +104,15 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ShowDualNotification(DualNotificationMessage message)
|
||||||
|
{
|
||||||
|
if (!_dalamudUtilService.IsLoggedIn) return;
|
||||||
|
|
||||||
|
var baseMsg = new NotificationMessage(message.Title, message.Message, message.Type, message.ToastDuration);
|
||||||
|
ShowToast(baseMsg);
|
||||||
|
ShowChat(baseMsg);
|
||||||
|
}
|
||||||
|
|
||||||
private static bool ShouldForceChat(NotificationMessage msg, out bool appendInstruction)
|
private static bool ShouldForceChat(NotificationMessage msg, out bool appendInstruction)
|
||||||
{
|
{
|
||||||
appendInstruction = false;
|
appendInstruction = false;
|
||||||
|
|||||||
112
MareSynchronos/Services/PartyListTypingService.cs
Normal file
112
MareSynchronos/Services/PartyListTypingService.cs
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
using MareSynchronos.MareConfiguration;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Linq;
|
||||||
|
using Dalamud.Game.Text;
|
||||||
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
|
using Dalamud.Plugin.Services;
|
||||||
|
using MareSynchronos.API.Dto.User;
|
||||||
|
using MareSynchronos.Services.Mediator;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MareSynchronos.Services;
|
||||||
|
|
||||||
|
public class PartyListTypingService : DisposableMediatorSubscriberBase
|
||||||
|
{
|
||||||
|
private readonly ILogger<PartyListTypingService> _logger;
|
||||||
|
private readonly IPartyList _partyList;
|
||||||
|
private readonly MareConfigService _configService;
|
||||||
|
private readonly PairManager _pairManager;
|
||||||
|
private readonly ConcurrentDictionary<string, DateTime> _typingUsers = new();
|
||||||
|
private readonly ConcurrentDictionary<string, DateTime> _typingNames = new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
private static readonly TimeSpan TypingDisplayTime = TimeSpan.FromSeconds(2);
|
||||||
|
|
||||||
|
public PartyListTypingService(ILogger<PartyListTypingService> logger,
|
||||||
|
MareMediator mediator,
|
||||||
|
IPartyList partyList,
|
||||||
|
PairManager pairManager,
|
||||||
|
MareConfigService configService)
|
||||||
|
: base(logger, mediator)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_partyList = partyList;
|
||||||
|
_pairManager = pairManager;
|
||||||
|
_configService = configService;
|
||||||
|
|
||||||
|
Mediator.Subscribe<UserTypingStateMessage>(this, OnUserTyping);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUserTyping(UserTypingStateMessage msg)
|
||||||
|
{
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
var uid = msg.Typing.User.UID;
|
||||||
|
var aliasOrUid = msg.Typing.User.AliasOrUID ?? uid;
|
||||||
|
|
||||||
|
if (msg.Typing.IsTyping)
|
||||||
|
{
|
||||||
|
_typingUsers[uid] = now;
|
||||||
|
_typingNames[aliasOrUid] = now;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_typingUsers.TryRemove(uid, out _);
|
||||||
|
_typingNames.TryRemove(aliasOrUid, out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasTypingBubble(SeString name)
|
||||||
|
{
|
||||||
|
return name.Payloads.Any(p => p is IconPayload ip && ip.Icon == BitmapFontIcon.AutoTranslateBegin);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SeString WithTypingBubble(SeString baseName)
|
||||||
|
{
|
||||||
|
var ssb = new SeStringBuilder();
|
||||||
|
ssb.Append(baseName);
|
||||||
|
ssb.Add(new IconPayload(BitmapFontIcon.AutoTranslateBegin));
|
||||||
|
ssb.AddText("...");
|
||||||
|
ssb.Add(new IconPayload(BitmapFontIcon.AutoTranslateEnd));
|
||||||
|
return ssb.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw()
|
||||||
|
{
|
||||||
|
if (!_configService.Current.TypingIndicatorShowOnPartyList) return;
|
||||||
|
// Build map of visible users by AliasOrUID -> UID (case-insensitive)
|
||||||
|
var visibleByAlias = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var visibleUsers = _pairManager.GetVisibleUsers();
|
||||||
|
foreach (var u in visibleUsers)
|
||||||
|
{
|
||||||
|
var alias = string.IsNullOrEmpty(u.AliasOrUID) ? u.UID : u.AliasOrUID;
|
||||||
|
if (!visibleByAlias.ContainsKey(alias)) visibleByAlias[alias] = u.UID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogDebug(ex, "PartyListTypingService: failed to get visible users");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var member in _partyList)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(member.Name?.TextValue)) continue;
|
||||||
|
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
var displayName = member.Name.TextValue;
|
||||||
|
if (visibleByAlias.TryGetValue(displayName, out var uid)
|
||||||
|
&& _typingUsers.TryGetValue(uid, out var last)
|
||||||
|
&& (now - last) < TypingDisplayTime)
|
||||||
|
{
|
||||||
|
if (!HasTypingBubble(member.Name))
|
||||||
|
{
|
||||||
|
// IPartyMember.Name is read-only; rendering bubble here requires Addon-level modification. Keeping compile-safe for now.
|
||||||
|
_logger.LogDebug("PartyListTypingService: bubble would be shown for {name}", displayName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
346
MareSynchronos/Services/SyncDefaultsService.cs
Normal file
346
MareSynchronos/Services/SyncDefaultsService.cs
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MareSynchronos.API.Data;
|
||||||
|
using MareSynchronos.API.Data.Enum;
|
||||||
|
using MareSynchronos.API.Data.Extensions;
|
||||||
|
using MareSynchronos.API.Dto.Group;
|
||||||
|
using MareSynchronos.API.Dto.User;
|
||||||
|
using MareSynchronos.MareConfiguration;
|
||||||
|
using MareSynchronos.MareConfiguration.Configurations;
|
||||||
|
using MareSynchronos.MareConfiguration.Models;
|
||||||
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
|
using MareSynchronos.Services.Mediator;
|
||||||
|
using MareSynchronos.WebAPI;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using NotificationType = MareSynchronos.MareConfiguration.Models.NotificationType;
|
||||||
|
|
||||||
|
namespace MareSynchronos.Services;
|
||||||
|
|
||||||
|
public sealed class SyncDefaultsService : DisposableMediatorSubscriberBase
|
||||||
|
{
|
||||||
|
private readonly ApiController _apiController;
|
||||||
|
private readonly MareConfigService _configService;
|
||||||
|
private readonly PairManager _pairManager;
|
||||||
|
|
||||||
|
public SyncDefaultsService(ILogger<SyncDefaultsService> logger, MareMediator mediator,
|
||||||
|
MareConfigService configService, ApiController apiController, PairManager pairManager) : base(logger, mediator)
|
||||||
|
{
|
||||||
|
_configService = configService;
|
||||||
|
_apiController = apiController;
|
||||||
|
_pairManager = pairManager;
|
||||||
|
|
||||||
|
Mediator.Subscribe<ApplyDefaultPairPermissionsMessage>(this, OnApplyPairDefaults);
|
||||||
|
Mediator.Subscribe<ApplyDefaultGroupPermissionsMessage>(this, OnApplyGroupDefaults);
|
||||||
|
Mediator.Subscribe<ApplyDefaultsToAllSyncsMessage>(this, _ => ApplyDefaultsToAll());
|
||||||
|
Mediator.Subscribe<PairSyncOverrideChanged>(this, OnPairOverrideChanged);
|
||||||
|
Mediator.Subscribe<GroupSyncOverrideChanged>(this, OnGroupOverrideChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnApplyPairDefaults(ApplyDefaultPairPermissionsMessage message)
|
||||||
|
{
|
||||||
|
var config = _configService.Current;
|
||||||
|
var permissions = message.Pair.OwnPermissions;
|
||||||
|
var overrides = TryGetPairOverride(message.Pair.User.UID);
|
||||||
|
if (!ApplyDefaults(ref permissions, config, overrides))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_ = _apiController.UserSetPairPermissions(new UserPermissionsDto(message.Pair.User, permissions));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnApplyGroupDefaults(ApplyDefaultGroupPermissionsMessage message)
|
||||||
|
{
|
||||||
|
if (!string.Equals(message.GroupPair.User.UID, _apiController.UID, StringComparison.Ordinal))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var config = _configService.Current;
|
||||||
|
var permissions = message.GroupPair.GroupUserPermissions;
|
||||||
|
var overrides = TryGetGroupOverride(message.GroupPair.Group.GID);
|
||||||
|
if (!ApplyDefaults(ref permissions, config, overrides))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_ = _apiController.GroupChangeIndividualPermissionState(new GroupPairUserPermissionDto(message.GroupPair.Group, message.GroupPair.User, permissions));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ApplyDefaultsToAllAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var config = _configService.Current;
|
||||||
|
var tasks = new List<Task>();
|
||||||
|
int updatedPairs = 0;
|
||||||
|
int updatedGroups = 0;
|
||||||
|
|
||||||
|
foreach (var pair in _pairManager.DirectPairs.Where(p => p.UserPair != null).ToList())
|
||||||
|
{
|
||||||
|
var permissions = pair.UserPair!.OwnPermissions;
|
||||||
|
var overrides = TryGetPairOverride(pair.UserData.UID);
|
||||||
|
if (!ApplyDefaults(ref permissions, config, overrides))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
updatedPairs++;
|
||||||
|
tasks.Add(_apiController.UserSetPairPermissions(new UserPermissionsDto(pair.UserData, permissions)));
|
||||||
|
}
|
||||||
|
|
||||||
|
var selfUser = new UserData(_apiController.UID);
|
||||||
|
foreach (var groupInfo in _pairManager.Groups.Values.ToList())
|
||||||
|
{
|
||||||
|
var permissions = groupInfo.GroupUserPermissions;
|
||||||
|
var overrides = TryGetGroupOverride(groupInfo.Group.GID);
|
||||||
|
if (!ApplyDefaults(ref permissions, config, overrides))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
updatedGroups++;
|
||||||
|
tasks.Add(_apiController.GroupChangeIndividualPermissionState(new GroupPairUserPermissionDto(groupInfo.Group, selfUser, permissions)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tasks.Count > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogWarning(ex, "Failed applying default sync settings to all pairs/groups");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Mediator.Publish(new DualNotificationMessage("Préférences appliquées", BuildSummaryMessage(updatedPairs, updatedGroups), NotificationType.Info));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex, "Unexpected error while applying default sync settings to all pairs/groups");
|
||||||
|
Mediator.Publish(new DualNotificationMessage("Préférences appliquées", "Une erreur est survenue lors de l'application des paramètres par défaut.", NotificationType.Error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyDefaultsToAll() => _ = ApplyDefaultsToAllAsync();
|
||||||
|
|
||||||
|
private static string BuildSummaryMessage(int pairs, int groups)
|
||||||
|
{
|
||||||
|
if (pairs == 0 && groups == 0)
|
||||||
|
return "Aucun pair ou syncshell n'avait besoin d'être modifié.";
|
||||||
|
|
||||||
|
if (pairs > 0 && groups > 0)
|
||||||
|
return $"Mise à jour de {pairs} pair(s) et {groups} syncshell(s).";
|
||||||
|
|
||||||
|
if (pairs > 0)
|
||||||
|
return $"Mise à jour de {pairs} pair(s).";
|
||||||
|
|
||||||
|
return $"Mise à jour de {groups} syncshell(s).";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPairOverrideChanged(PairSyncOverrideChanged message)
|
||||||
|
{
|
||||||
|
var overrides = _configService.Current.PairSyncOverrides ??= new(StringComparer.Ordinal);
|
||||||
|
var entry = overrides.TryGetValue(message.Uid, out var existing) ? existing : new SyncOverrideEntry();
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
if (message.DisableSounds.HasValue)
|
||||||
|
{
|
||||||
|
var val = message.DisableSounds.Value;
|
||||||
|
var defaultVal = _configService.Current.DefaultDisableSounds;
|
||||||
|
var newValue = val == defaultVal ? (bool?)null : val;
|
||||||
|
if (entry.DisableSounds != newValue)
|
||||||
|
{
|
||||||
|
entry.DisableSounds = newValue;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.DisableAnimations.HasValue)
|
||||||
|
{
|
||||||
|
var val = message.DisableAnimations.Value;
|
||||||
|
var defaultVal = _configService.Current.DefaultDisableAnimations;
|
||||||
|
var newValue = val == defaultVal ? (bool?)null : val;
|
||||||
|
if (entry.DisableAnimations != newValue)
|
||||||
|
{
|
||||||
|
entry.DisableAnimations = newValue;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.DisableVfx.HasValue)
|
||||||
|
{
|
||||||
|
var val = message.DisableVfx.Value;
|
||||||
|
var defaultVal = _configService.Current.DefaultDisableVfx;
|
||||||
|
var newValue = val == defaultVal ? (bool?)null : val;
|
||||||
|
if (entry.DisableVfx != newValue)
|
||||||
|
{
|
||||||
|
entry.DisableVfx = newValue;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!changed) return;
|
||||||
|
|
||||||
|
if (entry.IsEmpty)
|
||||||
|
overrides.Remove(message.Uid);
|
||||||
|
else
|
||||||
|
overrides[message.Uid] = entry;
|
||||||
|
|
||||||
|
_configService.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGroupOverrideChanged(GroupSyncOverrideChanged message)
|
||||||
|
{
|
||||||
|
var overrides = _configService.Current.GroupSyncOverrides ??= new(StringComparer.Ordinal);
|
||||||
|
var entry = overrides.TryGetValue(message.Gid, out var existing) ? existing : new SyncOverrideEntry();
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
if (message.DisableSounds.HasValue)
|
||||||
|
{
|
||||||
|
var val = message.DisableSounds.Value;
|
||||||
|
var defaultVal = _configService.Current.DefaultDisableSounds;
|
||||||
|
var newValue = val == defaultVal ? (bool?)null : val;
|
||||||
|
if (entry.DisableSounds != newValue)
|
||||||
|
{
|
||||||
|
entry.DisableSounds = newValue;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.DisableAnimations.HasValue)
|
||||||
|
{
|
||||||
|
var val = message.DisableAnimations.Value;
|
||||||
|
var defaultVal = _configService.Current.DefaultDisableAnimations;
|
||||||
|
var newValue = val == defaultVal ? (bool?)null : val;
|
||||||
|
if (entry.DisableAnimations != newValue)
|
||||||
|
{
|
||||||
|
entry.DisableAnimations = newValue;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.DisableVfx.HasValue)
|
||||||
|
{
|
||||||
|
var val = message.DisableVfx.Value;
|
||||||
|
var defaultVal = _configService.Current.DefaultDisableVfx;
|
||||||
|
var newValue = val == defaultVal ? (bool?)null : val;
|
||||||
|
if (entry.DisableVfx != newValue)
|
||||||
|
{
|
||||||
|
entry.DisableVfx = newValue;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!changed) return;
|
||||||
|
|
||||||
|
if (entry.IsEmpty)
|
||||||
|
overrides.Remove(message.Gid);
|
||||||
|
else
|
||||||
|
overrides[message.Gid] = entry;
|
||||||
|
|
||||||
|
_configService.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SyncOverrideEntry? TryGetPairOverride(string uid)
|
||||||
|
{
|
||||||
|
var overrides = _configService.Current.PairSyncOverrides;
|
||||||
|
return overrides != null && overrides.TryGetValue(uid, out var entry) ? entry : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SyncOverrideEntry? TryGetGroupOverride(string gid)
|
||||||
|
{
|
||||||
|
var overrides = _configService.Current.GroupSyncOverrides;
|
||||||
|
return overrides != null && overrides.TryGetValue(gid, out var entry) ? entry : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ApplyDefaults(ref UserPermissions permissions, MareConfig config, SyncOverrideEntry? overrides)
|
||||||
|
{
|
||||||
|
bool changed = false;
|
||||||
|
if (overrides?.DisableSounds is bool overrideSounds)
|
||||||
|
{
|
||||||
|
if (permissions.IsDisableSounds() != overrideSounds)
|
||||||
|
{
|
||||||
|
permissions.SetDisableSounds(overrideSounds);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (permissions.IsDisableSounds() != config.DefaultDisableSounds)
|
||||||
|
{
|
||||||
|
permissions.SetDisableSounds(config.DefaultDisableSounds);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overrides?.DisableAnimations is bool overrideAnims)
|
||||||
|
{
|
||||||
|
if (permissions.IsDisableAnimations() != overrideAnims)
|
||||||
|
{
|
||||||
|
permissions.SetDisableAnimations(overrideAnims);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (permissions.IsDisableAnimations() != config.DefaultDisableAnimations)
|
||||||
|
{
|
||||||
|
permissions.SetDisableAnimations(config.DefaultDisableAnimations);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overrides?.DisableVfx is bool overrideVfx)
|
||||||
|
{
|
||||||
|
if (permissions.IsDisableVFX() != overrideVfx)
|
||||||
|
{
|
||||||
|
permissions.SetDisableVFX(overrideVfx);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (permissions.IsDisableVFX() != config.DefaultDisableVfx)
|
||||||
|
{
|
||||||
|
permissions.SetDisableVFX(config.DefaultDisableVfx);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ApplyDefaults(ref GroupUserPermissions permissions, MareConfig config, SyncOverrideEntry? overrides)
|
||||||
|
{
|
||||||
|
bool changed = false;
|
||||||
|
if (overrides?.DisableSounds is bool overrideSounds)
|
||||||
|
{
|
||||||
|
if (permissions.IsDisableSounds() != overrideSounds)
|
||||||
|
{
|
||||||
|
permissions.SetDisableSounds(overrideSounds);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (permissions.IsDisableSounds() != config.DefaultDisableSounds)
|
||||||
|
{
|
||||||
|
permissions.SetDisableSounds(config.DefaultDisableSounds);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overrides?.DisableAnimations is bool overrideAnims)
|
||||||
|
{
|
||||||
|
if (permissions.IsDisableAnimations() != overrideAnims)
|
||||||
|
{
|
||||||
|
permissions.SetDisableAnimations(overrideAnims);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (permissions.IsDisableAnimations() != config.DefaultDisableAnimations)
|
||||||
|
{
|
||||||
|
permissions.SetDisableAnimations(config.DefaultDisableAnimations);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overrides?.DisableVfx is bool overrideVfx)
|
||||||
|
{
|
||||||
|
if (permissions.IsDisableVFX() != overrideVfx)
|
||||||
|
{
|
||||||
|
permissions.SetDisableVFX(overrideVfx);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (permissions.IsDisableVFX() != config.DefaultDisableVfx)
|
||||||
|
{
|
||||||
|
permissions.SetDisableVFX(config.DefaultDisableVfx);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -169,6 +169,13 @@ public sealed class ChangelogUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
return new List<ChangelogEntry>
|
return new List<ChangelogEntry>
|
||||||
{
|
{
|
||||||
|
new(new Version(0, 1, 9, 0), "0.1.9.0", new List<ChangelogLine>
|
||||||
|
{
|
||||||
|
new("Il est désormais possible de configurer par défaut nos choix de synchronisation (VFX, Music, Animation)."),
|
||||||
|
new("La catégorie 'En attente' ne s'affice uniquement que si une invitation est en attente"),
|
||||||
|
new("(EN PRÉ VERSION) Il est désormais possible de voir quand une personne appairé est en train d'écrire avec une bulle qui s'affiche."),
|
||||||
|
new("Correctif : Désormais, les invitation entrantes ne s'affichent qu'une seule fois au lieu de deux."),
|
||||||
|
}),
|
||||||
new(new Version(0, 1, 8, 2), "0.1.8.2", new List<ChangelogLine>
|
new(new Version(0, 1, 8, 2), "0.1.8.2", new List<ChangelogLine>
|
||||||
{
|
{
|
||||||
new("Détection Nearby : la liste rapide ne montre plus que les joueurs réellement invitables."),
|
new("Détection Nearby : la liste rapide ne montre plus que les joueurs réellement invitables."),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Dalamud.Utility;
|
|||||||
using MareSynchronos.API.Data.Extensions;
|
using MareSynchronos.API.Data.Extensions;
|
||||||
using MareSynchronos.API.Dto.User;
|
using MareSynchronos.API.Dto.User;
|
||||||
using MareSynchronos.MareConfiguration;
|
using MareSynchronos.MareConfiguration;
|
||||||
|
using MareSynchronos.MareConfiguration.Models;
|
||||||
using MareSynchronos.PlayerData.Handlers;
|
using MareSynchronos.PlayerData.Handlers;
|
||||||
using MareSynchronos.PlayerData.Pairs;
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
using MareSynchronos.Services;
|
using MareSynchronos.Services;
|
||||||
@@ -190,7 +191,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
UiSharedService.AttachToolTip("Syncshells");
|
UiSharedService.AttachToolTip("Syncshells");
|
||||||
|
|
||||||
ImGui.Separator();
|
DrawDefaultSyncSettings();
|
||||||
if (!hasShownSyncShells)
|
if (!hasShownSyncShells)
|
||||||
{
|
{
|
||||||
using (ImRaii.PushId("pairlist")) DrawPairList();
|
using (ImRaii.PushId("pairlist")) DrawPairList();
|
||||||
@@ -203,7 +204,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
using (ImRaii.PushId("transfers")) DrawTransfers();
|
using (ImRaii.PushId("transfers")) DrawTransfers();
|
||||||
TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight;
|
TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight;
|
||||||
using (ImRaii.PushId("group-user-popup")) _selectPairsForGroupUi.Draw(_pairManager.DirectPairs);
|
using (ImRaii.PushId("group-user-popup")) _selectPairsForGroupUi.Draw(_pairManager.DirectPairs);
|
||||||
using (ImRaii.PushId("grouping-popup")) _selectGroupForPairUi.Draw();
|
using (ImRaii.PushId("grouping-popup")) _selectGroupForPairUi.Draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_configService.Current.OpenPopupOnAdd && _pairManager.LastAddedUser != null)
|
if (_configService.Current.OpenPopupOnAdd && _pairManager.LastAddedUser != null)
|
||||||
@@ -253,6 +254,114 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
base.OnClose();
|
base.OnClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawDefaultSyncSettings()
|
||||||
|
{
|
||||||
|
ImGuiHelpers.ScaledDummy(4f);
|
||||||
|
using (ImRaii.PushId("sync-defaults"))
|
||||||
|
{
|
||||||
|
const string soundLabel = "Audio";
|
||||||
|
const string animLabel = "Anim";
|
||||||
|
const string vfxLabel = "VFX";
|
||||||
|
const string soundSubject = "de l'audio";
|
||||||
|
const string animSubject = "des animations";
|
||||||
|
const string vfxSubject = "des effets visuels";
|
||||||
|
|
||||||
|
bool soundsDisabled = _configService.Current.DefaultDisableSounds;
|
||||||
|
bool animsDisabled = _configService.Current.DefaultDisableAnimations;
|
||||||
|
bool vfxDisabled = _configService.Current.DefaultDisableVfx;
|
||||||
|
bool showNearby = _configService.Current.EnableAutoDetectDiscovery;
|
||||||
|
|
||||||
|
var soundIcon = soundsDisabled ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeMute;
|
||||||
|
var animIcon = animsDisabled ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop;
|
||||||
|
var vfxIcon = vfxDisabled ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle;
|
||||||
|
|
||||||
|
float spacing = ImGui.GetStyle().ItemSpacing.X;
|
||||||
|
float audioWidth = _uiSharedService.GetIconTextButtonSize(soundIcon, soundLabel);
|
||||||
|
float animWidth = _uiSharedService.GetIconTextButtonSize(animIcon, animLabel);
|
||||||
|
float vfxWidth = _uiSharedService.GetIconTextButtonSize(vfxIcon, vfxLabel);
|
||||||
|
float nearbyWidth = showNearby ? _uiSharedService.GetIconTextButtonSize(FontAwesomeIcon.UserPlus, "Nearby") : 0f;
|
||||||
|
int buttonCount = 3 + (showNearby ? 1 : 0);
|
||||||
|
float totalWidth = audioWidth + animWidth + vfxWidth + nearbyWidth + spacing * (buttonCount - 1);
|
||||||
|
float available = ImGui.GetContentRegionAvail().X;
|
||||||
|
float startCursorX = ImGui.GetCursorPosX();
|
||||||
|
if (totalWidth < available)
|
||||||
|
{
|
||||||
|
ImGui.SetCursorPosX(startCursorX + (available - totalWidth) / 2f);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawDefaultSyncButton(soundIcon, soundLabel, audioWidth, soundsDisabled,
|
||||||
|
state =>
|
||||||
|
{
|
||||||
|
_configService.Current.DefaultDisableSounds = state;
|
||||||
|
_configService.Save();
|
||||||
|
PublishSyncDefaultNotification(soundSubject, state);
|
||||||
|
Mediator.Publish(new ApplyDefaultsToAllSyncsMessage());
|
||||||
|
},
|
||||||
|
() => DisableStateTooltip(soundSubject, _configService.Current.DefaultDisableSounds));
|
||||||
|
|
||||||
|
DrawDefaultSyncButton(animIcon, animLabel, animWidth, animsDisabled,
|
||||||
|
state =>
|
||||||
|
{
|
||||||
|
_configService.Current.DefaultDisableAnimations = state;
|
||||||
|
_configService.Save();
|
||||||
|
PublishSyncDefaultNotification(animSubject, state);
|
||||||
|
Mediator.Publish(new ApplyDefaultsToAllSyncsMessage());
|
||||||
|
},
|
||||||
|
() => DisableStateTooltip(animSubject, _configService.Current.DefaultDisableAnimations), spacing);
|
||||||
|
|
||||||
|
DrawDefaultSyncButton(vfxIcon, vfxLabel, vfxWidth, vfxDisabled,
|
||||||
|
state =>
|
||||||
|
{
|
||||||
|
_configService.Current.DefaultDisableVfx = state;
|
||||||
|
_configService.Save();
|
||||||
|
PublishSyncDefaultNotification(vfxSubject, state);
|
||||||
|
Mediator.Publish(new ApplyDefaultsToAllSyncsMessage());
|
||||||
|
},
|
||||||
|
() => DisableStateTooltip(vfxSubject, _configService.Current.DefaultDisableVfx), spacing);
|
||||||
|
|
||||||
|
if (showNearby)
|
||||||
|
{
|
||||||
|
ImGui.SameLine(0, spacing);
|
||||||
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.UserPlus, "Nearby", nearbyWidth))
|
||||||
|
{
|
||||||
|
Mediator.Publish(new UiToggleMessage(typeof(AutoDetectUi)));
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip("Ouvrir la détection de proximité");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui.Separator();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawDefaultSyncButton(FontAwesomeIcon icon, string label, float width, bool currentState,
|
||||||
|
Action<bool> onToggle, Func<string> tooltipProvider, float spacingOverride = -1f)
|
||||||
|
{
|
||||||
|
if (spacingOverride >= 0f)
|
||||||
|
{
|
||||||
|
ImGui.SameLine(0, spacingOverride);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_uiSharedService.IconTextButton(icon, label, width))
|
||||||
|
{
|
||||||
|
var newState = !currentState;
|
||||||
|
onToggle(newState);
|
||||||
|
}
|
||||||
|
|
||||||
|
UiSharedService.AttachToolTip(tooltipProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string DisableStateTooltip(string context, bool disabled)
|
||||||
|
{
|
||||||
|
var state = disabled ? "désactivée" : "activée";
|
||||||
|
return $"Synchronisation {context} par défaut : {state}.\nCliquez pour modifier.";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PublishSyncDefaultNotification(string context, bool disabled)
|
||||||
|
{
|
||||||
|
var state = disabled ? "désactivée" : "activée";
|
||||||
|
var message = $"Synchronisation {context} par défaut {state}.";
|
||||||
|
Mediator.Publish(new DualNotificationMessage("Préférence de synchronisation", message, NotificationType.Info));
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawAddCharacter()
|
private void DrawAddCharacter()
|
||||||
{
|
{
|
||||||
ImGui.Dummy(new(10));
|
ImGui.Dummy(new(10));
|
||||||
@@ -388,49 +497,26 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var inbox = _nearbyPending;
|
// intentionally left blank; pending requests handled in collapsible section below
|
||||||
if (inbox != null && inbox.Pending.Count > 0)
|
|
||||||
{
|
|
||||||
ImGuiHelpers.ScaledDummy(6);
|
|
||||||
_uiSharedService.BigText("Incoming requests");
|
|
||||||
foreach (var kv in inbox.Pending)
|
|
||||||
{
|
|
||||||
ImGui.AlignTextToFramePadding();
|
|
||||||
ImGui.TextUnformatted($"{kv.Value} [{kv.Key}]");
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (_uiSharedService.IconButton(FontAwesomeIcon.Check))
|
|
||||||
{
|
|
||||||
_ = inbox.AcceptAsync(kv.Key);
|
|
||||||
}
|
|
||||||
UiSharedService.AttachToolTip("Accept and add as pair");
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (_uiSharedService.IconButton(FontAwesomeIcon.Times))
|
|
||||||
{
|
|
||||||
inbox.Remove(kv.Key);
|
|
||||||
}
|
|
||||||
UiSharedService.AttachToolTip("Dismiss request");
|
|
||||||
}
|
|
||||||
ImGui.Separator();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
// Add the "En attente" category
|
var pendingCount = _nearbyPending?.Pending.Count ?? 0;
|
||||||
using (ImRaii.PushId("group-Pending"))
|
if (pendingCount > 0)
|
||||||
{
|
{
|
||||||
var icon = _pendingOpen ? FontAwesomeIcon.CaretSquareDown : FontAwesomeIcon.CaretSquareRight;
|
using (ImRaii.PushId("group-Pending"))
|
||||||
_uiSharedService.IconText(icon);
|
|
||||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) _pendingOpen = !_pendingOpen;
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextUnformatted($"En attente ({_nearbyPending?.Pending.Count ?? 0})");
|
|
||||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) _pendingOpen = !_pendingOpen;
|
|
||||||
|
|
||||||
if (_pendingOpen)
|
|
||||||
{
|
{
|
||||||
ImGui.Indent();
|
var icon = _pendingOpen ? FontAwesomeIcon.CaretSquareDown : FontAwesomeIcon.CaretSquareRight;
|
||||||
if (_nearbyPending != null && _nearbyPending.Pending.Count > 0)
|
_uiSharedService.IconText(icon);
|
||||||
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) _pendingOpen = !_pendingOpen;
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextUnformatted($"En attente ({pendingCount})");
|
||||||
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) _pendingOpen = !_pendingOpen;
|
||||||
|
|
||||||
|
if (_pendingOpen)
|
||||||
{
|
{
|
||||||
foreach (var kv in _nearbyPending.Pending)
|
ImGui.Indent();
|
||||||
|
foreach (var kv in _nearbyPending!.Pending)
|
||||||
{
|
{
|
||||||
ImGui.AlignTextToFramePadding();
|
ImGui.AlignTextToFramePadding();
|
||||||
ImGui.TextUnformatted($"{kv.Value} [{kv.Key}]");
|
ImGui.TextUnformatted($"{kv.Value} [{kv.Key}]");
|
||||||
@@ -447,13 +533,9 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Dismiss request");
|
UiSharedService.AttachToolTip("Dismiss request");
|
||||||
}
|
}
|
||||||
|
ImGui.Unindent();
|
||||||
|
ImGui.Separator();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
UiSharedService.ColorTextWrapped("Aucune invitation en attente.", ImGuiColors.DalamudGrey3);
|
|
||||||
}
|
|
||||||
ImGui.Unindent();
|
|
||||||
ImGui.Separator();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,15 +558,6 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) _nearbyOpen = !_nearbyOpen;
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) _nearbyOpen = !_nearbyOpen;
|
||||||
if (_nearbyOpen)
|
if (_nearbyOpen)
|
||||||
{
|
{
|
||||||
var btnWidth = _uiSharedService.GetIconTextButtonSize(FontAwesomeIcon.UserPlus, "Nearby");
|
|
||||||
var headerRight = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth();
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.SetCursorPosX(headerRight - btnWidth);
|
|
||||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.UserPlus, "Nearby", btnWidth))
|
|
||||||
{
|
|
||||||
Mediator.Publish(new UiToggleMessage(typeof(AutoDetectUi)));
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Indent();
|
ImGui.Indent();
|
||||||
var nearby = _nearbyEntries == null
|
var nearby = _nearbyEntries == null
|
||||||
? new List<Services.Mediator.NearbyEntry>()
|
? new List<Services.Mediator.NearbyEntry>()
|
||||||
|
|||||||
@@ -272,6 +272,7 @@ public class DrawUserPair : DrawPairBase
|
|||||||
{
|
{
|
||||||
var permissions = entry.UserPair.OwnPermissions;
|
var permissions = entry.UserPair.OwnPermissions;
|
||||||
permissions.SetDisableSounds(!isDisableSounds);
|
permissions.SetDisableSounds(!isDisableSounds);
|
||||||
|
_mediator.Publish(new PairSyncOverrideChanged(entry.UserData.UID, permissions.IsDisableSounds(), null, null));
|
||||||
_ = _apiController.UserSetPairPermissions(new UserPermissionsDto(entry.UserData, permissions));
|
_ = _apiController.UserSetPairPermissions(new UserPermissionsDto(entry.UserData, permissions));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,6 +283,7 @@ public class DrawUserPair : DrawPairBase
|
|||||||
{
|
{
|
||||||
var permissions = entry.UserPair.OwnPermissions;
|
var permissions = entry.UserPair.OwnPermissions;
|
||||||
permissions.SetDisableAnimations(!isDisableAnims);
|
permissions.SetDisableAnimations(!isDisableAnims);
|
||||||
|
_mediator.Publish(new PairSyncOverrideChanged(entry.UserData.UID, null, permissions.IsDisableAnimations(), null));
|
||||||
_ = _apiController.UserSetPairPermissions(new UserPermissionsDto(entry.UserData, permissions));
|
_ = _apiController.UserSetPairPermissions(new UserPermissionsDto(entry.UserData, permissions));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,6 +294,7 @@ public class DrawUserPair : DrawPairBase
|
|||||||
{
|
{
|
||||||
var permissions = entry.UserPair.OwnPermissions;
|
var permissions = entry.UserPair.OwnPermissions;
|
||||||
permissions.SetDisableVFX(!isDisableVFX);
|
permissions.SetDisableVFX(!isDisableVFX);
|
||||||
|
_mediator.Publish(new PairSyncOverrideChanged(entry.UserData.UID, null, null, permissions.IsDisableVFX()));
|
||||||
_ = _apiController.UserSetPairPermissions(new UserPermissionsDto(entry.UserData, permissions));
|
_ = _apiController.UserSetPairPermissions(new UserPermissionsDto(entry.UserData, permissions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -769,6 +769,7 @@ internal sealed class GroupPanel
|
|||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
var perm = groupDto.GroupUserPermissions;
|
var perm = groupDto.GroupUserPermissions;
|
||||||
perm.SetDisableSounds(!perm.IsDisableSounds());
|
perm.SetDisableSounds(!perm.IsDisableSounds());
|
||||||
|
_mainUi.Mediator.Publish(new GroupSyncOverrideChanged(groupDto.Group.GID, perm.IsDisableSounds(), null, null));
|
||||||
_ = ApiController.GroupChangeIndividualPermissionState(new(groupDto.Group, new UserData(ApiController.UID), perm));
|
_ = ApiController.GroupChangeIndividualPermissionState(new(groupDto.Group, new UserData(ApiController.UID), perm));
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Sets your allowance for sound synchronization for users of this syncshell."
|
UiSharedService.AttachToolTip("Sets your allowance for sound synchronization for users of this syncshell."
|
||||||
@@ -782,6 +783,7 @@ internal sealed class GroupPanel
|
|||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
var perm = groupDto.GroupUserPermissions;
|
var perm = groupDto.GroupUserPermissions;
|
||||||
perm.SetDisableAnimations(!perm.IsDisableAnimations());
|
perm.SetDisableAnimations(!perm.IsDisableAnimations());
|
||||||
|
_mainUi.Mediator.Publish(new GroupSyncOverrideChanged(groupDto.Group.GID, null, perm.IsDisableAnimations(), null));
|
||||||
_ = ApiController.GroupChangeIndividualPermissionState(new(groupDto.Group, new UserData(ApiController.UID), perm));
|
_ = ApiController.GroupChangeIndividualPermissionState(new(groupDto.Group, new UserData(ApiController.UID), perm));
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Sets your allowance for animations synchronization for users of this syncshell."
|
UiSharedService.AttachToolTip("Sets your allowance for animations synchronization for users of this syncshell."
|
||||||
@@ -796,6 +798,7 @@ internal sealed class GroupPanel
|
|||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
var perm = groupDto.GroupUserPermissions;
|
var perm = groupDto.GroupUserPermissions;
|
||||||
perm.SetDisableVFX(!perm.IsDisableVFX());
|
perm.SetDisableVFX(!perm.IsDisableVFX());
|
||||||
|
_mainUi.Mediator.Publish(new GroupSyncOverrideChanged(groupDto.Group.GID, null, null, perm.IsDisableVFX()));
|
||||||
_ = ApiController.GroupChangeIndividualPermissionState(new(groupDto.Group, new UserData(ApiController.UID), perm));
|
_ = ApiController.GroupChangeIndividualPermissionState(new(groupDto.Group, new UserData(ApiController.UID), perm));
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Sets your allowance for VFX synchronization for users of this syncshell."
|
UiSharedService.AttachToolTip("Sets your allowance for VFX synchronization for users of this syncshell."
|
||||||
|
|||||||
@@ -128,6 +128,10 @@ public class PermissionWindowUI : WindowMediatorSubscriberBase
|
|||||||
using (ImRaii.Disabled(!hasChanges))
|
using (ImRaii.Disabled(!hasChanges))
|
||||||
if (_uiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.Save, "Save"))
|
if (_uiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.Save, "Save"))
|
||||||
{
|
{
|
||||||
|
Mediator.Publish(new PairSyncOverrideChanged(Pair.UserData.UID,
|
||||||
|
_ownPermissions.IsDisableSounds(),
|
||||||
|
_ownPermissions.IsDisableAnimations(),
|
||||||
|
_ownPermissions.IsDisableVFX()));
|
||||||
_ = _apiController.UserSetPairPermissions(new(Pair.UserData, _ownPermissions));
|
_ = _apiController.UserSetPairPermissions(new(Pair.UserData, _ownPermissions));
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Save and apply all changes");
|
UiSharedService.AttachToolTip("Save and apply all changes");
|
||||||
@@ -148,10 +152,15 @@ public class PermissionWindowUI : WindowMediatorSubscriberBase
|
|||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (_uiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.ArrowsSpin, "Reset to Default"))
|
if (_uiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.ArrowsSpin, "Reset to Default"))
|
||||||
{
|
{
|
||||||
|
var defaults = _uiSharedService.ConfigService.Current;
|
||||||
_ownPermissions.SetPaused(false);
|
_ownPermissions.SetPaused(false);
|
||||||
_ownPermissions.SetDisableVFX(false);
|
_ownPermissions.SetDisableSounds(defaults.DefaultDisableSounds);
|
||||||
_ownPermissions.SetDisableSounds(false);
|
_ownPermissions.SetDisableAnimations(defaults.DefaultDisableAnimations);
|
||||||
_ownPermissions.SetDisableAnimations(false);
|
_ownPermissions.SetDisableVFX(defaults.DefaultDisableVfx);
|
||||||
|
Mediator.Publish(new PairSyncOverrideChanged(Pair.UserData.UID,
|
||||||
|
_ownPermissions.IsDisableSounds(),
|
||||||
|
_ownPermissions.IsDisableAnimations(),
|
||||||
|
_ownPermissions.IsDisableVFX()));
|
||||||
_ = _apiController.UserSetPairPermissions(new(Pair.UserData, _ownPermissions));
|
_ = _apiController.UserSetPairPermissions(new(Pair.UserData, _ownPermissions));
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("This will set all permissions to their default setting");
|
UiSharedService.AttachToolTip("This will set all permissions to their default setting");
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
public string PlayerName => _dalamudUtil.GetPlayerName();
|
public string PlayerName => _dalamudUtil.GetPlayerName();
|
||||||
|
|
||||||
public IFontHandle UidFont { get; init; }
|
public IFontHandle UidFont { get; init; }
|
||||||
|
public MareConfigService ConfigService => _configService;
|
||||||
public Dictionary<ushort, string> WorldData => _dalamudUtil.WorldData.Value;
|
public Dictionary<ushort, string> WorldData => _dalamudUtil.WorldData.Value;
|
||||||
|
|
||||||
public uint WorldId => _dalamudUtil.GetHomeWorldId();
|
public uint WorldId => _dalamudUtil.GetHomeWorldId();
|
||||||
|
|||||||
@@ -97,6 +97,12 @@ public partial class ApiController
|
|||||||
await _mareHub!.InvokeAsync(nameof(UserSetProfile), userDescription).ConfigureAwait(false);
|
await _mareHub!.InvokeAsync(nameof(UserSetProfile), userDescription).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task UserSetTypingState(bool isTyping)
|
||||||
|
{
|
||||||
|
CheckConnection();
|
||||||
|
await _mareHub!.SendAsync(nameof(UserSetTypingState), isTyping).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task PushCharacterDataInternal(CharacterData character, List<UserData> visibleCharacters)
|
private async Task PushCharacterDataInternal(CharacterData character, List<UserData> visibleCharacters)
|
||||||
{
|
{
|
||||||
Logger.LogInformation("Pushing character data for {hash} to {charas}", character.DataHash.Value, string.Join(", ", visibleCharacters.Select(c => c.AliasOrUID)));
|
Logger.LogInformation("Pushing character data for {hash} to {charas}", character.DataHash.Value, string.Join(", ", visibleCharacters.Select(c => c.AliasOrUID)));
|
||||||
|
|||||||
@@ -138,6 +138,13 @@ public partial class ApiController
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task Client_UserTypingState(TypingStateDto dto)
|
||||||
|
{
|
||||||
|
Logger.LogTrace("Client_UserTypingState: {uid} typing={typing}", dto.User.UID, dto.IsTyping);
|
||||||
|
Mediator.Publish(new UserTypingStateMessage(dto));
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
public Task Client_UserReceiveCharacterData(OnlineUserCharaDataDto dataDto)
|
public Task Client_UserReceiveCharacterData(OnlineUserCharaDataDto dataDto)
|
||||||
{
|
{
|
||||||
Logger.LogTrace("Client_UserReceiveCharacterData: {user}", dataDto.User);
|
Logger.LogTrace("Client_UserReceiveCharacterData: {user}", dataDto.User);
|
||||||
@@ -313,6 +320,12 @@ public partial class ApiController
|
|||||||
_mareHub!.On(nameof(Client_UserChatMsg), act);
|
_mareHub!.On(nameof(Client_UserChatMsg), act);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnUserTypingState(Action<TypingStateDto> act)
|
||||||
|
{
|
||||||
|
if (_initialized) return;
|
||||||
|
_mareHub!.On(nameof(Client_UserTypingState), act);
|
||||||
|
}
|
||||||
|
|
||||||
public void OnUserReceiveCharacterData(Action<OnlineUserCharaDataDto> act)
|
public void OnUserReceiveCharacterData(Action<OnlineUserCharaDataDto> act)
|
||||||
{
|
{
|
||||||
if (_initialized) return;
|
if (_initialized) return;
|
||||||
|
|||||||
@@ -348,6 +348,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
|||||||
OnUserUpdateSelfPairPermissions(dto => _ = Client_UserUpdateSelfPairPermissions(dto));
|
OnUserUpdateSelfPairPermissions(dto => _ = Client_UserUpdateSelfPairPermissions(dto));
|
||||||
OnUserReceiveUploadStatus(dto => _ = Client_UserReceiveUploadStatus(dto));
|
OnUserReceiveUploadStatus(dto => _ = Client_UserReceiveUploadStatus(dto));
|
||||||
OnUserUpdateProfile(dto => _ = Client_UserUpdateProfile(dto));
|
OnUserUpdateProfile(dto => _ = Client_UserUpdateProfile(dto));
|
||||||
|
OnUserTypingState(dto => _ = Client_UserTypingState(dto));
|
||||||
|
|
||||||
OnGroupChangePermissions((dto) => _ = Client_GroupChangePermissions(dto));
|
OnGroupChangePermissions((dto) => _ = Client_GroupChangePermissions(dto));
|
||||||
OnGroupDelete((dto) => _ = Client_GroupDelete(dto));
|
OnGroupDelete((dto) => _ = Client_GroupDelete(dto));
|
||||||
@@ -393,7 +394,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
|||||||
foreach (var user in users)
|
foreach (var user in users)
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Group Pair: {user}", user);
|
Logger.LogDebug("Group Pair: {user}", user);
|
||||||
_pairManager.AddGroupPair(user);
|
_pairManager.AddGroupPair(user, isInitialLoad: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -479,4 +480,4 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
|||||||
ServerState = state;
|
ServerState = state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#pragma warning restore MA0040
|
#pragma warning restore MA0040
|
||||||
|
|||||||
Reference in New Issue
Block a user