Update 0.1.9 - Correctif UI + Default Synchronisation settings + Detect TypeChat
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System.Threading;
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
@@ -30,6 +31,11 @@ public class ChatService : DisposableMediatorSubscriberBase
|
||||
|
||||
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,
|
||||
PairManager pairManager, ILoggerFactory loggerFactory, IGameInteropProvider gameInteropProvider, IChatGui chatGui,
|
||||
MareConfigService mareConfig, ServerConfigurationManager serverConfigurationManager) : base(logger, mediator)
|
||||
@@ -46,13 +52,12 @@ public class ChatService : DisposableMediatorSubscriberBase
|
||||
Mediator.Subscribe<GroupChatMsgMessage>(this, HandleGroupChat);
|
||||
|
||||
_gameChatHooks = new(() => new GameChatHooks(loggerFactory.CreateLogger<GameChatHooks>(), gameInteropProvider, SendChatShell));
|
||||
|
||||
// Initialize chat hooks in advance
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = _gameChatHooks.Value;
|
||||
_isTypingAnnounced = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -64,9 +69,74 @@ public class ChatService : DisposableMediatorSubscriberBase
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
_typingCts?.Cancel();
|
||||
_typingCts?.Dispose();
|
||||
if (_gameChatHooks.IsValueCreated)
|
||||
_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)
|
||||
{
|
||||
@@ -124,7 +194,6 @@ public class ChatService : DisposableMediatorSubscriberBase
|
||||
msg.AddText($"[SS{shellNumber}]<");
|
||||
if (message.ChatMsg.Sender.UID.Equals(_apiController.UID, StringComparison.Ordinal))
|
||||
{
|
||||
// Don't link to your own character
|
||||
msg.AddText(chatMsg.SenderName);
|
||||
}
|
||||
else
|
||||
@@ -142,8 +211,6 @@ public class ChatService : DisposableMediatorSubscriberBase
|
||||
Type = logKind
|
||||
});
|
||||
}
|
||||
|
||||
// Print an example message to the configured global chat channel
|
||||
public void PrintChannelExample(string message, string gid = "")
|
||||
{
|
||||
int chatType = _mareConfig.Current.ChatLogKind;
|
||||
@@ -164,8 +231,6 @@ public class ChatService : DisposableMediatorSubscriberBase
|
||||
Type = (XivChatType)chatType
|
||||
});
|
||||
}
|
||||
|
||||
// Called to update the active chat shell name if its renamed
|
||||
public void MaybeUpdateShellName(int shellNumber)
|
||||
{
|
||||
if (_mareConfig.Current.DisableSyncshellChat)
|
||||
@@ -178,7 +243,6 @@ public class ChatService : DisposableMediatorSubscriberBase
|
||||
{
|
||||
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))
|
||||
SwitchChatShell(shellNumber);
|
||||
}
|
||||
@@ -197,7 +261,6 @@ public class ChatService : DisposableMediatorSubscriberBase
|
||||
if (shellConfig.Enabled && shellConfig.ShellNumber == shellNumber)
|
||||
{
|
||||
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()
|
||||
{
|
||||
ChannelName = $"SS [{shellNumber}]: {name}",
|
||||
@@ -221,7 +284,6 @@ public class ChatService : DisposableMediatorSubscriberBase
|
||||
if (shellConfig.Enabled && shellConfig.ShellNumber == shellNumber)
|
||||
{
|
||||
_ = Task.Run(async () => {
|
||||
// Should cache the name and home world instead of fetching it every time
|
||||
var chatMsg = await _dalamudUtil.RunOnFrameworkThread(() => {
|
||||
return new ChatMessage()
|
||||
{
|
||||
@@ -230,6 +292,7 @@ public class ChatService : DisposableMediatorSubscriberBase
|
||||
PayloadContent = chatBytes
|
||||
};
|
||||
}).ConfigureAwait(false);
|
||||
ClearTypingState();
|
||||
await _apiController.GroupChatSendMsg(new(group.Key), chatMsg).ConfigureAwait(false);
|
||||
}).ConfigureAwait(false);
|
||||
return;
|
||||
|
||||
@@ -7,6 +7,9 @@ using MareSynchronos.PlayerData.Pairs;
|
||||
using MareSynchronos.Services.Mediator;
|
||||
using MareSynchronos.UI;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using System.Collections.Concurrent;
|
||||
using Dalamud.Game.Text;
|
||||
|
||||
namespace MareSynchronos.Services;
|
||||
|
||||
@@ -20,6 +23,9 @@ public class GuiHookService : DisposableMediatorSubscriberBase
|
||||
private readonly IPartyList _partyList;
|
||||
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 _namePlateRoleColorsEnabled = false;
|
||||
|
||||
@@ -41,6 +47,18 @@ public class GuiHookService : DisposableMediatorSubscriberBase
|
||||
Mediator.Subscribe<DelayedFrameworkUpdateMessage>(this, (_) => GameSettingsCheck());
|
||||
Mediator.Subscribe<PairHandlerVisibleMessage>(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)
|
||||
@@ -72,6 +90,7 @@ public class GuiHookService : DisposableMediatorSubscriberBase
|
||||
if (!_configService.Current.UseNameColors)
|
||||
return;
|
||||
|
||||
var showTypingIndicator = _configService.Current.TypingIndicatorShowOnNameplates;
|
||||
var visibleUsers = _pairManager.GetOnlineUserPairs().Where(u => u.IsVisible && u.PlayerCharacterId != uint.MaxValue);
|
||||
var visibleUsersIds = visibleUsers.Select(u => (ulong)u.PlayerCharacterId).ToHashSet();
|
||||
|
||||
@@ -95,6 +114,17 @@ public class GuiHookService : DisposableMediatorSubscriberBase
|
||||
BuildColorEndSeString(colors)
|
||||
);
|
||||
_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.CharaData;
|
||||
using MareSynchronos.API.Dto.Group;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using MareSynchronos.MareConfiguration.Models;
|
||||
using MareSynchronos.PlayerData.Handlers;
|
||||
using MareSynchronos.PlayerData.Pairs;
|
||||
@@ -52,6 +53,7 @@ public record HaltScanMessage(string Source) : MessageBase;
|
||||
public record ResumeScanMessage(string Source) : MessageBase;
|
||||
public record NotificationMessage
|
||||
(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 ClearCacheForObjectMessage(GameObjectHandler ObjectToCreateFor) : MessageBase;
|
||||
public record CharacterDataCreatedMessage(CharacterData CharacterData) : SameThreadMessage;
|
||||
@@ -90,6 +92,7 @@ public record PenumbraDirectoryChangedMessage(string? ModDirectory) : MessageBas
|
||||
public record PenumbraRedrawCharacterMessage(ICharacter Character) : SameThreadMessage;
|
||||
public record UserChatMsgMessage(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 NameplateRedrawMessage : MessageBase;
|
||||
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 NearbyDetectionToggled(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);
|
||||
#pragma warning restore S2094
|
||||
|
||||
@@ -32,6 +32,7 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Mediator.Subscribe<NotificationMessage>(this, ShowNotification);
|
||||
Mediator.Subscribe<DualNotificationMessage>(this, ShowDualNotification);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user