Compare commits
31 Commits
1.12.2.5-D
...
1.12.2.9-D
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eeafe1d3d7 | ||
|
|
cdf34c82bd | ||
| b5bdededae | |||
|
|
487156e4f9 | ||
| 90d8f691d2 | |||
|
|
764bb8bae2 | ||
|
|
6f2213171c | ||
|
|
da47d3be01 | ||
| 5d2c58bf3e | |||
|
|
6bb00c50d8 | ||
|
|
1a89c2caee | ||
|
|
6e129f6a1d | ||
|
|
1e97f27cb8 | ||
|
|
7aadbcec10 | ||
|
|
a32ac02c6d | ||
| f48698373b | |||
| ee20b6fa5f | |||
| f11741225b | |||
|
|
147baa4c1b | ||
|
|
4f5ef8ff4b | ||
|
|
fae6d31792 | ||
|
|
b4dd0ee0e1 | ||
| f5458c7f97 | |||
| 923f118a47 | |||
|
|
0cb71e5444 | ||
|
|
268fd471fe | ||
|
|
d517a21f5d | ||
| cac94374d9 | |||
|
|
b513e0555b | ||
|
|
de8c9cf035 | ||
|
|
7f8872cbe0 |
Submodule LightlessAPI updated: 0bc7abb274...bb92cd477d
@@ -25,6 +25,7 @@ changelog:
|
|||||||
- "More customizable notification options."
|
- "More customizable notification options."
|
||||||
- "Perfomance limiter shows as notifications."
|
- "Perfomance limiter shows as notifications."
|
||||||
- "All notifications can be configured or disabled in Settings → Notifications."
|
- "All notifications can be configured or disabled in Settings → Notifications."
|
||||||
|
- "Cleaning up notifications implementation"
|
||||||
- number: "Bugfixes"
|
- number: "Bugfixes"
|
||||||
icon: ""
|
icon: ""
|
||||||
items:
|
items:
|
||||||
|
|||||||
@@ -51,12 +51,12 @@ credits:
|
|||||||
role: "Height offset integration"
|
role: "Height offset integration"
|
||||||
- name: "Honorific Team"
|
- name: "Honorific Team"
|
||||||
role: "Title system integration"
|
role: "Title system integration"
|
||||||
- name: "Moodles Team"
|
- name: "Glyceri"
|
||||||
role: "Status effect integration"
|
role: "Moodles - Status effect integration"
|
||||||
- name: "PetNicknames Team"
|
- name: "Glyceri"
|
||||||
role: "Pet naming integration"
|
role: "PetNicknames - Pet naming integration"
|
||||||
- name: "Brio Team"
|
- name: "Minmoose"
|
||||||
role: "GPose enhancement integration"
|
role: "Brio - GPose enhancement integration"
|
||||||
|
|
||||||
- category: "Special Thanks"
|
- category: "Special Thanks"
|
||||||
items:
|
items:
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Authors></Authors>
|
<Authors></Authors>
|
||||||
<Company></Company>
|
<Company></Company>
|
||||||
<Version>1.12.2.5</Version>
|
<Version>1.12.2.9</Version>
|
||||||
<Description></Description>
|
<Description></Description>
|
||||||
<Copyright></Copyright>
|
<Copyright></Copyright>
|
||||||
<PackageProjectUrl>https://github.com/Light-Public-Syncshells/LightlessClient</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/Light-Public-Syncshells/LightlessClient</PackageProjectUrl>
|
||||||
|
|||||||
@@ -255,9 +255,9 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddScoped<WindowMediatorSubscriberBase, BroadcastUI>((s) => new BroadcastUI(s.GetRequiredService<ILogger<BroadcastUI>>(), s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<PerformanceCollectorService>(), s.GetRequiredService<BroadcastService>(), s.GetRequiredService<LightlessConfigService>(), s.GetRequiredService<UiSharedService>(), s.GetRequiredService<ApiController>(), s.GetRequiredService<BroadcastScannerService>()));
|
collection.AddScoped<WindowMediatorSubscriberBase, BroadcastUI>((s) => new BroadcastUI(s.GetRequiredService<ILogger<BroadcastUI>>(), s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<PerformanceCollectorService>(), s.GetRequiredService<BroadcastService>(), s.GetRequiredService<LightlessConfigService>(), s.GetRequiredService<UiSharedService>(), s.GetRequiredService<ApiController>(), s.GetRequiredService<BroadcastScannerService>()));
|
||||||
collection.AddScoped<WindowMediatorSubscriberBase, SyncshellFinderUI>((s) => new SyncshellFinderUI(s.GetRequiredService<ILogger<SyncshellFinderUI>>(), s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<PerformanceCollectorService>(), s.GetRequiredService<BroadcastService>(), s.GetRequiredService<UiSharedService>(), s.GetRequiredService<ApiController>(), s.GetRequiredService<BroadcastScannerService>(), s.GetRequiredService<PairManager>(), s.GetRequiredService<DalamudUtilService>()));
|
collection.AddScoped<WindowMediatorSubscriberBase, SyncshellFinderUI>((s) => new SyncshellFinderUI(s.GetRequiredService<ILogger<SyncshellFinderUI>>(), s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<PerformanceCollectorService>(), s.GetRequiredService<BroadcastService>(), s.GetRequiredService<UiSharedService>(), s.GetRequiredService<ApiController>(), s.GetRequiredService<BroadcastScannerService>(), s.GetRequiredService<PairManager>(), s.GetRequiredService<DalamudUtilService>()));
|
||||||
collection.AddScoped<IPopupHandler, BanUserPopupHandler>();
|
collection.AddScoped<IPopupHandler, BanUserPopupHandler>();
|
||||||
collection.AddScoped<WindowMediatorSubscriberBase, LightlessNotificationUI>((s) =>
|
collection.AddScoped<WindowMediatorSubscriberBase, LightlessNotificationUi>((s) =>
|
||||||
new LightlessNotificationUI(
|
new LightlessNotificationUi(
|
||||||
s.GetRequiredService<ILogger<LightlessNotificationUI>>(),
|
s.GetRequiredService<ILogger<LightlessNotificationUi>>(),
|
||||||
s.GetRequiredService<LightlessMediator>(),
|
s.GetRequiredService<LightlessMediator>(),
|
||||||
s.GetRequiredService<PerformanceCollectorService>(),
|
s.GetRequiredService<PerformanceCollectorService>(),
|
||||||
s.GetRequiredService<LightlessConfigService>()));
|
s.GetRequiredService<LightlessConfigService>()));
|
||||||
@@ -269,8 +269,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
s.GetRequiredService<WindowSystem>(), s.GetServices<WindowMediatorSubscriberBase>(),
|
s.GetRequiredService<WindowSystem>(), s.GetServices<WindowMediatorSubscriberBase>(),
|
||||||
s.GetRequiredService<UiFactory>(),
|
s.GetRequiredService<UiFactory>(),
|
||||||
s.GetRequiredService<FileDialogManager>(),
|
s.GetRequiredService<FileDialogManager>(),
|
||||||
s.GetRequiredService<LightlessMediator>(),
|
s.GetRequiredService<LightlessMediator>()));
|
||||||
s.GetRequiredService<NotificationService>()));
|
|
||||||
collection.AddScoped((s) => new CommandManagerService(commandManager, s.GetRequiredService<PerformanceCollectorService>(),
|
collection.AddScoped((s) => new CommandManagerService(commandManager, s.GetRequiredService<PerformanceCollectorService>(),
|
||||||
s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<CacheMonitor>(), s.GetRequiredService<ApiController>(),
|
s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<CacheMonitor>(), s.GetRequiredService<ApiController>(),
|
||||||
s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<LightlessConfigService>()));
|
s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<LightlessConfigService>()));
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ using LightlessSync.UI;
|
|||||||
using LightlessSync.Utils;
|
using LightlessSync.Utils;
|
||||||
using Lumina.Data.Files;
|
using Lumina.Data.Files;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
namespace LightlessSync.Services;
|
namespace LightlessSync.Services;
|
||||||
|
|
||||||
public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
|
public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
|
||||||
@@ -16,6 +20,7 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
|
|||||||
private CancellationTokenSource? _analysisCts;
|
private CancellationTokenSource? _analysisCts;
|
||||||
private CancellationTokenSource _baseAnalysisCts = new();
|
private CancellationTokenSource _baseAnalysisCts = new();
|
||||||
private string _lastDataHash = string.Empty;
|
private string _lastDataHash = string.Empty;
|
||||||
|
private CharacterAnalysisSummary _latestSummary = CharacterAnalysisSummary.Empty;
|
||||||
|
|
||||||
public CharacterAnalyzer(ILogger<CharacterAnalyzer> logger, LightlessMediator mediator, FileCacheManager fileCacheManager, XivDataAnalyzer modelAnalyzer)
|
public CharacterAnalyzer(ILogger<CharacterAnalyzer> logger, LightlessMediator mediator, FileCacheManager fileCacheManager, XivDataAnalyzer modelAnalyzer)
|
||||||
: base(logger, mediator)
|
: base(logger, mediator)
|
||||||
@@ -34,6 +39,7 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
|
|||||||
public bool IsAnalysisRunning => _analysisCts != null;
|
public bool IsAnalysisRunning => _analysisCts != null;
|
||||||
public int TotalFiles { get; internal set; }
|
public int TotalFiles { get; internal set; }
|
||||||
internal Dictionary<ObjectKind, Dictionary<string, FileDataEntry>> LastAnalysis { get; } = [];
|
internal Dictionary<ObjectKind, Dictionary<string, FileDataEntry>> LastAnalysis { get; } = [];
|
||||||
|
public CharacterAnalysisSummary LatestSummary => _latestSummary;
|
||||||
|
|
||||||
public void CancelAnalyze()
|
public void CancelAnalyze()
|
||||||
{
|
{
|
||||||
@@ -80,6 +86,8 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecalculateSummary();
|
||||||
|
|
||||||
Mediator.Publish(new CharacterDataAnalyzedMessage());
|
Mediator.Publish(new CharacterDataAnalyzedMessage());
|
||||||
|
|
||||||
_analysisCts.CancelDispose();
|
_analysisCts.CancelDispose();
|
||||||
@@ -137,11 +145,39 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
|
|||||||
LastAnalysis[obj.Key] = data;
|
LastAnalysis[obj.Key] = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecalculateSummary();
|
||||||
|
|
||||||
Mediator.Publish(new CharacterDataAnalyzedMessage());
|
Mediator.Publish(new CharacterDataAnalyzedMessage());
|
||||||
|
|
||||||
_lastDataHash = charaData.DataHash.Value;
|
_lastDataHash = charaData.DataHash.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RecalculateSummary()
|
||||||
|
{
|
||||||
|
var builder = ImmutableDictionary.CreateBuilder<ObjectKind, CharacterAnalysisObjectSummary>();
|
||||||
|
|
||||||
|
foreach (var (objectKind, entries) in LastAnalysis)
|
||||||
|
{
|
||||||
|
long totalTriangles = 0;
|
||||||
|
long texOriginalBytes = 0;
|
||||||
|
long texCompressedBytes = 0;
|
||||||
|
|
||||||
|
foreach (var entry in entries.Values)
|
||||||
|
{
|
||||||
|
totalTriangles += entry.Triangles;
|
||||||
|
if (string.Equals(entry.FileType, "tex", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
texOriginalBytes += entry.OriginalSize;
|
||||||
|
texCompressedBytes += entry.CompressedSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder[objectKind] = new CharacterAnalysisObjectSummary(entries.Count, totalTriangles, texOriginalBytes, texCompressedBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
_latestSummary = new CharacterAnalysisSummary(builder.ToImmutable());
|
||||||
|
}
|
||||||
|
|
||||||
private void PrintAnalysis()
|
private void PrintAnalysis()
|
||||||
{
|
{
|
||||||
if (LastAnalysis.Count == 0) return;
|
if (LastAnalysis.Count == 0) return;
|
||||||
@@ -232,4 +268,24 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct CharacterAnalysisObjectSummary(int EntryCount, long TotalTriangles, long TexOriginalBytes, long TexCompressedBytes)
|
||||||
|
{
|
||||||
|
public bool HasEntries => EntryCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class CharacterAnalysisSummary
|
||||||
|
{
|
||||||
|
public static CharacterAnalysisSummary Empty { get; } =
|
||||||
|
new(ImmutableDictionary<ObjectKind, CharacterAnalysisObjectSummary>.Empty);
|
||||||
|
|
||||||
|
internal CharacterAnalysisSummary(IImmutableDictionary<ObjectKind, CharacterAnalysisObjectSummary> objects)
|
||||||
|
{
|
||||||
|
Objects = objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IImmutableDictionary<ObjectKind, CharacterAnalysisObjectSummary> Objects { get; }
|
||||||
|
|
||||||
|
public bool HasData => Objects.Any(kvp => kvp.Value.HasEntries);
|
||||||
}
|
}
|
||||||
@@ -108,7 +108,9 @@ public record OpenCharaDataHubWithFilterMessage(UserData UserData) : MessageBase
|
|||||||
public record EnableBroadcastMessage(string HashedCid, bool Enabled) : MessageBase;
|
public record EnableBroadcastMessage(string HashedCid, bool Enabled) : MessageBase;
|
||||||
public record BroadcastStatusChangedMessage(bool Enabled, TimeSpan? Ttl) : MessageBase;
|
public record BroadcastStatusChangedMessage(bool Enabled, TimeSpan? Ttl) : MessageBase;
|
||||||
public record SyncshellBroadcastsUpdatedMessage : MessageBase;
|
public record SyncshellBroadcastsUpdatedMessage : MessageBase;
|
||||||
|
public record PairRequestReceivedMessage(string HashedCid, string Message) : MessageBase;
|
||||||
public record PairRequestsUpdatedMessage : MessageBase;
|
public record PairRequestsUpdatedMessage : MessageBase;
|
||||||
|
public record PairDownloadStatusMessage(List<(string PlayerName, float Progress, string Status)> DownloadStatus, int QueueWaiting) : MessageBase;
|
||||||
public record VisibilityChange : MessageBase;
|
public record VisibilityChange : MessageBase;
|
||||||
#pragma warning restore S2094
|
#pragma warning restore S2094
|
||||||
#pragma warning restore MA0048 // File name must match type name
|
#pragma warning restore MA0048 // File name must match type name
|
||||||
@@ -45,7 +45,9 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
|||||||
public Task StartAsync(CancellationToken cancellationToken)
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
Mediator.Subscribe<NotificationMessage>(this, HandleNotificationMessage);
|
Mediator.Subscribe<NotificationMessage>(this, HandleNotificationMessage);
|
||||||
|
Mediator.Subscribe<PairRequestReceivedMessage>(this, HandlePairRequestReceived);
|
||||||
Mediator.Subscribe<PairRequestsUpdatedMessage>(this, HandlePairRequestsUpdated);
|
Mediator.Subscribe<PairRequestsUpdatedMessage>(this, HandlePairRequestsUpdated);
|
||||||
|
Mediator.Subscribe<PairDownloadStatusMessage>(this, HandlePairDownloadStatus);
|
||||||
Mediator.Subscribe<PerformanceNotificationMessage>(this, HandlePerformanceNotification);
|
Mediator.Subscribe<PerformanceNotificationMessage>(this, HandlePerformanceNotification);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
@@ -293,33 +295,8 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
|||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowPairDownloadNotification(List<(string playerName, float progress, string status)> downloadStatus,
|
|
||||||
int queueWaiting = 0)
|
|
||||||
{
|
|
||||||
var userDownloads = downloadStatus.Where(x => x.playerName != "Pair Queue").ToList();
|
|
||||||
var totalProgress = userDownloads.Count > 0 ? userDownloads.Average(x => x.progress) : 0f;
|
|
||||||
var message = BuildPairDownloadMessage(userDownloads, queueWaiting);
|
|
||||||
|
|
||||||
var notification = new LightlessNotification
|
private string BuildPairDownloadMessage(List<(string PlayerName, float Progress, string Status)> userDownloads,
|
||||||
{
|
|
||||||
Id = "pair_download_progress",
|
|
||||||
Title = "Downloading Pair Data",
|
|
||||||
Message = message,
|
|
||||||
Type = NotificationType.Download,
|
|
||||||
Duration = TimeSpan.FromSeconds(_configService.Current.DownloadNotificationDurationSeconds),
|
|
||||||
ShowProgress = true,
|
|
||||||
Progress = totalProgress
|
|
||||||
};
|
|
||||||
|
|
||||||
Mediator.Publish(new LightlessNotificationMessage(notification));
|
|
||||||
|
|
||||||
if (AreAllDownloadsCompleted(userDownloads))
|
|
||||||
{
|
|
||||||
DismissPairDownloadNotification();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string BuildPairDownloadMessage(List<(string playerName, float progress, string status)> userDownloads,
|
|
||||||
int queueWaiting)
|
int queueWaiting)
|
||||||
{
|
{
|
||||||
var messageParts = new List<string>();
|
var messageParts = new List<string>();
|
||||||
@@ -331,7 +308,7 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
|||||||
|
|
||||||
if (userDownloads.Count > 0)
|
if (userDownloads.Count > 0)
|
||||||
{
|
{
|
||||||
var completedCount = userDownloads.Count(x => x.progress >= 1.0f);
|
var completedCount = userDownloads.Count(x => x.Progress >= 1.0f);
|
||||||
messageParts.Add($"Progress: {completedCount}/{userDownloads.Count} completed");
|
messageParts.Add($"Progress: {completedCount}/{userDownloads.Count} completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,29 +321,29 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
|||||||
return string.Join("\n", messageParts);
|
return string.Join("\n", messageParts);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string BuildActiveDownloadLines(List<(string playerName, float progress, string status)> userDownloads)
|
private string BuildActiveDownloadLines(List<(string PlayerName, float Progress, string Status)> userDownloads)
|
||||||
{
|
{
|
||||||
var activeDownloads = userDownloads
|
var activeDownloads = userDownloads
|
||||||
.Where(x => x.progress < 1.0f)
|
.Where(x => x.Progress < 1.0f)
|
||||||
.Take(_configService.Current.MaxConcurrentPairApplications);
|
.Take(_configService.Current.MaxConcurrentPairApplications);
|
||||||
|
|
||||||
if (!activeDownloads.Any()) return string.Empty;
|
if (!activeDownloads.Any()) return string.Empty;
|
||||||
|
|
||||||
return string.Join("\n", activeDownloads.Select(x => $"• {x.playerName}: {FormatDownloadStatus(x)}"));
|
return string.Join("\n", activeDownloads.Select(x => $"• {x.PlayerName}: {FormatDownloadStatus(x)}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private string FormatDownloadStatus((string playerName, float progress, string status) download) =>
|
private string FormatDownloadStatus((string PlayerName, float Progress, string Status) download) =>
|
||||||
download.status switch
|
download.Status switch
|
||||||
{
|
{
|
||||||
"downloading" => $"{download.progress:P0}",
|
"downloading" => $"{download.Progress:P0}",
|
||||||
"decompressing" => "decompressing",
|
"decompressing" => "decompressing",
|
||||||
"queued" => "queued",
|
"queued" => "queued",
|
||||||
"waiting" => "waiting for slot",
|
"waiting" => "waiting for slot",
|
||||||
_ => download.status
|
_ => download.Status
|
||||||
};
|
};
|
||||||
|
|
||||||
private bool AreAllDownloadsCompleted(List<(string playerName, float progress, string status)> userDownloads) =>
|
private bool AreAllDownloadsCompleted(List<(string PlayerName, float Progress, string Status)> userDownloads) =>
|
||||||
userDownloads.Any() && userDownloads.All(x => x.progress >= 1.0f);
|
userDownloads.Any() && userDownloads.All(x => x.Progress >= 1.0f);
|
||||||
|
|
||||||
public void DismissPairDownloadNotification() =>
|
public void DismissPairDownloadNotification() =>
|
||||||
Mediator.Publish(new LightlessNotificationDismissMessage("pair_download_progress"));
|
Mediator.Publish(new LightlessNotificationDismissMessage("pair_download_progress"));
|
||||||
@@ -581,12 +558,25 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
|||||||
_chatGui.Print(se.BuiltString);
|
_chatGui.Print(se.BuiltString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandlePairRequestReceived(PairRequestReceivedMessage msg)
|
||||||
|
{
|
||||||
|
var request = _pairRequestService.RegisterIncomingRequest(msg.HashedCid, msg.Message);
|
||||||
|
var senderName = string.IsNullOrEmpty(request.DisplayName) ? "Unknown User" : request.DisplayName;
|
||||||
|
|
||||||
|
_shownPairRequestNotifications.Add(request.HashedCid);
|
||||||
|
ShowPairRequestNotification(
|
||||||
|
senderName,
|
||||||
|
request.HashedCid,
|
||||||
|
onAccept: () => _pairRequestService.AcceptPairRequest(request.HashedCid, senderName),
|
||||||
|
onDecline: () => _pairRequestService.DeclinePairRequest(request.HashedCid, senderName));
|
||||||
|
}
|
||||||
|
|
||||||
private void HandlePairRequestsUpdated(PairRequestsUpdatedMessage _)
|
private void HandlePairRequestsUpdated(PairRequestsUpdatedMessage _)
|
||||||
{
|
{
|
||||||
var activeRequests = _pairRequestService.GetActiveRequests();
|
var activeRequests = _pairRequestService.GetActiveRequests();
|
||||||
var activeRequestIds = activeRequests.Select(r => r.HashedCid).ToHashSet();
|
var activeRequestIds = activeRequests.Select(r => r.HashedCid).ToHashSet();
|
||||||
|
|
||||||
// Dismiss notifications for requests that are no longer active
|
// Dismiss notifications for requests that are no longer active (expired)
|
||||||
var notificationsToRemove = _shownPairRequestNotifications
|
var notificationsToRemove = _shownPairRequestNotifications
|
||||||
.Where(hashedCid => !activeRequestIds.Contains(hashedCid))
|
.Where(hashedCid => !activeRequestIds.Contains(hashedCid))
|
||||||
.ToList();
|
.ToList();
|
||||||
@@ -597,17 +587,30 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
|||||||
Mediator.Publish(new LightlessNotificationDismissMessage(notificationId));
|
Mediator.Publish(new LightlessNotificationDismissMessage(notificationId));
|
||||||
_shownPairRequestNotifications.Remove(hashedCid);
|
_shownPairRequestNotifications.Remove(hashedCid);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Show/update notifications for all active requests
|
private void HandlePairDownloadStatus(PairDownloadStatusMessage msg)
|
||||||
foreach (var request in activeRequests)
|
{
|
||||||
|
var userDownloads = msg.DownloadStatus.Where(x => x.PlayerName != "Pair Queue").ToList();
|
||||||
|
var totalProgress = userDownloads.Count > 0 ? userDownloads.Average(x => x.Progress) : 0f;
|
||||||
|
var message = BuildPairDownloadMessage(userDownloads, msg.QueueWaiting);
|
||||||
|
|
||||||
|
var notification = new LightlessNotification
|
||||||
{
|
{
|
||||||
_shownPairRequestNotifications.Add(request.HashedCid);
|
Id = "pair_download_progress",
|
||||||
ShowPairRequestNotification(
|
Title = "Downloading Pair Data",
|
||||||
request.DisplayName,
|
Message = message,
|
||||||
request.HashedCid,
|
Type = NotificationType.Download,
|
||||||
() => _pairRequestService.AcceptPairRequest(request.HashedCid, request.DisplayName),
|
Duration = TimeSpan.FromSeconds(_configService.Current.DownloadNotificationDurationSeconds),
|
||||||
() => _pairRequestService.DeclinePairRequest(request.HashedCid)
|
ShowProgress = true,
|
||||||
);
|
Progress = totalProgress
|
||||||
|
};
|
||||||
|
|
||||||
|
Mediator.Publish(new LightlessNotificationMessage(notification));
|
||||||
|
|
||||||
|
if (userDownloads.Count == 0 || AreAllDownloadsCompleted(userDownloads))
|
||||||
|
{
|
||||||
|
Mediator.Publish(new LightlessNotificationDismissMessage("pair_download_progress"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,12 @@ public sealed class PairRequestService : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
private static readonly TimeSpan Expiration = TimeSpan.FromMinutes(5);
|
private static readonly TimeSpan Expiration = TimeSpan.FromMinutes(5);
|
||||||
|
|
||||||
public PairRequestService(ILogger<PairRequestService> logger, LightlessMediator mediator, DalamudUtilService dalamudUtil, PairManager pairManager, Lazy<WebAPI.ApiController> apiController)
|
public PairRequestService(
|
||||||
|
ILogger<PairRequestService> logger,
|
||||||
|
LightlessMediator mediator,
|
||||||
|
DalamudUtilService dalamudUtil,
|
||||||
|
PairManager pairManager,
|
||||||
|
Lazy<WebAPI.ApiController> apiController)
|
||||||
: base(logger, mediator)
|
: base(logger, mediator)
|
||||||
{
|
{
|
||||||
_dalamudUtil = dalamudUtil;
|
_dalamudUtil = dalamudUtil;
|
||||||
@@ -215,9 +220,13 @@ public sealed class PairRequestService : DisposableMediatorSubscriberBase
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeclinePairRequest(string hashedCid)
|
public void DeclinePairRequest(string hashedCid, string displayName)
|
||||||
{
|
{
|
||||||
RemoveRequest(hashedCid);
|
RemoveRequest(hashedCid);
|
||||||
|
Mediator.Publish(new NotificationMessage("Pair request declined",
|
||||||
|
"Declined " + displayName + "'s pending pair request.",
|
||||||
|
NotificationType.Info,
|
||||||
|
TimeSpan.FromSeconds(3)));
|
||||||
Logger.LogDebug("Declined pair request from {HashedCid}", hashedCid);
|
Logger.LogDebug("Declined pair request from {HashedCid}", hashedCid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ public sealed class UiService : DisposableMediatorSubscriberBase
|
|||||||
LightlessConfigService lightlessConfigService, WindowSystem windowSystem,
|
LightlessConfigService lightlessConfigService, WindowSystem windowSystem,
|
||||||
IEnumerable<WindowMediatorSubscriberBase> windows,
|
IEnumerable<WindowMediatorSubscriberBase> windows,
|
||||||
UiFactory uiFactory, FileDialogManager fileDialogManager,
|
UiFactory uiFactory, FileDialogManager fileDialogManager,
|
||||||
LightlessMediator lightlessMediator,
|
LightlessMediator lightlessMediator) : base(logger, lightlessMediator)
|
||||||
NotificationService notificationService) : base(logger, lightlessMediator)
|
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_logger.LogTrace("Creating {type}", GetType().Name);
|
_logger.LogTrace("Creating {type}", GetType().Name);
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
private readonly BroadcastService _broadcastService;
|
private readonly BroadcastService _broadcastService;
|
||||||
|
|
||||||
private List<IDrawFolder> _drawFolders;
|
private List<IDrawFolder> _drawFolders;
|
||||||
private Dictionary<ObjectKind, Dictionary<string, CharacterAnalyzer.FileDataEntry>>? _cachedAnalysis;
|
|
||||||
private Pair? _lastAddedUser;
|
private Pair? _lastAddedUser;
|
||||||
private string _lastAddedUserComment = string.Empty;
|
private string _lastAddedUserComment = string.Empty;
|
||||||
private Vector2 _lastPosition = Vector2.One;
|
private Vector2 _lastPosition = Vector2.One;
|
||||||
@@ -382,15 +381,26 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
_uiSharedService.IconText(FontAwesomeIcon.Upload);
|
_uiSharedService.IconText(FontAwesomeIcon.Upload);
|
||||||
ImGui.SameLine(35 * ImGuiHelpers.GlobalScale);
|
ImGui.SameLine(35 * ImGuiHelpers.GlobalScale);
|
||||||
|
|
||||||
if (currentUploads.Any())
|
if (currentUploads.Count > 0)
|
||||||
{
|
{
|
||||||
var totalUploads = currentUploads.Count;
|
int totalUploads = currentUploads.Count;
|
||||||
|
int doneUploads = 0;
|
||||||
|
long totalUploaded = 0;
|
||||||
|
long totalToUpload = 0;
|
||||||
|
|
||||||
var doneUploads = currentUploads.Count(c => c.IsTransferred);
|
foreach (var upload in currentUploads)
|
||||||
var activeUploads = currentUploads.Count(c => !c.IsTransferred);
|
{
|
||||||
|
if (upload.IsTransferred)
|
||||||
|
{
|
||||||
|
doneUploads++;
|
||||||
|
}
|
||||||
|
|
||||||
|
totalUploaded += upload.Transferred;
|
||||||
|
totalToUpload += upload.Total;
|
||||||
|
}
|
||||||
|
|
||||||
|
int activeUploads = totalUploads - doneUploads;
|
||||||
var uploadSlotLimit = Math.Clamp(_configService.Current.ParallelUploads, 1, 8);
|
var uploadSlotLimit = Math.Clamp(_configService.Current.ParallelUploads, 1, 8);
|
||||||
var totalUploaded = currentUploads.Sum(c => c.Transferred);
|
|
||||||
var totalToUpload = currentUploads.Sum(c => c.Total);
|
|
||||||
|
|
||||||
ImGui.TextUnformatted($"{doneUploads}/{totalUploads} (slots {activeUploads}/{uploadSlotLimit})");
|
ImGui.TextUnformatted($"{doneUploads}/{totalUploads} (slots {activeUploads}/{uploadSlotLimit})");
|
||||||
var uploadText = $"({UiSharedService.ByteToString(totalUploaded)}/{UiSharedService.ByteToString(totalToUpload)})";
|
var uploadText = $"({UiSharedService.ByteToString(totalUploaded)}/{UiSharedService.ByteToString(totalToUpload)})";
|
||||||
@@ -405,17 +415,17 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.TextUnformatted("No uploads in progress");
|
ImGui.TextUnformatted("No uploads in progress");
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentDownloads = BuildCurrentDownloadSnapshot();
|
var downloadSummary = GetDownloadSummary();
|
||||||
ImGui.AlignTextToFramePadding();
|
ImGui.AlignTextToFramePadding();
|
||||||
_uiSharedService.IconText(FontAwesomeIcon.Download);
|
_uiSharedService.IconText(FontAwesomeIcon.Download);
|
||||||
ImGui.SameLine(35 * ImGuiHelpers.GlobalScale);
|
ImGui.SameLine(35 * ImGuiHelpers.GlobalScale);
|
||||||
|
|
||||||
if (currentDownloads.Any())
|
if (downloadSummary.HasDownloads)
|
||||||
{
|
{
|
||||||
var totalDownloads = currentDownloads.Sum(c => c.TotalFiles);
|
var totalDownloads = downloadSummary.TotalFiles;
|
||||||
var doneDownloads = currentDownloads.Sum(c => c.TransferredFiles);
|
var doneDownloads = downloadSummary.TransferredFiles;
|
||||||
var totalDownloaded = currentDownloads.Sum(c => c.TransferredBytes);
|
var totalDownloaded = downloadSummary.TransferredBytes;
|
||||||
var totalToDownload = currentDownloads.Sum(c => c.TotalBytes);
|
var totalToDownload = downloadSummary.TotalBytes;
|
||||||
|
|
||||||
ImGui.TextUnformatted($"{doneDownloads}/{totalDownloads}");
|
ImGui.TextUnformatted($"{doneDownloads}/{totalDownloads}");
|
||||||
var downloadText =
|
var downloadText =
|
||||||
@@ -433,27 +443,35 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private List<FileDownloadStatus> BuildCurrentDownloadSnapshot()
|
private DownloadSummary GetDownloadSummary()
|
||||||
{
|
{
|
||||||
List<FileDownloadStatus> snapshot = new();
|
long totalBytes = 0;
|
||||||
|
long transferredBytes = 0;
|
||||||
|
int totalFiles = 0;
|
||||||
|
int transferredFiles = 0;
|
||||||
|
|
||||||
foreach (var kvp in _currentDownloads.ToArray())
|
foreach (var kvp in _currentDownloads.ToArray())
|
||||||
{
|
{
|
||||||
var value = kvp.Value;
|
if (kvp.Value is not { Count: > 0 } statuses)
|
||||||
if (value == null || value.Count == 0)
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
snapshot.AddRange(value.Values.ToArray());
|
|
||||||
}
|
}
|
||||||
catch (System.ArgumentException)
|
|
||||||
|
foreach (var status in statuses.Values)
|
||||||
{
|
{
|
||||||
// skibidi
|
totalBytes += status.TotalBytes;
|
||||||
|
transferredBytes += status.TransferredBytes;
|
||||||
|
totalFiles += status.TotalFiles;
|
||||||
|
transferredFiles += status.TransferredFiles;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return snapshot;
|
return new DownloadSummary(totalFiles, transferredFiles, transferredBytes, totalBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly record struct DownloadSummary(int TotalFiles, int TransferredFiles, long TransferredBytes, long TotalBytes)
|
||||||
|
{
|
||||||
|
public bool HasDownloads => TotalFiles > 0 || TotalBytes > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawUIDHeader()
|
private void DrawUIDHeader()
|
||||||
@@ -480,7 +498,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Getting information of character and triangles threshold to show overlimit status in UID bar.
|
//Getting information of character and triangles threshold to show overlimit status in UID bar.
|
||||||
_cachedAnalysis = _characterAnalyzer.LastAnalysis.DeepClone();
|
var analysisSummary = _characterAnalyzer.LatestSummary;
|
||||||
|
|
||||||
Vector2 uidTextSize, iconSize;
|
Vector2 uidTextSize, iconSize;
|
||||||
using (_uiSharedService.UidFont.Push())
|
using (_uiSharedService.UidFont.Push())
|
||||||
@@ -509,6 +527,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
{
|
{
|
||||||
ImGui.BeginTooltip();
|
ImGui.BeginTooltip();
|
||||||
|
ImGui.PushTextWrapPos(ImGui.GetFontSize() * 32f);
|
||||||
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("PairBlue"));
|
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("PairBlue"));
|
||||||
ImGui.Text("Lightfinder");
|
ImGui.Text("Lightfinder");
|
||||||
@@ -556,6 +575,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.PopStyleColor();
|
ImGui.PopStyleColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui.PopTextWrapPos();
|
||||||
ImGui.EndTooltip();
|
ImGui.EndTooltip();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -574,7 +594,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
var seString = SeStringUtils.BuildFormattedPlayerName(uidText, vanityTextColor, vanityGlowColor);
|
var seString = SeStringUtils.BuildFormattedPlayerName(uidText, vanityTextColor, vanityGlowColor);
|
||||||
var cursorPos = ImGui.GetCursorScreenPos();
|
var cursorPos = ImGui.GetCursorScreenPos();
|
||||||
var fontPtr = ImGui.GetFont();
|
var fontPtr = ImGui.GetFont();
|
||||||
SeStringUtils.RenderSeStringWithHitbox(seString, cursorPos, fontPtr);
|
SeStringUtils.RenderSeStringWithHitbox(seString, cursorPos, fontPtr, "uid-header");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -591,56 +611,40 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
UiSharedService.AttachToolTip("Click to copy");
|
UiSharedService.AttachToolTip("Click to copy");
|
||||||
|
|
||||||
if (_cachedAnalysis != null && _apiController.ServerState is ServerState.Connected)
|
if (_apiController.ServerState is ServerState.Connected && analysisSummary.HasData)
|
||||||
{
|
{
|
||||||
var firstEntry = _cachedAnalysis.FirstOrDefault();
|
var objectSummary = analysisSummary.Objects.Values.FirstOrDefault(summary => summary.HasEntries);
|
||||||
var valueDict = firstEntry.Value;
|
if (objectSummary.HasEntries)
|
||||||
if (valueDict != null && valueDict.Count > 0)
|
|
||||||
{
|
{
|
||||||
var groupedfiles = valueDict
|
var actualVramUsage = objectSummary.TexOriginalBytes;
|
||||||
.Select(v => v.Value)
|
var actualTriCount = objectSummary.TotalTriangles;
|
||||||
.Where(v => v != null)
|
|
||||||
.GroupBy(f => f.FileType, StringComparer.Ordinal)
|
|
||||||
.OrderBy(k => k.Key, StringComparer.Ordinal)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
var actualTriCount = valueDict
|
var isOverVRAMUsage = _playerPerformanceConfig.Current.VRAMSizeWarningThresholdMiB * 1024 * 1024 < actualVramUsage;
|
||||||
.Select(v => v.Value)
|
var isOverTriHold = actualTriCount > (_playerPerformanceConfig.Current.TrisWarningThresholdThousands * 1000);
|
||||||
.Where(v => v != null)
|
|
||||||
.Sum(f => f.Triangles);
|
|
||||||
|
|
||||||
if (groupedfiles != null)
|
if ((isOverTriHold || isOverVRAMUsage) && _playerPerformanceConfig.Current.WarnOnExceedingThresholds)
|
||||||
{
|
{
|
||||||
//Checking of VRAM threshhold
|
ImGui.SameLine();
|
||||||
var texGroup = groupedfiles.SingleOrDefault(v => string.Equals(v.Key, "tex", StringComparison.Ordinal));
|
ImGui.SetCursorPosY(cursorY + 15f);
|
||||||
var actualVramUsage = texGroup != null ? texGroup.Sum(f => f.OriginalSize) : 0L;
|
_uiSharedService.IconText(FontAwesomeIcon.ExclamationTriangle, UIColors.Get("LightlessYellow"));
|
||||||
var isOverVRAMUsage = _playerPerformanceConfig.Current.VRAMSizeWarningThresholdMiB * 1024 * 1024 < actualVramUsage;
|
|
||||||
var isOverTriHold = actualTriCount > (_playerPerformanceConfig.Current.TrisWarningThresholdThousands * 1000);
|
|
||||||
|
|
||||||
if ((isOverTriHold || isOverVRAMUsage) && _playerPerformanceConfig.Current.WarnOnExceedingThresholds)
|
string warningMessage = "";
|
||||||
|
if (isOverTriHold)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
warningMessage += $"You exceed your own triangles threshold by " +
|
||||||
ImGui.SetCursorPosY(cursorY + 15f);
|
$"{actualTriCount - _playerPerformanceConfig.Current.TrisWarningThresholdThousands * 1000} triangles.";
|
||||||
_uiSharedService.IconText(FontAwesomeIcon.ExclamationTriangle, UIColors.Get("LightlessYellow"));
|
warningMessage += Environment.NewLine;
|
||||||
|
|
||||||
string warningMessage = "";
|
}
|
||||||
if (isOverTriHold)
|
if (isOverVRAMUsage)
|
||||||
{
|
{
|
||||||
warningMessage += $"You exceed your own triangles threshold by " +
|
warningMessage += $"You exceed your own VRAM threshold by " +
|
||||||
$"{actualTriCount - _playerPerformanceConfig.Current.TrisWarningThresholdThousands * 1000} triangles.";
|
$"{UiSharedService.ByteToString(actualVramUsage - (_playerPerformanceConfig.Current.VRAMSizeWarningThresholdMiB * 1024 * 1024))}.";
|
||||||
warningMessage += Environment.NewLine;
|
}
|
||||||
|
UiSharedService.AttachToolTip(warningMessage);
|
||||||
}
|
if (ImGui.IsItemClicked())
|
||||||
if (isOverVRAMUsage)
|
{
|
||||||
{
|
_lightlessMediator.Publish(new UiToggleMessage(typeof(DataAnalysisUi)));
|
||||||
warningMessage += $"You exceed your own VRAM threshold by " +
|
|
||||||
$"{UiSharedService.ByteToString(actualVramUsage - (_playerPerformanceConfig.Current.VRAMSizeWarningThresholdMiB * 1024 * 1024))}.";
|
|
||||||
}
|
|
||||||
UiSharedService.AttachToolTip(warningMessage);
|
|
||||||
if (ImGui.IsItemClicked())
|
|
||||||
{
|
|
||||||
_lightlessMediator.Publish(new UiToggleMessage(typeof(DataAnalysisUi)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -663,7 +667,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
var seString = SeStringUtils.BuildFormattedPlayerName(_apiController.UID, vanityTextColor, vanityGlowColor);
|
var seString = SeStringUtils.BuildFormattedPlayerName(_apiController.UID, vanityTextColor, vanityGlowColor);
|
||||||
var cursorPos = ImGui.GetCursorScreenPos();
|
var cursorPos = ImGui.GetCursorScreenPos();
|
||||||
var fontPtr = ImGui.GetFont();
|
var fontPtr = ImGui.GetFont();
|
||||||
SeStringUtils.RenderSeStringWithHitbox(seString, cursorPos, fontPtr);
|
SeStringUtils.RenderSeStringWithHitbox(seString, cursorPos, fontPtr, "uid-footer");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -921,4 +925,4 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
_wasOpen = IsOpen;
|
_wasOpen = IsOpen;
|
||||||
IsOpen = false;
|
IsOpen = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
using LightlessSync.API.Data.Enum;
|
||||||
using LightlessSync.API.Data.Extensions;
|
using LightlessSync.API.Data.Extensions;
|
||||||
using LightlessSync.API.Dto.Group;
|
using LightlessSync.API.Dto.Group;
|
||||||
using LightlessSync.API.Dto.User;
|
using LightlessSync.API.Dto.User;
|
||||||
@@ -13,6 +14,9 @@ using LightlessSync.Services.ServerConfiguration;
|
|||||||
using LightlessSync.UI.Handlers;
|
using LightlessSync.UI.Handlers;
|
||||||
using LightlessSync.Utils;
|
using LightlessSync.Utils;
|
||||||
using LightlessSync.WebAPI;
|
using LightlessSync.WebAPI;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace LightlessSync.UI.Components;
|
namespace LightlessSync.UI.Components;
|
||||||
|
|
||||||
@@ -32,6 +36,8 @@ public class DrawUserPair
|
|||||||
private readonly CharaDataManager _charaDataManager;
|
private readonly CharaDataManager _charaDataManager;
|
||||||
private float _menuWidth = -1;
|
private float _menuWidth = -1;
|
||||||
private bool _wasHovered = false;
|
private bool _wasHovered = false;
|
||||||
|
private TooltipSnapshot _tooltipSnapshot = TooltipSnapshot.Empty;
|
||||||
|
private string _cachedTooltip = string.Empty;
|
||||||
|
|
||||||
public DrawUserPair(string id, Pair entry, List<GroupFullInfoDto> syncedGroups,
|
public DrawUserPair(string id, Pair entry, List<GroupFullInfoDto> syncedGroups,
|
||||||
GroupFullInfoDto? currentGroup,
|
GroupFullInfoDto? currentGroup,
|
||||||
@@ -190,15 +196,12 @@ public class DrawUserPair
|
|||||||
|
|
||||||
private void DrawLeftSide()
|
private void DrawLeftSide()
|
||||||
{
|
{
|
||||||
string userPairText = string.Empty;
|
|
||||||
|
|
||||||
ImGui.AlignTextToFramePadding();
|
ImGui.AlignTextToFramePadding();
|
||||||
|
|
||||||
if (_pair.IsPaused)
|
if (_pair.IsPaused)
|
||||||
{
|
{
|
||||||
using var _ = ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("LightlessYellow"));
|
using var _ = ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("LightlessYellow"));
|
||||||
_uiSharedService.IconText(FontAwesomeIcon.PauseCircle);
|
_uiSharedService.IconText(FontAwesomeIcon.PauseCircle);
|
||||||
userPairText = _pair.UserData.AliasOrUID + " is paused";
|
|
||||||
}
|
}
|
||||||
else if (!_pair.IsOnline)
|
else if (!_pair.IsOnline)
|
||||||
{
|
{
|
||||||
@@ -207,12 +210,10 @@ public class DrawUserPair
|
|||||||
? FontAwesomeIcon.ArrowsLeftRight
|
? FontAwesomeIcon.ArrowsLeftRight
|
||||||
: (_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional
|
: (_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional
|
||||||
? FontAwesomeIcon.User : FontAwesomeIcon.Users));
|
? FontAwesomeIcon.User : FontAwesomeIcon.Users));
|
||||||
userPairText = _pair.UserData.AliasOrUID + " is offline";
|
|
||||||
}
|
}
|
||||||
else if (_pair.IsVisible)
|
else if (_pair.IsVisible)
|
||||||
{
|
{
|
||||||
_uiSharedService.IconText(FontAwesomeIcon.Eye, UIColors.Get("LightlessBlue"));
|
_uiSharedService.IconText(FontAwesomeIcon.Eye, UIColors.Get("LightlessBlue"));
|
||||||
userPairText = _pair.UserData.AliasOrUID + " is visible: " + _pair.PlayerName + Environment.NewLine + "Click to target this player";
|
|
||||||
if (ImGui.IsItemClicked())
|
if (ImGui.IsItemClicked())
|
||||||
{
|
{
|
||||||
_mediator.Publish(new TargetPairMessage(_pair));
|
_mediator.Publish(new TargetPairMessage(_pair));
|
||||||
@@ -223,46 +224,9 @@ public class DrawUserPair
|
|||||||
using var _ = ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("PairBlue"));
|
using var _ = ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("PairBlue"));
|
||||||
_uiSharedService.IconText(_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional
|
_uiSharedService.IconText(_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional
|
||||||
? FontAwesomeIcon.User : FontAwesomeIcon.Users);
|
? FontAwesomeIcon.User : FontAwesomeIcon.Users);
|
||||||
userPairText = _pair.UserData.AliasOrUID + " is online";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.OneSided)
|
UiSharedService.AttachToolTip(GetUserTooltip());
|
||||||
{
|
|
||||||
userPairText += UiSharedService.TooltipSeparator + "User has not added you back";
|
|
||||||
}
|
|
||||||
else if (_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional)
|
|
||||||
{
|
|
||||||
userPairText += UiSharedService.TooltipSeparator + "You are directly Paired";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_pair.LastAppliedDataBytes >= 0)
|
|
||||||
{
|
|
||||||
userPairText += UiSharedService.TooltipSeparator;
|
|
||||||
userPairText += ((!_pair.IsPaired) ? "(Last) " : string.Empty) + "Mods Info" + Environment.NewLine;
|
|
||||||
userPairText += "Files Size: " + UiSharedService.ByteToString(_pair.LastAppliedDataBytes, true);
|
|
||||||
if (_pair.LastAppliedApproximateVRAMBytes >= 0)
|
|
||||||
{
|
|
||||||
userPairText += Environment.NewLine + "Approx. VRAM Usage: " + UiSharedService.ByteToString(_pair.LastAppliedApproximateVRAMBytes, true);
|
|
||||||
}
|
|
||||||
if (_pair.LastAppliedDataTris >= 0)
|
|
||||||
{
|
|
||||||
userPairText += Environment.NewLine + "Approx. Triangle Count (excl. Vanilla): "
|
|
||||||
+ (_pair.LastAppliedDataTris > 1000 ? (_pair.LastAppliedDataTris / 1000d).ToString("0.0'k'") : _pair.LastAppliedDataTris);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_syncedGroups.Any())
|
|
||||||
{
|
|
||||||
userPairText += UiSharedService.TooltipSeparator + string.Join(Environment.NewLine,
|
|
||||||
_syncedGroups.Select(g =>
|
|
||||||
{
|
|
||||||
var groupNote = _serverConfigurationManager.GetNoteForGid(g.GID);
|
|
||||||
var groupString = string.IsNullOrEmpty(groupNote) ? g.GroupAliasOrGID : $"{groupNote} ({g.GroupAliasOrGID})";
|
|
||||||
return "Paired through " + groupString;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
UiSharedService.AttachToolTip(userPairText);
|
|
||||||
|
|
||||||
if (_performanceConfigService.Current.ShowPerformanceIndicator
|
if (_performanceConfigService.Current.ShowPerformanceIndicator
|
||||||
&& !_performanceConfigService.Current.UIDsToIgnore
|
&& !_performanceConfigService.Current.UIDsToIgnore
|
||||||
@@ -327,6 +291,143 @@ public class DrawUserPair
|
|||||||
_displayHandler.DrawPairText(_id, _pair, leftSide, () => rightSide - leftSide);
|
_displayHandler.DrawPairText(_id, _pair, leftSide, () => rightSide - leftSide);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetUserTooltip()
|
||||||
|
{
|
||||||
|
List<string>? groupDisplays = null;
|
||||||
|
if (_syncedGroups.Count > 0)
|
||||||
|
{
|
||||||
|
groupDisplays = new List<string>(_syncedGroups.Count);
|
||||||
|
foreach (var group in _syncedGroups)
|
||||||
|
{
|
||||||
|
var groupNote = _serverConfigurationManager.GetNoteForGid(group.GID);
|
||||||
|
groupDisplays.Add(string.IsNullOrEmpty(groupNote) ? group.GroupAliasOrGID : $"{groupNote} ({group.GroupAliasOrGID})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var snapshot = new TooltipSnapshot(
|
||||||
|
_pair.IsPaused,
|
||||||
|
_pair.IsOnline,
|
||||||
|
_pair.IsVisible,
|
||||||
|
_pair.IndividualPairStatus,
|
||||||
|
_pair.UserData.AliasOrUID,
|
||||||
|
_pair.PlayerName ?? string.Empty,
|
||||||
|
_pair.LastAppliedDataBytes,
|
||||||
|
_pair.LastAppliedApproximateVRAMBytes,
|
||||||
|
_pair.LastAppliedDataTris,
|
||||||
|
_pair.IsPaired,
|
||||||
|
groupDisplays is null ? ImmutableArray<string>.Empty : ImmutableArray.CreateRange(groupDisplays));
|
||||||
|
|
||||||
|
if (!_tooltipSnapshot.Equals(snapshot))
|
||||||
|
{
|
||||||
|
_cachedTooltip = BuildTooltip(snapshot);
|
||||||
|
_tooltipSnapshot = snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _cachedTooltip;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildTooltip(in TooltipSnapshot snapshot)
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder(256);
|
||||||
|
|
||||||
|
if (snapshot.IsPaused)
|
||||||
|
{
|
||||||
|
builder.Append(snapshot.AliasOrUid);
|
||||||
|
builder.Append(" is paused");
|
||||||
|
}
|
||||||
|
else if (!snapshot.IsOnline)
|
||||||
|
{
|
||||||
|
builder.Append(snapshot.AliasOrUid);
|
||||||
|
builder.Append(" is offline");
|
||||||
|
}
|
||||||
|
else if (snapshot.IsVisible)
|
||||||
|
{
|
||||||
|
builder.Append(snapshot.AliasOrUid);
|
||||||
|
builder.Append(" is visible: ");
|
||||||
|
builder.Append(snapshot.PlayerName);
|
||||||
|
builder.Append(Environment.NewLine);
|
||||||
|
builder.Append("Click to target this player");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.Append(snapshot.AliasOrUid);
|
||||||
|
builder.Append(" is online");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snapshot.PairStatus == IndividualPairStatus.OneSided)
|
||||||
|
{
|
||||||
|
builder.Append(UiSharedService.TooltipSeparator);
|
||||||
|
builder.Append("User has not added you back");
|
||||||
|
}
|
||||||
|
else if (snapshot.PairStatus == IndividualPairStatus.Bidirectional)
|
||||||
|
{
|
||||||
|
builder.Append(UiSharedService.TooltipSeparator);
|
||||||
|
builder.Append("You are directly Paired");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snapshot.LastAppliedDataBytes >= 0)
|
||||||
|
{
|
||||||
|
builder.Append(UiSharedService.TooltipSeparator);
|
||||||
|
if (!snapshot.IsPaired)
|
||||||
|
{
|
||||||
|
builder.Append("(Last) ");
|
||||||
|
}
|
||||||
|
builder.Append("Mods Info");
|
||||||
|
builder.Append(Environment.NewLine);
|
||||||
|
builder.Append("Files Size: ");
|
||||||
|
builder.Append(UiSharedService.ByteToString(snapshot.LastAppliedDataBytes, true));
|
||||||
|
|
||||||
|
if (snapshot.LastAppliedApproximateVRAMBytes >= 0)
|
||||||
|
{
|
||||||
|
builder.Append(Environment.NewLine);
|
||||||
|
builder.Append("Approx. VRAM Usage: ");
|
||||||
|
builder.Append(UiSharedService.ByteToString(snapshot.LastAppliedApproximateVRAMBytes, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snapshot.LastAppliedDataTris >= 0)
|
||||||
|
{
|
||||||
|
builder.Append(Environment.NewLine);
|
||||||
|
builder.Append("Approx. Triangle Count (excl. Vanilla): ");
|
||||||
|
builder.Append(snapshot.LastAppliedDataTris > 1000
|
||||||
|
? (snapshot.LastAppliedDataTris / 1000d).ToString("0.0'k'")
|
||||||
|
: snapshot.LastAppliedDataTris);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!snapshot.GroupDisplays.IsEmpty)
|
||||||
|
{
|
||||||
|
builder.Append(UiSharedService.TooltipSeparator);
|
||||||
|
for (int i = 0; i < snapshot.GroupDisplays.Length; i++)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
builder.Append(Environment.NewLine);
|
||||||
|
}
|
||||||
|
builder.Append("Paired through ");
|
||||||
|
builder.Append(snapshot.GroupDisplays[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly record struct TooltipSnapshot(
|
||||||
|
bool IsPaused,
|
||||||
|
bool IsOnline,
|
||||||
|
bool IsVisible,
|
||||||
|
IndividualPairStatus PairStatus,
|
||||||
|
string AliasOrUid,
|
||||||
|
string PlayerName,
|
||||||
|
long LastAppliedDataBytes,
|
||||||
|
long LastAppliedApproximateVRAMBytes,
|
||||||
|
long LastAppliedDataTris,
|
||||||
|
bool IsPaired,
|
||||||
|
ImmutableArray<string> GroupDisplays)
|
||||||
|
{
|
||||||
|
public static TooltipSnapshot Empty { get; } =
|
||||||
|
new(false, false, false, IndividualPairStatus.None, string.Empty, string.Empty, -1, -1, -1, false, ImmutableArray<string>.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawPairedClientMenu()
|
private void DrawPairedClientMenu()
|
||||||
{
|
{
|
||||||
DrawIndividualMenu();
|
DrawIndividualMenu();
|
||||||
|
|||||||
@@ -22,13 +22,12 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
private readonly UiSharedService _uiShared;
|
private readonly UiSharedService _uiShared;
|
||||||
private readonly PairProcessingLimiter _pairProcessingLimiter;
|
private readonly PairProcessingLimiter _pairProcessingLimiter;
|
||||||
private readonly ConcurrentDictionary<GameObjectHandler, bool> _uploadingPlayers = new();
|
private readonly ConcurrentDictionary<GameObjectHandler, bool> _uploadingPlayers = new();
|
||||||
private readonly NotificationService _notificationService;
|
|
||||||
private bool _notificationDismissed = true;
|
private bool _notificationDismissed = true;
|
||||||
private int _lastDownloadStateHash = 0;
|
private int _lastDownloadStateHash = 0;
|
||||||
|
|
||||||
public DownloadUi(ILogger<DownloadUi> logger, DalamudUtilService dalamudUtilService, LightlessConfigService configService,
|
public DownloadUi(ILogger<DownloadUi> logger, DalamudUtilService dalamudUtilService, LightlessConfigService configService,
|
||||||
PairProcessingLimiter pairProcessingLimiter, FileUploadManager fileTransferManager, LightlessMediator mediator, UiSharedService uiShared,
|
PairProcessingLimiter pairProcessingLimiter, FileUploadManager fileTransferManager, LightlessMediator mediator, UiSharedService uiShared,
|
||||||
PerformanceCollectorService performanceCollectorService, NotificationService notificationService)
|
PerformanceCollectorService performanceCollectorService)
|
||||||
: base(logger, mediator, "Lightless Sync Downloads", performanceCollectorService)
|
: base(logger, mediator, "Lightless Sync Downloads", performanceCollectorService)
|
||||||
{
|
{
|
||||||
_dalamudUtilService = dalamudUtilService;
|
_dalamudUtilService = dalamudUtilService;
|
||||||
@@ -36,7 +35,6 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
_pairProcessingLimiter = pairProcessingLimiter;
|
_pairProcessingLimiter = pairProcessingLimiter;
|
||||||
_fileTransferManager = fileTransferManager;
|
_fileTransferManager = fileTransferManager;
|
||||||
_uiShared = uiShared;
|
_uiShared = uiShared;
|
||||||
_notificationService = notificationService;
|
|
||||||
|
|
||||||
SizeConstraints = new WindowSizeConstraints()
|
SizeConstraints = new WindowSizeConstraints()
|
||||||
{
|
{
|
||||||
@@ -359,7 +357,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
_lastDownloadStateHash = currentHash;
|
_lastDownloadStateHash = currentHash;
|
||||||
if (downloadStatus.Count > 0 || queueWaiting > 0)
|
if (downloadStatus.Count > 0 || queueWaiting > 0)
|
||||||
{
|
{
|
||||||
_notificationService.ShowPairDownloadNotification(downloadStatus, queueWaiting);
|
Mediator.Publish(new PairDownloadStatusMessage(downloadStatus, queueWaiting));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ public class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
_showFileDialogError = false;
|
_showFileDialogError = false;
|
||||||
await _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, Convert.ToBase64String(fileContent), Description: null, Tags: null))
|
await _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, Convert.ToBase64String(fileContent), BannerPictureBase64: null, Description: null, Tags: null))
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -215,7 +215,7 @@ public class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear uploaded profile picture"))
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear uploaded profile picture"))
|
||||||
{
|
{
|
||||||
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, "", Description: null, Tags: null));
|
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, "", Description: null, BannerPictureBase64: null, Tags: null));
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Clear your currently uploaded profile picture");
|
UiSharedService.AttachToolTip("Clear your currently uploaded profile picture");
|
||||||
if (_showFileDialogError)
|
if (_showFileDialogError)
|
||||||
@@ -225,7 +225,7 @@ public class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
var isNsfw = profile.IsNSFW;
|
var isNsfw = profile.IsNSFW;
|
||||||
if (ImGui.Checkbox("Profile is NSFW", ref isNsfw))
|
if (ImGui.Checkbox("Profile is NSFW", ref isNsfw))
|
||||||
{
|
{
|
||||||
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, isNsfw, ProfilePictureBase64: null, Description: null, Tags: null));
|
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, isNsfw, ProfilePictureBase64: null, Description: null, BannerPictureBase64: null, Tags: null));
|
||||||
}
|
}
|
||||||
_uiSharedService.DrawHelpText("If your profile description or image can be considered NSFW, toggle this to ON");
|
_uiSharedService.DrawHelpText("If your profile description or image can be considered NSFW, toggle this to ON");
|
||||||
var widthTextBox = 400;
|
var widthTextBox = 400;
|
||||||
@@ -264,13 +264,13 @@ public class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Save, "Save Description"))
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Save, "Save Description"))
|
||||||
{
|
{
|
||||||
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, ProfilePictureBase64: null, _descriptionText, Tags: null));
|
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, ProfilePictureBase64: null, BannerPictureBase64: null, _descriptionText, Tags: null));
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Sets your profile description text");
|
UiSharedService.AttachToolTip("Sets your profile description text");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear Description"))
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear Description"))
|
||||||
{
|
{
|
||||||
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, ProfilePictureBase64: null, "", Tags: null));
|
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, ProfilePictureBase64: null, BannerPictureBase64: null, "", Tags: null));
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Clears your profile description text");
|
UiSharedService.AttachToolTip("Clears your profile description text");
|
||||||
|
|
||||||
@@ -281,7 +281,7 @@ public class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
_uiSharedService.MediumText("Supporter Vanity Settings", UIColors.Get("LightlessPurple"));
|
_uiSharedService.MediumText("Supporter Vanity Settings", UIColors.Get("LightlessPurple"));
|
||||||
ImGui.Dummy(new Vector2(4));
|
ImGui.Dummy(new Vector2(4));
|
||||||
_uiSharedService.DrawNoteLine("# ", UIColors.Get("LightlessPurple"), "Must be a supporter through Patreon/Ko-fi to access these settings.");
|
_uiSharedService.DrawNoteLine("# ", UIColors.Get("LightlessPurple"), "Must be a supporter through Patreon/Ko-fi to access these settings. If you have the vanity role, you must interact with the Discord bot first.");
|
||||||
|
|
||||||
var hasVanity = _apiController.HasVanity;
|
var hasVanity = _apiController.HasVanity;
|
||||||
|
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ public class IdDisplayHandler
|
|||||||
Vector2 textSize;
|
Vector2 textSize;
|
||||||
using (ImRaii.PushFont(font, textIsUid))
|
using (ImRaii.PushFont(font, textIsUid))
|
||||||
{
|
{
|
||||||
SeStringUtils.RenderSeStringWithHitbox(seString, rowStart, font);
|
SeStringUtils.RenderSeStringWithHitbox(seString, rowStart, font, pair.UserData.UID);
|
||||||
itemMin = ImGui.GetItemRectMin();
|
itemMin = ImGui.GetItemRectMin();
|
||||||
itemMax = ImGui.GetItemRectMax();
|
itemMax = ImGui.GetItemRectMax();
|
||||||
//textSize = itemMax - itemMin;
|
//textSize = itemMax - itemMin;
|
||||||
|
|||||||
@@ -15,17 +15,17 @@ using Dalamud.Bindings.ImGui;
|
|||||||
|
|
||||||
namespace LightlessSync.UI;
|
namespace LightlessSync.UI;
|
||||||
|
|
||||||
public class LightlessNotificationUI : WindowMediatorSubscriberBase
|
public class LightlessNotificationUi : WindowMediatorSubscriberBase
|
||||||
{
|
{
|
||||||
private const float NotificationMinHeight = 60f;
|
private const float _notificationMinHeight = 60f;
|
||||||
private const float NotificationMaxHeight = 250f;
|
private const float _notificationMaxHeight = 250f;
|
||||||
private const float WindowPaddingOffset = 6f;
|
private const float _windowPaddingOffset = 6f;
|
||||||
private const float SlideAnimationDistance = 100f;
|
private const float _slideAnimationDistance = 100f;
|
||||||
private const float OutAnimationSpeedMultiplier = 0.7f;
|
private const float _outAnimationSpeedMultiplier = 0.7f;
|
||||||
private const float ContentPaddingX = 10f;
|
private const float _contentPaddingX = 10f;
|
||||||
private const float ContentPaddingY = 6f;
|
private const float _contentPaddingY = 6f;
|
||||||
private const float TitleMessageSpacing = 4f;
|
private const float _titleMessageSpacing = 4f;
|
||||||
private const float ActionButtonSpacing = 8f;
|
private const float _actionButtonSpacing = 8f;
|
||||||
|
|
||||||
private readonly List<LightlessNotification> _notifications = new();
|
private readonly List<LightlessNotification> _notifications = new();
|
||||||
private readonly object _notificationLock = new();
|
private readonly object _notificationLock = new();
|
||||||
@@ -33,7 +33,7 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
|
|||||||
private readonly Dictionary<string, float> _notificationYOffsets = new();
|
private readonly Dictionary<string, float> _notificationYOffsets = new();
|
||||||
private readonly Dictionary<string, float> _notificationTargetYOffsets = new();
|
private readonly Dictionary<string, float> _notificationTargetYOffsets = new();
|
||||||
|
|
||||||
public LightlessNotificationUI(ILogger<LightlessNotificationUI> logger, LightlessMediator mediator, PerformanceCollectorService performanceCollector, LightlessConfigService configService)
|
public LightlessNotificationUi(ILogger<LightlessNotificationUi> logger, LightlessMediator mediator, PerformanceCollectorService performanceCollector, LightlessConfigService configService)
|
||||||
: base(logger, mediator, "Lightless Notifications##LightlessNotifications", performanceCollector)
|
: base(logger, mediator, "Lightless Notifications##LightlessNotifications", performanceCollector)
|
||||||
{
|
{
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
@@ -155,8 +155,8 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
|
|||||||
var width = _configService.Current.NotificationWidth;
|
var width = _configService.Current.NotificationWidth;
|
||||||
|
|
||||||
float posX = corner == NotificationCorner.Left
|
float posX = corner == NotificationCorner.Left
|
||||||
? viewport.WorkPos.X + offsetX - WindowPaddingOffset
|
? viewport.WorkPos.X + offsetX - _windowPaddingOffset
|
||||||
: viewport.WorkPos.X + viewport.WorkSize.X - width - offsetX - WindowPaddingOffset;
|
: viewport.WorkPos.X + viewport.WorkSize.X - width - offsetX - _windowPaddingOffset;
|
||||||
|
|
||||||
return new Vector2(posX, viewport.WorkPos.Y);
|
return new Vector2(posX, viewport.WorkPos.Y);
|
||||||
}
|
}
|
||||||
@@ -274,7 +274,7 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
|
|||||||
else if (notification.IsAnimatingOut && notification.AnimationProgress > 0f)
|
else if (notification.IsAnimatingOut && notification.AnimationProgress > 0f)
|
||||||
{
|
{
|
||||||
notification.AnimationProgress = Math.Max(0f,
|
notification.AnimationProgress = Math.Max(0f,
|
||||||
notification.AnimationProgress - deltaTime * _configService.Current.NotificationAnimationSpeed * OutAnimationSpeedMultiplier);
|
notification.AnimationProgress - deltaTime * _configService.Current.NotificationAnimationSpeed * _outAnimationSpeedMultiplier);
|
||||||
}
|
}
|
||||||
else if (!notification.IsAnimatingOut && !notification.IsDismissed)
|
else if (!notification.IsAnimatingOut && !notification.IsDismissed)
|
||||||
{
|
{
|
||||||
@@ -289,7 +289,7 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
private Vector2 CalculateSlideOffset(float alpha)
|
private Vector2 CalculateSlideOffset(float alpha)
|
||||||
{
|
{
|
||||||
var distance = (1f - alpha) * SlideAnimationDistance;
|
var distance = (1f - alpha) * _slideAnimationDistance;
|
||||||
var corner = _configService.Current.NotificationCorner;
|
var corner = _configService.Current.NotificationCorner;
|
||||||
return corner == NotificationCorner.Left ? new Vector2(-distance, 0) : new Vector2(distance, 0);
|
return corner == NotificationCorner.Left ? new Vector2(-distance, 0) : new Vector2(distance, 0);
|
||||||
}
|
}
|
||||||
@@ -466,7 +466,7 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
private void DrawNotificationText(LightlessNotification notification, float alpha)
|
private void DrawNotificationText(LightlessNotification notification, float alpha)
|
||||||
{
|
{
|
||||||
var contentPos = new Vector2(ContentPaddingX, ContentPaddingY);
|
var contentPos = new Vector2(_contentPaddingX, _contentPaddingY);
|
||||||
var windowSize = ImGui.GetWindowSize();
|
var windowSize = ImGui.GetWindowSize();
|
||||||
var contentWidth = CalculateContentWidth(windowSize.X);
|
var contentWidth = CalculateContentWidth(windowSize.X);
|
||||||
|
|
||||||
@@ -483,7 +483,7 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
private float CalculateContentWidth(float windowWidth) =>
|
private float CalculateContentWidth(float windowWidth) =>
|
||||||
windowWidth - (ContentPaddingX * 2);
|
windowWidth - (_contentPaddingX * 2);
|
||||||
|
|
||||||
private bool HasActions(LightlessNotification notification) =>
|
private bool HasActions(LightlessNotification notification) =>
|
||||||
notification.Actions.Count > 0;
|
notification.Actions.Count > 0;
|
||||||
@@ -491,9 +491,9 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
|
|||||||
private void PositionActionsAtBottom(float windowHeight)
|
private void PositionActionsAtBottom(float windowHeight)
|
||||||
{
|
{
|
||||||
var actionHeight = ImGui.GetFrameHeight();
|
var actionHeight = ImGui.GetFrameHeight();
|
||||||
var bottomY = windowHeight - ContentPaddingY - actionHeight;
|
var bottomY = windowHeight - _contentPaddingY - actionHeight;
|
||||||
ImGui.SetCursorPosY(bottomY);
|
ImGui.SetCursorPosY(bottomY);
|
||||||
ImGui.SetCursorPosX(ContentPaddingX);
|
ImGui.SetCursorPosX(_contentPaddingX);
|
||||||
}
|
}
|
||||||
|
|
||||||
private float DrawTitle(LightlessNotification notification, float contentWidth, float alpha)
|
private float DrawTitle(LightlessNotification notification, float contentWidth, float alpha)
|
||||||
@@ -530,7 +530,7 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(notification.Message)) return;
|
if (string.IsNullOrEmpty(notification.Message)) return;
|
||||||
|
|
||||||
var messagePos = contentPos + new Vector2(0f, titleHeight + TitleMessageSpacing);
|
var messagePos = contentPos + new Vector2(0f, titleHeight + _titleMessageSpacing);
|
||||||
var messageColor = new Vector4(0.9f, 0.9f, 0.9f, alpha);
|
var messageColor = new Vector4(0.9f, 0.9f, 0.9f, alpha);
|
||||||
|
|
||||||
ImGui.SetCursorPos(messagePos);
|
ImGui.SetCursorPos(messagePos);
|
||||||
@@ -563,13 +563,13 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
private float CalculateActionButtonWidth(int actionCount, float availableWidth)
|
private float CalculateActionButtonWidth(int actionCount, float availableWidth)
|
||||||
{
|
{
|
||||||
var totalSpacing = (actionCount - 1) * ActionButtonSpacing;
|
var totalSpacing = (actionCount - 1) * _actionButtonSpacing;
|
||||||
return (availableWidth - totalSpacing) / actionCount;
|
return (availableWidth - totalSpacing) / actionCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PositionActionButton(int index, float startX, float buttonWidth)
|
private void PositionActionButton(int index, float startX, float buttonWidth)
|
||||||
{
|
{
|
||||||
var xPosition = startX + index * (buttonWidth + ActionButtonSpacing);
|
var xPosition = startX + index * (buttonWidth + _actionButtonSpacing);
|
||||||
ImGui.SetCursorPosX(xPosition);
|
ImGui.SetCursorPosX(xPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -687,7 +687,7 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
|
|||||||
height += 12f;
|
height += 12f;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Math.Clamp(height, NotificationMinHeight, NotificationMaxHeight);
|
return Math.Clamp(height, _notificationMinHeight, _notificationMaxHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
private float CalculateTitleHeight(LightlessNotification notification, float contentWidth)
|
private float CalculateTitleHeight(LightlessNotification notification, float contentWidth)
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
private readonly IProgress<(int, int, FileCacheEntity)> _validationProgress;
|
private readonly IProgress<(int, int, FileCacheEntity)> _validationProgress;
|
||||||
private readonly NameplateService _nameplateService;
|
private readonly NameplateService _nameplateService;
|
||||||
private readonly NameplateHandler _nameplateHandler;
|
private readonly NameplateHandler _nameplateHandler;
|
||||||
private readonly NotificationService _lightlessNotificationService;
|
|
||||||
private (int, int, FileCacheEntity) _currentProgress;
|
private (int, int, FileCacheEntity) _currentProgress;
|
||||||
private bool _deleteAccountPopupModalShown = false;
|
private bool _deleteAccountPopupModalShown = false;
|
||||||
private bool _deleteFilesPopupModalShown = false;
|
private bool _deleteFilesPopupModalShown = false;
|
||||||
@@ -107,8 +106,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
IpcManager ipcManager, CacheMonitor cacheMonitor,
|
IpcManager ipcManager, CacheMonitor cacheMonitor,
|
||||||
DalamudUtilService dalamudUtilService, HttpClient httpClient,
|
DalamudUtilService dalamudUtilService, HttpClient httpClient,
|
||||||
NameplateService nameplateService,
|
NameplateService nameplateService,
|
||||||
NameplateHandler nameplateHandler,
|
NameplateHandler nameplateHandler) : base(logger, mediator, "Lightless Sync Settings",
|
||||||
NotificationService lightlessNotificationService) : base(logger, mediator, "Lightless Sync Settings",
|
|
||||||
performanceCollector)
|
performanceCollector)
|
||||||
{
|
{
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
@@ -130,7 +128,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
_uiShared = uiShared;
|
_uiShared = uiShared;
|
||||||
_nameplateService = nameplateService;
|
_nameplateService = nameplateService;
|
||||||
_nameplateHandler = nameplateHandler;
|
_nameplateHandler = nameplateHandler;
|
||||||
_lightlessNotificationService = lightlessNotificationService;
|
|
||||||
AllowClickthrough = false;
|
AllowClickthrough = false;
|
||||||
AllowPinning = true;
|
AllowPinning = true;
|
||||||
_validationProgress = new Progress<(int, int, FileCacheEntity)>(v => _currentProgress = v);
|
_validationProgress = new Progress<(int, int, FileCacheEntity)>(v => _currentProgress = v);
|
||||||
@@ -140,6 +137,25 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
MinimumSize = new Vector2(800, 400), MaximumSize = new Vector2(800, 2000),
|
MinimumSize = new Vector2(800, 400), MaximumSize = new Vector2(800, 2000),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TitleBarButtons = new()
|
||||||
|
{
|
||||||
|
new TitleBarButton()
|
||||||
|
{
|
||||||
|
Icon = FontAwesomeIcon.FileAlt,
|
||||||
|
Click = (msg) =>
|
||||||
|
{
|
||||||
|
Mediator.Publish(new UiToggleMessage(typeof(UpdateNotesUi)));
|
||||||
|
},
|
||||||
|
IconOffset = new(2, 1),
|
||||||
|
ShowTooltip = () =>
|
||||||
|
{
|
||||||
|
ImGui.BeginTooltip();
|
||||||
|
ImGui.Text("View Update Notes");
|
||||||
|
ImGui.EndTooltip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Mediator.Subscribe<OpenSettingsUiMessage>(this, (_) => Toggle());
|
Mediator.Subscribe<OpenSettingsUiMessage>(this, (_) => Toggle());
|
||||||
Mediator.Subscribe<OpenLightfinderSettingsMessage>(this, (_) =>
|
Mediator.Subscribe<OpenLightfinderSettingsMessage>(this, (_) =>
|
||||||
{
|
{
|
||||||
@@ -2339,9 +2355,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
_uiShared.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f);
|
_uiShared.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f);
|
||||||
ImGui.TreePop();
|
ImGui.TreePop();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawPerformance()
|
private void DrawPerformance()
|
||||||
@@ -3599,20 +3613,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
if (ImGui.Button($"{FontAwesomeIcon.Play.ToIconString()}##test_pair", new Vector2(availableWidth, 0)))
|
if (ImGui.Button($"{FontAwesomeIcon.Play.ToIconString()}##test_pair", new Vector2(availableWidth, 0)))
|
||||||
{
|
{
|
||||||
_lightlessNotificationService.ShowPairRequestNotification(
|
Mediator.Publish(new PairRequestReceivedMessage("test-uid-123", "Test User wants to pair with you."));
|
||||||
"Test User",
|
|
||||||
"test-uid-123",
|
|
||||||
() =>
|
|
||||||
{
|
|
||||||
Mediator.Publish(new NotificationMessage("Accepted", "You accepted the test pair request.",
|
|
||||||
NotificationType.Info));
|
|
||||||
},
|
|
||||||
() =>
|
|
||||||
{
|
|
||||||
Mediator.Publish(new NotificationMessage("Declined", "You declined the test pair request.",
|
|
||||||
NotificationType.Info));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Test pair request notification");
|
UiSharedService.AttachToolTip("Test pair request notification");
|
||||||
@@ -3635,15 +3636,14 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
if (ImGui.Button($"{FontAwesomeIcon.Play.ToIconString()}##test_download", new Vector2(availableWidth, 0)))
|
if (ImGui.Button($"{FontAwesomeIcon.Play.ToIconString()}##test_download", new Vector2(availableWidth, 0)))
|
||||||
{
|
{
|
||||||
_lightlessNotificationService.ShowPairDownloadNotification(
|
Mediator.Publish(new PairDownloadStatusMessage(
|
||||||
new List<(string playerName, float progress, string status)>
|
[
|
||||||
{
|
|
||||||
("Player One", 0.35f, "downloading"),
|
("Player One", 0.35f, "downloading"),
|
||||||
("Player Two", 0.75f, "downloading"),
|
("Player Two", 0.75f, "downloading"),
|
||||||
("Player Three", 1.0f, "downloading")
|
("Player Three", 1.0f, "downloading")
|
||||||
},
|
],
|
||||||
queueWaiting: 2
|
2
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Test download progress notification");
|
UiSharedService.AttachToolTip("Test download progress notification");
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
_showFileDialogError = false;
|
_showFileDialogError = false;
|
||||||
await _apiController.GroupSetProfile(new GroupProfileDto(new GroupData(GroupFullInfo.Group.AliasOrGID), Description: null, Tags: null, Convert.ToBase64String(fileContent), IsNsfw: null, IsDisabled: null))
|
await _apiController.GroupSetProfile(new GroupProfileDto(new GroupData(GroupFullInfo.Group.AliasOrGID), Description: null, Tags: null, Convert.ToBase64String(fileContent), BannerBase64: null, IsNsfw: null, IsDisabled: null))
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -313,7 +313,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear uploaded profile picture"))
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear uploaded profile picture"))
|
||||||
{
|
{
|
||||||
_ = _apiController.GroupSetProfile(new GroupProfileDto(new GroupData(GroupFullInfo.Group.AliasOrGID), Description: null, Tags: null, PictureBase64: null, IsNsfw: null, IsDisabled: null));
|
_ = _apiController.GroupSetProfile(new GroupProfileDto(new GroupData(GroupFullInfo.Group.AliasOrGID), Description: null, Tags: null, PictureBase64: null, BannerBase64: null, IsNsfw: null, IsDisabled: null));
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Clear your currently uploaded profile picture");
|
UiSharedService.AttachToolTip("Clear your currently uploaded profile picture");
|
||||||
if (_showFileDialogError)
|
if (_showFileDialogError)
|
||||||
@@ -368,13 +368,13 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Save, "Save Description"))
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Save, "Save Description"))
|
||||||
{
|
{
|
||||||
_ = _apiController.GroupSetProfile(new GroupProfileDto(new GroupData(GroupFullInfo.Group.AliasOrGID), Description: _descriptionText, Tags: null, PictureBase64: null, IsNsfw: null, IsDisabled: null));
|
_ = _apiController.GroupSetProfile(new GroupProfileDto(new GroupData(GroupFullInfo.Group.AliasOrGID), Description: _descriptionText, Tags: null, PictureBase64: null, BannerBase64: null, IsNsfw: null, IsDisabled: null));
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Sets your profile description text");
|
UiSharedService.AttachToolTip("Sets your profile description text");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear Description"))
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear Description"))
|
||||||
{
|
{
|
||||||
_ = _apiController.GroupSetProfile(new GroupProfileDto(new GroupData(GroupFullInfo.Group.AliasOrGID), Description: null, Tags: null, PictureBase64: null, IsNsfw: null, IsDisabled: null));
|
_ = _apiController.GroupSetProfile(new GroupProfileDto(new GroupData(GroupFullInfo.Group.AliasOrGID), Description: null, Tags: null, PictureBase64: null, BannerBase64: null, IsNsfw: null, IsDisabled: null));
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Clears your profile description text");
|
UiSharedService.AttachToolTip("Clears your profile description text");
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
@@ -382,7 +382,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
var isNsfw = _profileData.IsNsfw;
|
var isNsfw = _profileData.IsNsfw;
|
||||||
if (ImGui.Checkbox("Profile is NSFW", ref isNsfw))
|
if (ImGui.Checkbox("Profile is NSFW", ref isNsfw))
|
||||||
{
|
{
|
||||||
_ = _apiController.GroupSetProfile(new GroupProfileDto(new GroupData(GroupFullInfo.Group.AliasOrGID), Description: null, Tags: null, PictureBase64: null, IsNsfw: isNsfw, IsDisabled: null));
|
_ = _apiController.GroupSetProfile(new GroupProfileDto(new GroupData(GroupFullInfo.Group.AliasOrGID), Description: null, Tags: null, PictureBase64: null, BannerBase64: null, IsNsfw: isNsfw, IsDisabled: null));
|
||||||
}
|
}
|
||||||
_uiSharedService.DrawHelpText("If your profile description or image can be considered NSFW, toggle this to ON");
|
_uiSharedService.DrawHelpText("If your profile description or image can be considered NSFW, toggle this to ON");
|
||||||
ImGui.TreePop();
|
ImGui.TreePop();
|
||||||
@@ -744,12 +744,12 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
|||||||
if (HasTag)
|
if (HasTag)
|
||||||
{
|
{
|
||||||
_selectedTags.Add(tag);
|
_selectedTags.Add(tag);
|
||||||
_ = _apiController.GroupSetProfile(new GroupProfileDto(new GroupData(GroupFullInfo.Group.AliasOrGID), Description: null, Tags: _selectedTags.ToArray(), PictureBase64: null, IsNsfw: null, IsDisabled: null));
|
_ = _apiController.GroupSetProfile(new GroupProfileDto(new GroupData(GroupFullInfo.Group.AliasOrGID), Description: null, Tags: _selectedTags.ToArray(), PictureBase64: null, BannerBase64: null, IsNsfw: null, IsDisabled: null));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_selectedTags.Remove(tag);
|
_selectedTags.Remove(tag);
|
||||||
_ = _apiController.GroupSetProfile(new GroupProfileDto(new GroupData(GroupFullInfo.Group.AliasOrGID), Description: null, Tags: _selectedTags.ToArray(), PictureBase64: null, IsNsfw: null, IsDisabled: null));
|
_ = _apiController.GroupSetProfile(new GroupProfileDto(new GroupData(GroupFullInfo.Group.AliasOrGID), Description: null, Tags: _selectedTags.ToArray(), PictureBase64: null, BannerBase64: null, IsNsfw: null, IsDisabled: null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,12 +52,12 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
private float _particleSpawnTimer;
|
private float _particleSpawnTimer;
|
||||||
private readonly Random _random = new();
|
private readonly Random _random = new();
|
||||||
|
|
||||||
private const float HeaderHeight = 150f;
|
private const float _headerHeight = 150f;
|
||||||
private const float ParticleSpawnInterval = 0.2f;
|
private const float _particleSpawnInterval = 0.2f;
|
||||||
private const int MaxParticles = 50;
|
private const int _maxParticles = 50;
|
||||||
private const int MaxTrailLength = 50;
|
private const int _maxTrailLength = 50;
|
||||||
private const float EdgeFadeDistance = 30f;
|
private const float _edgeFadeDistance = 30f;
|
||||||
private const float ExtendedParticleHeight = 40f;
|
private const float _extendedParticleHeight = 40f;
|
||||||
|
|
||||||
public UpdateNotesUi(ILogger<UpdateNotesUi> logger,
|
public UpdateNotesUi(ILogger<UpdateNotesUi> logger,
|
||||||
LightlessMediator mediator,
|
LightlessMediator mediator,
|
||||||
@@ -111,16 +111,16 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
var headerWidth = (800f * ImGuiHelpers.GlobalScale) - (windowPadding.X * 2);
|
var headerWidth = (800f * ImGuiHelpers.GlobalScale) - (windowPadding.X * 2);
|
||||||
|
|
||||||
var headerStart = windowPos + new Vector2(windowPadding.X, windowPadding.Y);
|
var headerStart = windowPos + new Vector2(windowPadding.X, windowPadding.Y);
|
||||||
var headerEnd = headerStart + new Vector2(headerWidth, HeaderHeight);
|
var headerEnd = headerStart + new Vector2(headerWidth, _headerHeight);
|
||||||
|
|
||||||
var extendedParticleSize = new Vector2(headerWidth, HeaderHeight + ExtendedParticleHeight);
|
var extendedParticleSize = new Vector2(headerWidth, _headerHeight + _extendedParticleHeight);
|
||||||
|
|
||||||
DrawGradientBackground(headerStart, headerEnd);
|
DrawGradientBackground(headerStart, headerEnd);
|
||||||
DrawHeaderText(headerStart);
|
DrawHeaderText(headerStart);
|
||||||
DrawHeaderButtons(headerStart, headerWidth);
|
DrawHeaderButtons(headerStart, headerWidth);
|
||||||
DrawBottomGradient(headerStart, headerEnd, headerWidth);
|
DrawBottomGradient(headerStart, headerEnd, headerWidth);
|
||||||
|
|
||||||
ImGui.SetCursorPosY(windowPadding.Y + HeaderHeight + 5);
|
ImGui.SetCursorPosY(windowPadding.Y + _headerHeight + 5);
|
||||||
ImGui.SetCursorPosX(20);
|
ImGui.SetCursorPosX(20);
|
||||||
using (ImRaii.PushFont(UiBuilder.IconFont))
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
||||||
{
|
{
|
||||||
@@ -260,7 +260,7 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
var deltaTime = ImGui.GetIO().DeltaTime;
|
var deltaTime = ImGui.GetIO().DeltaTime;
|
||||||
_particleSpawnTimer += deltaTime;
|
_particleSpawnTimer += deltaTime;
|
||||||
|
|
||||||
if (_particleSpawnTimer > ParticleSpawnInterval && _particles.Count < MaxParticles)
|
if (_particleSpawnTimer > _particleSpawnInterval && _particles.Count < _maxParticles)
|
||||||
{
|
{
|
||||||
SpawnParticle(bannerSize);
|
SpawnParticle(bannerSize);
|
||||||
_particleSpawnTimer = 0f;
|
_particleSpawnTimer = 0f;
|
||||||
@@ -282,7 +282,7 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
if (particle.Type == ParticleType.ShootingStar && particle.Trail != null)
|
if (particle.Type == ParticleType.ShootingStar && particle.Trail != null)
|
||||||
{
|
{
|
||||||
particle.Trail.Insert(0, particle.Position);
|
particle.Trail.Insert(0, particle.Position);
|
||||||
if (particle.Trail.Count > MaxTrailLength)
|
if (particle.Trail.Count > _maxTrailLength)
|
||||||
particle.Trail.RemoveAt(particle.Trail.Count - 1);
|
particle.Trail.RemoveAt(particle.Trail.Count - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,12 +316,12 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
var lifeFade = Math.Min(fadeIn, fadeOut);
|
var lifeFade = Math.Min(fadeIn, fadeOut);
|
||||||
|
|
||||||
var edgeFadeX = Math.Min(
|
var edgeFadeX = Math.Min(
|
||||||
Math.Min(1f, (particle.Position.X + EdgeFadeDistance) / EdgeFadeDistance),
|
Math.Min(1f, (particle.Position.X + _edgeFadeDistance) / _edgeFadeDistance),
|
||||||
Math.Min(1f, (bannerSize.X - particle.Position.X + EdgeFadeDistance) / EdgeFadeDistance)
|
Math.Min(1f, (bannerSize.X - particle.Position.X + _edgeFadeDistance) / _edgeFadeDistance)
|
||||||
);
|
);
|
||||||
var edgeFadeY = Math.Min(
|
var edgeFadeY = Math.Min(
|
||||||
Math.Min(1f, (particle.Position.Y + EdgeFadeDistance) / EdgeFadeDistance),
|
Math.Min(1f, (particle.Position.Y + _edgeFadeDistance) / _edgeFadeDistance),
|
||||||
Math.Min(1f, (bannerSize.Y - particle.Position.Y + EdgeFadeDistance) / EdgeFadeDistance)
|
Math.Min(1f, (bannerSize.Y - particle.Position.Y + _edgeFadeDistance) / _edgeFadeDistance)
|
||||||
);
|
);
|
||||||
var edgeFade = Math.Min(edgeFadeX, edgeFadeY);
|
var edgeFade = Math.Min(edgeFadeX, edgeFadeY);
|
||||||
|
|
||||||
@@ -589,6 +589,11 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
IsOpen = false;
|
IsOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui.IsItemHovered())
|
||||||
|
{
|
||||||
|
ImGui.SetTooltip("You can view this window again in the settings (title menu)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Dalamud.Interface.Utility;
|
|||||||
using Lumina.Text;
|
using Lumina.Text;
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Threading;
|
||||||
using DalamudSeString = Dalamud.Game.Text.SeStringHandling.SeString;
|
using DalamudSeString = Dalamud.Game.Text.SeStringHandling.SeString;
|
||||||
using DalamudSeStringBuilder = Dalamud.Game.Text.SeStringHandling.SeStringBuilder;
|
using DalamudSeStringBuilder = Dalamud.Game.Text.SeStringHandling.SeStringBuilder;
|
||||||
using LuminaSeStringBuilder = Lumina.Text.SeStringBuilder;
|
using LuminaSeStringBuilder = Lumina.Text.SeStringBuilder;
|
||||||
@@ -15,6 +16,9 @@ namespace LightlessSync.Utils;
|
|||||||
|
|
||||||
public static class SeStringUtils
|
public static class SeStringUtils
|
||||||
{
|
{
|
||||||
|
private static int _seStringHitboxCounter;
|
||||||
|
private static int _iconHitboxCounter;
|
||||||
|
|
||||||
public static DalamudSeString BuildFormattedPlayerName(string text, Vector4? textColor, Vector4? glowColor)
|
public static DalamudSeString BuildFormattedPlayerName(string text, Vector4? textColor, Vector4? glowColor)
|
||||||
{
|
{
|
||||||
var b = new DalamudSeStringBuilder();
|
var b = new DalamudSeStringBuilder();
|
||||||
@@ -119,7 +123,7 @@ public static class SeStringUtils
|
|||||||
|
|
||||||
ImGui.Dummy(new Vector2(0f, textSize.Y));
|
ImGui.Dummy(new Vector2(0f, textSize.Y));
|
||||||
}
|
}
|
||||||
public static Vector2 RenderSeStringWithHitbox(DalamudSeString seString, Vector2 position, ImFontPtr? font = null)
|
public static Vector2 RenderSeStringWithHitbox(DalamudSeString seString, Vector2 position, ImFontPtr? font = null, string? id = null)
|
||||||
{
|
{
|
||||||
var drawList = ImGui.GetWindowDrawList();
|
var drawList = ImGui.GetWindowDrawList();
|
||||||
|
|
||||||
@@ -137,12 +141,28 @@ public static class SeStringUtils
|
|||||||
var textSize = ImGui.CalcTextSize(seString.TextValue);
|
var textSize = ImGui.CalcTextSize(seString.TextValue);
|
||||||
|
|
||||||
ImGui.SetCursorScreenPos(position);
|
ImGui.SetCursorScreenPos(position);
|
||||||
ImGui.InvisibleButton($"##hitbox_{Guid.NewGuid()}", textSize);
|
if (id is not null)
|
||||||
|
{
|
||||||
|
ImGui.PushID(id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui.PushID(Interlocked.Increment(ref _seStringHitboxCounter));
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ImGui.InvisibleButton("##hitbox", textSize);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ImGui.PopID();
|
||||||
|
}
|
||||||
|
|
||||||
return textSize;
|
return textSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Vector2 RenderIconWithHitbox(int iconId, Vector2 position, ImFontPtr? font = null)
|
public static Vector2 RenderIconWithHitbox(int iconId, Vector2 position, ImFontPtr? font = null, string? id = null)
|
||||||
{
|
{
|
||||||
var drawList = ImGui.GetWindowDrawList();
|
var drawList = ImGui.GetWindowDrawList();
|
||||||
|
|
||||||
@@ -158,7 +178,23 @@ public static class SeStringUtils
|
|||||||
var drawResult = ImGuiHelpers.CompileSeStringWrapped(iconMacro, drawParams);
|
var drawResult = ImGuiHelpers.CompileSeStringWrapped(iconMacro, drawParams);
|
||||||
|
|
||||||
ImGui.SetCursorScreenPos(position);
|
ImGui.SetCursorScreenPos(position);
|
||||||
ImGui.InvisibleButton($"##iconHitbox_{Guid.NewGuid()}", drawResult.Size);
|
if (id is not null)
|
||||||
|
{
|
||||||
|
ImGui.PushID(id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui.PushID(Interlocked.Increment(ref _iconHitboxCounter));
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ImGui.InvisibleButton("##iconHitbox", drawResult.Size);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ImGui.PopID();
|
||||||
|
}
|
||||||
|
|
||||||
return drawResult.Size;
|
return drawResult.Size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,6 +215,26 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
await Task.Delay(retryDelay, ct).ConfigureAwait(false);
|
await Task.Delay(retryDelay, ct).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
catch (TaskCanceledException ex) when (!ct.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
response?.Dispose();
|
||||||
|
retryCount++;
|
||||||
|
|
||||||
|
Logger.LogWarning(ex, "Cancellation/timeout during download of {requestUrl}. Attempt {attempt} of {maxRetries}", requestUrl, retryCount, maxRetries);
|
||||||
|
|
||||||
|
if (retryCount >= maxRetries)
|
||||||
|
{
|
||||||
|
Logger.LogError("Max retries reached for {requestUrl} after TaskCanceledException", requestUrl);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(retryDelay, ct).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) when (ct.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
response?.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
catch (HttpRequestException ex)
|
catch (HttpRequestException ex)
|
||||||
{
|
{
|
||||||
response?.Dispose();
|
response?.Dispose();
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ public partial class ApiController
|
|||||||
|
|
||||||
public async Task<UserProfileDto> UserGetProfile(UserDto dto)
|
public async Task<UserProfileDto> UserGetProfile(UserDto dto)
|
||||||
{
|
{
|
||||||
if (!IsConnected) return new UserProfileDto(dto.User, Disabled: false, IsNSFW: null, ProfilePictureBase64: null, Description: null, Tags: null);
|
if (!IsConnected) return new UserProfileDto(dto.User, Disabled: false, IsNSFW: null, ProfilePictureBase64: null, Description: null, BannerPictureBase64: null, Tags: null);
|
||||||
return await _lightlessHub!.InvokeAsync<UserProfileDto>(nameof(UserGetProfile), dto).ConfigureAwait(false);
|
return await _lightlessHub!.InvokeAsync<UserProfileDto>(nameof(UserGetProfile), dto).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,17 +107,17 @@ public partial class ApiController
|
|||||||
}
|
}
|
||||||
public Task Client_ReceiveBroadcastPairRequest(UserPairNotificationDto dto)
|
public Task Client_ReceiveBroadcastPairRequest(UserPairNotificationDto dto)
|
||||||
{
|
{
|
||||||
if (dto == null)
|
Logger.LogDebug("Client_ReceiveBroadcastPairRequest: {dto}", dto);
|
||||||
|
|
||||||
|
if (dto is null)
|
||||||
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
var request = _pairRequestService.RegisterIncomingRequest(dto.myHashedCid, dto.message ?? string.Empty);
|
ExecuteSafely(() =>
|
||||||
var senderName = string.IsNullOrEmpty(request.DisplayName) ? "Unknown User" : request.DisplayName;
|
{
|
||||||
|
Mediator.Publish(new PairRequestReceivedMessage(dto.myHashedCid, dto.message ?? string.Empty));
|
||||||
_lightlessNotificationService.ShowPairRequestNotification(
|
});
|
||||||
senderName,
|
|
||||||
request.HashedCid,
|
|
||||||
onAccept: () => _pairRequestService.AcceptPairRequest(request.HashedCid, senderName),
|
|
||||||
onDecline: () => _pairRequestService.DeclinePairRequest(request.HashedCid));
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ public partial class ApiController
|
|||||||
public async Task<GroupProfileDto> GroupGetProfile(GroupDto dto)
|
public async Task<GroupProfileDto> GroupGetProfile(GroupDto dto)
|
||||||
{
|
{
|
||||||
CheckConnection();
|
CheckConnection();
|
||||||
if (!IsConnected) return new GroupProfileDto(Group: dto.Group, Description: null, Tags: null, PictureBase64: null, IsNsfw: false, IsDisabled: false);
|
if (!IsConnected) return new GroupProfileDto(Group: dto.Group, Description: null, Tags: null, PictureBase64: null, IsNsfw: false, BannerBase64: null, IsDisabled: false);
|
||||||
return await _lightlessHub!.InvokeAsync<GroupProfileDto>(nameof(GroupGetProfile), dto).ConfigureAwait(false);
|
return await _lightlessHub!.InvokeAsync<GroupProfileDto>(nameof(GroupGetProfile), dto).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IL
|
|||||||
private readonly ServerConfigurationManager _serverManager;
|
private readonly ServerConfigurationManager _serverManager;
|
||||||
private readonly TokenProvider _tokenProvider;
|
private readonly TokenProvider _tokenProvider;
|
||||||
private readonly LightlessConfigService _lightlessConfigService;
|
private readonly LightlessConfigService _lightlessConfigService;
|
||||||
private readonly NotificationService _lightlessNotificationService;
|
|
||||||
private CancellationTokenSource _connectionCancellationTokenSource;
|
private CancellationTokenSource _connectionCancellationTokenSource;
|
||||||
private ConnectionDto? _connectionDto;
|
private ConnectionDto? _connectionDto;
|
||||||
private bool _doNotNotifyOnNextInfo = false;
|
private bool _doNotNotifyOnNextInfo = false;
|
||||||
@@ -54,7 +53,6 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IL
|
|||||||
_serverManager = serverManager;
|
_serverManager = serverManager;
|
||||||
_tokenProvider = tokenProvider;
|
_tokenProvider = tokenProvider;
|
||||||
_lightlessConfigService = lightlessConfigService;
|
_lightlessConfigService = lightlessConfigService;
|
||||||
_lightlessNotificationService = lightlessNotificationService;
|
|
||||||
_connectionCancellationTokenSource = new CancellationTokenSource();
|
_connectionCancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
Mediator.Subscribe<DalamudLoginMessage>(this, (_) => DalamudUtilOnLogIn());
|
Mediator.Subscribe<DalamudLoginMessage>(this, (_) => DalamudUtilOnLogIn());
|
||||||
|
|||||||
Reference in New Issue
Block a user