11 Commits

Author SHA1 Message Date
f5e84b6da7 fix delete Artifacts directory 2025-11-21 19:57:52 +01:00
c864d9125a merge update urls and include app short name in distro 2025-11-21 19:52:58 +01:00
Pascal
e7aa79db64 ui(client): recent profile selector 2025-11-20 06:49:21 +01:00
Pascal
c0f013a6c5 code clenaup 2025-11-19 07:56:55 +01:00
Pascal
f21e31ebcc ui(manager): use popup for app update message 2025-11-19 07:55:46 +01:00
Pascal
0ae2a780b0 ui(manager): arrange DataGrid columns width & order 2025-11-19 07:47:25 +01:00
Pascal
474f76df4a disable update on debug builds 2025-11-19 07:40:05 +01:00
Pascal
6454d97173 ui(manager): add name column 2025-11-19 07:30:56 +01:00
Pascal
e57d2316de ui(manager): search for DataGrid 2025-11-19 07:21:19 +01:00
2cbe25e0f8 ui(manager): improve search bindings 2025-11-18 16:21:34 +01:00
86f93cf3d7 publish: remove artifacts 2025-11-18 07:30:05 +01:00
25 changed files with 751 additions and 396 deletions

View File

@@ -16,6 +16,10 @@
<NoWarn>1591</NoWarn> <NoWarn>1591</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>$(DefineConstants);DISABLE_UPDATE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'"> <PropertyGroup Condition="'$(Configuration)'=='Release'">
<DebugSymbols>False</DebugSymbols> <DebugSymbols>False</DebugSymbols>
<DebugType>None</DebugType> <DebugType>None</DebugType>

View File

@@ -7,19 +7,22 @@ public class AppConfig : ISettingsNode, ISettingsIdentifier
{ {
public static string Identifier => "pilz.appconfig"; public static string Identifier => "pilz.appconfig";
public string? LastMinecraftProfilePath { get; set; } public List<string> RecentMinecraftProfilePaths { get; } = [];
[JsonIgnore, Obsolete] [JsonProperty, Obsolete]
public string? ConfigFilePath { get; private set; } private string? LastMinecraftProfilePath
[JsonProperty("ConfigFilePath"), Obsolete]
private string ConfigFilePathLegacy
{ {
set => ConfigFilePath = value; get => RecentMinecraftProfilePaths.FirstOrDefault();
set
{
if (!string.IsNullOrWhiteSpace(value))
RecentMinecraftProfilePaths.Insert(0, value);
}
} }
public void Reset() public void Reset()
{ {
LastMinecraftProfilePath = null; RecentMinecraftProfilePaths.Clear();
} }
public static AppConfig Instance => Program.Settings.Get<AppConfig>(); public static AppConfig Instance => Program.Settings.Get<AppConfig>();

View File

@@ -11,32 +11,46 @@ namespace ModpackUpdater.Apps.Client.Gui.LangRes {
using System; using System;
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] /// <summary>
[System.Diagnostics.DebuggerNonUserCodeAttribute()] /// A strongly-typed resource class, for looking up localized strings, etc.
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()] /// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class GeneralLangRes { public class GeneralLangRes {
private static System.Resources.ResourceManager resourceMan; private static global::System.Resources.ResourceManager resourceMan;
private static System.Globalization.CultureInfo resourceCulture; private static global::System.Globalization.CultureInfo resourceCulture;
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal GeneralLangRes() { internal GeneralLangRes() {
} }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] /// <summary>
public static System.Resources.ResourceManager ResourceManager { /// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get { get {
if (object.Equals(null, resourceMan)) { if (object.ReferenceEquals(resourceMan, null)) {
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("ModpackUpdater.Apps.Client.Gui.LangRes.GeneralLangRes", typeof(GeneralLangRes).Assembly); global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ModpackUpdater.Apps.Client.Gui.LangRes.GeneralLangRes", typeof(GeneralLangRes).Assembly);
resourceMan = temp; resourceMan = temp;
} }
return resourceMan; return resourceMan;
} }
} }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] /// <summary>
public static System.Globalization.CultureInfo Culture { /// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get { get {
return resourceCulture; return resourceCulture;
} }
@@ -45,118 +59,184 @@ namespace ModpackUpdater.Apps.Client.Gui.LangRes {
} }
} }
/// <summary>
/// Looks up a localized string similar to An update is available!.
/// </summary>
public static string AnUpdateIsAvailable { public static string AnUpdateIsAvailable {
get { get {
return ResourceManager.GetString("AnUpdateIsAvailable", resourceCulture); return ResourceManager.GetString("AnUpdateIsAvailable", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Check for updates.
/// </summary>
public static string CheckForUpdates { public static string CheckForUpdates {
get { get {
return ResourceManager.GetString("CheckForUpdates", resourceCulture); return ResourceManager.GetString("CheckForUpdates", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Checking for Updates....
/// </summary>
public static string CheckingForUpdates { public static string CheckingForUpdates {
get { get {
return ResourceManager.GetString("CheckingForUpdates", resourceCulture); return ResourceManager.GetString("CheckingForUpdates", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Config incomplete or not loaded!.
/// </summary>
public static string ConfigIncompleteOrNotLoaded { public static string ConfigIncompleteOrNotLoaded {
get { get {
return ResourceManager.GetString("ConfigIncompleteOrNotLoaded", resourceCulture); return ResourceManager.GetString("ConfigIncompleteOrNotLoaded", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Downloading program update....
/// </summary>
public static string DownloadProgramUpdate { public static string DownloadProgramUpdate {
get { get {
return ResourceManager.GetString("DownloadProgramUpdate", resourceCulture); return ResourceManager.GetString("DownloadProgramUpdate", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Error on update check or while updating!.
/// </summary>
public static string ErrorOnUpdateCheckOrUpdating { public static string ErrorOnUpdateCheckOrUpdating {
get { get {
return ResourceManager.GetString("ErrorOnUpdateCheckOrUpdating", resourceCulture); return ResourceManager.GetString("ErrorOnUpdateCheckOrUpdating", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Everything is right and up-to-date..
/// </summary>
public static string EverythingIsRightAndUpToDate { public static string EverythingIsRightAndUpToDate {
get { get {
return ResourceManager.GetString("EverythingIsRightAndUpToDate", resourceCulture); return ResourceManager.GetString("EverythingIsRightAndUpToDate", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Install.
/// </summary>
public static string Install { public static string Install {
get { get {
return ResourceManager.GetString("Install", resourceCulture); return ResourceManager.GetString("Install", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Installation key.
/// </summary>
public static string InstallationKey { public static string InstallationKey {
get { get {
return ResourceManager.GetString("InstallationKey", resourceCulture); return ResourceManager.GetString("InstallationKey", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Installation key seems to be invalid.
/// </summary>
public static string InstallationKeyNotValid {
get {
return ResourceManager.GetString("InstallationKeyNotValid", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Installing....
/// </summary>
public static string Installing { public static string Installing {
get { get {
return ResourceManager.GetString("Installing", resourceCulture); return ResourceManager.GetString("Installing", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Minecraft profile.
/// </summary>
public static string MinecraftProfile { public static string MinecraftProfile {
get { get {
return ResourceManager.GetString("MinecraftProfile", resourceCulture); return ResourceManager.GetString("MinecraftProfile", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Minecraft profile folder seems to be not valid..
/// </summary>
public static string MinecraftProfileFolderSeemsInvalid { public static string MinecraftProfileFolderSeemsInvalid {
get { get {
return ResourceManager.GetString("MinecraftProfileFolderSeemsInvalid", resourceCulture); return ResourceManager.GetString("MinecraftProfileFolderSeemsInvalid", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Modpack config url.
/// </summary>
public static string ModpackConfigUrl { public static string ModpackConfigUrl {
get { get {
return ResourceManager.GetString("ModpackConfigUrl", resourceCulture); return ResourceManager.GetString("ModpackConfigUrl", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to No recent profiles available..
/// </summary>
public static string NoRecentProfilesAvailable {
get {
return ResourceManager.GetString("NoRecentProfilesAvailable", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Repair.
/// </summary>
public static string Repair { public static string Repair {
get { get {
return ResourceManager.GetString("Repair", resourceCulture); return ResourceManager.GetString("Repair", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Select.
/// </summary>
public static string Select { public static string Select {
get { get {
return ResourceManager.GetString("Select", resourceCulture); return ResourceManager.GetString("Select", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Select the minecraft profile folder (usually named .minecraft).
/// </summary>
public static string SelectMinecraftProfileFolder { public static string SelectMinecraftProfileFolder {
get { get {
return ResourceManager.GetString("SelectMinecraftProfileFolder", resourceCulture); return ResourceManager.GetString("SelectMinecraftProfileFolder", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Status.
/// </summary>
public static string Status { public static string Status {
get { get {
return ResourceManager.GetString("Status", resourceCulture); return ResourceManager.GetString("Status", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to The update servers are in maintenance..
/// </summary>
public static string UpdateServerInMaintenance { public static string UpdateServerInMaintenance {
get { get {
return ResourceManager.GetString("UpdateServerInMaintenance", resourceCulture); return ResourceManager.GetString("UpdateServerInMaintenance", resourceCulture);
} }
} }
public static string InstallationKeyNotValid {
get {
return ResourceManager.GetString("InstallationKeyNotValid", resourceCulture);
}
}
} }
} }

View File

@@ -174,4 +174,7 @@
<data name="InstallationKeyNotValid" xml:space="preserve"> <data name="InstallationKeyNotValid" xml:space="preserve">
<value>Installation key seems to be invalid</value> <value>Installation key seems to be invalid</value>
</data> </data>
<data name="NoRecentProfilesAvailable" xml:space="preserve">
<value>No recent profiles available.</value>
</data>
</root> </root>

View File

@@ -12,7 +12,8 @@
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
CanMaximize="false" CanMaximize="false"
Title="Minecraft Modpack Updater" Title="Minecraft Modpack Updater"
Icon="/Assets/app.ico"> Icon="/Assets/app.ico"
Loaded="MainForm_Loaded">
<Grid <Grid
RowDefinitions="Auto,Auto,Auto,Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto"
@@ -71,7 +72,7 @@
IsVisible="false"/> IsVisible="false"/>
<!-- Button: SearchProfileFolder --> <!-- Button: SearchProfileFolder -->
<pilz:ImageButton <pilz:ImageSplitButton
x:Name="ButtonSearchProfileFolder" x:Name="ButtonSearchProfileFolder"
Grid.Row="0" Grid.Row="0"
Grid.Column="2" Grid.Column="2"

View File

@@ -7,18 +7,15 @@ using Avalonia.Platform.Storage;
using Avalonia.Threading; using Avalonia.Threading;
using ModpackUpdater.Apps.Client.Gui.LangRes; using ModpackUpdater.Apps.Client.Gui.LangRes;
using ModpackUpdater.Manager; using ModpackUpdater.Manager;
using MsBox.Avalonia;
using MsBox.Avalonia.Enums;
using Pilz;
using Pilz.Extensions; using Pilz.Extensions;
using Pilz.Runtime; using Pilz.Extensions.Collections;
using Pilz.UI.Symbols; using Pilz.UI.Symbols;
using Pilz.Updating.Client;
namespace ModpackUpdater.Apps.Client.Gui; namespace ModpackUpdater.Apps.Client.Gui;
public partial class MainForm : Window public partial class MainForm : Window
{ {
private readonly MenuFlyout menuFlyoutSearchProfileFolder = new();
private readonly UpdateCheckOptions updateOptions = new(); private readonly UpdateCheckOptions updateOptions = new();
private ModpackInfo modpackInfo = new(); private ModpackInfo modpackInfo = new();
private ModpackConfig updateConfig = new(); private ModpackConfig updateConfig = new();
@@ -26,16 +23,13 @@ public partial class MainForm : Window
private UpdateCheckResult? lastUpdateCheckResult; private UpdateCheckResult? lastUpdateCheckResult;
private bool currentUpdating; private bool currentUpdating;
private bool loadingData; private bool loadingData;
private int curOptionsRow = 3;
public MainForm() public MainForm()
{ {
InitializeComponent(); InitializeComponent();
Title = $"{Title} (v{Assembly.GetExecutingAssembly().GetAppVersion().ToShortString()})"; Title = $"{Title} (v{Assembly.GetExecutingAssembly().GetAppVersion().ToShortString()})";
ButtonSearchProfileFolder.Flyout = menuFlyoutSearchProfileFolder;
Closing += MainForm_Closing;
Loaded += MainForm_Loaded;
ButtonSearchProfileFolder.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.opened_folder); ButtonSearchProfileFolder.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.opened_folder);
ButtonCheckForUpdates.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.update_done); ButtonCheckForUpdates.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.update_done);
@@ -59,12 +53,50 @@ public partial class MainForm : Window
TextStatus.Text = "-"; TextStatus.Text = "-";
ImageStatus.Source = null; ImageStatus.Source = null;
} }
private void LoadRecentFilesToUi()
{
menuFlyoutSearchProfileFolder.Items.Clear();
if (AppConfig.Instance.RecentMinecraftProfilePaths.Count == 0)
{
menuFlyoutSearchProfileFolder.Items.Add(new TextBlock
{
Text = GeneralLangRes.NoRecentProfilesAvailable,
FontStyle = FontStyle.Italic,
});
return;
}
AppConfig.Instance.RecentMinecraftProfilePaths.ForEach(path =>
{
if (File.Exists(path))
return;
var item = new MenuItem
{
Header = path.Length > 50 ? $"...{path[^50..]}" : path,
DataContext = path,
};
item.Click += MenuItemRecentMinecraftProfilePathItem_Click;
menuFlyoutSearchProfileFolder.Items.Add(item);
});
}
private void StoreRecentMinecraftProfilePath(string? path)
{
if (string.IsNullOrWhiteSpace(path))
return;
AppConfig.Instance.RecentMinecraftProfilePaths.Remove(path);
AppConfig.Instance.RecentMinecraftProfilePaths.Insert(0, path);
AppConfig.Instance.RecentMinecraftProfilePaths.Skip(10).ForEach(n => AppConfig.Instance.RecentMinecraftProfilePaths.Remove(n));
}
private void LoadProfileToUi() private void LoadProfileToUi()
{ {
loadingData = true; loadingData = true;
TextBoxMinecraftProfileFolder.Text = modpackInfo.LocalPath ?? AppConfig.Instance.LastMinecraftProfilePath ?? TextBoxMinecraftProfileFolder.Text; TextBoxMinecraftProfileFolder.Text = modpackInfo.LocalPath ?? AppConfig.Instance.RecentMinecraftProfilePaths.FirstOrDefault() ?? TextBoxMinecraftProfileFolder.Text;
TextBoxModpackConfig.Text = modpackInfo.ConfigUrl ?? TextBoxModpackConfig.Text; TextBoxModpackConfig.Text = modpackInfo.ConfigUrl ?? TextBoxModpackConfig.Text;
TextBoxInstallKey.Text = modpackInfo.ExtrasKey ?? TextBoxInstallKey.Text; TextBoxInstallKey.Text = modpackInfo.ExtrasKey ?? TextBoxInstallKey.Text;
@@ -81,8 +113,13 @@ public partial class MainForm : Window
private async void CheckStatusAndUpdate(bool loadProfileToUi) private async void CheckStatusAndUpdate(bool loadProfileToUi)
{ {
if (CheckStatus(loadProfileToUi)) if (!CheckStatus(loadProfileToUi))
await ExecuteUpdate(false, false); return;
await ExecuteUpdate(false, false);
StoreRecentMinecraftProfilePath(modpackInfo.LocalPath);
LoadRecentFilesToUi();
} }
private bool CheckStatus(bool loadProfileToUi) private bool CheckStatus(bool loadProfileToUi)
@@ -267,18 +304,13 @@ public partial class MainForm : Window
#region Gui #region Gui
private void MainForm_Closing(object? sender, WindowClosingEventArgs e)
{
AppConfig.Instance.LastMinecraftProfilePath = TextBoxMinecraftProfileFolder.Text?.Trim();
}
private async void MainForm_Loaded(object? sender, RoutedEventArgs e) private async void MainForm_Loaded(object? sender, RoutedEventArgs e)
{ {
var updates = new AppUpdates(Program.UpdateUrl, this); var updates = new AppUpdates("manager", this);
updates.OnDownloadProgramUpdate += (o, _) => SetStatus(GeneralLangRes.DownloadProgramUpdate, AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer)); updates.OnDownloadProgramUpdate += (_, _) => SetStatus(GeneralLangRes.DownloadProgramUpdate, AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer));
await updates.UpdateApp(); await updates.UpdateApp();
ClearStatus(); ClearStatus();
LoadRecentFilesToUi();
CheckStatusAndUpdate(true); CheckStatusAndUpdate(true);
} }
@@ -337,5 +369,11 @@ public partial class MainForm : Window
await ExecuteUpdate(true, true); await ExecuteUpdate(true, true);
} }
private void MenuItemRecentMinecraftProfilePathItem_Click(object? sender, RoutedEventArgs e)
{
if (sender is MenuItem item && item.DataContext is string path)
TextBoxMinecraftProfileFolder.Text = path;
}
#endregion #endregion
} }

View File

@@ -8,8 +8,6 @@ namespace ModpackUpdater.Apps.Client.Gui;
public static class Program public static class Program
{ {
public const string UpdateUrl = "https://git.pilzinsel64.de/litw-refined/minecraft-modpack-updater/-/snippets/3/raw/main/updates-new.json";
private static readonly SettingsManager settingsManager; private static readonly SettingsManager settingsManager;
public static ISettings Settings => settingsManager.Instance; public static ISettings Settings => settingsManager.Instance;
@@ -18,7 +16,6 @@ public static class Program
static Program() static Program()
{ {
settingsManager = new(GetSettingsPath(2), true); settingsManager = new(GetSettingsPath(2), true);
MigrateLegacySettings(GetSettingsPath(null));
} }
[STAThread] [STAThread]
@@ -47,25 +44,4 @@ public static class Program
return settingsPath; return settingsPath;
} }
private static void MigrateLegacySettings(string settingsPath)
{
// Try load legacy config file
if (!File.Exists(settingsPath) || JsonConvert.DeserializeObject<AppConfig>(File.ReadAllText(settingsPath)) is not AppConfig legacyConfig)
return;
// Migrate
var newConfig = Settings.Get<AppConfig>();
newConfig.LastMinecraftProfilePath = legacyConfig.LastMinecraftProfilePath;
if (ModpackInfo.TryLoad(legacyConfig.LastMinecraftProfilePath) is ModpackInfo info)
#pragma warning disable CS0612 // Typ oder Element ist veraltet
info.ConfigUrl = legacyConfig.ConfigFilePath;
// Ensure save settings
settingsManager.Save();
// Delete legacy config file
File.Delete(settingsPath);
}
} }

View File

@@ -70,7 +70,7 @@ internal static class SharedFunctions
foreach (var update in updates) foreach (var update in updates)
{ {
var sourceTag = update.AvailableVersions[update.NewVersion].Tag; var sourceTag = update.AvailableVersions[update.NewVersion].Tag;
if (api.Model.CurrentGridRows?.FirstOrDefault(n => n.Action == update.Origin) is { } row) if (api.Model.CurrentGridRows.List.Items.FirstOrDefault(n => n.Action == update.Origin) is { } row)
row.SourceTag = sourceTag; row.SourceTag = sourceTag;
else else
update.Origin.SourceTag = sourceTag; update.Origin.SourceTag = sourceTag;

View File

@@ -17,10 +17,10 @@ internal class CheckAllActionsHealthyFeature : PluginFunction, IPluginFeaturePro
protected override async Task<object?> ExecuteFunctionAsync(PluginFunctionParameter? @params) protected override async Task<object?> ExecuteFunctionAsync(PluginFunctionParameter? @params)
{ {
if (@params is not MainApiParameters p || p.Api.Model.CurrentGridRows is null) if (@params is not MainApiParameters p)
return null; return null;
await SharedFunctions.CheckActionHealthy(p.Api, [.. p.Api.Model.CurrentGridRows]); await SharedFunctions.CheckActionHealthy(p.Api, [.. p.Api.Model.CurrentGridRows.View]);
return null; return null;
} }

View File

@@ -17,8 +17,8 @@ internal class ClearDirectLinksFeature : PluginFunction, IPluginFeatureProvider<
protected override Task<object?> ExecuteFunctionAsync(PluginFunctionParameter? @params) protected override Task<object?> ExecuteFunctionAsync(PluginFunctionParameter? @params)
{ {
if (@params is MainApiParameters p && p.Api.Model.CurrentGridRows is not null) if (@params is MainApiParameters p)
SharedFunctions.ClearDirectLinks(p.Api, [.. p.Api.Model.CurrentGridRows]); SharedFunctions.ClearDirectLinks(p.Api, [.. p.Api.Model.CurrentGridRows.View]);
return Task.FromResult<object?>(null); return Task.FromResult<object?>(null);
} }
} }

View File

@@ -17,10 +17,10 @@ internal class UpdateDirectLinksFeature : PluginFunction, IPluginFeatureProvider
protected override async Task<object?> ExecuteFunctionAsync(PluginFunctionParameter? @params) protected override async Task<object?> ExecuteFunctionAsync(PluginFunctionParameter? @params)
{ {
if (@params is not MainApiParameters p || p.Api.Model.CurrentGridRows is null) if (@params is not MainApiParameters p)
return null; return null;
await SharedFunctions.FindNewDirectLinks(p.Api, [.. p.Api.Model.CurrentGridRows]); await SharedFunctions.FindNewDirectLinks(p.Api, [.. p.Api.Model.CurrentGridRows.View]);
return null; return null;
} }

View File

@@ -11,32 +11,46 @@ namespace ModpackUpdater.Apps.Manager.LangRes {
using System; using System;
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] /// <summary>
[System.Diagnostics.DebuggerNonUserCodeAttribute()] /// A strongly-typed resource class, for looking up localized strings, etc.
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()] /// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class GeneralLangRes { public class GeneralLangRes {
private static System.Resources.ResourceManager resourceMan; private static global::System.Resources.ResourceManager resourceMan;
private static System.Globalization.CultureInfo resourceCulture; private static global::System.Globalization.CultureInfo resourceCulture;
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal GeneralLangRes() { internal GeneralLangRes() {
} }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] /// <summary>
public static System.Resources.ResourceManager ResourceManager { /// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get { get {
if (object.Equals(null, resourceMan)) { if (object.ReferenceEquals(resourceMan, null)) {
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("ModpackUpdater.Apps.Manager.LangRes.GeneralLangRes", typeof(GeneralLangRes).Assembly); global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ModpackUpdater.Apps.Manager.LangRes.GeneralLangRes", typeof(GeneralLangRes).Assembly);
resourceMan = temp; resourceMan = temp;
} }
return resourceMan; return resourceMan;
} }
} }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] /// <summary>
public static System.Globalization.CultureInfo Culture { /// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get { get {
return resourceCulture; return resourceCulture;
} }
@@ -45,274 +59,418 @@ namespace ModpackUpdater.Apps.Manager.LangRes {
} }
} }
public static string ModlistForVersionX { /// <summary>
get { /// Looks up a localized string similar to Add.
return ResourceManager.GetString("ModlistForVersionX", resourceCulture); /// </summary>
}
}
public static string GitLabInstanceUrl {
get {
return ResourceManager.GetString("GitLabInstanceUrl", resourceCulture);
}
}
public static string GitLabApiToken {
get {
return ResourceManager.GetString("GitLabApiToken", resourceCulture);
}
}
public static string RepositoryId {
get {
return ResourceManager.GetString("RepositoryId", resourceCulture);
}
}
public static string RootFolderPath {
get {
return ResourceManager.GetString("RootFolderPath", resourceCulture);
}
}
public static string FileLocationOfInstallJson {
get {
return ResourceManager.GetString("FileLocationOfInstallJson", resourceCulture);
}
}
public static string FileLocationOfUpdateJson {
get {
return ResourceManager.GetString("FileLocationOfUpdateJson", resourceCulture);
}
}
public static string ModpackConfigUrl {
get {
return ResourceManager.GetString("ModpackConfigUrl", resourceCulture);
}
}
public static string Remove {
get {
return ResourceManager.GetString("Remove", resourceCulture);
}
}
public static string WorkspacePreferences {
get {
return ResourceManager.GetString("WorkspacePreferences", resourceCulture);
}
}
public static string SaveWorkspace {
get {
return ResourceManager.GetString("SaveWorkspace", resourceCulture);
}
}
public static string NewWorkspace {
get {
return ResourceManager.GetString("NewWorkspace", resourceCulture);
}
}
public static string RecentWorkspaces {
get {
return ResourceManager.GetString("RecentWorkspaces", resourceCulture);
}
}
public static string Workspace {
get {
return ResourceManager.GetString("Workspace", resourceCulture);
}
}
public static string CreateUpdate {
get {
return ResourceManager.GetString("CreateUpdate", resourceCulture);
}
}
public static string RemoveUpdate {
get {
return ResourceManager.GetString("RemoveUpdate", resourceCulture);
}
}
public static string Update {
get {
return ResourceManager.GetString("Update", resourceCulture);
}
}
public static string Tools {
get {
return ResourceManager.GetString("Tools", resourceCulture);
}
}
public static string Updates {
get {
return ResourceManager.GetString("Updates", resourceCulture);
}
}
public static string Add { public static string Add {
get { get {
return ResourceManager.GetString("Add", resourceCulture); return ResourceManager.GetString("Add", resourceCulture);
} }
} }
public static string Public { /// <summary>
/// Looks up a localized string similar to Create update.
/// </summary>
public static string CreateUpdate {
get { get {
return ResourceManager.GetString("Public", resourceCulture); return ResourceManager.GetString("CreateUpdate", resourceCulture);
}
}
public static string Id {
get {
return ResourceManager.GetString("Id", resourceCulture);
}
}
public static string Side {
get {
return ResourceManager.GetString("Side", resourceCulture);
}
}
public static string UpdateType {
get {
return ResourceManager.GetString("UpdateType", resourceCulture);
}
}
public static string SourceType {
get {
return ResourceManager.GetString("SourceType", resourceCulture);
}
}
public static string DestinationPath {
get {
return ResourceManager.GetString("DestinationPath", resourceCulture);
}
}
public static string State {
get {
return ResourceManager.GetString("State", resourceCulture);
}
}
public static string InheritFrom {
get {
return ResourceManager.GetString("InheritFrom", resourceCulture);
}
}
public static string SourcePath {
get {
return ResourceManager.GetString("SourcePath", resourceCulture);
}
}
public static string IsDirectory {
get {
return ResourceManager.GetString("IsDirectory", resourceCulture);
}
}
public static string General {
get {
return ResourceManager.GetString("General", resourceCulture);
}
}
public static string Name {
get {
return ResourceManager.GetString("Name", resourceCulture);
}
}
public static string IsExtra {
get {
return ResourceManager.GetString("IsExtra", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Destination.
/// </summary>
public static string Destination { public static string Destination {
get { get {
return ResourceManager.GetString("Destination", resourceCulture); return ResourceManager.GetString("Destination", resourceCulture);
} }
} }
public static string Source { /// <summary>
/// Looks up a localized string similar to Destination path.
/// </summary>
public static string DestinationPath {
get { get {
return ResourceManager.GetString("Source", resourceCulture); return ResourceManager.GetString("DestinationPath", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to File location of &quot;install.json&quot;.
/// </summary>
public static string FileLocationOfInstallJson {
get {
return ResourceManager.GetString("FileLocationOfInstallJson", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to File location of &quot;update.json&quot;.
/// </summary>
public static string FileLocationOfUpdateJson {
get {
return ResourceManager.GetString("FileLocationOfUpdateJson", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to General.
/// </summary>
public static string General {
get {
return ResourceManager.GetString("General", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to GitLab Api token.
/// </summary>
public static string GitLabApiToken {
get {
return ResourceManager.GetString("GitLabApiToken", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to GitLab instance url.
/// </summary>
public static string GitLabInstanceUrl {
get {
return ResourceManager.GetString("GitLabInstanceUrl", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Id.
/// </summary>
public static string Id {
get {
return ResourceManager.GetString("Id", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Inherit from.
/// </summary>
public static string InheritFrom {
get {
return ResourceManager.GetString("InheritFrom", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Is directory.
/// </summary>
public static string IsDirectory {
get {
return ResourceManager.GetString("IsDirectory", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Is extra.
/// </summary>
public static string IsExtra {
get {
return ResourceManager.GetString("IsExtra", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Is Zip archive.
/// </summary>
public static string IsZipArchive { public static string IsZipArchive {
get { get {
return ResourceManager.GetString("IsZipArchive", resourceCulture); return ResourceManager.GetString("IsZipArchive", resourceCulture);
} }
} }
public static string SourceOwner { /// <summary>
get { /// Looks up a localized string similar to Metadata.
return ResourceManager.GetString("SourceOwner", resourceCulture); /// </summary>
}
}
public static string SourceName {
get {
return ResourceManager.GetString("SourceName", resourceCulture);
}
}
public static string SourceRegex {
get {
return ResourceManager.GetString("SourceRegex", resourceCulture);
}
}
public static string SourceUrl {
get {
return ResourceManager.GetString("SourceUrl", resourceCulture);
}
}
public static string ZipArchivePath {
get {
return ResourceManager.GetString("ZipArchivePath", resourceCulture);
}
}
public static string Metadata { public static string Metadata {
get { get {
return ResourceManager.GetString("Metadata", resourceCulture); return ResourceManager.GetString("Metadata", resourceCulture);
} }
} }
public static string Website { /// <summary>
/// Looks up a localized string similar to Modlist for v{0}.
/// </summary>
public static string ModlistForVersionX {
get { get {
return ResourceManager.GetString("Website", resourceCulture); return ResourceManager.GetString("ModlistForVersionX", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Modpack config url.
/// </summary>
public static string ModpackConfigUrl {
get {
return ResourceManager.GetString("ModpackConfigUrl", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Name.
/// </summary>
public static string Name {
get {
return ResourceManager.GetString("Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to New workspace.
/// </summary>
public static string NewWorkspace {
get {
return ResourceManager.GetString("NewWorkspace", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Public.
/// </summary>
public static string Public {
get {
return ResourceManager.GetString("Public", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Recent workspace.
/// </summary>
public static string RecentWorkspaces {
get {
return ResourceManager.GetString("RecentWorkspaces", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Remove.
/// </summary>
public static string Remove {
get {
return ResourceManager.GetString("Remove", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Remove update.
/// </summary>
public static string RemoveUpdate {
get {
return ResourceManager.GetString("RemoveUpdate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Repository Id.
/// </summary>
public static string RepositoryId {
get {
return ResourceManager.GetString("RepositoryId", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Root folder path.
/// </summary>
public static string RootFolderPath {
get {
return ResourceManager.GetString("RootFolderPath", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Save workspace.
/// </summary>
public static string SaveWorkspace {
get {
return ResourceManager.GetString("SaveWorkspace", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Search.
/// </summary>
public static string Search {
get {
return ResourceManager.GetString("Search", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Select.
/// </summary>
public static string Select { public static string Select {
get { get {
return ResourceManager.GetString("Select", resourceCulture); return ResourceManager.GetString("Select", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Select root folder.
/// </summary>
public static string SelectRootFolder { public static string SelectRootFolder {
get { get {
return ResourceManager.GetString("SelectRootFolder", resourceCulture); return ResourceManager.GetString("SelectRootFolder", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Side.
/// </summary>
public static string Side {
get {
return ResourceManager.GetString("Side", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Source.
/// </summary>
public static string Source {
get {
return ResourceManager.GetString("Source", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Source name.
/// </summary>
public static string SourceName {
get {
return ResourceManager.GetString("SourceName", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Source owner.
/// </summary>
public static string SourceOwner {
get {
return ResourceManager.GetString("SourceOwner", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Source path.
/// </summary>
public static string SourcePath {
get {
return ResourceManager.GetString("SourcePath", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Source RegEx.
/// </summary>
public static string SourceRegex {
get {
return ResourceManager.GetString("SourceRegex", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Source type.
/// </summary>
public static string SourceType {
get {
return ResourceManager.GetString("SourceType", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Source URL.
/// </summary>
public static string SourceUrl {
get {
return ResourceManager.GetString("SourceUrl", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to State.
/// </summary>
public static string State {
get {
return ResourceManager.GetString("State", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Tools.
/// </summary>
public static string Tools {
get {
return ResourceManager.GetString("Tools", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Update.
/// </summary>
public static string Update {
get {
return ResourceManager.GetString("Update", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Updates.
/// </summary>
public static string Updates {
get {
return ResourceManager.GetString("Updates", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Update type.
/// </summary>
public static string UpdateType {
get {
return ResourceManager.GetString("UpdateType", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Website.
/// </summary>
public static string Website {
get {
return ResourceManager.GetString("Website", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Workspace.
/// </summary>
public static string Workspace {
get {
return ResourceManager.GetString("Workspace", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Workspace preferences.
/// </summary>
public static string WorkspacePreferences {
get {
return ResourceManager.GetString("WorkspacePreferences", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ZIP archive path.
/// </summary>
public static string ZipArchivePath {
get {
return ResourceManager.GetString("ZipArchivePath", resourceCulture);
}
}
} }
} }

View File

@@ -252,4 +252,7 @@
<data name="SelectRootFolder" xml:space="preserve"> <data name="SelectRootFolder" xml:space="preserve">
<value>Select root folder</value> <value>Select root folder</value>
</data> </data>
<data name="Search" xml:space="preserve">
<value>Search</value>
</data>
</root> </root>

View File

@@ -50,6 +50,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.3.8" /> <PackageReference Include="Avalonia.Controls.DataGrid" Version="11.3.8" />
<PackageReference Include="DynamicData" Version="9.4.1" />
<PackageReference Include="MessageBox.Avalonia" Version="3.3.0" /> <PackageReference Include="MessageBox.Avalonia" Version="3.3.0" />
<PackageReference Include="EPPlus" Version="8.2.1" /> <PackageReference Include="EPPlus" Version="8.2.1" />
<PackageReference Include="NGitLab" Version="11.0.1" /> <PackageReference Include="NGitLab" Version="11.0.1" />

View File

@@ -7,8 +7,6 @@ namespace ModpackUpdater.Apps.Manager;
public static class Program public static class Program
{ {
public const string UpdateUrl = "https://git.pilzinsel64.de/litw-refined/minecraft-modpack-updater/-/snippets/3/raw/main/updates-manager.json";
internal static readonly SettingsManager settingsManager; internal static readonly SettingsManager settingsManager;
public static ISettings Settings => settingsManager.Instance; public static ISettings Settings => settingsManager.Instance;

View File

@@ -8,7 +8,7 @@
xmlns:symbols="clr-namespace:Pilz.UI.Symbols;assembly=Pilz.UI" xmlns:symbols="clr-namespace:Pilz.UI.Symbols;assembly=Pilz.UI"
xmlns:mainWindow="clr-namespace:ModpackUpdater.Apps.Manager.Ui.Models.MainWindow" xmlns:mainWindow="clr-namespace:ModpackUpdater.Apps.Manager.Ui.Models.MainWindow"
xmlns:langRes="clr-namespace:ModpackUpdater.Apps.Manager.LangRes" xmlns:langRes="clr-namespace:ModpackUpdater.Apps.Manager.LangRes"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="450"
x:Class="ModpackUpdater.Apps.Manager.Ui.MainWindow" x:Class="ModpackUpdater.Apps.Manager.Ui.MainWindow"
Title="Minecraft Modpack Manager" Title="Minecraft Modpack Manager"
WindowState="Maximized" WindowState="Maximized"
@@ -109,42 +109,53 @@
Orientation="Horizontal" Orientation="Horizontal"
VerticalAlignment="Center"> VerticalAlignment="Center">
<!-- Panel: Menu list -->
<StackPanel
Orientation="Horizontal">
<!-- Button: Add action -->
<pilz:ImageButton
x:Name="ButtonAddAction"
Text="{x:Static langRes:GeneralLangRes.Add}"
ImageSource="{x:Static local:MainWindow.ButtonImageAddAction}"
Background="Transparent"
Click="ButtonAddAction_OnClick"/>
<!-- Button: Remove action -->
<pilz:ImageButton
x:Name="ButtonRemoveAction"
Text="{x:Static langRes:GeneralLangRes.Remove}"
ImageSource="{x:Static local:MainWindow.ButtonImageRemoveAction}"
Background="Transparent"
Click="ButtonRemoveAction_OnClick"/>
</StackPanel>
<!-- TextBox: Search -->
<TextBox
Width="200"
Watermark="{x:Static langRes:GeneralLangRes.Search}"
Text="{Binding CurrentGridRows.SearchText}"/>
<!-- Panel: Menu --> <!-- Panel: Menu -->
<ContentControl <ContentControl
Content="{Binding SelectedTreeNode}"> Content="{Binding SelectedTreeNode}"
>
<ContentControl.DataTemplates> <ContentControl.DataTemplates>
<DataTemplate <DataTemplate
DataType="mainWindow:ActionSetTreeNode"> DataType="mainWindow:ActionSetTreeNode">
<StackPanel <StackPanel
Orientation="Horizontal"> Orientation="Horizontal"
Spacing="6">
<!-- Button: Add action -->
<pilz:ImageButton
x:Name="ButtonAddAction"
Text="{x:Static langRes:GeneralLangRes.Add}"
ImageSource="{x:Static local:MainWindow.ButtonImageAddAction}"
Background="Transparent"
Click="ButtonAddAction_OnClick"/>
<!-- Button: Remove action -->
<pilz:ImageButton
x:Name="ButtonRemoveAction"
Text="{x:Static langRes:GeneralLangRes.Remove}"
ImageSource="{x:Static local:MainWindow.ButtonImageRemoveAction}"
Background="Transparent"
Click="ButtonRemoveAction_OnClick"/>
<!-- TextBox: Version --> <!-- TextBox: Version -->
<TextBox <TextBox
Margin="3, 0, 3, 0"
Width="100" Width="100"
Text="{Binding Version}"/> Text="{Binding Version}"/>
<!-- CheckBox: Is public --> <!-- CheckBox: Is public -->
<CheckBox <CheckBox
Margin="3, 0, 3, 0"
Content="{x:Static langRes:GeneralLangRes.Public}" Content="{x:Static langRes:GeneralLangRes.Public}"
IsChecked="{Binding IsPublic}"/> IsChecked="{Binding IsPublic}"/>
</StackPanel> </StackPanel>
@@ -172,7 +183,7 @@
Grid.Row="1" Grid.Row="1"
x:Name="DataGridActions" x:Name="DataGridActions"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
ItemsSource="{Binding CurrentGridRows}" ItemsSource="{Binding CurrentGridRows.View}"
SelectedItem="{Binding SelectedGridRow}"> SelectedItem="{Binding SelectedGridRow}">
<DataGrid.ContextMenu> <DataGrid.ContextMenu>
@@ -182,24 +193,31 @@
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn <DataGridTextColumn
Header="{x:Static langRes:GeneralLangRes.Id}" Header="{x:Static langRes:GeneralLangRes.Id}"
Binding="{Binding InheritedId}"/> Binding="{Binding InheritedId}"
Width="*"/>
<DataGridTextColumn <DataGridTextColumn
Header="{x:Static langRes:GeneralLangRes.Side}" Header="{x:Static langRes:GeneralLangRes.Name}"
Binding="{Binding InheritedSide}"/> Binding="{Binding InheritedName}"
Width="*"/>
<DataGridTextColumn <DataGridTextColumn
Header="{x:Static langRes:GeneralLangRes.UpdateType}" Header="{x:Static langRes:GeneralLangRes.UpdateType}"
Binding="{Binding InheritedUpdateType}" Binding="{Binding InheritedUpdateType}"
IsVisible="{Binding IsUpdate}"/> IsVisible="{Binding IsUpdate}"/>
<DataGridTextColumn
Header="{x:Static langRes:GeneralLangRes.Side}"
Binding="{Binding InheritedSide}"/>
<DataGridTextColumn <DataGridTextColumn
Header="{x:Static langRes:GeneralLangRes.SourceType}" Header="{x:Static langRes:GeneralLangRes.SourceType}"
Binding="{Binding InheritedSourceType}"/> Binding="{Binding InheritedSourceType}"/>
<DataGridTextColumn <DataGridTextColumn
Header="{x:Static langRes:GeneralLangRes.DestinationPath}" Header="{x:Static langRes:GeneralLangRes.DestinationPath}"
Binding="{Binding InheritedDestPath}"/> Binding="{Binding InheritedDestPath}"
Width="*"/>
<DataGridTemplateColumn <DataGridTemplateColumn
Header="{x:Static langRes:GeneralLangRes.State}"> Header="{x:Static langRes:GeneralLangRes.State}">

View File

@@ -3,6 +3,7 @@ using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Media; using Avalonia.Media;
using DynamicData;
using ModpackUpdater.Apps.Manager.Api; using ModpackUpdater.Apps.Manager.Api;
using ModpackUpdater.Apps.Manager.Api.Model; using ModpackUpdater.Apps.Manager.Api.Model;
using ModpackUpdater.Apps.Manager.Api.Plugins.Features; using ModpackUpdater.Apps.Manager.Api.Plugins.Features;
@@ -10,6 +11,7 @@ using ModpackUpdater.Apps.Manager.Api.Plugins.Params;
using ModpackUpdater.Apps.Manager.Settings; using ModpackUpdater.Apps.Manager.Settings;
using ModpackUpdater.Apps.Manager.Ui.Models.MainWindow; using ModpackUpdater.Apps.Manager.Ui.Models.MainWindow;
using Pilz.Extensions; using Pilz.Extensions;
using Pilz.Extensions.Collections;
using Pilz.Features; using Pilz.Features;
using Pilz.UI.AvaloniaUI.Features; using Pilz.UI.AvaloniaUI.Features;
using Pilz.UI.Symbols; using Pilz.UI.Symbols;
@@ -118,18 +120,18 @@ public partial class MainWindow : Window, IMainApi
private static void AddToRecentFiles(IWorkspace workspace) private static void AddToRecentFiles(IWorkspace workspace)
{ {
var settings = Program.Settings.Get<WorkspaceSettings>(); var settings = Program.Settings.Get<WorkspaceSettings>();
settings.Workspaces.Remove(workspace.Config); settings.Workspaces.Remove(workspace.Config);
settings.Workspaces.Insert(0, workspace.Config); settings.Workspaces.Insert(0, workspace.Config);
settings.Workspaces.Skip(20).ForEach(n => settings.Workspaces.Remove(n));
while (settings.Workspaces.Count > 20)
settings.Workspaces.RemoveAt(20);
} }
private async void Window_OnLoaded(object? sender, RoutedEventArgs e) private async void Window_OnLoaded(object? sender, RoutedEventArgs e)
{ {
var updater = new AppUpdates(Program.UpdateUrl, this); var updater = new AppUpdates("client", this)
updater.OnDownloadProgramUpdate += (o, args) => Model.Progress.Start(); {
UsePopups = true,
};
updater.OnDownloadProgramUpdate += (_, _) => Model.Progress.Start();
await updater.UpdateApp(); await updater.UpdateApp();
Model.Progress.Stop(); Model.Progress.Stop();
@@ -239,7 +241,7 @@ public partial class MainWindow : Window, IMainApi
return; return;
} }
rows.Add(new MainWindowGridRow(action, rootInfos)); rows.List.Add(new MainWindowGridRow(action, rootInfos));
} }
private void ButtonRemoveAction_OnClick(object? sender, RoutedEventArgs e) private void ButtonRemoveAction_OnClick(object? sender, RoutedEventArgs e)
@@ -262,6 +264,6 @@ public partial class MainWindow : Window, IMainApi
return; return;
} }
rows.Remove(row); rows.List.Remove(row);
} }
} }

View File

@@ -0,0 +1,34 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using DynamicData;
namespace ModpackUpdater.Apps.Manager.Ui.Models;
public class DynamicDataView<T> : INotifyPropertyChanged where T : notnull
{
public event PropertyChangedEventHandler? PropertyChanged;
private string? searchText;
private readonly Subject<string?> searchTextSubject = new();
public SourceList<T> List { get; } = new();
public ReadOnlyObservableCollection<T> View { get; }
public DynamicDataView(Func<string?, Func<T, bool>> predicate)
{
List.Connect()
.Filter(searchTextSubject/*.Throttle(TimeSpan.FromMilliseconds(250))*/.Select(predicate))
.Bind(out var view)
.Subscribe();
searchTextSubject?.OnNext(searchText);
View = view;
}
public string? SearchText
{
get => searchText;
set => searchTextSubject.OnNext(searchText = value);
}
}

View File

@@ -23,14 +23,21 @@ public class MainWindowGridRow(InstallAction action, IActionSet baseActions) : I
[DependsOn(nameof(Id), nameof(InheritFrom))] [DependsOn(nameof(Id), nameof(InheritFrom))]
public string? InheritedId => action is UpdateAction ua && !string.IsNullOrWhiteSpace(ua.InheritFrom) ? ua.InheritFrom : action.Id; public string? InheritedId => action is UpdateAction ua && !string.IsNullOrWhiteSpace(ua.InheritFrom) ? ua.InheritFrom : action.Id;
[DependsOn(nameof(Side), nameof(InheritedId), nameof(Id))] [DependsOn(nameof(Side), nameof(InheritedId), nameof(Id))]
public string InheritedSide => Sides[Inherited.Side]; public string InheritedSide => Sides[Inherited.Side];
[DependsOn(nameof(UpdateType), nameof(InheritedId), nameof(Id))] [DependsOn(nameof(UpdateType), nameof(InheritedId), nameof(Id))]
public string InheritedUpdateType => action is UpdateAction ua ? UpdateActionTypes[ua.Type] : string.Empty; public string InheritedUpdateType => action is UpdateAction ua ? UpdateActionTypes[ua.Type] : string.Empty;
[DependsOn(nameof(SourceType), nameof(InheritedId), nameof(Id))] [DependsOn(nameof(SourceType), nameof(InheritedId), nameof(Id))]
public string InheritedSourceType => SourceTypes[Inherited.SourceType]; public string InheritedSourceType => SourceTypes[Inherited.SourceType];
[DependsOn(nameof(DestPath), nameof(InheritedId), nameof(Id))] [DependsOn(nameof(DestPath), nameof(InheritedId), nameof(Id))]
public string? InheritedDestPath => Inherited.DestPath; public string? InheritedDestPath => Inherited.DestPath;
[DependsOn(nameof(Name), nameof(InheritedId), nameof(Id))]
public string? InheritedName => Inherited.Name;
public string? Id public string? Id
{ {

View File

@@ -11,13 +11,13 @@ public class MainWindowViewModel : INotifyPropertyChanged
public event PropertyChangedEventHandler? PropertyChanged; public event PropertyChangedEventHandler? PropertyChanged;
private ObservableCollection<MainWindowTreeNode>? currentTreeNodes; private ObservableCollection<MainWindowTreeNode>? currentTreeNodes;
private ObservableCollection<MainWindowGridRow>? currentGridRows;
private MainWindowTreeNode? selectedTreeNode; private MainWindowTreeNode? selectedTreeNode;
private IWorkspace? currentWorkspace; private IWorkspace? currentWorkspace;
public bool IsUpdate => selectedTreeNode is ActionSetTreeNode node && node.Infos is UpdateInfo; public bool IsUpdate => selectedTreeNode is ActionSetTreeNode node && node.Infos is UpdateInfo;
public ProgressInfos Progress { get; } = new(); public ProgressInfos Progress { get; } = new();
public MainWindowGridRow? SelectedGridRow { get; set; } public MainWindowGridRow? SelectedGridRow { get; set; }
public DynamicDataView<MainWindowGridRow> CurrentGridRows { get; } = new(FilterGridRows);
[AlsoNotifyFor(nameof(CurrentTreeNodes))] [AlsoNotifyFor(nameof(CurrentTreeNodes))]
public IWorkspace? CurrentWorkspace public IWorkspace? CurrentWorkspace
@@ -48,24 +48,38 @@ public class MainWindowViewModel : INotifyPropertyChanged
} }
} }
[AlsoNotifyFor(nameof(CurrentGridRows))]
public MainWindowTreeNode? SelectedTreeNode public MainWindowTreeNode? SelectedTreeNode
{ {
get => selectedTreeNode; get => selectedTreeNode;
set set
{ {
currentGridRows = null;
selectedTreeNode = value; selectedTreeNode = value;
CurrentGridRows.List.Edit(list =>
{
list.Clear();
if (CurrentWorkspace?.InstallInfos != null && selectedTreeNode is ActionSetTreeNode node)
list.AddRange(node.Infos.Actions.Select(n => new MainWindowGridRow(n, CurrentWorkspace.InstallInfos)));
});
} }
} }
public ObservableCollection<MainWindowGridRow>? CurrentGridRows private static Func<MainWindowGridRow, bool> FilterGridRows(string? searchText)
{ {
get return n => string.IsNullOrWhiteSpace(searchText)
{ || (!string.IsNullOrWhiteSpace(n.Name) && n.Name.Contains(searchText))
if (currentGridRows == null && CurrentWorkspace?.InstallInfos != null && selectedTreeNode is ActionSetTreeNode node) || (!string.IsNullOrWhiteSpace(n.InheritFrom) && n.InheritFrom.Contains(searchText))
currentGridRows = [.. node.Infos.Actions.Select(n => new MainWindowGridRow(n, CurrentWorkspace.InstallInfos))]; || (!string.IsNullOrWhiteSpace(n.InheritedDestPath) && n.InheritedDestPath.Contains(searchText))
return currentGridRows; || (!string.IsNullOrWhiteSpace(n.InheritedId) && n.InheritedId.Contains(searchText))
} || (!string.IsNullOrWhiteSpace(n.InheritedSide) && n.InheritedSide.Contains(searchText))
|| (!string.IsNullOrWhiteSpace(n.InheritedSourceType) && n.InheritedSourceType.Contains(searchText))
|| (!string.IsNullOrWhiteSpace(n.InheritedUpdateType) && n.InheritedUpdateType.Contains(searchText))
|| (!string.IsNullOrWhiteSpace(n.SourceName) && n.SourceName.Contains(searchText))
|| (!string.IsNullOrWhiteSpace(n.SourceOwner) && n.SourceOwner.Contains(searchText))
|| (!string.IsNullOrWhiteSpace(n.SourceRegex) && n.SourceRegex.Contains(searchText))
|| (!string.IsNullOrWhiteSpace(n.SourceTag) && n.SourceTag.Contains(searchText))
|| (!string.IsNullOrWhiteSpace(n.SourceUrl) && n.SourceUrl.Contains(searchText))
|| (!string.IsNullOrWhiteSpace(n.SrcPath) && n.SrcPath.Contains(searchText))
|| (!string.IsNullOrWhiteSpace(n.Website) && n.Website.Contains(searchText))
;
} }
} }

View File

@@ -1,5 +1,9 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using DynamicData;
using PropertyChanged; using PropertyChanged;
namespace ModpackUpdater.Apps.Manager.Ui.Models.UpdatesCollectorViewMode; namespace ModpackUpdater.Apps.Manager.Ui.Models.UpdatesCollectorViewMode;
@@ -9,6 +13,10 @@ public class UpdatesCollectorViewModel : INotifyPropertyChanged
public event PropertyChangedEventHandler? PropertyChanged; public event PropertyChangedEventHandler? PropertyChanged;
public ProgressInfos Progress { get; } = new(); public ProgressInfos Progress { get; } = new();
public string? SearchText { get; set; } public DynamicDataView<ModUpdateInfo> Updates { get; } = new(FilterUpdates);
public ObservableCollection<ModUpdateInfo> Updates { get; } = [];
private static Func<ModUpdateInfo, bool> FilterUpdates(string? searchText)
{
return n => string.IsNullOrWhiteSpace(searchText) || (n.Origin.Name != null && n.Origin.Name.Contains(searchText, StringComparison.InvariantCultureIgnoreCase));
}
} }

View File

@@ -25,16 +25,15 @@
<!-- TextBox: Search --> <!-- TextBox: Search -->
<TextBox <TextBox
Grid.Row="0" Grid.Row="0"
Watermark="Search" Watermark="{x:Static langRes:GeneralLangRes.Search}"
Text="{Binding SearchText}" Text="{Binding Updates.SearchText}"/>
TextChanged="TextBoxSearch_OnTextChanged"/>
<!-- ScrollViewer: Updates --> <!-- ScrollViewer: Updates -->
<ScrollViewer <ScrollViewer
Grid.Row="1"> Grid.Row="1">
<ItemsControl <ItemsControl
ItemsSource="{Binding Updates}"> ItemsSource="{Binding Updates.View}">
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
@@ -43,8 +42,7 @@
ColumnDefinitions="20*,20*,20*,Auto" ColumnDefinitions="20*,20*,20*,Auto"
ColumnSpacing="6" ColumnSpacing="6"
RowSpacing="6" RowSpacing="6"
Margin="3" Margin="3">
IsVisible="{Binding Visible}">
<!-- Label: Name --> <!-- Label: Name -->
<TextBlock <TextBlock

View File

@@ -1,5 +1,6 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using DynamicData;
using ModpackUpdater.Apps.Manager.Api.Model; using ModpackUpdater.Apps.Manager.Api.Model;
using ModpackUpdater.Apps.Manager.Ui.Models.UpdatesCollectorViewMode; using ModpackUpdater.Apps.Manager.Ui.Models.UpdatesCollectorViewMode;
using ModpackUpdater.Manager; using ModpackUpdater.Manager;
@@ -36,7 +37,7 @@ public partial class UpdatesCollectorView : AvaloniaFlyoutBase
if (updates == null || updates.Length == 0 || updates[0].Tag == action.SourceTag) if (updates == null || updates.Length == 0 || updates[0].Tag == action.SourceTag)
continue; continue;
Model.Updates.Add(new(updates, action)); Model.Updates.List.Add(new(updates, action));
if (IsClosed) if (IsClosed)
break; break;
@@ -47,7 +48,7 @@ public partial class UpdatesCollectorView : AvaloniaFlyoutBase
protected override object GetResult() protected override object GetResult()
{ {
return new ModUpdates(Model.Updates); return new ModUpdates([.. Model.Updates.List.Items]);
} }
private async void Me_OnLoaded(object? sender, RoutedEventArgs e) private async void Me_OnLoaded(object? sender, RoutedEventArgs e)
@@ -55,18 +56,9 @@ public partial class UpdatesCollectorView : AvaloniaFlyoutBase
await FindUpdates(); await FindUpdates();
} }
private void TextBoxSearch_OnTextChanged(object? sender, TextChangedEventArgs e)
{
var searchString = Model.SearchText?.Trim().ToLowerInvariant();
var hasNoSearch = string.IsNullOrWhiteSpace(searchString);
foreach (var item in Model.Updates)
item.Visible = hasNoSearch || (item.Origin.Name != null && item.Origin.Name.Contains(searchString!, StringComparison.InvariantCultureIgnoreCase));
}
private void ButtonRemoveUpdate_Click(object? sender, RoutedEventArgs e) private void ButtonRemoveUpdate_Click(object? sender, RoutedEventArgs e)
{ {
if (sender is Button button && button.DataContext is ModUpdateInfo update) if (sender is Button button && button.DataContext is ModUpdateInfo update)
Model.Updates.Remove(update); Model.Updates.List.Remove(update);
} }
} }

View File

@@ -10,13 +10,19 @@ using Pilz.Updating.Client;
namespace ModpackUpdater.Apps; namespace ModpackUpdater.Apps;
public class AppUpdates(string updateUrl, Window mainWindow) public class AppUpdates(string appShortName, Window mainWindow)
{ {
public const string UpdateUrl = "https://git.pilzinsel64.de/litw-refined/minecraft-modpack-updater/-/snippets/3/raw/main/updates-new.json";
public event EventHandler? OnDownloadProgramUpdate; public event EventHandler? OnDownloadProgramUpdate;
public bool UsePopups { get; set; }
public async Task UpdateApp() public async Task UpdateApp()
{ {
#if !DISABLE_UPDATE #if DISABLE_UPDATE
await Task.CompletedTask;
#else
try try
{ {
await UpdateAppCore(); await UpdateAppCore();
@@ -35,25 +41,33 @@ public class AppUpdates(string updateUrl, Window mainWindow)
return; return;
var myAppPath = EnvironmentEx.ProcessPath!; var myAppPath = EnvironmentEx.ProcessPath!;
var updater = new UpdateClient(updateUrl, Assembly.GetEntryAssembly()!.GetAppVersion(), AppChannel.Stable) var updater = new UpdateClient(UpdateUrl, Assembly.GetEntryAssembly()!.GetAppVersion(), AppChannel.Stable)
{ {
Distro = RuntimeInformationsEx.GetRuntimeIdentifier(), Distro = $"{appShortName}-{RuntimeInformationsEx.GetRuntimeIdentifier()}",
}; };
if (await updater.CheckForUpdate() is not { } packageToInstall || await AskForUpdate() != ButtonResult.Yes)
return;
if (await updater.CheckForUpdate() is {} packageToInstall OnDownloadProgramUpdate?.Invoke(this, EventArgs.Empty);
&& await MessageBoxManager.GetMessageBoxStandard(GeneralMsgBoxLangRes.UpdateAvailable_Title, GeneralMsgBoxLangRes.UpdateAvailable, ButtonEnum.YesNo, MsBox.Avalonia.Enums.Icon.Info).ShowWindowDialogAsync(mainWindow) == ButtonResult.Yes) mainWindow.IsEnabled = false;
if (await updater.DownloadPackageAsync(packageToInstall) && await updater.InstallPackageAsync(packageToInstall, myAppPath))
{ {
OnDownloadProgramUpdate?.Invoke(this, EventArgs.Empty); mainWindow.IsVisible = false;
mainWindow.IsEnabled = false; await Process.Start(myAppPath).WaitForExitAsync();
if (await updater.DownloadPackageAsync(packageToInstall) Environment.Exit(0);
&& await updater.InstallPackageAsync(packageToInstall, myAppPath)) return;
{
mainWindow.IsVisible = false;
await Process.Start(myAppPath).WaitForExitAsync();
Environment.Exit(0);
return;
}
mainWindow.IsEnabled = true;
} }
mainWindow.IsEnabled = true;
}
private Task<ButtonResult> AskForUpdate()
{
var msgBox = MessageBoxManager.GetMessageBoxStandard(GeneralMsgBoxLangRes.UpdateAvailable_Title, GeneralMsgBoxLangRes.UpdateAvailable, ButtonEnum.YesNo, Icon.Info);
if (UsePopups)
return msgBox.ShowAsPopupAsync(mainWindow);
return msgBox.ShowWindowDialogAsync(mainWindow);
} }
} }

View File

@@ -3,4 +3,7 @@ set -e
for dir in ModpackUpdater.Apps.Client ModpackUpdater.Apps.Client.Gui ModpackUpdater.Apps.Manager; do for dir in ModpackUpdater.Apps.Client ModpackUpdater.Apps.Client.Gui ModpackUpdater.Apps.Manager; do
( cd "$dir" && ./publish.sh "$1" ) ( cd "$dir" && ./publish.sh "$1" )
done done
# Remove artifacts
rm -r publish/*/Artifacts*