Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
f089629c74
|
|||
|
836282c6ac
|
|||
|
ee6b8d443d
|
|||
|
5947f81307
|
|||
| 32c4065940 | |||
| 8c29cf9e8a | |||
|
16b1a655aa
|
|||
|
12b5a63003
|
|||
|
035d2eeb9b
|
|||
|
eaaca4ddb8
|
|||
|
c1960abe3a
|
|||
|
cbd4b1c346
|
|||
|
db108fe36e
|
|||
|
bd8a08f03c
|
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "publish-scripts"]
|
||||
path = publish-scripts
|
||||
url = https://git.pilzinsel64.de/pilz-framework/publish-scripts.git
|
||||
url = https://git.pilzinsel64.de/Pilz.NET/publish-scripts.git
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="ModpackUpdater.Apps.Client.Gui.App"
|
||||
RequestedThemeVariant="Default">
|
||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="ModpackUpdater.Apps.Client.Gui.App"
|
||||
RequestedThemeVariant="Default">
|
||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||
|
||||
<Application.Styles>
|
||||
<FluentTheme />
|
||||
<FluentTheme/>
|
||||
<StyleInclude Source="avares://Pilz.UI.AvaloniaUI/Assets/Styles/EnhancedDefaults.axaml"/>
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
@@ -1,5 +1,6 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Data.Core.Plugins;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace ModpackUpdater.Apps.Client.Gui;
|
||||
@@ -15,7 +16,26 @@ public partial class App : Application
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
desktop.MainWindow = new MainForm();
|
||||
{
|
||||
DisableAvaloniaDataAnnotationValidation();
|
||||
desktop.MainWindow = new MainView
|
||||
{
|
||||
DataContext = new MainViewModel(),
|
||||
};
|
||||
}
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
|
||||
private void DisableAvaloniaDataAnnotationValidation()
|
||||
{
|
||||
// Get an array of plugins to remove
|
||||
var dataValidationPluginsToRemove =
|
||||
BindingPlugins.DataValidators.OfType<DataAnnotationsValidationPlugin>().ToArray();
|
||||
|
||||
// remove each entry found
|
||||
foreach (var plugin in dataValidationPluginsToRemove)
|
||||
{
|
||||
BindingPlugins.DataValidators.Remove(plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ public class AppConfig : ISettingsNode, ISettingsIdentifier
|
||||
[JsonProperty, Obsolete]
|
||||
private string? LastMinecraftProfilePath
|
||||
{
|
||||
get => RecentMinecraftProfilePaths.FirstOrDefault();
|
||||
set
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
|
||||
@@ -1,379 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using ModpackUpdater.Apps.Client.Gui.LangRes;
|
||||
using ModpackUpdater.Manager;
|
||||
using Pilz.Extensions;
|
||||
using Pilz.Extensions.Collections;
|
||||
using Pilz.UI.Symbols;
|
||||
|
||||
namespace ModpackUpdater.Apps.Client.Gui;
|
||||
|
||||
public partial class MainForm : Window
|
||||
{
|
||||
private readonly MenuFlyout menuFlyoutSearchProfileFolder = new();
|
||||
private readonly UpdateCheckOptions updateOptions = new();
|
||||
private ModpackInfo modpackInfo = new();
|
||||
private ModpackConfig updateConfig = new();
|
||||
private ModpackFeatures? features;
|
||||
private UpdateCheckResult? lastUpdateCheckResult;
|
||||
private bool currentUpdating;
|
||||
private bool loadingData;
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Title = $"{Title} (v{Assembly.GetExecutingAssembly().GetAppVersion().ToShortString()})";
|
||||
ButtonSearchProfileFolder.Flyout = menuFlyoutSearchProfileFolder;
|
||||
|
||||
ButtonSearchProfileFolder.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.opened_folder);
|
||||
ButtonCheckForUpdates.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.update_done);
|
||||
ButtonInstall.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer);
|
||||
MenuItemRepair.Icon = AppGlobals.Symbols.GetImage(AppSymbols.wrench, SymbolSize.Small);
|
||||
|
||||
ClearStatus();
|
||||
LoadProfileToUi();
|
||||
}
|
||||
|
||||
#region Features
|
||||
|
||||
private void SetStatus(string statusText, IImage? image)
|
||||
{
|
||||
TextStatus.Text = statusText;
|
||||
ImageStatus.Source = image;
|
||||
}
|
||||
|
||||
private void ClearStatus()
|
||||
{
|
||||
TextStatus.Text = "-";
|
||||
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()
|
||||
{
|
||||
loadingData = true;
|
||||
|
||||
TextBoxMinecraftProfileFolder.Text = modpackInfo.LocalPath ?? AppConfig.Instance.RecentMinecraftProfilePaths.FirstOrDefault() ?? TextBoxMinecraftProfileFolder.Text;
|
||||
TextBoxModpackConfig.Text = modpackInfo.ConfigUrl ?? TextBoxModpackConfig.Text;
|
||||
TextBoxInstallKey.Text = modpackInfo.ExtrasKey ?? TextBoxInstallKey.Text;
|
||||
|
||||
Dispatcher.UIThread.Post(() => loadingData = false, DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
private void LoadOptionsToUi()
|
||||
{
|
||||
//foreach (var set in )
|
||||
//{
|
||||
// // ...
|
||||
//}
|
||||
}
|
||||
|
||||
private async void CheckStatusAndUpdate(bool loadProfileToUi)
|
||||
{
|
||||
if (!CheckStatus(loadProfileToUi))
|
||||
return;
|
||||
|
||||
await ExecuteUpdate(false, false);
|
||||
|
||||
StoreRecentMinecraftProfilePath(modpackInfo.LocalPath);
|
||||
LoadRecentFilesToUi();
|
||||
}
|
||||
|
||||
private bool CheckStatus(bool loadProfileToUi)
|
||||
{
|
||||
try
|
||||
{
|
||||
modpackInfo = ModpackInfo.TryLoad(TextBoxMinecraftProfileFolder.Text?.Trim());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
|
||||
if (loadProfileToUi)
|
||||
LoadProfileToUi();
|
||||
|
||||
try
|
||||
{
|
||||
updateConfig = ModpackConfig.LoadFromUrl(TextBoxModpackConfig.Text);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
|
||||
features = new(updateConfig);
|
||||
modpackInfo.ExtrasKey = TextBoxInstallKey.Text?.Trim();
|
||||
if (!features.IsInvalid() && !string.IsNullOrWhiteSpace(TextBoxInstallKey.Text) && !AllowExtras())
|
||||
{
|
||||
SetStatus(GeneralLangRes.InstallationKeyNotValid, AppGlobals.Symbols.GetImageSource(AppSymbols.general_warning_sign));
|
||||
return false;
|
||||
}
|
||||
|
||||
LabelInstallKey.IsVisible = TextBoxInstallKey.IsVisible = !string.IsNullOrWhiteSpace(updateConfig.UnleashApiUrl);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(TextBoxMinecraftProfileFolder.Text) /*|| modpackInfo.Valid*/)
|
||||
{
|
||||
SetStatus(GeneralLangRes.MinecraftProfileFolderSeemsInvalid, AppGlobals.Symbols.GetImageSource(AppSymbols.general_warning_sign));
|
||||
ButtonCheckForUpdates.IsEnabled = false;
|
||||
ButtonInstall.IsEnabled = false;
|
||||
return false;
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(TextBoxModpackConfig.Text))
|
||||
{
|
||||
SetStatus(GeneralLangRes.ConfigIncompleteOrNotLoaded, AppGlobals.Symbols.GetImageSource(AppSymbols.general_warning_sign));
|
||||
ButtonCheckForUpdates.IsEnabled = false;
|
||||
ButtonInstall.IsEnabled = false;
|
||||
return false;
|
||||
}
|
||||
else if (updateConfig.Maintenance && !updateOptions.IgnoreMaintenance)
|
||||
{
|
||||
SetStatus(GeneralLangRes.UpdateServerInMaintenance, AppGlobals.Symbols.GetImageSource(AppSymbols.services));
|
||||
ButtonCheckForUpdates.IsEnabled = false;
|
||||
ButtonInstall.IsEnabled = false;
|
||||
return false;
|
||||
}
|
||||
LoadOptionsToUi();
|
||||
ButtonCheckForUpdates.IsEnabled = true;
|
||||
ButtonInstall.IsEnabled = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task ExecuteUpdate(bool doInstall, bool repair)
|
||||
{
|
||||
// Ensure set extras key
|
||||
modpackInfo.ExtrasKey = TextBoxInstallKey.Text?.Trim();
|
||||
|
||||
var updater = new ModpackInstaller(updateConfig, modpackInfo);
|
||||
updater.InstallProgessUpdated += Update_InstallProgessUpdated;
|
||||
updater.CheckingProgressUpdated += Updated_CheckingProgresssUpdated;
|
||||
|
||||
void error()
|
||||
{
|
||||
SetStatus(GeneralLangRes.ErrorOnUpdateCheckOrUpdating, AppGlobals.Symbols.GetImageSource(AppSymbols.close));
|
||||
currentUpdating = false;
|
||||
}
|
||||
void installing()
|
||||
{
|
||||
SetStatus(GeneralLangRes.Installing, AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer));
|
||||
currentUpdating = true;
|
||||
}
|
||||
void updatesAvailable()
|
||||
{
|
||||
SetStatus(GeneralLangRes.AnUpdateIsAvailable, AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer));
|
||||
}
|
||||
void everythingOk()
|
||||
{
|
||||
SetStatus(GeneralLangRes.EverythingIsRightAndUpToDate, AppGlobals.Symbols.GetImageSource(AppSymbols.done));
|
||||
currentUpdating = false;
|
||||
}
|
||||
|
||||
// Check only if not pressed "install", not really needed otherwise.
|
||||
if (lastUpdateCheckResult is null || !doInstall || repair)
|
||||
{
|
||||
SetStatus(GeneralLangRes.CheckingForUpdates, AppGlobals.Symbols.GetImageSource(AppSymbols.update_done));
|
||||
|
||||
// Check for extras once again
|
||||
updateOptions.IncludeExtras = AllowExtras();
|
||||
|
||||
// Force re-install on repair
|
||||
updateOptions.IgnoreInstalledVersion = repair;
|
||||
|
||||
try
|
||||
{
|
||||
lastUpdateCheckResult = await updater.Check(updateOptions);
|
||||
}
|
||||
catch
|
||||
{
|
||||
error();
|
||||
if (Debugger.IsAttached)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// Error while update check
|
||||
if (lastUpdateCheckResult is null || lastUpdateCheckResult.HasError)
|
||||
{
|
||||
error();
|
||||
return;
|
||||
}
|
||||
|
||||
// Load options
|
||||
// lastUpdateCheckResult.OptionsAvailable...
|
||||
// lastUpdateCheckResult.OptionsEnabled...
|
||||
|
||||
// No updates available
|
||||
if (!lastUpdateCheckResult.HasUpdates)
|
||||
{
|
||||
everythingOk();
|
||||
return;
|
||||
}
|
||||
|
||||
// Updates available (but don't install)
|
||||
if (!doInstall)
|
||||
{
|
||||
updatesAvailable();
|
||||
return;
|
||||
}
|
||||
|
||||
// Install updates
|
||||
installing();
|
||||
currentUpdating = true;
|
||||
try
|
||||
{
|
||||
// Install
|
||||
if (await updater.Install(lastUpdateCheckResult) == false)
|
||||
{
|
||||
error();
|
||||
return;
|
||||
}
|
||||
|
||||
// Success
|
||||
lastUpdateCheckResult = null; // Reset last update check, a new one would be needed now.
|
||||
everythingOk();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Error
|
||||
error();
|
||||
if (Debugger.IsAttached)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private bool AllowExtras()
|
||||
{
|
||||
return features != null && features.IsEnabled(ModpackFeatures.FeatureAllowExtas, new AllowExtrasFeatureContext(modpackInfo));
|
||||
}
|
||||
|
||||
private void Updated_CheckingProgresssUpdated(int toCheck, int processed)
|
||||
{
|
||||
SetStatus(Math.Round(processed / (double)toCheck * 100d, 1) + "%", AppGlobals.Symbols.GetImageSource(AppSymbols.update_done));
|
||||
}
|
||||
|
||||
private void Update_InstallProgessUpdated(UpdateCheckResult result, int processedSyncs)
|
||||
{
|
||||
var actionCount = result.Actions.Count;
|
||||
SetStatus(Math.Round(processedSyncs / (double)actionCount * 100d, 1) + "%", AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Gui
|
||||
|
||||
private async void MainForm_Loaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var updates = new AppUpdates("client", this);
|
||||
updates.OnDownloadProgramUpdate += (_, _) => SetStatus(GeneralLangRes.DownloadProgramUpdate, AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer));
|
||||
await updates.UpdateApp();
|
||||
ClearStatus();
|
||||
LoadRecentFilesToUi();
|
||||
CheckStatusAndUpdate(true);
|
||||
}
|
||||
|
||||
private void TextBoxMinecraftProfileFolder_TextChanged(object? o, TextChangedEventArgs args)
|
||||
{
|
||||
if (!loadingData)
|
||||
CheckStatusAndUpdate(true);
|
||||
}
|
||||
|
||||
private void TextBoxModpackConfig_TextChanged(object? o, RoutedEventArgs args)
|
||||
{
|
||||
if (!loadingData)
|
||||
CheckStatusAndUpdate(false);
|
||||
}
|
||||
|
||||
private void TextBoxInstallKey_TextChanged(object? o, RoutedEventArgs args)
|
||||
{
|
||||
if (!loadingData)
|
||||
CheckStatusAndUpdate(false);
|
||||
}
|
||||
|
||||
private async void ButtonSearchProfileFolder_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var filePaths = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||
{
|
||||
Title = GeneralLangRes.SelectMinecraftProfileFolder,
|
||||
SuggestedStartLocation = await StorageProvider.TryGetFolderFromPathAsync(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)),
|
||||
AllowMultiple = false,
|
||||
});
|
||||
|
||||
if (filePaths.Count >= 1)
|
||||
TextBoxMinecraftProfileFolder.Text = filePaths[0].Path.AbsolutePath;
|
||||
}
|
||||
|
||||
private async void ButtonCheckForUpdates_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
ClearStatus();
|
||||
await ExecuteUpdate(false, false);
|
||||
}
|
||||
|
||||
private async void ButtonInstall_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (currentUpdating)
|
||||
return;
|
||||
|
||||
ClearStatus();
|
||||
await ExecuteUpdate(true, false);
|
||||
}
|
||||
|
||||
private async void MenuItemRepair_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (currentUpdating)
|
||||
return;
|
||||
|
||||
ClearStatus();
|
||||
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
|
||||
}
|
||||
@@ -4,16 +4,23 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:lang="clr-namespace:ModpackUpdater.Apps.Client.Gui.LangRes"
|
||||
xmlns:pilz="https://git.pilzinsel64.de/pilz-framework/pilz"
|
||||
xmlns:gui="clr-namespace:ModpackUpdater.Apps.Client.Gui"
|
||||
xmlns:symbols="clr-namespace:Pilz.UI.Symbols;assembly=Pilz.UI"
|
||||
mc:Ignorable="d"
|
||||
x:Class="ModpackUpdater.Apps.Client.Gui.MainForm"
|
||||
x:Class="ModpackUpdater.Apps.Client.Gui.MainView"
|
||||
x:DataType="gui:MainViewModel"
|
||||
x:Name="window"
|
||||
Width="520"
|
||||
SizeToContent="Height"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
CanMaximize="false"
|
||||
Title="Minecraft Modpack Updater"
|
||||
Icon="/Assets/app.ico"
|
||||
Loaded="MainForm_Loaded">
|
||||
Loaded="Control_OnLoaded">
|
||||
|
||||
<Design.DataContext>
|
||||
<gui:MainViewModel/>
|
||||
</Design.DataContext>
|
||||
|
||||
<Grid
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto"
|
||||
@@ -21,6 +28,13 @@
|
||||
VerticalAlignment="Top"
|
||||
Margin="3"
|
||||
x:Name="MainGrid">
|
||||
|
||||
<Grid.IsEnabled>
|
||||
<MultiBinding Converter="{x:Static BoolConverters.And}">
|
||||
<Binding Path="HasInitialized"/>
|
||||
<Binding Path="!IsUpdating"/>
|
||||
</MultiBinding>
|
||||
</Grid.IsEnabled>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
@@ -31,11 +45,11 @@
|
||||
<!-- Labels -->
|
||||
<Label Grid.Row="0" Grid.Column="0" Margin="3" Content="{x:Static lang:GeneralLangRes.MinecraftProfile}" Target="TextBoxMinecraftProfileFolder"/>
|
||||
<Label Grid.Row="1" Grid.Column="0" Margin="3" Content="{x:Static lang:GeneralLangRes.ModpackConfigUrl}" Target="TextBoxModpackConfig"/>
|
||||
<Label Grid.Row="2" Grid.Column="0" Margin="3" IsVisible="false" x:Name="LabelInstallKey" Content="{x:Static lang:GeneralLangRes.InstallationKey}" Target="TextBoxInstallKey"/>
|
||||
<Label Grid.Row="2" Grid.Column="0" Margin="3" IsVisible="{Binding CanUseExtrasKey}" Content="{x:Static lang:GeneralLangRes.InstallationKey}" Target="TextBoxInstallKey"/>
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Margin="3" Text="{x:Static lang:GeneralLangRes.Status}"/>
|
||||
<StackPanel Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" Margin="3" Orientation="Horizontal" Spacing="6" MinHeight="{Binding MinHeight, ElementName=TextBoxMinecraftProfileFolder}">
|
||||
<Image Width="{x:Static symbols:SymbolGlobals.DefaultImageSmallSize}" x:Name="ImageStatus"/>
|
||||
<TextBlock x:Name="TextStatus"/>
|
||||
<Image Width="{x:Static symbols:SymbolGlobals.DefaultImageSmallSize}" Source="{Binding StatusImage}"/>
|
||||
<TextBlock Text="{Binding StatusText}"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- TextBoxes: Profile -->
|
||||
@@ -46,7 +60,7 @@
|
||||
Margin="3"
|
||||
VerticalAlignment="Center"
|
||||
Watermark="C:\..."
|
||||
TextChanged="TextBoxMinecraftProfileFolder_TextChanged"/>
|
||||
Text="{Binding MinecraftProfileFolder}"/>
|
||||
|
||||
<!-- TextBoxes: ModpackConfig -->
|
||||
<TextBox
|
||||
@@ -57,7 +71,7 @@
|
||||
Margin="3"
|
||||
VerticalAlignment="Center"
|
||||
Watermark="https://..."
|
||||
TextChanged="TextBoxModpackConfig_TextChanged"/>
|
||||
Text="{Binding ModpackConfigUrl}"/>
|
||||
|
||||
<!-- TextBoxes: InstallKey -->
|
||||
<TextBox
|
||||
@@ -68,8 +82,8 @@
|
||||
Margin="3"
|
||||
VerticalAlignment="Center"
|
||||
Watermark="XXXXX-YYYYY-ZZZZZ-AAAAA-BBBBB"
|
||||
TextChanged="TextBoxInstallKey_TextChanged"
|
||||
IsVisible="false"/>
|
||||
Text="{Binding InstallKey}"
|
||||
IsVisible="{Binding CanUseExtrasKey}"/>
|
||||
|
||||
<!-- Button: SearchProfileFolder -->
|
||||
<pilz:ImageSplitButton
|
||||
@@ -80,7 +94,28 @@
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{x:Static lang:GeneralLangRes.Select}"
|
||||
Click="ButtonSearchProfileFolder_Click"/>
|
||||
Click="ButtonSearchProfileFolder_Click">
|
||||
|
||||
<pilz:ImageSplitButton.DataTemplates>
|
||||
<DataTemplate DataType="gui:MainViewModel+EmptyRecentFilesItem">
|
||||
<TextBlock Text="{x:Static lang:GeneralLangRes.NoRecentProfilesAvailable}" FontStyle="Italic"/>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate DataType="gui:MainViewModel+RecentFilesItem">
|
||||
<MenuItem Header="{Binding Display}" Command="{Binding DataContext.OpenRecentPathCommand, ElementName=window}" CommandParameter="{Binding Path}" />
|
||||
</DataTemplate>
|
||||
</pilz:ImageSplitButton.DataTemplates>
|
||||
|
||||
<pilz:ImageSplitButton.Flyout>
|
||||
<MenuFlyout ItemsSource="{Binding RecentMinecraftProfilePathItems}">
|
||||
<MenuFlyout.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl Content="{Binding}"/>
|
||||
</DataTemplate>
|
||||
</MenuFlyout.ItemTemplate>
|
||||
</MenuFlyout>
|
||||
</pilz:ImageSplitButton.Flyout>
|
||||
</pilz:ImageSplitButton>
|
||||
|
||||
<!-- Button: CheckForUpdates -->
|
||||
<pilz:ImageButton
|
||||
@@ -91,7 +126,8 @@
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{x:Static lang:GeneralLangRes.CheckForUpdates}"
|
||||
Click="ButtonCheckForUpdates_Click"/>
|
||||
Command="{Binding CheckForUpdatesCommand}"
|
||||
IsEnabled="{Binding CanUpdate}"/>
|
||||
|
||||
<!-- Button: Install -->
|
||||
<pilz:ImageSplitButton
|
||||
@@ -103,10 +139,12 @@
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Center"
|
||||
Text="{x:Static lang:GeneralLangRes.Install}"
|
||||
Click="ButtonInstall_Click">
|
||||
Command="{Binding InstallCommand}"
|
||||
IsEnabled="{Binding CanUpdate}">
|
||||
|
||||
<SplitButton.Flyout>
|
||||
<MenuFlyout>
|
||||
<MenuItem x:Name="MenuItemRepair" Header="{x:Static lang:GeneralLangRes.Repair}" Click="MenuItemRepair_Click"/>
|
||||
<MenuItem x:Name="MenuItemRepair" Header="{x:Static lang:GeneralLangRes.Repair}" Command="{Binding RepairCommand}"/>
|
||||
</MenuFlyout>
|
||||
</SplitButton.Flyout>
|
||||
</pilz:ImageSplitButton>
|
||||
51
ModpackUpdater.Apps.Client.Gui/MainView.axaml.cs
Normal file
51
ModpackUpdater.Apps.Client.Gui/MainView.axaml.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Reflection;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using ModpackUpdater.Apps.Client.Gui.LangRes;
|
||||
using Pilz.Extensions;
|
||||
using Pilz.UI.Symbols;
|
||||
|
||||
namespace ModpackUpdater.Apps.Client.Gui;
|
||||
|
||||
public partial class MainView : Window
|
||||
{
|
||||
public MainViewModel Model => DataContext as MainViewModel ?? throw new NullReferenceException();
|
||||
|
||||
public MainView()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Title = $"{Title} (v{Assembly.GetExecutingAssembly().GetAppVersion().ToShortHumanString()})";
|
||||
|
||||
ButtonSearchProfileFolder.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.opened_folder);
|
||||
ButtonCheckForUpdates.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.update_done);
|
||||
ButtonInstall.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer);
|
||||
MenuItemRepair.Icon = AppGlobals.Symbols.GetImage(AppSymbols.wrench, SymbolSize.Small);
|
||||
}
|
||||
|
||||
private async void InitializeViewModel()
|
||||
{
|
||||
await Model.CheckForUpdates(this);
|
||||
await Model.Initialize();
|
||||
}
|
||||
|
||||
private void Control_OnLoaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
Dispatcher.UIThread.Post(InitializeViewModel, DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
private async void ButtonSearchProfileFolder_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var filePaths = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||
{
|
||||
Title = GeneralLangRes.SelectMinecraftProfileFolder,
|
||||
SuggestedStartLocation = await StorageProvider.TryGetFolderFromPathAsync(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)),
|
||||
AllowMultiple = false,
|
||||
});
|
||||
|
||||
if (filePaths.Count >= 1)
|
||||
Model.MinecraftProfileFolder = filePaths[0].Path.AbsolutePath;
|
||||
}
|
||||
}
|
||||
340
ModpackUpdater.Apps.Client.Gui/MainViewModel.cs
Normal file
340
ModpackUpdater.Apps.Client.Gui/MainViewModel.cs
Normal file
@@ -0,0 +1,340 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Input;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using ModpackUpdater.Apps.Client.Gui.LangRes;
|
||||
using ModpackUpdater.Manager;
|
||||
using Pilz.Extensions.Collections;
|
||||
|
||||
namespace ModpackUpdater.Apps.Client.Gui;
|
||||
|
||||
public partial class MainViewModel : ObservableObject
|
||||
{
|
||||
public class EmptyRecentFilesItem;
|
||||
|
||||
public class RecentFilesItem(string path)
|
||||
{
|
||||
public string Path => path;
|
||||
public string Display => path.Length > 50 ? $"...{path[^50..]}" : path;
|
||||
}
|
||||
|
||||
private readonly UpdateCheckOptions updateOptions = new();
|
||||
private ModpackInfo modpackInfo = new();
|
||||
private ModpackConfig updateConfig = new();
|
||||
private UpdateCheckResult? lastUpdateCheckResult;
|
||||
|
||||
[ObservableProperty] private string? minecraftProfileFolder;
|
||||
[ObservableProperty] private string? modpackConfigUrl;
|
||||
[ObservableProperty] private string? installKey;
|
||||
[ObservableProperty] private string statusText = "-";
|
||||
[ObservableProperty] private IImage? statusImage;
|
||||
[ObservableProperty] private bool hasInitialized;
|
||||
[ObservableProperty] private bool isUpdating;
|
||||
[ObservableProperty] private bool loadingData;
|
||||
[ObservableProperty] private bool canUpdate;
|
||||
[ObservableProperty] private bool canUseExtrasKey;
|
||||
|
||||
public ObservableCollection<object> RecentMinecraftProfilePathItems { get; } = [];
|
||||
|
||||
public ICommand CheckForUpdatesCommand { get; }
|
||||
public ICommand InstallCommand { get; }
|
||||
public ICommand RepairCommand { get; }
|
||||
|
||||
public MainViewModel()
|
||||
{
|
||||
CheckForUpdatesCommand = new AsyncRelayCommand(async () => await CheckStatusAndUpdate(false));
|
||||
InstallCommand = new AsyncRelayCommand(async () => await ExecuteUpdate(true, false));
|
||||
RepairCommand = new AsyncRelayCommand(async () => await ExecuteUpdate(true, true));
|
||||
}
|
||||
|
||||
public async Task CheckForUpdates(Window parent)
|
||||
{
|
||||
var updates = new AppUpdates("client", parent);
|
||||
updates.OnDownloadProgramUpdate += (_, _) => SetStatus(GeneralLangRes.DownloadProgramUpdate, AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer));
|
||||
await updates.UpdateApp();
|
||||
}
|
||||
|
||||
public async Task Initialize()
|
||||
{
|
||||
ClearStatus();
|
||||
LoadProfileToUi();
|
||||
LoadRecentFilesToUi();
|
||||
HasInitialized = true;
|
||||
await CheckStatusAndUpdate(true);
|
||||
}
|
||||
|
||||
partial void OnMinecraftProfileFolderChanged(string? value)
|
||||
{
|
||||
if (!LoadingData)
|
||||
_ = CheckStatusAndUpdate(true);
|
||||
}
|
||||
|
||||
partial void OnModpackConfigUrlChanged(string? value)
|
||||
{
|
||||
if (!LoadingData)
|
||||
_ = CheckStatusAndUpdate(false);
|
||||
}
|
||||
|
||||
partial void OnInstallKeyChanged(string? value)
|
||||
{
|
||||
if (!LoadingData)
|
||||
_ = CheckStatusAndUpdate(false);
|
||||
}
|
||||
|
||||
public void SetStatus(string statusText, IImage? image)
|
||||
{
|
||||
StatusText = statusText;
|
||||
StatusImage = image;
|
||||
}
|
||||
|
||||
public void ClearStatus()
|
||||
{
|
||||
StatusText = "-";
|
||||
StatusImage = null;
|
||||
}
|
||||
|
||||
private bool AllowExtras()
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(modpackInfo.ExtrasKey) && updateConfig.ExtrasKeys.Contains(modpackInfo.ExtrasKey);
|
||||
}
|
||||
|
||||
public void LoadProfileToUi()
|
||||
{
|
||||
LoadingData = true;
|
||||
|
||||
MinecraftProfileFolder = modpackInfo.LocalPath ?? AppConfig.Instance.RecentMinecraftProfilePaths.FirstOrDefault() ?? MinecraftProfileFolder;
|
||||
ModpackConfigUrl = modpackInfo.ConfigUrl ?? ModpackConfigUrl;
|
||||
InstallKey = modpackInfo.ExtrasKey ?? InstallKey;
|
||||
|
||||
LoadingData = false;
|
||||
}
|
||||
|
||||
private void LoadOptionsToUi()
|
||||
{
|
||||
//foreach (var set in )
|
||||
//{
|
||||
// // ...
|
||||
//}
|
||||
}
|
||||
|
||||
public void LoadRecentFilesToUi()
|
||||
{
|
||||
RecentMinecraftProfilePathItems.Clear();
|
||||
|
||||
if (AppConfig.Instance.RecentMinecraftProfilePaths.Count == 0)
|
||||
{
|
||||
RecentMinecraftProfilePathItems.Add(new EmptyRecentFilesItem());
|
||||
return;
|
||||
}
|
||||
|
||||
AppConfig.Instance.RecentMinecraftProfilePaths.ForEach(path =>
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
RecentMinecraftProfilePathItems.Add(new RecentFilesItem(path));
|
||||
});
|
||||
}
|
||||
|
||||
private void StoreRecentMinecraftProfilePath(string? path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
return;
|
||||
|
||||
AppConfig.Instance.RecentMinecraftProfilePaths.RemoveAll(n => n == path);
|
||||
AppConfig.Instance.RecentMinecraftProfilePaths.Insert(0, path);
|
||||
AppConfig.Instance.RecentMinecraftProfilePaths.Skip(10).ForEach(n => AppConfig.Instance.RecentMinecraftProfilePaths.Remove(n));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenRecentPath(string path)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(path))
|
||||
MinecraftProfileFolder = path;
|
||||
}
|
||||
|
||||
public async Task CheckStatusAndUpdate(bool loadProfileToUi)
|
||||
{
|
||||
if (!CheckStatus(loadProfileToUi))
|
||||
return;
|
||||
|
||||
await ExecuteUpdate(false, false);
|
||||
|
||||
StoreRecentMinecraftProfilePath(modpackInfo.LocalPath);
|
||||
LoadRecentFilesToUi();
|
||||
}
|
||||
|
||||
private bool CheckStatus(bool loadProfileToUi)
|
||||
{
|
||||
ClearStatus();
|
||||
|
||||
try
|
||||
{
|
||||
modpackInfo = ModpackInfo.TryLoad(MinecraftProfileFolder?.Trim());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
|
||||
if (loadProfileToUi)
|
||||
LoadProfileToUi();
|
||||
|
||||
try
|
||||
{
|
||||
updateConfig = ModpackConfig.LoadFromUrl(ModpackConfigUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
|
||||
modpackInfo.ExtrasKey = InstallKey?.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(modpackInfo.ExtrasKey) && !AllowExtras())
|
||||
{
|
||||
SetStatus(GeneralLangRes.InstallationKeyNotValid, AppGlobals.Symbols.GetImageSource(AppSymbols.general_warning_sign));
|
||||
return false;
|
||||
}
|
||||
|
||||
CanUseExtrasKey = updateConfig.ExtrasKeys.Count > 0;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(MinecraftProfileFolder) /*|| modpackInfo.Valid*/)
|
||||
{
|
||||
SetStatus(GeneralLangRes.MinecraftProfileFolderSeemsInvalid, AppGlobals.Symbols.GetImageSource(AppSymbols.general_warning_sign));
|
||||
CanUpdate = false;
|
||||
return false;
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(ModpackConfigUrl))
|
||||
{
|
||||
SetStatus(GeneralLangRes.ConfigIncompleteOrNotLoaded, AppGlobals.Symbols.GetImageSource(AppSymbols.general_warning_sign));
|
||||
CanUpdate = false;
|
||||
return false;
|
||||
}
|
||||
else if (updateConfig.Maintenance && !updateOptions.IgnoreMaintenance)
|
||||
{
|
||||
SetStatus(GeneralLangRes.UpdateServerInMaintenance, AppGlobals.Symbols.GetImageSource(AppSymbols.services));
|
||||
CanUpdate = false;
|
||||
return false;
|
||||
}
|
||||
LoadOptionsToUi();
|
||||
CanUpdate = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task ExecuteUpdate(bool doInstall, bool repair)
|
||||
{
|
||||
ClearStatus();
|
||||
|
||||
// Ensure set extras key
|
||||
modpackInfo.ExtrasKey = InstallKey?.Trim();
|
||||
|
||||
var updater = new ModpackInstaller(updateConfig, modpackInfo);
|
||||
updater.InstallProgessUpdated += Updater_InstallProgressUpdated;
|
||||
updater.CheckingProgressUpdated += Updater_CheckingProgressUpdated;
|
||||
|
||||
void error()
|
||||
{
|
||||
SetStatus(GeneralLangRes.ErrorOnUpdateCheckOrUpdating, AppGlobals.Symbols.GetImageSource(AppSymbols.close));
|
||||
IsUpdating = false;
|
||||
}
|
||||
void installing()
|
||||
{
|
||||
SetStatus(GeneralLangRes.Installing, AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer));
|
||||
IsUpdating = true;
|
||||
}
|
||||
void updatesAvailable()
|
||||
{
|
||||
SetStatus(GeneralLangRes.AnUpdateIsAvailable, AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer));
|
||||
IsUpdating = false;
|
||||
}
|
||||
void everythingOk()
|
||||
{
|
||||
SetStatus(GeneralLangRes.EverythingIsRightAndUpToDate, AppGlobals.Symbols.GetImageSource(AppSymbols.done));
|
||||
IsUpdating = false;
|
||||
}
|
||||
|
||||
// Check only if not pressed "install", not really needed otherwise.
|
||||
if (lastUpdateCheckResult is null || !doInstall || repair)
|
||||
{
|
||||
SetStatus(GeneralLangRes.CheckingForUpdates, AppGlobals.Symbols.GetImageSource(AppSymbols.update_done));
|
||||
|
||||
// Check for extras once again
|
||||
updateOptions.IncludeExtras = AllowExtras();
|
||||
|
||||
// Force re-install on repair
|
||||
updateOptions.IgnoreInstalledVersion = repair;
|
||||
|
||||
try
|
||||
{
|
||||
lastUpdateCheckResult = await updater.Check(updateOptions);
|
||||
}
|
||||
catch
|
||||
{
|
||||
error();
|
||||
if (Debugger.IsAttached)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// Error while update check
|
||||
if (lastUpdateCheckResult is null || lastUpdateCheckResult.HasError)
|
||||
{
|
||||
error();
|
||||
return;
|
||||
}
|
||||
|
||||
// Load options
|
||||
// lastUpdateCheckResult.OptionsAvailable...
|
||||
// lastUpdateCheckResult.OptionsEnabled...
|
||||
|
||||
// No updates available
|
||||
if (!lastUpdateCheckResult.HasUpdates)
|
||||
{
|
||||
everythingOk();
|
||||
return;
|
||||
}
|
||||
|
||||
// Updates available (but don't install)
|
||||
if (!doInstall)
|
||||
{
|
||||
updatesAvailable();
|
||||
return;
|
||||
}
|
||||
|
||||
// Install updates
|
||||
installing();
|
||||
IsUpdating = true;
|
||||
try
|
||||
{
|
||||
// Install
|
||||
if (await updater.Install(lastUpdateCheckResult) == false)
|
||||
{
|
||||
error();
|
||||
return;
|
||||
}
|
||||
|
||||
// Success
|
||||
lastUpdateCheckResult = null; // Reset last update check, a new one would be needed now.
|
||||
everythingOk();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Error
|
||||
error();
|
||||
if (Debugger.IsAttached)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void Updater_CheckingProgressUpdated(int toCheck, int processed)
|
||||
{
|
||||
SetStatus(Math.Round(processed / (double)toCheck * 100d, 1) + "%", AppGlobals.Symbols.GetImageSource(AppSymbols.update_done));
|
||||
}
|
||||
|
||||
private void Updater_InstallProgressUpdated(UpdateCheckResult result, int processedSyncs)
|
||||
{
|
||||
var actionCount = result.Actions.Count;
|
||||
SetStatus(Math.Round(processedSyncs / (double)actionCount * 100d, 1) + "%", AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer));
|
||||
}
|
||||
}
|
||||
@@ -66,27 +66,28 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MessageBox.Avalonia" Version="3.3.0" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="MessageBox.Avalonia" Version="3.3.1" />
|
||||
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageReference Include="Pilz" Version="2.6.1" />
|
||||
<PackageReference Include="Pilz.Configuration" Version="3.2.7" />
|
||||
<PackageReference Include="Pilz.Cryptography" Version="2.1.2" />
|
||||
<PackageReference Include="Pilz.IO" Version="2.1.0" />
|
||||
<PackageReference Include="Pilz.UI" Version="3.1.4" />
|
||||
<PackageReference Include="Pilz.UI.AvaloniaUI" Version="1.2.18" />
|
||||
<PackageReference Include="Avalonia" Version="11.3.8" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.3.8" />
|
||||
<PackageReference Include="Pilz" Version="2.7.8" />
|
||||
<PackageReference Include="Pilz.Configuration" Version="3.2.8" />
|
||||
<PackageReference Include="Pilz.Cryptography" Version="2.1.3" />
|
||||
<PackageReference Include="Pilz.IO" Version="2.1.1" />
|
||||
<PackageReference Include="Pilz.UI" Version="3.1.5" />
|
||||
<PackageReference Include="Pilz.UI.AvaloniaUI" Version="1.2.21" />
|
||||
<PackageReference Include="Avalonia" Version="11.3.10" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.3.10" />
|
||||
<PackageReference Include="Avalonia.Svg" Version="11.3.0" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.8" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.8" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.10" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.10" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.8">
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.10">
|
||||
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
|
||||
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Pilz.Updating" Version="4.3.5" />
|
||||
<PackageReference Include="Pilz.Updating.Client" Version="4.4.6" />
|
||||
<PackageReference Include="Pilz.Updating" Version="4.3.6" />
|
||||
<PackageReference Include="Pilz.Updating.Client" Version="4.4.8" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageReference Include="Pilz" Version="2.6.1" />
|
||||
<PackageReference Include="Pilz.Cryptography" Version="2.1.2" />
|
||||
<PackageReference Include="Pilz.IO" Version="2.1.0" />
|
||||
<PackageReference Include="Pilz" Version="2.7.8" />
|
||||
<PackageReference Include="Pilz.Cryptography" Version="2.1.3" />
|
||||
<PackageReference Include="Pilz.IO" Version="2.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Castle.Core.Logging;
|
||||
using System.Reflection;
|
||||
using Castle.Core.Logging;
|
||||
using ModpackUpdater.Manager;
|
||||
using Pilz.Extensions;
|
||||
|
||||
namespace ModpackUpdater.Apps.Client;
|
||||
|
||||
@@ -15,22 +17,34 @@ public static class Program
|
||||
{
|
||||
Options = new Options(args);
|
||||
if (Options.Help)
|
||||
{
|
||||
DrawInfo();
|
||||
Options.DrawHelp();
|
||||
else
|
||||
InstallWithoutGui(Options.UpdateOptions, Options.Silent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Options.Silent)
|
||||
DrawInfo();
|
||||
InstallWithoutGui(Options.UpdateOptions, Options.Silent);
|
||||
}
|
||||
|
||||
private static void DrawInfo()
|
||||
{
|
||||
Console.WriteLine("Minecraft Modpack Updater CLI");
|
||||
Console.WriteLine("Version " + Assembly.GetExecutingAssembly().GetAppVersion().ToShortHumanString());
|
||||
Console.WriteLine("------------------------------");
|
||||
}
|
||||
|
||||
private static void InstallWithoutGui(UpdateCheckOptionsAdv updateOptions, bool silent)
|
||||
{
|
||||
var info = ModpackInfo.TryLoad(updateOptions.ProfileFolder);
|
||||
var config = ModpackConfig.LoadFromUrl(CheckModpackConfigUrl(updateOptions.ModpackConfig!, info));
|
||||
var features = new ModpackFeatures(config);
|
||||
|
||||
// Check features
|
||||
if (!string.IsNullOrWhiteSpace(updateOptions.ExtrasKey))
|
||||
info.ExtrasKey = updateOptions.ExtrasKey;
|
||||
if (!string.IsNullOrWhiteSpace(info.ExtrasKey))
|
||||
updateOptions.IncludeExtras = features.IsEnabled(ModpackFeatures.FeatureAllowExtas, new AllowExtrasFeatureContext(info));
|
||||
updateOptions.IncludeExtras = !string.IsNullOrWhiteSpace(info.ExtrasKey) && config.ExtrasKeys.Contains(info.ExtrasKey);
|
||||
|
||||
// Check for update
|
||||
var installer = new ModpackInstaller(config, info)
|
||||
|
||||
@@ -49,25 +49,25 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.3.8" />
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.3.10" />
|
||||
<PackageReference Include="DynamicData" Version="9.4.1" />
|
||||
<PackageReference Include="MessageBox.Avalonia" Version="3.3.0" />
|
||||
<PackageReference Include="EPPlus" Version="8.2.1" />
|
||||
<PackageReference Include="NGitLab" Version="11.0.1" />
|
||||
<PackageReference Include="Pilz" Version="2.6.1" />
|
||||
<PackageReference Include="Pilz.Configuration" Version="3.2.7" />
|
||||
<PackageReference Include="Pilz.Cryptography" Version="2.1.2" />
|
||||
<PackageReference Include="Pilz.Features" Version="2.13.0" />
|
||||
<PackageReference Include="Pilz.UI" Version="3.1.4" />
|
||||
<PackageReference Include="Pilz.UI.AvaloniaUI" Version="1.2.18" />
|
||||
<PackageReference Include="Pilz.UI.AvaloniaUI.Features" Version="1.0.1" />
|
||||
<PackageReference Include="Avalonia" Version="11.3.8" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.3.8" />
|
||||
<PackageReference Include="MessageBox.Avalonia" Version="3.3.1" />
|
||||
<PackageReference Include="EPPlus" Version="8.4.0" />
|
||||
<PackageReference Include="NGitLab" Version="11.1.0" />
|
||||
<PackageReference Include="Pilz" Version="2.7.8" />
|
||||
<PackageReference Include="Pilz.Configuration" Version="3.2.8" />
|
||||
<PackageReference Include="Pilz.Cryptography" Version="2.1.3" />
|
||||
<PackageReference Include="Pilz.Features" Version="2.13.1" />
|
||||
<PackageReference Include="Pilz.UI" Version="3.1.5" />
|
||||
<PackageReference Include="Pilz.UI.AvaloniaUI" Version="1.2.21" />
|
||||
<PackageReference Include="Pilz.UI.AvaloniaUI.Features" Version="1.0.2" />
|
||||
<PackageReference Include="Avalonia" Version="11.3.10" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.3.10" />
|
||||
<PackageReference Include="Avalonia.Svg" Version="11.3.0" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.8" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.8" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.10" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.10" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.8">
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.10">
|
||||
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
|
||||
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
|
||||
</PackageReference>
|
||||
@@ -143,10 +143,4 @@
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Pilz">
|
||||
<HintPath>..\..\..\.nuget\packages\pilz\2.6.1\lib\net8.0\Pilz.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -3,7 +3,6 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:ModpackUpdater.Apps.Manager.Ui"
|
||||
xmlns:pilz="https://git.pilzinsel64.de/pilz-framework/pilz"
|
||||
xmlns:symbols="clr-namespace:Pilz.UI.Symbols;assembly=Pilz.UI"
|
||||
xmlns:mainWindow="clr-namespace:ModpackUpdater.Apps.Manager.Ui.Models.MainWindow"
|
||||
@@ -15,7 +14,7 @@
|
||||
Loaded="Window_OnLoaded"
|
||||
Closed="Window_OnClosed"
|
||||
KeyDown="Window_OnKeyDown">
|
||||
|
||||
|
||||
<Grid
|
||||
x:Name="GridMain"
|
||||
x:DataType="mainWindow:MainWindowViewModel"
|
||||
@@ -25,55 +24,45 @@
|
||||
ColumnSpacing="6"
|
||||
Margin="3">
|
||||
|
||||
<!-- StackPanel: Workspace -->
|
||||
<StackPanel
|
||||
<!-- Menu: Workspace -->
|
||||
<Menu
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Orientation="Horizontal">
|
||||
Grid.Row="0">
|
||||
|
||||
<Menu.Items>
|
||||
<!-- MenuItem: Workspace -->
|
||||
<pilz:HeaderMenuItem
|
||||
x:Name="MenuItemWorkspace"
|
||||
HeaderText="{x:Static langRes:GeneralLangRes.Workspace}">
|
||||
|
||||
<!-- Button: Workspace -->
|
||||
<pilz:ImageButton
|
||||
x:Name="ButtonWorkspace"
|
||||
Text="{x:Static langRes:GeneralLangRes.Workspace}"
|
||||
Background="Transparent">
|
||||
<pilz:HeaderMenuItem.Items>
|
||||
<MenuItem x:Name="MenuItemWorkspacePreferences" Header="{x:Static langRes:GeneralLangRes.WorkspacePreferences}" Click="MenuItemWorkspacePreferences_OnClick"/>
|
||||
<MenuItem x:Name="MenuItemSaveWorkspace" Header="{x:Static langRes:GeneralLangRes.SaveWorkspace}" HotKey="Ctrl+S" Click="MenuItemSaveWorkspace_OnClick"/>
|
||||
<Separator/>
|
||||
<MenuItem x:Name="MenuItemNewWorkspace" Header="{x:Static langRes:GeneralLangRes.NewWorkspace}"/>
|
||||
<Separator/>
|
||||
<MenuItem x:Name="MenuItemRecentWorkspaces" Header="{x:Static langRes:GeneralLangRes.RecentWorkspaces}"/>
|
||||
</pilz:HeaderMenuItem.Items>
|
||||
</pilz:HeaderMenuItem>
|
||||
|
||||
<pilz:ImageButton.Flyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyout.Items>
|
||||
<MenuItem x:Name="MenuItemWorkspacePreferences" Header="{x:Static langRes:GeneralLangRes.WorkspacePreferences}" Click="MenuItemWorkspacePreferences_OnClick"/>
|
||||
<MenuItem x:Name="MenuItemSaveWorkspace" Header="{x:Static langRes:GeneralLangRes.SaveWorkspace}" HotKey="Ctrl+S" Click="MenuItemSaveWorkspace_OnClick"/>
|
||||
<Separator/>
|
||||
<MenuItem x:Name="MenuItemNewWorkspace" Header="{x:Static langRes:GeneralLangRes.NewWorkspace}"/>
|
||||
<Separator/>
|
||||
<MenuItem x:Name="MenuItemRecentWorkspaces" Header="{x:Static langRes:GeneralLangRes.RecentWorkspaces}"/>
|
||||
</MenuFlyout.Items>
|
||||
</MenuFlyout>
|
||||
</pilz:ImageButton.Flyout>
|
||||
</pilz:ImageButton>
|
||||
<!-- MenuItem: Update -->
|
||||
<pilz:HeaderMenuItem
|
||||
x:Name="MenuItemUpdate"
|
||||
HeaderText="{x:Static langRes:GeneralLangRes.Update}">
|
||||
|
||||
<!-- Button: Update -->
|
||||
<pilz:ImageButton
|
||||
x:Name="ButtonUpdate"
|
||||
Text="{x:Static langRes:GeneralLangRes.Update}"
|
||||
Background="Transparent">
|
||||
<pilz:HeaderMenuItem.Items>
|
||||
<MenuItem x:Name="MenuItemCreateUpdate" Header="{x:Static langRes:GeneralLangRes.CreateUpdate}" Click="MenuItemCreateUpdate_OnClick"/>
|
||||
<MenuItem x:Name="MenuItemRemoveUpdate" Header="{x:Static langRes:GeneralLangRes.RemoveUpdate}" Click="MenuItemRemoveUpdate_OnClick"/>
|
||||
</pilz:HeaderMenuItem.Items>
|
||||
</pilz:HeaderMenuItem>
|
||||
|
||||
<pilz:ImageButton.Flyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyout.Items>
|
||||
<MenuItem x:Name="MenuItemCreateUpdate" Header="{x:Static langRes:GeneralLangRes.CreateUpdate}" Click="MenuItemCreateUpdate_OnClick"/>
|
||||
<MenuItem x:Name="MenuItemRemoveUpdate" Header="{x:Static langRes:GeneralLangRes.RemoveUpdate}" Click="MenuItemRemoveUpdate_OnClick"/>
|
||||
</MenuFlyout.Items>
|
||||
</MenuFlyout>
|
||||
</pilz:ImageButton.Flyout>
|
||||
</pilz:ImageButton>
|
||||
<!-- MenuItem: Workspace -->
|
||||
<pilz:HeaderMenuItem
|
||||
x:Name="MenuItemTools"
|
||||
HeaderText="{x:Static langRes:GeneralLangRes.Tools}"/>
|
||||
</Menu.Items>
|
||||
</Menu>
|
||||
|
||||
<!-- Button: Tools -->
|
||||
<pilz:ImageButton
|
||||
x:Name="ButtonTools"
|
||||
Text="{x:Static langRes:GeneralLangRes.Tools}"
|
||||
Background="Transparent"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- TreeView: Workspace -->
|
||||
<ScrollViewer
|
||||
Grid.Column="0"
|
||||
@@ -82,6 +71,7 @@
|
||||
|
||||
<StackPanel>
|
||||
<TreeView
|
||||
x:Name="TreeViewWorkspace"
|
||||
ItemsSource="{Binding CurrentTreeNodes}"
|
||||
SelectedItem="{Binding SelectedTreeNode}">
|
||||
|
||||
@@ -109,27 +99,14 @@
|
||||
Orientation="Horizontal"
|
||||
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"/>
|
||||
<!-- Menu: Actions -->
|
||||
<Menu>
|
||||
<Menu.Items>
|
||||
<pilz:HeaderMenuItem x:Name="MenuItemAddAction" HeaderText="{x:Static langRes:GeneralLangRes.Add}" Click="ButtonAddAction_OnClick"/>
|
||||
<pilz:HeaderMenuItem x:Name="MenuItemRemoveAction" HeaderText="{x:Static langRes:GeneralLangRes.Remove}" Click="ButtonRemoveAction_OnClick"/>
|
||||
</Menu.Items>
|
||||
</Menu>
|
||||
|
||||
<!-- 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"
|
||||
@@ -138,8 +115,7 @@
|
||||
|
||||
<!-- Panel: Menu -->
|
||||
<ContentControl
|
||||
Content="{Binding SelectedTreeNode}"
|
||||
>
|
||||
Content="{Binding SelectedTreeNode}">
|
||||
|
||||
<ContentControl.DataTemplates>
|
||||
<DataTemplate
|
||||
@@ -185,11 +161,11 @@
|
||||
VerticalAlignment="Stretch"
|
||||
ItemsSource="{Binding CurrentGridRows.View}"
|
||||
SelectedItem="{Binding SelectedGridRow}">
|
||||
|
||||
|
||||
<DataGrid.ContextMenu>
|
||||
<ContextMenu x:Name="ContextMenuActions"/>
|
||||
</DataGrid.ContextMenu>
|
||||
|
||||
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn
|
||||
Header="{x:Static langRes:GeneralLangRes.Id}"
|
||||
@@ -218,10 +194,10 @@
|
||||
Header="{x:Static langRes:GeneralLangRes.DestinationPath}"
|
||||
Binding="{Binding InheritedDestPath}"
|
||||
Width="*"/>
|
||||
|
||||
|
||||
<DataGridTemplateColumn
|
||||
Header="{x:Static langRes:GeneralLangRes.State}">
|
||||
|
||||
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Image
|
||||
|
||||
@@ -2,7 +2,6 @@ using System.Reflection;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Media;
|
||||
using DynamicData;
|
||||
using ModpackUpdater.Apps.Manager.Api;
|
||||
using ModpackUpdater.Apps.Manager.Api.Model;
|
||||
@@ -20,9 +19,6 @@ namespace ModpackUpdater.Apps.Manager.Ui;
|
||||
|
||||
public partial class MainWindow : Window, IMainApi
|
||||
{
|
||||
public static IImage? ButtonImageAddAction => AppGlobals.Symbols.GetImageSource(AppSymbols.add);
|
||||
public static IImage? ButtonImageRemoveAction => AppGlobals.Symbols.GetImageSource(AppSymbols.remove);
|
||||
|
||||
private WorkspaceFeature? curWs;
|
||||
|
||||
public MainWindowViewModel Model { get; } = new();
|
||||
@@ -34,19 +30,21 @@ public partial class MainWindow : Window, IMainApi
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Title = $"{Title} (v{Assembly.GetExecutingAssembly().GetAppVersion().ToShortString()})";
|
||||
Title = $"{Title} (v{Assembly.GetExecutingAssembly().GetAppVersion().ToShortHumanString()})";
|
||||
|
||||
GridMain.DataContext = Model;
|
||||
|
||||
ButtonWorkspace.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.workspace);
|
||||
MenuItemWorkspace.Icon = AppGlobals.Symbols.GetImage(AppSymbols.workspace, SymbolSize.Small);
|
||||
MenuItemWorkspacePreferences.Icon = AppGlobals.Symbols.GetImage(AppSymbols.settings, SymbolSize.Small);
|
||||
MenuItemSaveWorkspace.Icon = AppGlobals.Symbols.GetImage(AppSymbols.save, SymbolSize.Small);
|
||||
MenuItemNewWorkspace.Icon = AppGlobals.Symbols.GetImage(AppSymbols.new_window, SymbolSize.Small);
|
||||
MenuItemRecentWorkspaces.Icon = AppGlobals.Symbols.GetImage(AppSymbols.time_machine, SymbolSize.Small);
|
||||
ButtonUpdate.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.update_done);
|
||||
ButtonTools.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.tools);
|
||||
MenuItemUpdate.Icon = AppGlobals.Symbols.GetImage(AppSymbols.update_done, SymbolSize.Small);
|
||||
MenuItemTools.Icon = AppGlobals.Symbols.GetImage(AppSymbols.tools, SymbolSize.Small);
|
||||
MenuItemCreateUpdate.Icon = AppGlobals.Symbols.GetImage(AppSymbols.add, SymbolSize.Small);
|
||||
MenuItemRemoveUpdate.Icon = AppGlobals.Symbols.GetImage(AppSymbols.remove, SymbolSize.Small);
|
||||
MenuItemAddAction.HeaderIcon = AppGlobals.Symbols.GetImage(AppSymbols.add, SymbolSize.Small);
|
||||
MenuItemRemoveAction.HeaderIcon = AppGlobals.Symbols.GetImage(AppSymbols.remove, SymbolSize.Small);
|
||||
ImageUpdate.Source = AppGlobals.Symbols.GetImageSource(AppSymbols.update_done);
|
||||
ImageMetadata.Source = AppGlobals.Symbols.GetImageSource(AppSymbols.show_property);
|
||||
ImageGeneral.Source = AppGlobals.Symbols.GetImageSource(AppSymbols.normal_screen);
|
||||
@@ -61,9 +59,7 @@ public partial class MainWindow : Window, IMainApi
|
||||
customClickHandler: MenuItemActionItem_Click,
|
||||
insertPrioSplitters: true);
|
||||
|
||||
var menuFlyoutTools = new MenuFlyout();
|
||||
ButtonTools.Flyout = menuFlyoutTools;
|
||||
PluginFeatureController.Instance.Functions.Get(FeatureTypes.Tools).InsertItemsTo(menuFlyoutTools.Items,
|
||||
PluginFeatureController.Instance.Functions.Get(FeatureTypes.Tools).InsertItemsTo(MenuItemTools.Items,
|
||||
customClickHandler: MenuItemToolsItem_Click,
|
||||
insertPrioSplitters: true);
|
||||
}
|
||||
@@ -120,7 +116,7 @@ public partial class MainWindow : Window, IMainApi
|
||||
private static void AddToRecentFiles(IWorkspace workspace)
|
||||
{
|
||||
var settings = Program.Settings.Get<WorkspaceSettings>();
|
||||
settings.Workspaces.Remove(workspace.Config);
|
||||
settings.Workspaces.RemoveAll(n => n == workspace.Config);
|
||||
settings.Workspaces.Insert(0, workspace.Config);
|
||||
settings.Workspaces.Skip(20).ForEach(n => settings.Workspaces.Remove(n));
|
||||
}
|
||||
@@ -205,7 +201,10 @@ public partial class MainWindow : Window, IMainApi
|
||||
Version = new(),
|
||||
};
|
||||
Model.CurrentWorkspace.UpdateInfos.Updates.Insert(0, update);
|
||||
nodeUpdates.Nodes.Insert(0, new ActionSetTreeNode(update));
|
||||
var item = new ActionSetTreeNode(update);
|
||||
nodeUpdates.Nodes.Insert(0, item);
|
||||
TreeViewWorkspace.SelectedItem = item;
|
||||
TreeViewWorkspace.ScrollIntoView(item);
|
||||
}
|
||||
|
||||
private void MenuItemRemoveUpdate_OnClick(object? sender, RoutedEventArgs e)
|
||||
@@ -240,8 +239,11 @@ public partial class MainWindow : Window, IMainApi
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
rows.List.Add(new MainWindowGridRow(action, rootInfos));
|
||||
|
||||
var row = new MainWindowGridRow(action, rootInfos);
|
||||
rows.List.Add(row);
|
||||
DataGridActions.SelectedItem = row;
|
||||
DataGridActions.ScrollIntoView(row, null);
|
||||
}
|
||||
|
||||
private void ButtonRemoveAction_OnClick(object? sender, RoutedEventArgs e)
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace ModpackUpdater.Apps;
|
||||
|
||||
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 const string UpdateUrl = "https://git.pilzinsel64.de/LITW-Refined/minecraft-modpack-updater/raw/branch/updates/updates.json";
|
||||
|
||||
public event EventHandler? OnDownloadProgramUpdate;
|
||||
|
||||
|
||||
@@ -19,22 +19,22 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Pilz" Version="2.6.1" />
|
||||
<PackageReference Include="Pilz.UI" Version="3.1.4" />
|
||||
<PackageReference Include="Pilz.UI.AvaloniaUI" Version="1.2.18" />
|
||||
<PackageReference Include="MessageBox.Avalonia" Version="3.3.0" />
|
||||
<PackageReference Include="Avalonia" Version="11.3.8" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.3.8" />
|
||||
<PackageReference Include="Pilz" Version="2.7.8" />
|
||||
<PackageReference Include="Pilz.UI" Version="3.1.5" />
|
||||
<PackageReference Include="Pilz.UI.AvaloniaUI" Version="1.2.21" />
|
||||
<PackageReference Include="MessageBox.Avalonia" Version="3.3.1" />
|
||||
<PackageReference Include="Avalonia" Version="11.3.10" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.3.10" />
|
||||
<PackageReference Include="Avalonia.Svg" Version="11.3.0" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.8" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.8" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.10" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.10" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.8">
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.10">
|
||||
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
|
||||
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Pilz.Updating" Version="4.3.5" />
|
||||
<PackageReference Include="Pilz.Updating.Client" Version="4.4.6" />
|
||||
<PackageReference Include="Pilz.Updating" Version="4.3.6" />
|
||||
<PackageReference Include="Pilz.Updating.Client" Version="4.4.8" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -73,7 +73,7 @@ public class ModpackFactory
|
||||
{
|
||||
var repo = await github.Repository.Get(action.SourceOwner, action.SourceName);
|
||||
var releases = await github.Repository.Release.GetAll(repo.Id);
|
||||
return releases.Select(r => new ModVersionInfo(r.Name ?? r.TagName, r.TagName)).ToArray();
|
||||
return releases.Select(r => new ModVersionInfo(string.IsNullOrWhiteSpace(r.Name) ? r.TagName : r.Name, r.TagName)).ToArray();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Unleash;
|
||||
using Unleash.ClientFactory;
|
||||
|
||||
namespace ModpackUpdater.Manager;
|
||||
|
||||
public class ModpackFeatures(ModpackConfig modpackConfig)
|
||||
{
|
||||
private IUnleash? api;
|
||||
private UnleashContext context;
|
||||
private UnleashSettings settings;
|
||||
|
||||
public static string FeatureAllowExtas => "allow-extras";
|
||||
|
||||
~ModpackFeatures()
|
||||
{
|
||||
api?.Dispose();
|
||||
}
|
||||
|
||||
public bool IsEnabled(string feature)
|
||||
{
|
||||
return IsEnabled(feature, null);
|
||||
}
|
||||
|
||||
public bool IsEnabled(string feature, AppFeatureContext context)
|
||||
{
|
||||
return CheckFeature(feature, context);
|
||||
}
|
||||
|
||||
public bool IsInvalid()
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(modpackConfig.UnleashApiUrl) || string.IsNullOrWhiteSpace(modpackConfig.UnleashInstanceId);
|
||||
}
|
||||
|
||||
[MemberNotNullWhen(true, nameof(api))]
|
||||
private bool InitializeApi()
|
||||
{
|
||||
if (api != null
|
||||
|| string.IsNullOrWhiteSpace(modpackConfig.UnleashApiUrl)
|
||||
|| string.IsNullOrWhiteSpace(modpackConfig.UnleashInstanceId))
|
||||
return api != null;
|
||||
|
||||
settings = new UnleashSettings
|
||||
{
|
||||
AppName = "Modpack Updater",
|
||||
UnleashApi = new Uri(modpackConfig.UnleashApiUrl),
|
||||
FetchTogglesInterval = TimeSpan.FromSeconds(0),
|
||||
InstanceTag = modpackConfig.UnleashInstanceId,
|
||||
};
|
||||
|
||||
api = new UnleashClientFactory().CreateClient(settings, synchronousInitialization: true);
|
||||
|
||||
return api != null;
|
||||
}
|
||||
|
||||
private bool CheckFeature(string name, AppFeatureContext context)
|
||||
{
|
||||
return InitializeApi() && api.IsEnabled(name, GetContext(context));
|
||||
}
|
||||
|
||||
private UnleashContext GetContext(AppFeatureContext ccontext)
|
||||
{
|
||||
context ??= new();
|
||||
context.CurrentTime = DateTime.Now;
|
||||
ccontext?.Apply(context);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class AppFeatureContext
|
||||
{
|
||||
public abstract void Apply(UnleashContext context);
|
||||
}
|
||||
|
||||
public class AllowExtrasFeatureContext(ModpackInfo info) : AppFeatureContext
|
||||
{
|
||||
public override void Apply(UnleashContext context)
|
||||
{
|
||||
context.UserId = info.ExtrasKey;
|
||||
}
|
||||
}
|
||||
@@ -169,9 +169,7 @@ public class ModpackInstaller(ModpackConfig updateConfig, ModpackInfo modpackInf
|
||||
foreach (InstallAction iaction in checkResult.Actions)
|
||||
{
|
||||
var destFilePath = iaction.GetDestPath(modpackInfo.LocalPath);
|
||||
var sourceUrl = updateConfig.PreferDirectLinks && !string.IsNullOrWhiteSpace(iaction.SourceUrl)
|
||||
? iaction.GetSourceUrl(checkResult.LatestVersion, overwriteVersion: OverwriteVersion)
|
||||
: await factory.ResolveSourceUrl(iaction, targetVersion: checkResult.LatestVersion, overwriteVersion: OverwriteVersion);
|
||||
var sourceUrl = iaction.GetSourceUrl(checkResult.LatestVersion, overwriteVersion: OverwriteVersion);
|
||||
|
||||
if (iaction is UpdateAction uaction)
|
||||
{
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Castle.Core" Version="5.2.1" />
|
||||
<PackageReference Include="CurseForge.APIClient" Version="4.2.0" />
|
||||
<PackageReference Include="LaunchDarkly.EventSource" Version="5.2.1" />
|
||||
<PackageReference Include="LaunchDarkly.EventSource" Version="5.3.0" />
|
||||
<PackageReference Include="Modrinth.Net" Version="3.6.0" />
|
||||
<PackageReference Include="Octokit" Version="14.0.0" />
|
||||
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
|
||||
<PackageReference Include="Unleash.Client" Version="5.5.3" />
|
||||
<PackageReference Include="Unleash.Client" Version="5.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -9,9 +9,7 @@ public class ModpackConfig
|
||||
public string? Name { get; set; }
|
||||
public string? UpdateUrl { get; set; }
|
||||
public string? InstallUrl { get; set; }
|
||||
public string? UnleashApiUrl { get; set; }
|
||||
public string? UnleashInstanceId { get; set; }
|
||||
public bool PreferDirectLinks { get; set; }
|
||||
public List<string> ExtrasKeys { get; } = [];
|
||||
public string? MinecraftVersion { get; set; }
|
||||
public string? RefTag { get; set; }
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Pilz.Cryptography" Version="2.1.2" />
|
||||
<PackageReference Include="Pilz.Extensions" Version="2.1.1" />
|
||||
<PackageReference Include="Pilz.Cryptography" Version="2.1.3" />
|
||||
<PackageReference Include="Pilz.Extensions" Version="2.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="Pilz" value="https://git.pilzinsel64.de/api/v4/projects/6/packages/nuget/index.json" />
|
||||
<add key="Pilz.Updating" value="https://git.pilzinsel64.de/api/v4/projects/8/packages/nuget/index.json" />
|
||||
<add key="Pilz" value="https://git.pilzinsel64.de/api/packages/Pilz.NET/nuget/index.json" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
@@ -1,3 +1,3 @@
|
||||
using Pilz;
|
||||
|
||||
[assembly: AssemblyAppVersion(AssemblyAppVersionAttribute.EntryAssemblyVersionKey)]
|
||||
[assembly: AssemblyAppVersion]
|
||||
Reference in New Issue
Block a user