From 27bbe966184ccc8d67c3b6b0fb4c880fd2fabc90 Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Sat, 8 Feb 2025 00:14:16 +0100 Subject: [PATCH] MCDO fixes 2 implement RestoreThenUpload for charadata cleanup, actually show success/failure actually sort chara data list lmao add shared character data sets display to main ui (partial) increase size of mcdo table slightly fix selecting last new entry --- MareAPI | 2 +- .../Services/CharaData/CharaDataManager.cs | 37 ++++++++++++--- MareSynchronos/Services/Mediator/Messages.cs | 1 + MareSynchronos/UI/CharaDataHubUi.McdOnline.cs | 40 ++++++++++------- MareSynchronos/UI/CharaDataHubUi.cs | 45 ++++++++++++++++--- .../ApiController.Functions.CharaData.cs | 20 ++++++++- 6 files changed, 113 insertions(+), 32 deletions(-) diff --git a/MareAPI b/MareAPI index 4d8c380..fef2365 160000 --- a/MareAPI +++ b/MareAPI @@ -1 +1 @@ -Subproject commit 4d8c380dab3397eda9bcbfd249be91c32af9d01b +Subproject commit fef23652805ec4cd5a62e5a5597bc3b61002b0ae diff --git a/MareSynchronos/Services/CharaData/CharaDataManager.cs b/MareSynchronos/Services/CharaData/CharaDataManager.cs index 073982a..18a30fc 100644 --- a/MareSynchronos/Services/CharaData/CharaDataManager.cs +++ b/MareSynchronos/Services/CharaData/CharaDataManager.cs @@ -103,7 +103,7 @@ public sealed partial class CharaDataManager : DisposableMediatorSubscriberBase public IDictionary> SharedWithYouData => _sharedWithYouData; public Task? UiBlockingComputation { get; private set; } public ValueProgress? UploadProgress { get; private set; } - public Task<(string Output, bool Success)>? UploadTask { get; private set; } + public Task<(string Output, bool Success)>? UploadTask { get; set; } public bool BrioAvailable => _ipcManager.Brio.APIAvailable; public Task ApplyCharaData(CharaDataMetaInfoDto dataMetaInfoDto, string charaName) @@ -581,8 +581,31 @@ public sealed partial class CharaDataManager : DisposableMediatorSubscriberBase var hasDto = _ownCharaData.TryGetValue(id, out var dto); if (!hasDto || dto == null) return; - var missingFileList = dto.MissingFiles.ToList(); - UiBlockingComputation = UploadTask = UploadFiles(missingFileList, async () => + UiBlockingComputation = UploadTask = RestoreThenUpload(dto); + } + + private async Task<(string Output, bool Success)> RestoreThenUpload(CharaDataFullExtendedDto dto) + { + var newDto = await _apiController.CharaDataAttemptRestore(dto.Id).ConfigureAwait(false); + if (newDto == null) + { + _ownCharaData.Remove(dto.Id); + _metaInfoCache.Remove(dto.FullId, out _); + UiBlockingComputation = null; + return ("No such DTO found", false); + } + + await AddOrUpdateDto(newDto).ConfigureAwait(false); + _ = _ownCharaData.TryGetValue(dto.Id, out var extendedDto); + + if (!extendedDto!.HasMissingFiles) + { + UiBlockingComputation = null; + return ("Restored successfully", true); + } + + var missingFileList = extendedDto!.MissingFiles.ToList(); + var result = await UploadFiles(missingFileList, async () => { var newFilePaths = dto.FileGamePaths; foreach (var missing in missingFileList) @@ -595,7 +618,10 @@ public sealed partial class CharaDataManager : DisposableMediatorSubscriberBase }; var res = await _apiController.CharaDataUpdate(updateDto).ConfigureAwait(false); await AddOrUpdateDto(res).ConfigureAwait(false); - }); + }).ConfigureAwait(false); + + UiBlockingComputation = null; + return result; } internal void ApplyDataToSelf(CharaDataFullExtendedDto dataDto) @@ -926,8 +952,7 @@ public sealed partial class CharaDataManager : DisposableMediatorSubscriberBase } finally { - UploadTask = null; - UploadProgress = null; + UiBlockingComputation = null; } } diff --git a/MareSynchronos/Services/Mediator/Messages.cs b/MareSynchronos/Services/Mediator/Messages.cs index 7fe9f4e..a64f7da 100644 --- a/MareSynchronos/Services/Mediator/Messages.cs +++ b/MareSynchronos/Services/Mediator/Messages.cs @@ -100,6 +100,7 @@ public record PairDataAnalyzedMessage(string UID) : KeyedMessage(UID); public record GameObjectHandlerCreatedMessage(GameObjectHandler GameObjectHandler, bool OwnedObject) : MessageBase; public record GameObjectHandlerDestroyedMessage(GameObjectHandler GameObjectHandler, bool OwnedObject) : MessageBase; public record HaltCharaDataCreation(bool Resume = false) : SameThreadMessage; +public record OpenCharaDataHubWithFilterMessage(UserData UserData) : MessageBase; public record PluginChangeMessage(string InternalName, Version Version, bool IsLoaded) : KeyedMessage(InternalName); #pragma warning restore S2094 diff --git a/MareSynchronos/UI/CharaDataHubUi.McdOnline.cs b/MareSynchronos/UI/CharaDataHubUi.McdOnline.cs index cefc037..0c3d1aa 100644 --- a/MareSynchronos/UI/CharaDataHubUi.McdOnline.cs +++ b/MareSynchronos/UI/CharaDataHubUi.McdOnline.cs @@ -38,8 +38,9 @@ internal sealed partial class CharaDataHubUi } var indent = ImRaii.PushIndent(10f); - if (canUpdate || (!_charaDataManager.UploadTask?.IsCompleted ?? false)) + if (canUpdate || _charaDataManager.UploadTask != null) { + ImGuiHelpers.ScaledDummy(5); UiSharedService.DrawGrouped(() => { if (canUpdate) @@ -84,6 +85,11 @@ internal sealed partial class CharaDataHubUi } }); } + else if (_charaDataManager.UploadTask?.IsCompleted ?? false) + { + var color = UiSharedService.GetBoolColor(_charaDataManager.UploadTask.Result.Success); + UiSharedService.ColorTextWrapped(_charaDataManager.UploadTask.Result.Output, color); + } }); } indent.Dispose(); @@ -359,7 +365,7 @@ internal sealed partial class CharaDataHubUi if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Character Data")) { _ = _charaDataManager.DeleteCharaData(dataDto); - _selectedDtoId = string.Empty; + SelectedDtoId = string.Empty; } } if (!UiSharedService.CtrlPressed()) @@ -548,7 +554,7 @@ internal sealed partial class CharaDataHubUi } using (var table = ImRaii.Table("Own Character Data", 12, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.ScrollY, - new Vector2(ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X, 100))) + new Vector2(ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X, 110))) { if (table) { @@ -566,11 +572,11 @@ internal sealed partial class CharaDataHubUi ImGui.TableSetupColumn("Expires", ImGuiTableColumnFlags.WidthFixed, 18); ImGui.TableSetupScrollFreeze(0, 1); ImGui.TableHeadersRow(); - foreach (var entry in _charaDataManager.OwnCharaData.Values) + foreach (var entry in _charaDataManager.OwnCharaData.Values.OrderBy(b => b.CreatedDate)) { var uDto = _charaDataManager.GetUpdateDto(entry.Id); ImGui.TableNextColumn(); - if (string.Equals(entry.Id, _selectedDtoId, StringComparison.Ordinal)) + if (string.Equals(entry.Id, SelectedDtoId, StringComparison.Ordinal)) _uiSharedService.IconText(FontAwesomeIcon.CaretRight); ImGui.TableNextColumn(); @@ -587,48 +593,48 @@ internal sealed partial class CharaDataHubUi { ImGui.TextUnformatted(idText); } - if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id; + if (ImGui.IsItemClicked()) SelectedDtoId = entry.Id; ImGui.TableNextColumn(); ImGui.TextUnformatted(entry.Description); - if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id; + if (ImGui.IsItemClicked()) SelectedDtoId = entry.Id; UiSharedService.AttachToolTip(entry.Description); ImGui.TableNextColumn(); ImGui.TextUnformatted(entry.CreatedDate.ToLocalTime().ToString()); - if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id; + if (ImGui.IsItemClicked()) SelectedDtoId = entry.Id; ImGui.TableNextColumn(); ImGui.TextUnformatted(entry.UpdatedDate.ToLocalTime().ToString()); - if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id; + if (ImGui.IsItemClicked()) SelectedDtoId = entry.Id; ImGui.TableNextColumn(); ImGui.TextUnformatted(entry.DownloadCount.ToString()); - if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id; + if (ImGui.IsItemClicked()) SelectedDtoId = entry.Id; ImGui.TableNextColumn(); bool isDownloadable = !entry.HasMissingFiles && !string.IsNullOrEmpty(entry.GlamourerData); _uiSharedService.BooleanToColoredIcon(isDownloadable, false); - if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id; + if (ImGui.IsItemClicked()) SelectedDtoId = entry.Id; UiSharedService.AttachToolTip(isDownloadable ? "Can be downloaded by others" : "Cannot be downloaded: Has missing files or data, please review this entry manually"); ImGui.TableNextColumn(); var count = entry.FileGamePaths.Concat(entry.FileSwaps).Count(); ImGui.TextUnformatted(count.ToString()); - if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id; + if (ImGui.IsItemClicked()) SelectedDtoId = entry.Id; UiSharedService.AttachToolTip(count == 0 ? "No File data attached" : "Has File data attached"); ImGui.TableNextColumn(); bool hasGlamourerData = !string.IsNullOrEmpty(entry.GlamourerData); _uiSharedService.BooleanToColoredIcon(hasGlamourerData, false); - if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id; + if (ImGui.IsItemClicked()) SelectedDtoId = entry.Id; UiSharedService.AttachToolTip(string.IsNullOrEmpty(entry.GlamourerData) ? "No Glamourer data attached" : "Has Glamourer data attached"); ImGui.TableNextColumn(); bool hasCustomizeData = !string.IsNullOrEmpty(entry.CustomizeData); _uiSharedService.BooleanToColoredIcon(hasCustomizeData, false); - if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id; + if (ImGui.IsItemClicked()) SelectedDtoId = entry.Id; UiSharedService.AttachToolTip(string.IsNullOrEmpty(entry.CustomizeData) ? "No Customize+ data attached" : "Has Customize+ data attached"); ImGui.TableNextColumn(); @@ -636,7 +642,7 @@ internal sealed partial class CharaDataHubUi if (!Equals(DateTime.MaxValue, entry.ExpiryDate)) eIcon = FontAwesomeIcon.Clock; _uiSharedService.IconText(eIcon, ImGuiColors.DalamudYellow); - if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id; + if (ImGui.IsItemClicked()) SelectedDtoId = entry.Id; if (eIcon != FontAwesomeIcon.None) { UiSharedService.AttachToolTip($"This entry will expire on {entry.ExpiryDate.ToLocalTime()}"); @@ -690,12 +696,12 @@ internal sealed partial class CharaDataHubUi var charaDataEntries = _charaDataManager.OwnCharaData.Count; if (charaDataEntries != _dataEntries && _selectNewEntry && _charaDataManager.OwnCharaData.Any()) { - _selectedDtoId = _charaDataManager.OwnCharaData.Last().Value.Id; + SelectedDtoId = _charaDataManager.OwnCharaData.OrderBy(o => o.Value.CreatedDate).Last().Value.Id; _selectNewEntry = false; } _dataEntries = _charaDataManager.OwnCharaData.Count; - _ = _charaDataManager.OwnCharaData.TryGetValue(_selectedDtoId, out var dto); + _ = _charaDataManager.OwnCharaData.TryGetValue(SelectedDtoId, out var dto); DrawEditCharaData(dto); } diff --git a/MareSynchronos/UI/CharaDataHubUi.cs b/MareSynchronos/UI/CharaDataHubUi.cs index ad1d3c7..0ec9422 100644 --- a/MareSynchronos/UI/CharaDataHubUi.cs +++ b/MareSynchronos/UI/CharaDataHubUi.cs @@ -47,6 +47,19 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase private bool _openMcdOnlineOnNextRun = false; private bool _readExport; private string _selectedDtoId = string.Empty; + private string SelectedDtoId + { + get => _selectedDtoId; + set + { + if (!string.Equals(_selectedDtoId, value, StringComparison.Ordinal)) + { + _charaDataManager.UploadTask = null; + _selectedDtoId = value; + } + + } + } private string _selectedSpecificUserIndividual = string.Empty; private string _selectedSpecificGroupIndividual = string.Empty; private string _sharedWithYouDescriptionFilter = string.Empty; @@ -76,8 +89,17 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase _fileDialogManager = fileDialogManager; _pairManager = pairManager; Mediator.Subscribe(this, (_) => IsOpen |= _configService.Current.OpenMareHubOnGposeStart); + Mediator.Subscribe(this, (msg) => + { + IsOpen = true; + _openDataApplicationShared = true; + _sharedWithYouOwnerFilter = msg.UserData.AliasOrUID; + UpdateFilteredItems(); + }); } + private bool _openDataApplicationShared = false; + public string CharaName(string name) { if (_abbreviateCharaName) @@ -98,7 +120,7 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase } _closalCts.Cancel(); - _selectedDtoId = string.Empty; + SelectedDtoId = string.Empty; _filteredDict = null; _sharedWithYouOwnerFilter = string.Empty; _importCode = string.Empty; @@ -180,7 +202,7 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase _isHandlingSelf = _charaDataManager.HandledCharaData.Any(c => c.IsSelf); if (_isHandlingSelf) _openMcdOnlineOnNextRun = false; - using (var applicationTabItem = ImRaii.TabItem("Data Application")) + using (var applicationTabItem = ImRaii.TabItem("Data Application", _openDataApplicationShared ? ImGuiTabItemFlags.SetSelected : ImGuiTabItemFlags.None)) { if (applicationTabItem) { @@ -216,7 +238,7 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase } } - using (var gposeTabItem = ImRaii.TabItem("Apply Data")) + using (var gposeTabItem = ImRaii.TabItem("Apply Data", _openDataApplicationShared ? ImGuiTabItemFlags.SetSelected : ImGuiTabItemFlags.None)) { if (gposeTabItem) { @@ -677,7 +699,7 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase } } - using (var sharedWithYouTabItem = ImRaii.TabItem("Shared With You")) + using (var sharedWithYouTabItem = ImRaii.TabItem("Shared With You", _openDataApplicationShared ? ImGuiTabItemFlags.SetSelected : ImGuiTabItemFlags.None)) { using var id = ImRaii.PushId("sharedWithYouTab"); if (sharedWithYouTabItem) @@ -691,8 +713,12 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase DrawUpdateSharedDataButton(); - - UiSharedService.DrawTree("Filters", () => + int activeFilters = 0; + if (!string.IsNullOrEmpty(_sharedWithYouOwnerFilter)) activeFilters++; + if (!string.IsNullOrEmpty(_sharedWithYouDescriptionFilter)) activeFilters++; + if (_sharedWithYouDownloadableFilter) activeFilters++; + string filtersText = activeFilters == 0 ? "Filters" : $"Filters ({activeFilters} active)"; + UiSharedService.DrawTree($"{filtersText}##filters", () => { var filterWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X; ImGui.SetNextItemWidth(filterWidth); @@ -731,6 +757,9 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase ImGuiHelpers.ScaledDummy(5); foreach (var entry in _filteredDict ?? []) { + bool isFilteredAndHasToBeOpened = entry.Key.Contains(_sharedWithYouOwnerFilter) && _openDataApplicationShared; + if (isFilteredAndHasToBeOpened) + ImGui.SetNextItemOpen(isFilteredAndHasToBeOpened); UiSharedService.DrawTree($"{entry.Key} - [{entry.Value.Count} Character Data Sets]##{entry.Key}", () => { foreach (var data in entry.Value) @@ -739,6 +768,8 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase } ImGuiHelpers.ScaledDummy(5); }); + if (isFilteredAndHasToBeOpened) + _openDataApplicationShared = false; } } } @@ -912,7 +943,7 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase { if (_uiSharedService.IconTextButton(FontAwesomeIcon.Edit, "Open in MCD Online Editor")) { - _selectedDtoId = data.Id; + SelectedDtoId = data.Id; _openMcdOnlineOnNextRun = true; } } diff --git a/MareSynchronos/WebAPI/SignalR/ApiController.Functions.CharaData.cs b/MareSynchronos/WebAPI/SignalR/ApiController.Functions.CharaData.cs index 865c4aa..1110827 100644 --- a/MareSynchronos/WebAPI/SignalR/ApiController.Functions.CharaData.cs +++ b/MareSynchronos/WebAPI/SignalR/ApiController.Functions.CharaData.cs @@ -1,4 +1,6 @@ -using MareSynchronos.API.Dto.CharaData; +using MareSynchronos.API.Data; +using MareSynchronos.API.Dto.CharaData; +using MareSynchronos.Services.CharaData.Models; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.Logging; @@ -69,6 +71,22 @@ public partial class ApiController } } + public async Task CharaDataAttemptRestore(string id) + { + if (!IsConnected) return null; + + try + { + Logger.LogDebug("Attempting to restore chara data {id}", id); + return await _mareHub!.InvokeAsync(nameof(CharaDataAttemptRestore), id).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.LogWarning(ex, "Failed to restore chara data for {id}", id); + return null; + } + } + public async Task> CharaDataGetOwn() { if (!IsConnected) return [];