From 6892d8104188d26a8123a28a88080684f319c5ad Mon Sep 17 00:00:00 2001 From: defnotken Date: Wed, 24 Dec 2025 01:34:37 -0600 Subject: [PATCH] Add notifications --- .../CharaData/CharaDataFileHandler.cs | 72 ++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/LightlessSync/Services/CharaData/CharaDataFileHandler.cs b/LightlessSync/Services/CharaData/CharaDataFileHandler.cs index 7f84e90..f0553f7 100644 --- a/LightlessSync/Services/CharaData/CharaDataFileHandler.cs +++ b/LightlessSync/Services/CharaData/CharaDataFileHandler.cs @@ -4,10 +4,13 @@ using LightlessSync.API.Data; using LightlessSync.API.Data.Enum; using LightlessSync.API.Dto.CharaData; using LightlessSync.FileCache; +using LightlessSync.LightlessConfiguration.Models; using LightlessSync.PlayerData.Factories; using LightlessSync.PlayerData.Handlers; using LightlessSync.Services.CharaData; using LightlessSync.Services.CharaData.Models; +using LightlessSync.Services.Mediator; +using LightlessSync.UI.Models; using LightlessSync.Utils; using LightlessSync.WebAPI.Files; using Microsoft.Extensions.Logging; @@ -24,10 +27,11 @@ public sealed class CharaDataFileHandler : IDisposable private readonly ILogger _logger; private readonly LightlessCharaFileDataFactory _lightlessCharaFileDataFactory; private readonly PlayerDataFactory _playerDataFactory; + private readonly NotificationService _notificationService; private int _globalFileCounter = 0; public CharaDataFileHandler(ILogger logger, FileDownloadManagerFactory fileDownloadManagerFactory, FileUploadManager fileUploadManager, FileCacheManager fileCacheManager, - DalamudUtilService dalamudUtilService, GameObjectHandlerFactory gameObjectHandlerFactory, PlayerDataFactory playerDataFactory) + DalamudUtilService dalamudUtilService, GameObjectHandlerFactory gameObjectHandlerFactory, PlayerDataFactory playerDataFactory, NotificationService notificationService) { _fileDownloadManager = fileDownloadManagerFactory.Create(); _logger = logger; @@ -36,6 +40,7 @@ public sealed class CharaDataFileHandler : IDisposable _dalamudUtilService = dalamudUtilService; _gameObjectHandlerFactory = gameObjectHandlerFactory; _playerDataFactory = playerDataFactory; + _notificationService = notificationService; _lightlessCharaFileDataFactory = new(fileCacheManager); } @@ -276,6 +281,9 @@ public sealed class CharaDataFileHandler : IDisposable int fileIndex = 0; long totalBytesWritten = 0; + long totalBytesToWrite = output.CharaFileData.Files.Sum(f => f.Length); + var fileWriteStopwatch = System.Diagnostics.Stopwatch.StartNew(); + const long updateIntervalMs = 1000; foreach (var item in output.CharaFileData.Files) { @@ -305,6 +313,28 @@ public sealed class CharaDataFileHandler : IDisposable fileStopwatch.Stop(); _logger.LogDebug("Wrote file [{fileNum}/{totalFiles}] in {elapsed}ms ({sizeKb}kb)", fileIndex, output.CharaFileData.Files.Count, fileStopwatch.ElapsedMilliseconds, item.Length / 1024); + + if (fileWriteStopwatch.ElapsedMilliseconds >= updateIntervalMs && totalBytesToWrite > 0) + { + float progress = (float)totalBytesWritten / totalBytesToWrite; + var elapsed = overallStopwatch.Elapsed; + var eta = CalculateEta(elapsed, progress); + + var notification = new LightlessNotification + { + Id = "chara_file_save_progress", + Title = "Character Data", + Message = $"Compressing and saving character file... {(progress * 100):F0}%\nETA: {FormatTimespan(eta)}", + Type = NotificationType.Info, + Duration = TimeSpan.FromMinutes(5), + ShowProgress = true, + Progress = progress + }; + + _notificationService.Mediator.Publish(new LightlessNotificationMessage(notification)); + + fileWriteStopwatch.Restart(); + } } var flushStopwatch = System.Diagnostics.Stopwatch.StartNew(); @@ -322,6 +352,14 @@ public sealed class CharaDataFileHandler : IDisposable overallStopwatch.Stop(); _logger.LogInformation("SaveCharaFileAsync completed successfully in {elapsed}ms. Total bytes written: {totalBytes}mb", overallStopwatch.ElapsedMilliseconds, totalBytesWritten / (1024 * 1024)); + + _notificationService.ShowNotification( + "Character Data", + "Character file saved successfully!", + NotificationType.Info, + duration: TimeSpan.FromSeconds(5)); + + _notificationService.Mediator.Publish(new LightlessNotificationDismissMessage("chara_file_save_progress")); } catch (Exception ex) { @@ -335,9 +373,41 @@ public sealed class CharaDataFileHandler : IDisposable { _logger.LogError(deleteEx, "Failed to delete temporary file {file}", tempFilePath); } + + _notificationService.ShowErrorNotification( + "Character Data Save Failed", + "Failed to save character file", + ex); + + _notificationService.Mediator.Publish(new LightlessNotificationDismissMessage("chara_file_save_progress")); } } + private static TimeSpan CalculateEta(TimeSpan elapsed, float progress) + { + if (progress <= 0 || elapsed.TotalSeconds < 0.1) + return TimeSpan.Zero; + + double totalSeconds = elapsed.TotalSeconds / progress; + double remainingSeconds = totalSeconds - elapsed.TotalSeconds; + + return TimeSpan.FromSeconds(Math.Max(0, remainingSeconds)); + } + + private static string FormatTimespan(TimeSpan ts) + { + if (ts.TotalSeconds < 1) + return "< 1s"; + + if (ts.TotalSeconds < 60) + return $"{ts.TotalSeconds:F0}s"; + + if (ts.TotalMinutes < 60) + return $"{ts.TotalMinutes:F1}m"; + + return $"{ts.TotalHours:F1}h"; + } + internal async Task> UploadFiles(List fileList, ValueProgress uploadProgress, CancellationToken token) { return await _fileUploadManager.UploadFiles(fileList, uploadProgress, token).ConfigureAwait(false);