From 9b8145fdddfeedd814d8d7b7da8cf9e3bb91e166 Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Fri, 24 Jun 2022 15:43:39 +0200 Subject: [PATCH] add downloads UI, fix some bugs on disconnect --- MareSynchronos/Configuration.cs | 3 + .../Managers/CharacterCacheManager.cs | 6 +- MareSynchronos/Managers/FileCacheManager.cs | 2 +- MareSynchronos/Plugin.cs | 13 +-- MareSynchronos/UI/DownloadUi.cs | 88 +++++++++++++++++++ MareSynchronos/UI/IntroUI.cs | 48 +++++----- MareSynchronos/UI/PluginUI.cs | 51 ++++++++--- MareSynchronos/UI/UIShared.cs | 44 +++++++++- MareSynchronos/WebAPI/ApiController.cs | 11 ++- 9 files changed, 216 insertions(+), 50 deletions(-) create mode 100644 MareSynchronos/UI/DownloadUi.cs diff --git a/MareSynchronos/Configuration.cs b/MareSynchronos/Configuration.cs index d904142..7329f7a 100644 --- a/MareSynchronos/Configuration.cs +++ b/MareSynchronos/Configuration.cs @@ -47,6 +47,9 @@ namespace MareSynchronos public Dictionary UidComments { get; set; } = new(); public bool UseCustomService { get; set; } = false; public int Version { get; set; } = 0; + + public bool ShowTransferWindow { get; set; } = true; + // the below exist just to make saving less cumbersome public void Initialize(DalamudPluginInterface pluginInterface) { diff --git a/MareSynchronos/Managers/CharacterCacheManager.cs b/MareSynchronos/Managers/CharacterCacheManager.cs index 0113dd7..71cf1b9 100644 --- a/MareSynchronos/Managers/CharacterCacheManager.cs +++ b/MareSynchronos/Managers/CharacterCacheManager.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; -using System.Threading; using System.Threading.Tasks; using Dalamud.Game; using Dalamud.Game.ClientState; @@ -14,7 +12,6 @@ using MareSynchronos.FileCacheDB; using MareSynchronos.Models; using MareSynchronos.Utils; using MareSynchronos.WebAPI; -using Microsoft.EntityFrameworkCore; namespace MareSynchronos.Managers; @@ -63,6 +60,8 @@ public class CharacterCacheManager : IDisposable { RestoreCharacter(character); } + + _onlineCachedPlayers.Clear(); } public void Initialize() @@ -74,6 +73,7 @@ public class CharacterCacheManager : IDisposable _apiController.UnpairedFromOther += ApiControllerOnUnpairedFromOther; _framework.Update += FrameworkOnUpdate; } + public async Task UpdatePlayersFromService(Dictionary playerJobIds) { await _apiController.GetCharacterData(playerJobIds); diff --git a/MareSynchronos/Managers/FileCacheManager.cs b/MareSynchronos/Managers/FileCacheManager.cs index fcc5e50..e7c6dea 100644 --- a/MareSynchronos/Managers/FileCacheManager.cs +++ b/MareSynchronos/Managers/FileCacheManager.cs @@ -12,7 +12,7 @@ using MareSynchronos.Utils; namespace MareSynchronos.Managers { - internal class FileCacheManager : IDisposable + public class FileCacheManager : IDisposable { private readonly FileCacheFactory _fileCacheFactory; private readonly IpcManager _ipcManager; diff --git a/MareSynchronos/Plugin.cs b/MareSynchronos/Plugin.cs index 96f8bc6..13f17d0 100644 --- a/MareSynchronos/Plugin.cs +++ b/MareSynchronos/Plugin.cs @@ -24,7 +24,7 @@ namespace MareSynchronos private readonly CommandManager _commandManager; private readonly Configuration _configuration; private readonly FileCacheManager _fileCacheManager; - private readonly IntroUI _introUi; + private readonly IntroUi _introUi; private readonly IpcManager _ipcManager; private readonly ObjectTable _objectTable; private readonly DalamudPluginInterface _pluginInterface; @@ -34,6 +34,7 @@ namespace MareSynchronos private readonly DalamudUtil _dalamudUtil; private readonly CharacterCacheManager _characterCacheManager; private readonly IPlayerWatcher _playerWatcher; + private readonly DownloadUi _downloadUi; public Plugin(DalamudPluginInterface pluginInterface, CommandManager commandManager, Framework framework, ObjectTable objectTable, ClientState clientState) @@ -58,16 +59,17 @@ namespace MareSynchronos _playerWatcher.Enable(); var uiSharedComponent = - new UIShared(_ipcManager, _apiController, _fileCacheManager, _configuration); + new UiShared(_ipcManager, _apiController, _fileCacheManager, _configuration); _pluginUi = new PluginUi(_windowSystem, uiSharedComponent, _configuration, _apiController); - _introUi = new IntroUI(_windowSystem, uiSharedComponent, _configuration, _fileCacheManager); + _introUi = new IntroUi(_windowSystem, uiSharedComponent, _configuration, _fileCacheManager); _introUi.FinishedRegistration += (_, _) => { _introUi.IsOpen = false; _pluginUi.IsOpen = true; ReLaunchCharacterManager(); }; + _downloadUi = new DownloadUi(_windowSystem, _configuration, _apiController); new FileCacheContext().Dispose(); // make sure db is initialized I guess @@ -91,6 +93,7 @@ namespace MareSynchronos _pluginUi?.Dispose(); _introUi?.Dispose(); + _downloadUi?.Dispose(); _fileCacheManager?.Dispose(); _ipcManager?.Dispose(); @@ -156,7 +159,7 @@ namespace MareSynchronos } }); } - + private void Draw() { _windowSystem.Draw(); @@ -172,7 +175,7 @@ namespace MareSynchronos private void OpenConfigUi() { - if(_configuration.HasValidSetup) + if (_configuration.HasValidSetup) _pluginUi.Toggle(); else _introUi.Toggle(); diff --git a/MareSynchronos/UI/DownloadUi.cs b/MareSynchronos/UI/DownloadUi.cs new file mode 100644 index 0000000..4c4761e --- /dev/null +++ b/MareSynchronos/UI/DownloadUi.cs @@ -0,0 +1,88 @@ +using System; +using System.Linq; +using System.Numerics; +using Dalamud.Interface.Windowing; +using ImGuiNET; +using MareSynchronos.Utils; +using MareSynchronos.WebAPI; + +namespace MareSynchronos.UI; + +public class DownloadUi : Window, IDisposable +{ + private readonly WindowSystem _windowSystem; + private readonly Configuration _pluginConfiguration; + private readonly ApiController _apiController; + + public void Dispose() + { + Logger.Debug("Disposing " + nameof(DownloadUi)); + _windowSystem.RemoveWindow(this); + } + + public DownloadUi(WindowSystem windowSystem, Configuration pluginConfiguration, ApiController apiController) : base("Mare Synchronos Downloads") + { + Logger.Debug("Creating " + nameof(DownloadUi)); + _windowSystem = windowSystem; + _pluginConfiguration = pluginConfiguration; + _apiController = apiController; + + SizeConstraints = new WindowSizeConstraints() + { + MaximumSize = new(300, 90), + MinimumSize = new(300, 90) + }; + + Flags = ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoBackground; + + windowSystem.AddWindow(this); + IsOpen = true; + } + + public override void Draw() + { + if (!_pluginConfiguration.ShowTransferWindow) return; + if (!_apiController.IsDownloading && !_apiController.IsUploading) return; + + var drawList = ImGui.GetWindowDrawList(); + var yDistance = 20; + var xDistance = 20; + + var basePosition = ImGui.GetWindowPos() + ImGui.GetWindowContentRegionMin(); + + if (_apiController.IsUploading) + { + var doneUploads = _apiController.CurrentUploads.Count(c => c.Value.Item1 == c.Value.Item2); + var totalUploads = _apiController.CurrentUploads.Keys.Count; + var totalUploaded = _apiController.CurrentUploads.Sum(c => c.Value.Item1); + var totalToUpload = _apiController.CurrentUploads.Sum(c => c.Value.Item2); + UiShared.DrawOutlinedFont(drawList, "▲", + new Vector2(basePosition.X + 0, basePosition.Y + (int)(yDistance * 0.5)), + UiShared.Color(255, 255, 255, 255), UiShared.Color(0, 0, 0, 255), 2); + UiShared.DrawOutlinedFont(drawList, $"Uploading {doneUploads}/{totalUploads}", + new Vector2(basePosition.X + xDistance, basePosition.Y + yDistance * 0), + UiShared.Color(255, 255, 255, 255), UiShared.Color(0, 0, 0, 255), 2); + UiShared.DrawOutlinedFont(drawList, $"{UiShared.ByteToString(totalUploaded)}/{UiShared.ByteToString(totalToUpload)}", + new Vector2(basePosition.X + xDistance, basePosition.Y + yDistance * 1), + UiShared.Color(255, 255, 255, 255), UiShared.Color(0, 0, 0, 255), 2); + } + + if (_apiController.IsDownloading) + { + var multBase = _apiController.IsDownloading ? 0 : 2; + var doneDownloads = _apiController.CurrentDownloads.Count(c => c.Value.Item1 == c.Value.Item2); + var totalDownloads = _apiController.CurrentDownloads.Keys.Count; + var totalDownloaded = _apiController.CurrentDownloads.Sum(c => c.Value.Item1); + var totalToDownload = _apiController.CurrentDownloads.Sum(c => c.Value.Item2); + UiShared.DrawOutlinedFont(drawList, "▼", + new Vector2(basePosition.X + 0, basePosition.Y + (int)(yDistance * multBase + (yDistance * 0.5))), + UiShared.Color(255, 255, 255, 255), UiShared.Color(0, 0, 0, 255), 2); + UiShared.DrawOutlinedFont(drawList, $"Downloading {doneDownloads}/{totalDownloads}", + new Vector2(basePosition.X + xDistance, basePosition.Y + yDistance * multBase), + UiShared.Color(255, 255, 255, 255), UiShared.Color(0, 0, 0, 255), 2); + UiShared.DrawOutlinedFont(drawList, $"{UiShared.ByteToString(totalDownloaded)}/{UiShared.ByteToString(totalToDownload)}", + new Vector2(basePosition.X + xDistance, basePosition.Y + yDistance * (1 + multBase)), + UiShared.Color(255, 255, 255, 255), UiShared.Color(0, 0, 0, 255), 2); + } + } +} \ No newline at end of file diff --git a/MareSynchronos/UI/IntroUI.cs b/MareSynchronos/UI/IntroUI.cs index 6273316..4bef6f9 100644 --- a/MareSynchronos/UI/IntroUI.cs +++ b/MareSynchronos/UI/IntroUI.cs @@ -7,9 +7,9 @@ using MareSynchronos.Utils; namespace MareSynchronos.UI { - internal class IntroUI : Window, IDisposable + internal class IntroUi : Window, IDisposable { - private readonly UIShared _uiShared; + private readonly UiShared _uiShared; private readonly Configuration _pluginConfiguration; private readonly FileCacheManager _fileCacheManager; private readonly WindowSystem _windowSystem; @@ -19,14 +19,16 @@ namespace MareSynchronos.UI public void Dispose() { - Logger.Debug("Disposing " + nameof(IntroUI)); + Logger.Debug("Disposing " + nameof(IntroUi)); _windowSystem.RemoveWindow(this); } - public IntroUI(WindowSystem windowSystem, UIShared uiShared, Configuration pluginConfiguration, + public IntroUi(WindowSystem windowSystem, UiShared uiShared, Configuration pluginConfiguration, FileCacheManager fileCacheManager) : base("Mare Synchronos Setup") { + Logger.Debug("Creating " + nameof(IntroUi)); + _uiShared = uiShared; _pluginConfiguration = pluginConfiguration; _fileCacheManager = fileCacheManager; @@ -54,12 +56,12 @@ namespace MareSynchronos.UI ImGui.Text("Welcome to Mare Synchronos!"); ImGui.SetWindowFontScale(1.0f); ImGui.Separator(); - UIShared.TextWrapped("Mare Synchronos is a plugin that will replicate your full current character state including all Penumbra mods to other paired Mare Synchronos users. " + + UiShared.TextWrapped("Mare Synchronos is a plugin that will replicate your full current character state including all Penumbra mods to other paired Mare Synchronos users. " + "Note that you will have to have Penumbra as well as Glamourer installed to use this plugin."); - UIShared.TextWrapped("We will have to setup a few things first before you can start using this plugin. Click on next to continue."); + UiShared.TextWrapped("We will have to setup a few things first before you can start using this plugin. Click on next to continue."); ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); - UIShared.TextWrapped("Note: Any modifications you have applied through anything but Penumbra cannot be shared and your character state on other clients " + + UiShared.TextWrapped("Note: Any modifications you have applied through anything but Penumbra cannot be shared and your character state on other clients " + "might look broken because of this. If you want to use this plugin you will have to move your mods to Penumbra."); ImGui.PopStyleColor(); if (!_uiShared.DrawOtherPluginState()) return; @@ -82,25 +84,25 @@ namespace MareSynchronos.UI ImGui.TextColored(ImGuiColors.DalamudRed, readThis); ImGui.SetWindowFontScale(1.0f); ImGui.Separator(); - UIShared.TextWrapped("All of the mod files currently active on your character as well as your current character state will be uploaded to the service you registered yourself at automatically. " + + UiShared.TextWrapped("All of the mod files currently active on your character as well as your current character state will be uploaded to the service you registered yourself at automatically. " + "The plugin will exclusively upload the necessary mod files and not the whole mod."); - UIShared.TextWrapped("If you are on a data capped internet connection, higher fees due to data usage depending on the amount of downloaded and uploaded mod files might occur. " + + UiShared.TextWrapped("If you are on a data capped internet connection, higher fees due to data usage depending on the amount of downloaded and uploaded mod files might occur. " + "Mod files will be compressed on up- and download to save on bandwidth usage. Due to varying up- and download speeds, changes in characters might not be visible immediately. " + "Files present on the service that already represent your active mod files will not be uploaded again."); ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); - UIShared.TextWrapped("The mod files you are uploading are confidential and will not be distributed to parties other than the ones who are requesting the exact same mod files. " + + UiShared.TextWrapped("The mod files you are uploading are confidential and will not be distributed to parties other than the ones who are requesting the exact same mod files. " + "Please think about who you are going to pair since it is unavoidable that they will receive and locally cache the necessary mod files that you have currently in use. " + "Locally cached mod files will have arbitrary file names to discourage attempts at replicating the original mod."); ImGui.PopStyleColor(); ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); - UIShared.TextWrapped("The plugin creator tried their best to keep you secure. However, there is no guarantee for 100% security. Do not blindly pair your client with everyone."); + UiShared.TextWrapped("The plugin creator tried their best to keep you secure. However, there is no guarantee for 100% security. Do not blindly pair your client with everyone."); ImGui.PopStyleColor(); - UIShared.TextWrapped("Mod files that are saved on the service will remain on the service as long as there are requests for the files from clients. " + + UiShared.TextWrapped("Mod files that are saved on the service will remain on the service as long as there are requests for the files from clients. " + "After a period of not being used, the mod files will be automatically deleted. " + "You will also be able to wipe all the files you have personally uploaded on request. " + "The service holds no information about which mod files belong to which mod."); ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); - UIShared.TextWrapped("This service is provided as-is. In case of abuse, contact darkarchon#4313 on Discord or join the Mare Synchronos Discord. " + + UiShared.TextWrapped("This service is provided as-is. In case of abuse, contact darkarchon#4313 on Discord or join the Mare Synchronos Discord. " + "To accept those conditions hold CTRL while clicking 'I agree'"); ImGui.PopStyleColor(); ImGui.Separator(); @@ -120,12 +122,12 @@ namespace MareSynchronos.UI ImGui.Text("File Cache Setup"); ImGui.SetWindowFontScale(1.0f); ImGui.Separator(); - UIShared.TextWrapped("To not unnecessary download files already present on your computer, Mare Synchronos will have to scan your Penumbra mod directory. " + + UiShared.TextWrapped("To not unnecessary download files already present on your computer, Mare Synchronos will have to scan your Penumbra mod directory. " + "Additionally, a local cache folder must be set where Mare Synchronos will download its local file cache to. " + "Once the Cache Folder is set and the scan complete, this page will automatically forward to registration at a service."); - UIShared.TextWrapped("Note: The initial scan, depending on the amount of mods you have, might take a while. Please wait until it is completed."); + UiShared.TextWrapped("Note: The initial scan, depending on the amount of mods you have, might take a while. Please wait until it is completed."); ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); - UIShared.TextWrapped("Warning: once past this step you should not delete the FileCache.db of Mare Synchronos in the Plugin Configurations folder of Dalamud. " + + UiShared.TextWrapped("Warning: once past this step you should not delete the FileCache.db of Mare Synchronos in the Plugin Configurations folder of Dalamud. " + "Otherwise on the next launch a full re-scan of the file cache database will be initiated."); ImGui.PopStyleColor(); _uiShared.DrawCacheDirectorySetting(); @@ -133,7 +135,7 @@ namespace MareSynchronos.UI if (!_fileCacheManager.IsScanRunning) { - UIShared.TextWrapped("You can adjust how many parallel threads will be used for scanning. Mind that ultimately it will depend on the amount of mods, your disk speed and your CPU. " + + UiShared.TextWrapped("You can adjust how many parallel threads will be used for scanning. Mind that ultimately it will depend on the amount of mods, your disk speed and your CPU. " + "More is not necessarily better, the default of 10 should be fine for most cases."); _uiShared.DrawParallelScansSetting(); @@ -157,18 +159,18 @@ namespace MareSynchronos.UI if (_pluginConfiguration.ClientSecret.ContainsKey(_pluginConfiguration.ApiUri)) { ImGui.Separator(); - UIShared.TextWrapped(_pluginConfiguration.ClientSecret[_pluginConfiguration.ApiUri]); + UiShared.TextWrapped(_pluginConfiguration.ClientSecret[_pluginConfiguration.ApiUri]); ImGui.Separator(); if (ImGui.Button("Copy secret key to clipboard")) { ImGui.SetClipboardText(_pluginConfiguration.ClientSecret[_pluginConfiguration.ApiUri]); } ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); - UIShared.TextWrapped("This is the only time you will be able to see this key in the UI. You can copy it to make a backup somewhere."); + UiShared.TextWrapped("This is the only time you will be able to see this key in the UI. You can copy it to make a backup somewhere."); ImGui.PopStyleColor(); ImGui.Separator(); ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.ParsedGreen); - UIShared.TextWrapped("You are now ready to go. Press Finish to finalize the settings and open the Mare Synchronos main UI."); + UiShared.TextWrapped("You are now ready to go. Press Finish to finalize the settings and open the Mare Synchronos main UI."); ImGui.PopStyleColor(); ImGui.Separator(); if (ImGui.Button("Finish##finishIntro")) @@ -179,13 +181,13 @@ namespace MareSynchronos.UI } else { - UIShared.TextWrapped("You will now have to register at a service. You can use the provided central service or pick a custom one. " + + UiShared.TextWrapped("You will now have to register at a service. You can use the provided central service or pick a custom one. " + "There is no support for custom services from the plugin creator. Use at your own risk."); ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); - UIShared.TextWrapped("On registration on a service the plugin will create and save a secret key to your plugin configuration. " + + UiShared.TextWrapped("On registration on a service the plugin will create and save a secret key to your plugin configuration. " + "Make a backup of your secret key. In case of loss, it cannot be restored. The secret key is your identification to the service " + "to verify who you are. It is directly tied to the UID you will be receiving. In case of loss, you will have to re-register an account."); - UIShared.TextWrapped("Do not ever, under any circumstances, share your secret key to anyone! Likewise do not share your Mare Synchronos plugin configuration to anyone!"); + UiShared.TextWrapped("Do not ever, under any circumstances, share your secret key to anyone! Likewise do not share your Mare Synchronos plugin configuration to anyone!"); ImGui.PopStyleColor(); _uiShared.DrawServiceSelection(); } diff --git a/MareSynchronos/UI/PluginUI.cs b/MareSynchronos/UI/PluginUI.cs index aae6627..3ab5cf0 100644 --- a/MareSynchronos/UI/PluginUI.cs +++ b/MareSynchronos/UI/PluginUI.cs @@ -5,21 +5,23 @@ using ImGuiNET; using MareSynchronos.WebAPI; using System; using System.Linq; -using MareSynchronos.Managers; +using System.Threading.Tasks; using MareSynchronos.Utils; namespace MareSynchronos.UI { - class PluginUi : Window, IDisposable + public class PluginUi : Window, IDisposable { private readonly Configuration _configuration; private readonly WindowSystem _windowSystem; private readonly ApiController _apiController; - private readonly UIShared _uiShared; + private readonly UiShared _uiShared; public PluginUi(WindowSystem windowSystem, - UIShared uiShared, Configuration configuration, ApiController apiController) : base("Mare Synchronos Settings", ImGuiWindowFlags.None) + UiShared uiShared, Configuration configuration, ApiController apiController) : base("Mare Synchronos Settings", ImGuiWindowFlags.None) { + Logger.Debug("Creating " + nameof(PluginUi)); + SizeConstraints = new WindowSizeConstraints() { MinimumSize = new(800, 400), @@ -85,6 +87,7 @@ namespace MareSynchronos.UI DrawPairedClientsContent(); DrawFileCacheSettings(); DrawCurrentTransfers(); + DrawAdministration(); } else { @@ -93,16 +96,36 @@ namespace MareSynchronos.UI } } + private void DrawAdministration() + { + if (ImGui.TreeNode( + $"User Administration")) + { + if (ImGui.Button("Delete all my files")) + { + Task.Run(() => _apiController.DeleteAllMyFiles()); + } + ImGui.TreePop(); + } + } + private void DrawCurrentTransfers() { if (ImGui.TreeNode( $"Current Transfers")) { + bool showTransferWindow = _configuration.ShowTransferWindow; + if (ImGui.Checkbox("Show separate Transfer window while transfers are active", ref showTransferWindow)) + { + _configuration.ShowTransferWindow = showTransferWindow; + _configuration.Save(); + } + if (ImGui.BeginTable("TransfersTable", 2)) { ImGui.TableSetupColumn( - $"Uploads ({UIShared.ByteToKiB(_apiController.CurrentUploads.Sum(a => a.Value.Item1))} / {UIShared.ByteToKiB(_apiController.CurrentUploads.Sum(a => a.Value.Item2))})"); - ImGui.TableSetupColumn($"Downloads ({UIShared.ByteToKiB(_apiController.CurrentDownloads.Sum(a => a.Value.Item1))} / {UIShared.ByteToKiB(_apiController.CurrentDownloads.Sum(a => a.Value.Item2))})"); + $"Uploads ({UiShared.ByteToString(_apiController.CurrentUploads.Sum(a => a.Value.Item1))} / {UiShared.ByteToString(_apiController.CurrentUploads.Sum(a => a.Value.Item2))})"); + ImGui.TableSetupColumn($"Downloads ({UiShared.ByteToString(_apiController.CurrentDownloads.Sum(a => a.Value.Item1))} / {UiShared.ByteToString(_apiController.CurrentDownloads.Sum(a => a.Value.Item2))})"); ImGui.TableHeadersRow(); @@ -115,14 +138,14 @@ namespace MareSynchronos.UI ImGui.TableHeadersRow(); foreach (var hash in _apiController.CurrentUploads.Keys) { - var color = UIShared.UploadColor(_apiController.CurrentUploads[hash]); + var color = UiShared.UploadColor(_apiController.CurrentUploads[hash]); ImGui.PushStyleColor(ImGuiCol.Text, color); ImGui.TableNextColumn(); ImGui.Text(hash); ImGui.TableNextColumn(); - ImGui.Text(UIShared.ByteToKiB(_apiController.CurrentUploads[hash].Item1)); + ImGui.Text(UiShared.ByteToString(_apiController.CurrentUploads[hash].Item1)); ImGui.TableNextColumn(); - ImGui.Text(UIShared.ByteToKiB(_apiController.CurrentUploads[hash].Item2)); + ImGui.Text(UiShared.ByteToString(_apiController.CurrentUploads[hash].Item2)); ImGui.PopStyleColor(); ImGui.TableNextRow(); } @@ -139,14 +162,14 @@ namespace MareSynchronos.UI ImGui.TableHeadersRow(); foreach (var hash in _apiController.CurrentDownloads.Keys) { - var color = UIShared.UploadColor(_apiController.CurrentDownloads[hash]); + var color = UiShared.UploadColor(_apiController.CurrentDownloads[hash]); ImGui.PushStyleColor(ImGuiCol.Text, color); ImGui.TableNextColumn(); ImGui.Text(hash); ImGui.TableNextColumn(); - ImGui.Text(UIShared.ByteToKiB(_apiController.CurrentDownloads[hash].Item1)); + ImGui.Text(UiShared.ByteToString(_apiController.CurrentDownloads[hash].Item1)); ImGui.TableNextColumn(); - ImGui.Text(UIShared.ByteToKiB(_apiController.CurrentDownloads[hash].Item2)); + ImGui.Text(UiShared.ByteToString(_apiController.CurrentDownloads[hash].Item2)); ImGui.PopStyleColor(); ImGui.TableNextRow(); } @@ -199,13 +222,13 @@ namespace MareSynchronos.UI ImGui.TableNextColumn(); ImGui.TextColored( - UIShared.GetBoolColor(item.IsSynced && !item.IsPausedFromOthers && !item.IsPaused), + UiShared.GetBoolColor(item.IsSynced && !item.IsPausedFromOthers && !item.IsPaused), item.OtherUID); ImGui.TableNextColumn(); string pairString = !item.IsSynced ? "Has not added you" : ((item.IsPaused || item.IsPausedFromOthers) ? "Unpaired" : "Paired"); - ImGui.TextColored(UIShared.GetBoolColor(item.IsSynced && !item.IsPaused && !item.IsPausedFromOthers), pairString); + ImGui.TextColored(UiShared.GetBoolColor(item.IsSynced && !item.IsPaused && !item.IsPausedFromOthers), pairString); ImGui.TableNextColumn(); string charComment = _configuration.UidComments.ContainsKey(item.OtherUID) ? _configuration.UidComments[item.OtherUID] : string.Empty; ImGui.SetNextItemWidth(400); diff --git a/MareSynchronos/UI/UIShared.cs b/MareSynchronos/UI/UIShared.cs index 22c0c23..6533439 100644 --- a/MareSynchronos/UI/UIShared.cs +++ b/MareSynchronos/UI/UIShared.cs @@ -9,14 +9,14 @@ using MareSynchronos.WebAPI; namespace MareSynchronos.UI { - internal class UIShared + public class UiShared { private readonly IpcManager _ipcManager; private readonly ApiController _apiController; private readonly FileCacheManager _fileCacheManager; private readonly Configuration _pluginConfiguration; - public UIShared(IpcManager ipcManager, ApiController apiController, FileCacheManager fileCacheManager, Configuration pluginConfiguration) + public UiShared(IpcManager ipcManager, ApiController apiController, FileCacheManager fileCacheManager, Configuration pluginConfiguration) { _ipcManager = ipcManager; _apiController = apiController; @@ -87,7 +87,45 @@ namespace MareSynchronos.UI public static Vector4 UploadColor((long, long) data) => data.Item1 == 0 ? ImGuiColors.DalamudGrey : data.Item1 == data.Item2 ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudYellow; - public static string ByteToKiB(long bytes) => (bytes / 1024.0).ToString("0.00") + " KiB"; + public static uint Color(byte r, byte g, byte b, byte a) + { uint ret = a; ret <<= 8; ret += b; ret <<= 8; ret += g; ret <<= 8; ret += r; return ret; } + + public static void DrawOutlinedFont(ImDrawListPtr drawList, string text, Vector2 textPos, uint fontColor, uint outlineColor, int thickness) + { + drawList.AddText(textPos with { Y = textPos.Y - thickness }, + outlineColor, text); + drawList.AddText(textPos with { X = textPos.X - thickness }, + outlineColor, text); + drawList.AddText(textPos with { Y = textPos.Y + thickness }, + outlineColor, text); + drawList.AddText(textPos with { X = textPos.X + thickness }, + outlineColor, text); + drawList.AddText(new Vector2(textPos.X - thickness, textPos.Y - thickness), + outlineColor, text); + drawList.AddText(new Vector2(textPos.X + thickness, textPos.Y + thickness), + outlineColor, text); + drawList.AddText(new Vector2(textPos.X - thickness, textPos.Y + thickness), + outlineColor, text); + drawList.AddText(new Vector2(textPos.X + thickness, textPos.Y - thickness), + outlineColor, text); + + drawList.AddText(textPos, fontColor, text); + drawList.AddText(textPos, fontColor, text); + } + + public static string ByteToString(long bytes) + { + double output = bytes; + var suffix = new[] { "B", "KiB", "MiB", "GiB" }; + var suffixIdx = 0; + while (output / 1024.0 > 1024) + { + output /= 1024.0; + suffixIdx++; + } + + return output.ToString("0.00") + " " + suffix[suffixIdx]; + } private int _serverSelectionIndex = 0; diff --git a/MareSynchronos/WebAPI/ApiController.cs b/MareSynchronos/WebAPI/ApiController.cs index 7805ea8..23d9611 100644 --- a/MareSynchronos/WebAPI/ApiController.cs +++ b/MareSynchronos/WebAPI/ApiController.cs @@ -87,7 +87,7 @@ namespace MareSynchronos.WebAPI public void Dispose() { Logger.Debug("Disposing " + nameof(ApiController)); - + _cts?.Cancel(); _ = DisposeHubConnections(); } @@ -119,8 +119,10 @@ namespace MareSynchronos.WebAPI CurrentDownloads[file.Hash] = (0, fileSize); } + List downloadedHashes = new(); foreach (var file in fileReplacementDto.Where(f => CurrentDownloads[f.Hash].Item2 > 0)) { + if (downloadedHashes.Contains(file.Hash)) continue; var hash = file.Hash; var data = await DownloadFile(hash); var extractedFile = LZ4Codec.Unwrap(data); @@ -128,6 +130,7 @@ namespace MareSynchronos.WebAPI var filePath = Path.Combine(_pluginConfiguration.CacheFolder, file.Hash + "." + ext); await File.WriteAllBytesAsync(filePath, extractedFile); Logger.Debug("File downloaded to " + filePath); + downloadedHashes.Add(hash); } CurrentDownloads.Clear(); @@ -311,6 +314,11 @@ namespace MareSynchronos.WebAPI long fileSize = await _fileHub!.InvokeAsync("GetFileSize", hash); } + public async Task DeleteAllMyFiles() + { + await _fileHub!.SendAsync("DeleteAllFiles"); + } + private async Task DisposeHubConnections() { if (_fileHub != null) @@ -430,6 +438,7 @@ namespace MareSynchronos.WebAPI UnpairedFromOther?.Invoke(characterIdentifier, EventArgs.Empty); } } + private async Task UploadFile(byte[] compressedFile, string fileHash, CancellationToken uploadToken) { if (uploadToken.IsCancellationRequested) return;