Compare commits
2 Commits
fr-transla
...
78089a9fc7
| Author | SHA1 | Date | |
|---|---|---|---|
|
78089a9fc7
|
|||
|
3c81e1f243
|
2
MareAPI
2
MareAPI
Submodule MareAPI updated: 7a48ca9823...fa9b7bce43
@@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<AssemblyName>UmbraSync</AssemblyName>
|
||||
<RootNamespace>UmbraSync</RootNamespace>
|
||||
<Version>0.1.7.0</Version>
|
||||
<Version>0.1.8.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -210,9 +210,16 @@ public sealed class PairManager : DisposableMediatorSubscriberBase
|
||||
|
||||
public void SetGroupInfo(GroupInfoDto dto)
|
||||
{
|
||||
_allGroups[dto.Group].Group = dto.Group;
|
||||
_allGroups[dto.Group].Owner = dto.Owner;
|
||||
_allGroups[dto.Group].GroupPermissions = dto.GroupPermissions;
|
||||
if (!_allGroups.TryGetValue(dto.Group, out var groupInfo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
groupInfo.Group = dto.Group;
|
||||
groupInfo.Owner = dto.Owner;
|
||||
groupInfo.GroupPermissions = dto.GroupPermissions;
|
||||
groupInfo.IsTemporary = dto.IsTemporary;
|
||||
groupInfo.ExpiresAt = dto.ExpiresAt;
|
||||
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
@@ -145,6 +145,7 @@ public sealed class Plugin : IDalamudPlugin
|
||||
collection.AddSingleton<IpcCallerMare>();
|
||||
collection.AddSingleton<IpcManager>();
|
||||
collection.AddSingleton<NotificationService>();
|
||||
collection.AddSingleton<TemporarySyncshellNotificationService>();
|
||||
|
||||
collection.AddSingleton((s) => new MareConfigService(pluginInterface.ConfigDirectory.FullName));
|
||||
collection.AddSingleton((s) => new ServerConfigService(pluginInterface.ConfigDirectory.FullName));
|
||||
@@ -203,6 +204,7 @@ public sealed class Plugin : IDalamudPlugin
|
||||
collection.AddHostedService(p => p.GetRequiredService<ConfigurationSaveService>());
|
||||
collection.AddHostedService(p => p.GetRequiredService<MareMediator>());
|
||||
collection.AddHostedService(p => p.GetRequiredService<NotificationService>());
|
||||
collection.AddHostedService(p => p.GetRequiredService<TemporarySyncshellNotificationService>());
|
||||
collection.AddHostedService(p => p.GetRequiredService<FileCacheManager>());
|
||||
collection.AddHostedService(p => p.GetRequiredService<ConfigurationMigrator>());
|
||||
collection.AddHostedService(p => p.GetRequiredService<DalamudUtilService>());
|
||||
|
||||
225
MareSynchronos/Services/TemporarySyncshellNotificationService.cs
Normal file
225
MareSynchronos/Services/TemporarySyncshellNotificationService.cs
Normal file
@@ -0,0 +1,225 @@
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using MareSynchronos.API.Dto.Group;
|
||||
using MareSynchronos.MareConfiguration.Models;
|
||||
using MareSynchronos.PlayerData.Pairs;
|
||||
using MareSynchronos.Services.Mediator;
|
||||
using MareSynchronos.WebAPI;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MareSynchronos.Services;
|
||||
|
||||
public sealed class TemporarySyncshellNotificationService : MediatorSubscriberBase, IHostedService
|
||||
{
|
||||
private static readonly int[] NotificationThresholdMinutes = [30, 15, 5, 1];
|
||||
private readonly ApiController _apiController;
|
||||
private readonly PairManager _pairManager;
|
||||
private readonly Lock _stateLock = new();
|
||||
private readonly Dictionary<string, TrackedGroup> _trackedGroups = new(StringComparer.Ordinal);
|
||||
private CancellationTokenSource? _loopCts;
|
||||
private Task? _loopTask;
|
||||
|
||||
public TemporarySyncshellNotificationService(ILogger<TemporarySyncshellNotificationService> logger, MareMediator mediator, PairManager pairManager, ApiController apiController)
|
||||
: base(logger, mediator)
|
||||
{
|
||||
_pairManager = pairManager;
|
||||
_apiController = apiController;
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_loopCts = new CancellationTokenSource();
|
||||
Mediator.Subscribe<ConnectedMessage>(this, _ => ResetTrackedGroups());
|
||||
Mediator.Subscribe<DisconnectedMessage>(this, _ => ResetTrackedGroups());
|
||||
_loopTask = Task.Run(() => MonitorLoopAsync(_loopCts.Token), _loopCts.Token);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Mediator.UnsubscribeAll(this);
|
||||
if (_loopCts == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_loopCts.Cancel();
|
||||
if (_loopTask != null)
|
||||
{
|
||||
await _loopTask.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
_loopTask = null;
|
||||
_loopCts.Dispose();
|
||||
_loopCts = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task MonitorLoopAsync(CancellationToken ct)
|
||||
{
|
||||
var delay = TimeSpan.FromSeconds(30);
|
||||
while (!ct.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
CheckGroups();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogDebug(ex, "Failed to check temporary syncshell expirations");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Task.Delay(delay, ct).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckGroups()
|
||||
{
|
||||
var nowUtc = DateTime.UtcNow;
|
||||
var groupsSnapshot = _pairManager.Groups.Values.ToList();
|
||||
var notifications = new List<NotificationPayload>();
|
||||
var expiredGroups = new List<GroupFullInfoDto>();
|
||||
var seenTemporaryGids = new HashSet<string>(StringComparer.Ordinal);
|
||||
|
||||
using (var guard = _stateLock.EnterScope())
|
||||
{
|
||||
foreach (var group in groupsSnapshot)
|
||||
{
|
||||
if (!group.IsTemporary || group.ExpiresAt == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(_apiController.UID) || !string.Equals(group.OwnerUID, _apiController.UID, StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var gid = group.Group.GID;
|
||||
seenTemporaryGids.Add(gid);
|
||||
var expiresAtUtc = NormalizeToUtc(group.ExpiresAt.Value);
|
||||
var remaining = expiresAtUtc - nowUtc;
|
||||
|
||||
if (!_trackedGroups.TryGetValue(gid, out var state))
|
||||
{
|
||||
state = new TrackedGroup(expiresAtUtc);
|
||||
_trackedGroups[gid] = state;
|
||||
}
|
||||
else if (state.ExpiresAtUtc != expiresAtUtc)
|
||||
{
|
||||
state.UpdateExpiresAt(expiresAtUtc);
|
||||
}
|
||||
|
||||
if (remaining <= TimeSpan.Zero)
|
||||
{
|
||||
_trackedGroups.Remove(gid);
|
||||
expiredGroups.Add(group);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!state.LastRemaining.HasValue)
|
||||
{
|
||||
state.UpdateRemaining(remaining);
|
||||
continue;
|
||||
}
|
||||
|
||||
var previousRemaining = state.LastRemaining.Value;
|
||||
|
||||
foreach (var thresholdMinutes in NotificationThresholdMinutes)
|
||||
{
|
||||
var threshold = TimeSpan.FromMinutes(thresholdMinutes);
|
||||
if (previousRemaining > threshold && remaining <= threshold)
|
||||
{
|
||||
notifications.Add(new NotificationPayload(group, thresholdMinutes, expiresAtUtc));
|
||||
}
|
||||
}
|
||||
|
||||
state.UpdateRemaining(remaining);
|
||||
}
|
||||
|
||||
var toRemove = _trackedGroups.Keys.Where(k => !seenTemporaryGids.Contains(k)).ToList();
|
||||
foreach (var gid in toRemove)
|
||||
{
|
||||
_trackedGroups.Remove(gid);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var expiredGroup in expiredGroups)
|
||||
{
|
||||
Logger.LogInformation("Temporary syncshell {gid} expired locally; removing", expiredGroup.Group.GID);
|
||||
_pairManager.RemoveGroup(expiredGroup.Group);
|
||||
}
|
||||
|
||||
foreach (var notification in notifications)
|
||||
{
|
||||
PublishNotification(notification.Group, notification.ThresholdMinutes, notification.ExpiresAtUtc);
|
||||
}
|
||||
}
|
||||
|
||||
private void PublishNotification(GroupFullInfoDto group, int thresholdMinutes, DateTime expiresAtUtc)
|
||||
{
|
||||
string displayName = string.IsNullOrWhiteSpace(group.GroupAlias) ? group.Group.GID : group.GroupAlias!;
|
||||
string threshold = thresholdMinutes == 1 ? "1 minute" : $"{thresholdMinutes} minutes";
|
||||
string expiresLocal = expiresAtUtc.ToLocalTime().ToString("t", CultureInfo.CurrentCulture);
|
||||
|
||||
string message = $"La Syncshell temporaire \"{displayName}\" sera supprimee dans {threshold} (a {expiresLocal}).";
|
||||
Mediator.Publish(new NotificationMessage("Syncshell temporaire", message, NotificationType.Warning, TimeSpan.FromSeconds(6)));
|
||||
}
|
||||
|
||||
private static DateTime NormalizeToUtc(DateTime expiresAt)
|
||||
{
|
||||
return expiresAt.Kind switch
|
||||
{
|
||||
DateTimeKind.Utc => expiresAt,
|
||||
DateTimeKind.Local => expiresAt.ToUniversalTime(),
|
||||
_ => DateTime.SpecifyKind(expiresAt, DateTimeKind.Utc)
|
||||
};
|
||||
}
|
||||
|
||||
private void ResetTrackedGroups()
|
||||
{
|
||||
using (var guard = _stateLock.EnterScope())
|
||||
{
|
||||
_trackedGroups.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class TrackedGroup
|
||||
{
|
||||
public TrackedGroup(DateTime expiresAtUtc)
|
||||
{
|
||||
ExpiresAtUtc = expiresAtUtc;
|
||||
}
|
||||
|
||||
public DateTime ExpiresAtUtc { get; private set; }
|
||||
public TimeSpan? LastRemaining { get; private set; }
|
||||
|
||||
public void UpdateExpiresAt(DateTime expiresAtUtc)
|
||||
{
|
||||
ExpiresAtUtc = expiresAtUtc;
|
||||
LastRemaining = null;
|
||||
}
|
||||
|
||||
public void UpdateRemaining(TimeSpan remaining)
|
||||
{
|
||||
LastRemaining = remaining;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed record NotificationPayload(GroupFullInfoDto Group, int ThresholdMinutes, DateTime ExpiresAtUtc);
|
||||
}
|
||||
@@ -17,6 +17,7 @@ using MareSynchronos.Services.ServerConfiguration;
|
||||
using MareSynchronos.UI.Components;
|
||||
using MareSynchronos.UI.Handlers;
|
||||
using MareSynchronos.WebAPI;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
|
||||
@@ -40,6 +41,7 @@ internal sealed class GroupPanel
|
||||
private string _editGroupComment = string.Empty;
|
||||
private string _editGroupEntry = string.Empty;
|
||||
private bool _errorGroupCreate = false;
|
||||
private string _errorGroupCreateMessage = string.Empty;
|
||||
private bool _errorGroupJoin;
|
||||
private bool _isPasswordValid;
|
||||
private GroupPasswordDto? _lastCreatedGroup = null;
|
||||
@@ -53,6 +55,20 @@ internal sealed class GroupPanel
|
||||
private bool _showModalCreateGroup;
|
||||
private bool _showModalEnterPassword;
|
||||
private string _newSyncShellAlias = string.Empty;
|
||||
private bool _createIsTemporary = false;
|
||||
private int _tempSyncshellDurationHours = 24;
|
||||
private readonly int[] _temporaryDurationOptions = new[]
|
||||
{
|
||||
1,
|
||||
12,
|
||||
24,
|
||||
48,
|
||||
72,
|
||||
96,
|
||||
120,
|
||||
144,
|
||||
168
|
||||
};
|
||||
private string _syncShellPassword = string.Empty;
|
||||
private string _syncShellToJoin = string.Empty;
|
||||
|
||||
@@ -110,6 +126,9 @@ internal sealed class GroupPanel
|
||||
_lastCreatedGroup = null;
|
||||
_errorGroupCreate = false;
|
||||
_newSyncShellAlias = string.Empty;
|
||||
_createIsTemporary = false;
|
||||
_tempSyncshellDurationHours = 24;
|
||||
_errorGroupCreateMessage = string.Empty;
|
||||
_showModalCreateGroup = true;
|
||||
ImGui.OpenPopup("Create Syncshell");
|
||||
}
|
||||
@@ -151,15 +170,77 @@ internal sealed class GroupPanel
|
||||
}
|
||||
|
||||
if (ImGui.BeginPopupModal("Create Syncshell", ref _showModalCreateGroup, UiSharedService.PopupWindowFlags))
|
||||
{
|
||||
UiSharedService.TextWrapped("Choisissez le type de Syncshell à créer.");
|
||||
bool showPermanent = !_createIsTemporary;
|
||||
if (ImGui.RadioButton("Permanente", showPermanent))
|
||||
{
|
||||
_createIsTemporary = false;
|
||||
}
|
||||
ImGui.SameLine();
|
||||
if (ImGui.RadioButton("Temporaire", _createIsTemporary))
|
||||
{
|
||||
_createIsTemporary = true;
|
||||
_newSyncShellAlias = string.Empty;
|
||||
}
|
||||
|
||||
if (!_createIsTemporary)
|
||||
{
|
||||
UiSharedService.TextWrapped("Donnez un nom à votre Syncshell (optionnel) puis créez-la.");
|
||||
ImGui.SetNextItemWidth(-1);
|
||||
ImGui.InputTextWithHint("##syncshellalias", "Nom du Syncshell", ref _newSyncShellAlias, 50);
|
||||
}
|
||||
else
|
||||
{
|
||||
_newSyncShellAlias = string.Empty;
|
||||
}
|
||||
|
||||
if (_createIsTemporary)
|
||||
{
|
||||
UiSharedService.TextWrapped("Durée maximale d'une Syncshell temporaire : 7 jours.");
|
||||
if (_tempSyncshellDurationHours > 168) _tempSyncshellDurationHours = 168;
|
||||
for (int i = 0; i < _temporaryDurationOptions.Length; i++)
|
||||
{
|
||||
var option = _temporaryDurationOptions[i];
|
||||
var isSelected = _tempSyncshellDurationHours == option;
|
||||
string label = option switch
|
||||
{
|
||||
>= 24 when option % 24 == 0 => option == 24 ? "24h" : $"{option / 24}j",
|
||||
_ => option + "h"
|
||||
};
|
||||
|
||||
if (ImGui.RadioButton(label, isSelected))
|
||||
{
|
||||
_tempSyncshellDurationHours = option;
|
||||
}
|
||||
|
||||
// Start a new line after every 3 buttons
|
||||
if ((i + 1) % 3 == 0)
|
||||
{
|
||||
ImGui.NewLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.SameLine();
|
||||
}
|
||||
}
|
||||
|
||||
var expiresLocal = DateTime.Now.AddHours(_tempSyncshellDurationHours);
|
||||
UiSharedService.TextWrapped($"Expiration le {expiresLocal:g} (heure locale).");
|
||||
}
|
||||
|
||||
UiSharedService.TextWrapped("Appuyez sur le bouton ci-dessous pour créer une nouvelle Syncshell.");
|
||||
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
|
||||
if (ImGui.Button("Create Syncshell"))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_createIsTemporary)
|
||||
{
|
||||
var expiresAtUtc = DateTime.UtcNow.AddHours(_tempSyncshellDurationHours);
|
||||
_lastCreatedGroup = ApiController.GroupCreateTemporary(expiresAtUtc).Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
var aliasInput = string.IsNullOrWhiteSpace(_newSyncShellAlias) ? null : _newSyncShellAlias.Trim();
|
||||
_lastCreatedGroup = ApiController.GroupCreate(aliasInput).Result;
|
||||
@@ -168,10 +249,19 @@ internal sealed class GroupPanel
|
||||
_newSyncShellAlias = string.Empty;
|
||||
}
|
||||
}
|
||||
catch
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_lastCreatedGroup = null;
|
||||
_errorGroupCreate = true;
|
||||
if (ex.Message.Contains("name is already in use", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_errorGroupCreateMessage = "Le nom de la Syncshell est déjà utilisé.";
|
||||
}
|
||||
else
|
||||
{
|
||||
_errorGroupCreateMessage = ex.Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +269,7 @@ internal sealed class GroupPanel
|
||||
{
|
||||
ImGui.Separator();
|
||||
_errorGroupCreate = false;
|
||||
_errorGroupCreateMessage = string.Empty;
|
||||
if (!string.IsNullOrWhiteSpace(_lastCreatedGroup.Group.Alias))
|
||||
{
|
||||
ImGui.TextUnformatted("Syncshell Name: " + _lastCreatedGroup.Group.Alias);
|
||||
@@ -192,12 +283,19 @@ internal sealed class GroupPanel
|
||||
ImGui.SetClipboardText(_lastCreatedGroup.Password);
|
||||
}
|
||||
UiSharedService.TextWrapped("You can change the Syncshell password later at any time.");
|
||||
if (_lastCreatedGroup.IsTemporary && _lastCreatedGroup.ExpiresAt != null)
|
||||
{
|
||||
var expiresLocal = _lastCreatedGroup.ExpiresAt.Value.ToLocalTime();
|
||||
UiSharedService.TextWrapped($"Cette Syncshell expirera le {expiresLocal:g} (heure locale).");
|
||||
}
|
||||
}
|
||||
|
||||
if (_errorGroupCreate)
|
||||
{
|
||||
UiSharedService.ColorTextWrapped("You are already owner of the maximum amount of Syncshells (3) or joined the maximum amount of Syncshells (6). Relinquish ownership of your own Syncshells to someone else or leave existing Syncshells.",
|
||||
new Vector4(1, 0, 0, 1));
|
||||
var msg = string.IsNullOrWhiteSpace(_errorGroupCreateMessage)
|
||||
? "You are already owner of the maximum amount of Syncshells (3) or joined the maximum amount of Syncshells (6). Relinquish ownership of your own Syncshells to someone else or leave existing Syncshells."
|
||||
: _errorGroupCreateMessage;
|
||||
UiSharedService.ColorTextWrapped(msg, new Vector4(1, 0, 0, 1));
|
||||
}
|
||||
|
||||
UiSharedService.SetScaledWindowSize(350);
|
||||
@@ -257,11 +355,13 @@ internal sealed class GroupPanel
|
||||
if (!string.Equals(_editGroupEntry, groupDto.GID, StringComparison.Ordinal))
|
||||
{
|
||||
var shellConfig = _serverConfigurationManager.GetShellConfigForGid(groupDto.GID);
|
||||
if (!_mareConfig.Current.DisableSyncshellChat && shellConfig.Enabled)
|
||||
{
|
||||
ImGui.TextUnformatted($"[{shellNumber}]");
|
||||
UiSharedService.AttachToolTip("Chat command prefix: /ss" + shellNumber);
|
||||
}
|
||||
var totalMembers = pairsInGroup.Count + 1;
|
||||
var connectedMembers = pairsInGroup.Count(p => p.IsOnline) + 1;
|
||||
var maxCapacity = ApiController.ServerInfo.MaxGroupUserCount;
|
||||
ImGui.TextUnformatted($"{connectedMembers}/{totalMembers}");
|
||||
UiSharedService.AttachToolTip("Membres connectés / membres totaux" + Environment.NewLine +
|
||||
$"Capacité maximale : {maxCapacity}" + Environment.NewLine +
|
||||
"Syncshell ID: " + groupDto.Group.GID);
|
||||
if (textIsGid) ImGui.PushFont(UiBuilder.MonoFont);
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(groupName);
|
||||
@@ -269,6 +369,20 @@ internal sealed class GroupPanel
|
||||
UiSharedService.AttachToolTip("Left click to switch between GID display and comment" + Environment.NewLine +
|
||||
"Right click to change comment for " + groupName + Environment.NewLine + Environment.NewLine
|
||||
+ "Users: " + (pairsInGroup.Count + 1) + ", Owner: " + groupDto.OwnerAliasOrUID);
|
||||
if (groupDto.IsTemporary)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
UiSharedService.ColorText("(Temp)", ImGuiColors.DalamudOrange);
|
||||
if (groupDto.ExpiresAt != null)
|
||||
{
|
||||
var tempExpireLocal = groupDto.ExpiresAt.Value.ToLocalTime();
|
||||
UiSharedService.AttachToolTip($"Expire le {tempExpireLocal:g}");
|
||||
}
|
||||
else
|
||||
{
|
||||
UiSharedService.AttachToolTip("Syncshell temporaire");
|
||||
}
|
||||
}
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
||||
{
|
||||
var prevState = textIsGid;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Interface;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.MareConfiguration.Configurations;
|
||||
using MareSynchronos.PlayerData.Pairs;
|
||||
@@ -15,6 +16,7 @@ namespace MareSynchronos.UI;
|
||||
|
||||
public sealed class DtrEntry : IDisposable, IHostedService
|
||||
{
|
||||
public const string DefaultGlyph = "\u25CB";
|
||||
private enum DtrStyle
|
||||
{
|
||||
Default,
|
||||
@@ -196,7 +198,8 @@ public sealed class DtrEntry : IDisposable, IHostedService
|
||||
{
|
||||
var style = (DtrStyle)styleNum;
|
||||
|
||||
return style switch {
|
||||
return style switch
|
||||
{
|
||||
DtrStyle.Style1 => $"\xE039 {text}",
|
||||
DtrStyle.Style2 => $"\xE0BC {text}",
|
||||
DtrStyle.Style3 => $"\xE0BD {text}",
|
||||
@@ -206,7 +209,7 @@ public sealed class DtrEntry : IDisposable, IHostedService
|
||||
DtrStyle.Style7 => $"\xE05D {text}",
|
||||
DtrStyle.Style8 => $"\xE03C{text}",
|
||||
DtrStyle.Style9 => $"\xE040 {text} \xE041",
|
||||
_ => $"\uE044 {text}"
|
||||
_ => DefaultGlyph + " " + text
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
private readonly AccountRegistrationService _registerService;
|
||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||
private readonly UiSharedService _uiShared;
|
||||
private static readonly string DtrDefaultPreviewText = DtrEntry.DefaultGlyph + " 123";
|
||||
private bool _deleteAccountPopupModalShown = false;
|
||||
private string _lastTab = string.Empty;
|
||||
private bool? _notesSuccessfullyApplied = null;
|
||||
@@ -1049,13 +1050,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
_configService.Save();
|
||||
}
|
||||
|
||||
ImGui.SetNextItemWidth(250 * ImGuiHelpers.GlobalScale);
|
||||
_uiShared.DrawCombo("Server Info Bar style", Enumerable.Range(0, DtrEntry.NumStyles), (i) => DtrEntry.RenderDtrStyle(i, "123"),
|
||||
(i) =>
|
||||
{
|
||||
_configService.Current.DtrStyle = i;
|
||||
_configService.Save();
|
||||
}, _configService.Current.DtrStyle);
|
||||
DrawDtrStyleCombo();
|
||||
|
||||
if (ImGui.Checkbox("Color-code the Server Info Bar entry according to status", ref useColorsInDtr))
|
||||
{
|
||||
@@ -1941,12 +1936,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
ImGui.EndDisabled();
|
||||
}
|
||||
|
||||
if (ImGui.BeginTabItem("Chat"))
|
||||
{
|
||||
DrawChatConfig();
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui.BeginTabItem("Advanced"))
|
||||
{
|
||||
DrawAdvanced();
|
||||
@@ -1957,6 +1946,38 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawDtrStyleCombo()
|
||||
{
|
||||
var styleIndex = _configService.Current.DtrStyle;
|
||||
string previewText = styleIndex == 0 ? DtrDefaultPreviewText : DtrEntry.RenderDtrStyle(styleIndex, "123");
|
||||
|
||||
ImGui.SetNextItemWidth(250 * ImGuiHelpers.GlobalScale);
|
||||
bool comboOpen = ImGui.BeginCombo("Server Info Bar style", previewText);
|
||||
|
||||
if (comboOpen)
|
||||
{
|
||||
for (int i = 0; i < DtrEntry.NumStyles; i++)
|
||||
{
|
||||
string label = i == 0 ? DtrDefaultPreviewText : DtrEntry.RenderDtrStyle(i, "123");
|
||||
bool isSelected = i == styleIndex;
|
||||
if (ImGui.Selectable(label, isSelected))
|
||||
{
|
||||
_configService.Current.DtrStyle = i;
|
||||
_configService.Save();
|
||||
}
|
||||
|
||||
if (isSelected)
|
||||
{
|
||||
ImGui.SetItemDefaultFocus();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void UiSharedService_GposeEnd()
|
||||
{
|
||||
IsOpen = _wasOpen;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"Author": "SirConstance",
|
||||
"Author": "Keda",
|
||||
"Name": "UmbraSync",
|
||||
"Punchline": "Share your true self.",
|
||||
"Description": "This plugin will synchronize your Penumbra mods and current Glamourer state with other paired clients automatically.",
|
||||
"Punchline": "Parce que nous le valons bien.",
|
||||
"Description": "Ce plugin synchronisera automatiquement vos mods Penumbra et l'état actuel de Glamourer avec les autres clients appairés.",
|
||||
"InternalName": "UmbraSync",
|
||||
"ApplicableVersion": "any",
|
||||
"Tags": [
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using MareSynchronos.API.Data;
|
||||
using System;
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Dto.Group;
|
||||
using MareSynchronos.WebAPI.SignalR.Utils;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
@@ -49,12 +50,23 @@ public partial class ApiController
|
||||
await _mareHub!.SendAsync(nameof(GroupClear), group).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<GroupPasswordDto> GroupCreate(string? alias = null)
|
||||
public Task<GroupPasswordDto> GroupCreate()
|
||||
{
|
||||
return GroupCreate(null);
|
||||
}
|
||||
|
||||
public async Task<GroupPasswordDto> GroupCreate(string? alias)
|
||||
{
|
||||
CheckConnection();
|
||||
return await _mareHub!.InvokeAsync<GroupPasswordDto>(nameof(GroupCreate), string.IsNullOrWhiteSpace(alias) ? null : alias.Trim()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<GroupPasswordDto> GroupCreateTemporary(DateTime expiresAtUtc)
|
||||
{
|
||||
CheckConnection();
|
||||
return await _mareHub!.InvokeAsync<GroupPasswordDto>(nameof(GroupCreateTemporary), expiresAtUtc).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<List<string>> GroupCreateTempInvite(GroupDto group, int amount)
|
||||
{
|
||||
CheckConnection();
|
||||
|
||||
Reference in New Issue
Block a user