diff --git a/ModpackUpdater.Apps.Manager/Api/Model/IMainApi.cs b/ModpackUpdater.Apps.Manager/Api/Model/IMainApi.cs index 59859b5..f5170e0 100644 --- a/ModpackUpdater.Apps.Manager/Api/Model/IMainApi.cs +++ b/ModpackUpdater.Apps.Manager/Api/Model/IMainApi.cs @@ -8,4 +8,5 @@ public interface IMainApi { Window MainWindow { get; } MainWindowViewModel Model { get; } + bool HasClosed { get; } } diff --git a/ModpackUpdater.Apps.Manager/Features/CM/ClearDirectLinkFeature.cs b/ModpackUpdater.Apps.Manager/Features/CM/ClearDirectLinkFeature.cs index e9c50a0..b07552f 100644 --- a/ModpackUpdater.Apps.Manager/Features/CM/ClearDirectLinkFeature.cs +++ b/ModpackUpdater.Apps.Manager/Features/CM/ClearDirectLinkFeature.cs @@ -18,7 +18,7 @@ internal class ClearDirectLinkFeature : PluginFunction, IPluginFeatureProvider ExecuteFunctionAsync(PluginFunctionParameter? @params) { if (@params is MainApiParameters p && p.Api.Model.SelectedGridRow is { } row) - SharedFunctions.ClearDirectLinks(row); + SharedFunctions.ClearDirectLinks(p.Api, row); return Task.FromResult(null); } } diff --git a/ModpackUpdater.Apps.Manager/Features/CM/UpdateDirectLinkFeature.cs b/ModpackUpdater.Apps.Manager/Features/CM/UpdateDirectLinkFeature.cs index 7ed24ec..f8d6c3c 100644 --- a/ModpackUpdater.Apps.Manager/Features/CM/UpdateDirectLinkFeature.cs +++ b/ModpackUpdater.Apps.Manager/Features/CM/UpdateDirectLinkFeature.cs @@ -21,7 +21,7 @@ internal class UpdateDirectLinkFeature : PluginFunction, IPluginFeatureProvider< if (@params is not MainApiParameters p || p.Api.Model.SelectedGridRow is not MainWindowGridRow row) return null; - await SharedFunctions.FindNewDirectLinks(row); + await SharedFunctions.FindNewDirectLinks(p.Api, row); return null; } diff --git a/ModpackUpdater.Apps.Manager/Features/SharedFunctions.cs b/ModpackUpdater.Apps.Manager/Features/SharedFunctions.cs index f621958..d9072cc 100644 --- a/ModpackUpdater.Apps.Manager/Features/SharedFunctions.cs +++ b/ModpackUpdater.Apps.Manager/Features/SharedFunctions.cs @@ -1,4 +1,5 @@ using System.Text; +using Avalonia.Media; using ModpackUpdater.Apps.Manager.Api.Model; using ModpackUpdater.Apps.Manager.LangRes; using ModpackUpdater.Apps.Manager.Ui; @@ -14,16 +15,26 @@ namespace ModpackUpdater.Apps.Manager.Features; internal static class SharedFunctions { - public static async Task CheckActionHealthy(IMainApi api, params MainWindowGridRow[] rows) + private static readonly IImage? imageSourceSuccess = AppGlobals.Symbols.GetImageSource(AppSymbols.done); + private static readonly IImage? imageSourceWorking = AppGlobals.Symbols.GetImageSource(AppSymbols.hourglass); + private static readonly IImage? imageSourceFailed = AppGlobals.Symbols.GetImageSource(AppSymbols.close); + + public static async Task CheckActionHealthy(IMainApi api, params MainWindowGridRow[] rows) { var rowsCount = rows.Length; var failed = false; - var msg = default(string); var factory = new ModpackFactory(); + + api.Model.Progress.Value = 0; + api.Model.Progress.MaxValue = rowsCount; + api.Model.Progress.Visible = true; - for (var i = 0; i < rows.Length; i++) + for (var i = 0; i < rowsCount; i++) { var row = rows[i]; + if (row.SourceType == SourceType.DirectLink) + continue; + row.StateImage = imageSourceWorking; try { @@ -32,18 +43,17 @@ internal static class SharedFunctions } catch (Exception ex) { - msg = ex.Message; + // Ignore } + + if (api.HasClosed) + return; - row.IsValid = !failed; - - // rwb.Text = $"{i} / {rowsCount}"; + row.StateImage = failed ? imageSourceFailed : imageSourceSuccess; + api.Model.Progress.Value = i; } - if (rowsCount == 1 && failed && !string.IsNullOrWhiteSpace(msg)) - _ = MessageBoxManager.GetMessageBoxStandard(string.Empty, msg).ShowAsPopupAsync(api.MainWindow); - - return true; + api.Model.Progress.Visible = false; } public static async Task CollectUpdates(IMainApi api, params InstallAction[] actions) @@ -53,7 +63,7 @@ internal static class SharedFunctions // Collect updates var result = await AvaloniaFlyoutBase.Show(new UpdatesCollectorView(api.Model.CurrentWorkspace, actions), api.MainWindow); - if (result.Result is not ModUpdates resultUpdates) + if (api.HasClosed || result.Result is not ModUpdates resultUpdates) return false; // Collect versions with changes @@ -86,33 +96,55 @@ internal static class SharedFunctions return true; } - public static async Task FindNewDirectLinks(params MainWindowGridRow[] rows) + public static async Task FindNewDirectLinks(IMainApi api, params MainWindowGridRow[] rows) { var factory = new ModpackFactory(); - - foreach (var row in rows) + + api.Model.Progress.Value = 0; + api.Model.Progress.MaxValue = rows.Length; + api.Model.Progress.Visible = true; + + for (var i = 0; i < rows.Length; i++) { + var row = rows[i]; if (row.SourceType == SourceType.DirectLink) continue; - + row.StateImage = imageSourceWorking; + try { row.SourceUrl = await factory.ResolveSourceUrl(row.Action); + row.StateImage = imageSourceSuccess; } catch (Exception) { - // Fail silently + row.StateImage = imageSourceFailed; } + + if (api.HasClosed) + return; + api.Model.Progress.Value = i; } + + api.Model.Progress.Visible = false; } - public static void ClearDirectLinks(params MainWindowGridRow[] rows) + public static void ClearDirectLinks(IMainApi api, params MainWindowGridRow[] rows) { - foreach (var row in rows) + api.Model.Progress.Value = 0; + api.Model.Progress.MaxValue = rows.Length; + api.Model.Progress.Visible = true; + + for (var i = 0; i < rows.Length; i++) { + var row = rows[i]; if (row.SourceType != SourceType.DirectLink) row.SourceUrl = null; + row.StateImage = null; + api.Model.Progress.Value = i; } + + api.Model.Progress.Visible = false; } public static string GenerateChangelog(InstallInfos installInfos, UpdateInfo updateInfos) diff --git a/ModpackUpdater.Apps.Manager/Features/Tools/ClearDirectLinksFeature.cs b/ModpackUpdater.Apps.Manager/Features/Tools/ClearDirectLinksFeature.cs index 8d2806d..29336b6 100644 --- a/ModpackUpdater.Apps.Manager/Features/Tools/ClearDirectLinksFeature.cs +++ b/ModpackUpdater.Apps.Manager/Features/Tools/ClearDirectLinksFeature.cs @@ -18,7 +18,7 @@ internal class ClearDirectLinksFeature : PluginFunction, IPluginFeatureProvider< protected override Task ExecuteFunctionAsync(PluginFunctionParameter? @params) { if (@params is MainApiParameters p && p.Api.Model.CurrentGridRows is not null) - SharedFunctions.ClearDirectLinks([.. p.Api.Model.CurrentGridRows]); + SharedFunctions.ClearDirectLinks(p.Api, [.. p.Api.Model.CurrentGridRows]); return Task.FromResult(null); } } diff --git a/ModpackUpdater.Apps.Manager/Features/Tools/UpdateDirectLinksFeature.cs b/ModpackUpdater.Apps.Manager/Features/Tools/UpdateDirectLinksFeature.cs index 1b5d6d4..70cac79 100644 --- a/ModpackUpdater.Apps.Manager/Features/Tools/UpdateDirectLinksFeature.cs +++ b/ModpackUpdater.Apps.Manager/Features/Tools/UpdateDirectLinksFeature.cs @@ -20,7 +20,7 @@ internal class UpdateDirectLinksFeature : PluginFunction, IPluginFeatureProvider if (@params is not MainApiParameters p || p.Api.Model.CurrentGridRows is null) return null; - await SharedFunctions.FindNewDirectLinks([.. p.Api.Model.CurrentGridRows]); + await SharedFunctions.FindNewDirectLinks(p.Api, [.. p.Api.Model.CurrentGridRows]); return null; } diff --git a/ModpackUpdater.Apps.Manager/LangRes/GeneralLangRes.Designer.cs b/ModpackUpdater.Apps.Manager/LangRes/GeneralLangRes.Designer.cs index 4d23cce..bf54e62 100644 --- a/ModpackUpdater.Apps.Manager/LangRes/GeneralLangRes.Designer.cs +++ b/ModpackUpdater.Apps.Manager/LangRes/GeneralLangRes.Designer.cs @@ -195,6 +195,12 @@ namespace ModpackUpdater.Apps.Manager.LangRes { } } + public static string State { + get { + return ResourceManager.GetString("State", resourceCulture); + } + } + public static string InheritFrom { get { return ResourceManager.GetString("InheritFrom", resourceCulture); diff --git a/ModpackUpdater.Apps.Manager/LangRes/GeneralLangRes.resx b/ModpackUpdater.Apps.Manager/LangRes/GeneralLangRes.resx index f8e1f03..7d76ebc 100644 --- a/ModpackUpdater.Apps.Manager/LangRes/GeneralLangRes.resx +++ b/ModpackUpdater.Apps.Manager/LangRes/GeneralLangRes.resx @@ -192,6 +192,9 @@ Destination path + + State + Inherit from diff --git a/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml b/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml index 505a640..9c3c585 100644 --- a/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml +++ b/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml @@ -13,7 +13,8 @@ x:DataType="mainWindow:MainWindowViewModel" Title="Minecraft Modpack Manager" WindowState="Maximized" - Loaded="Window_OnLoaded"> + Loaded="Window_OnLoaded" + Closed="Window_OnClosed"> - + Spacing="6" + Orientation="Horizontal" + VerticalAlignment="Center"> - - + + - + + - - + - - + + - - + + - - - - + + - - - + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + diff --git a/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml.cs b/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml.cs index ec2f65f..fe11d55 100644 --- a/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml.cs +++ b/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml.cs @@ -1,4 +1,5 @@ using Avalonia.Controls; +using Avalonia.Data; using Avalonia.Interactivity; using Avalonia.Media; using ModpackUpdater.Apps.Manager.Api; @@ -22,6 +23,7 @@ public partial class MainWindow : Window, IMainApi public MainWindowViewModel Model { get; } = new(); Window IMainApi.MainWindow => this; public IMainApi MainApi => this; + public bool HasClosed { get; private set; } public MainWindow() { @@ -110,6 +112,11 @@ public partial class MainWindow : Window, IMainApi LoadRecentWorkspaces(); } + private void Window_OnClosed(object? sender, EventArgs e) + { + HasClosed = true; + } + private async void MenuItemNewWorkspaceItem_Click(object? sender, RoutedEventArgs e) { if (sender is not MenuItem item || item.Tag is not WorkspaceFeature feature) diff --git a/ModpackUpdater.Apps.Manager/Ui/Models/MainWindow/MainWindowGridRow.cs b/ModpackUpdater.Apps.Manager/Ui/Models/MainWindow/MainWindowGridRow.cs index 058acca..c9397a8 100644 --- a/ModpackUpdater.Apps.Manager/Ui/Models/MainWindow/MainWindowGridRow.cs +++ b/ModpackUpdater.Apps.Manager/Ui/Models/MainWindow/MainWindowGridRow.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using Avalonia.Media; using ModpackUpdater.Apps.Manager.LangRes; using ModpackUpdater.Apps.Manager.Utils; using PropertyChanged; @@ -18,7 +19,7 @@ public class MainWindowGridRow(InstallAction action, IActionSet baseActions) : I private InstallAction Inherited => Base ?? action; public bool IsUpdate => action is UpdateAction; - public bool? IsValid { get; set; } = null; + public IImage? StateImage { get; set; } [DependsOn(nameof(Id), nameof(InheritFrom))] public string? InheritedId => action is UpdateAction ua && !string.IsNullOrWhiteSpace(ua.InheritFrom) ? ua.InheritFrom : action.Id; diff --git a/ModpackUpdater.Apps.Manager/Ui/Models/MainWindow/MainWindowViewModel.cs b/ModpackUpdater.Apps.Manager/Ui/Models/MainWindow/MainWindowViewModel.cs index c6cf61d..832e129 100644 --- a/ModpackUpdater.Apps.Manager/Ui/Models/MainWindow/MainWindowViewModel.cs +++ b/ModpackUpdater.Apps.Manager/Ui/Models/MainWindow/MainWindowViewModel.cs @@ -16,7 +16,7 @@ public class MainWindowViewModel : INotifyPropertyChanged private IWorkspace? currentWorkspace; public bool IsUpdate => selectedTreeNode is ActionSetTreeNode node && node.Infos is UpdateInfo; - + public ProgressInfos Progress { get; } = new(); public MainWindowGridRow? SelectedGridRow { get; set; } [AlsoNotifyFor(nameof(CurrentTreeNodes))] diff --git a/ModpackUpdater.Apps.Manager/Ui/Models/ProgressInfos.cs b/ModpackUpdater.Apps.Manager/Ui/Models/ProgressInfos.cs new file mode 100644 index 0000000..91866ce --- /dev/null +++ b/ModpackUpdater.Apps.Manager/Ui/Models/ProgressInfos.cs @@ -0,0 +1,15 @@ +using System.ComponentModel; +using PropertyChanged; + +namespace ModpackUpdater.Apps.Manager.Ui.Models; + +public class ProgressInfos : INotifyPropertyChanged +{ + public event PropertyChangedEventHandler? PropertyChanged; + + public bool Visible { get; set; } + public double MaxValue { get; set; } + public double Value { get; set; } + [DependsOn(nameof(Value), nameof(MaxValue))] + public string? Text => $"{Math.Round(Value / MaxValue * 100)}%"; +} \ No newline at end of file diff --git a/ModpackUpdater.Apps.Manager/Ui/Models/UpdatesCollectorViewMode/UpdatesCollectorViewModel.cs b/ModpackUpdater.Apps.Manager/Ui/Models/UpdatesCollectorViewMode/UpdatesCollectorViewModel.cs index 7d9b411..15d627a 100644 --- a/ModpackUpdater.Apps.Manager/Ui/Models/UpdatesCollectorViewMode/UpdatesCollectorViewModel.cs +++ b/ModpackUpdater.Apps.Manager/Ui/Models/UpdatesCollectorViewMode/UpdatesCollectorViewModel.cs @@ -7,12 +7,8 @@ namespace ModpackUpdater.Apps.Manager.Ui.Models.UpdatesCollectorViewMode; public class UpdatesCollectorViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler? PropertyChanged; - - public bool ProgressVisible { get; set; } - public double ProgressMaxValue { get; set; } - public double ProgressValue { get; set; } - [DependsOn(nameof(ProgressValue), nameof(ProgressMaxValue))] - public string? ProgressText => $"{Math.Round(ProgressValue / ProgressMaxValue * 100)}%"; + + public ProgressInfos Progress { get; } = new(); public string? SearchText { get; set; } public ObservableCollection Updates { get; } = []; } \ No newline at end of file diff --git a/ModpackUpdater.Apps.Manager/Ui/UpdatesCollectorView.axaml b/ModpackUpdater.Apps.Manager/Ui/UpdatesCollectorView.axaml index 794f8bc..c818977 100644 --- a/ModpackUpdater.Apps.Manager/Ui/UpdatesCollectorView.axaml +++ b/ModpackUpdater.Apps.Manager/Ui/UpdatesCollectorView.axaml @@ -85,20 +85,20 @@ + IsVisible="{Binding Progress.Visible}"> + Maximum="{Binding Progress.MaxValue}" + Value="{Binding Progress.Value}"/> + Text="{Binding Progress.Text}"/> \ No newline at end of file diff --git a/ModpackUpdater.Apps.Manager/Ui/UpdatesCollectorView.axaml.cs b/ModpackUpdater.Apps.Manager/Ui/UpdatesCollectorView.axaml.cs index 6df4f2f..9972ef1 100644 --- a/ModpackUpdater.Apps.Manager/Ui/UpdatesCollectorView.axaml.cs +++ b/ModpackUpdater.Apps.Manager/Ui/UpdatesCollectorView.axaml.cs @@ -25,14 +25,15 @@ public partial class UpdatesCollectorView : AvaloniaFlyoutBase private async Task FindUpdates() { - Model.ProgressVisible = true; - Model.ProgressMaxValue = actions.Length; + Model.Progress.Value = 0; + Model.Progress.MaxValue = actions.Length; + Model.Progress.Visible = true; foreach (var action in actions) { var updates = await factory.FindUpdates(action, workspace.ModpackConfig?.MinecraftVersion, workspace.ModpackConfig?.ModLoader ?? ModLoader.Any); - Model.ProgressValue += 1; + Model.Progress.Value += 1; if (updates == null || updates.Length == 0 || updates[0].Value == action.SourceTag) continue; @@ -43,7 +44,7 @@ public partial class UpdatesCollectorView : AvaloniaFlyoutBase break; } - Model.ProgressVisible = false; + Model.Progress.Visible = false; } protected override object GetResult() diff --git a/ModpackUpdater.Apps/AppSymbols.cs b/ModpackUpdater.Apps/AppSymbols.cs index 3515b43..9507185 100644 --- a/ModpackUpdater.Apps/AppSymbols.cs +++ b/ModpackUpdater.Apps/AppSymbols.cs @@ -41,4 +41,5 @@ public enum AppSymbols input, output, git, + hourglass, }