client(ui): migrate to mvvm
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
|
using Avalonia.Data.Core.Plugins;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
namespace ModpackUpdater.Apps.Client.Gui;
|
namespace ModpackUpdater.Apps.Client.Gui;
|
||||||
@@ -15,7 +16,26 @@ public partial class App : Application
|
|||||||
public override void OnFrameworkInitializationCompleted()
|
public override void OnFrameworkInitializationCompleted()
|
||||||
{
|
{
|
||||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
desktop.MainWindow = new MainForm();
|
{
|
||||||
|
DisableAvaloniaDataAnnotationValidation();
|
||||||
|
desktop.MainWindow = new MainView
|
||||||
|
{
|
||||||
|
DataContext = new MainViewModel(),
|
||||||
|
};
|
||||||
|
}
|
||||||
base.OnFrameworkInitializationCompleted();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,16 +4,23 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:lang="clr-namespace:ModpackUpdater.Apps.Client.Gui.LangRes"
|
xmlns:lang="clr-namespace:ModpackUpdater.Apps.Client.Gui.LangRes"
|
||||||
xmlns:pilz="https://git.pilzinsel64.de/pilz-framework/pilz"
|
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"
|
xmlns:symbols="clr-namespace:Pilz.UI.Symbols;assembly=Pilz.UI"
|
||||||
mc:Ignorable="d"
|
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"
|
Width="520"
|
||||||
SizeToContent="Height"
|
SizeToContent="Height"
|
||||||
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">
|
Loaded="Control_OnLoaded">
|
||||||
|
|
||||||
|
<Design.DataContext>
|
||||||
|
<gui:MainViewModel/>
|
||||||
|
</Design.DataContext>
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto"
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto"
|
||||||
@@ -22,6 +29,13 @@
|
|||||||
Margin="3"
|
Margin="3"
|
||||||
x:Name="MainGrid">
|
x:Name="MainGrid">
|
||||||
|
|
||||||
|
<Grid.IsEnabled>
|
||||||
|
<MultiBinding Converter="{x:Static BoolConverters.And}">
|
||||||
|
<Binding Path="HasInitialized"/>
|
||||||
|
<Binding Path="!IsUpdating"/>
|
||||||
|
</MultiBinding>
|
||||||
|
</Grid.IsEnabled>
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto"/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
<ColumnDefinition Width="*" MinWidth="250"/>
|
<ColumnDefinition Width="*" MinWidth="250"/>
|
||||||
@@ -31,11 +45,11 @@
|
|||||||
<!-- Labels -->
|
<!-- Labels -->
|
||||||
<Label Grid.Row="0" Grid.Column="0" Margin="3" Content="{x:Static lang:GeneralLangRes.MinecraftProfile}" Target="TextBoxMinecraftProfileFolder"/>
|
<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="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}"/>
|
<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}">
|
<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"/>
|
<Image Width="{x:Static symbols:SymbolGlobals.DefaultImageSmallSize}" Source="{Binding StatusImage}"/>
|
||||||
<TextBlock x:Name="TextStatus"/>
|
<TextBlock Text="{Binding StatusText}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- TextBoxes: Profile -->
|
<!-- TextBoxes: Profile -->
|
||||||
@@ -46,7 +60,7 @@
|
|||||||
Margin="3"
|
Margin="3"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Watermark="C:\..."
|
Watermark="C:\..."
|
||||||
TextChanged="TextBoxMinecraftProfileFolder_TextChanged"/>
|
Text="{Binding MinecraftProfileFolder}"/>
|
||||||
|
|
||||||
<!-- TextBoxes: ModpackConfig -->
|
<!-- TextBoxes: ModpackConfig -->
|
||||||
<TextBox
|
<TextBox
|
||||||
@@ -57,7 +71,7 @@
|
|||||||
Margin="3"
|
Margin="3"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Watermark="https://..."
|
Watermark="https://..."
|
||||||
TextChanged="TextBoxModpackConfig_TextChanged"/>
|
Text="{Binding ModpackConfigUrl}"/>
|
||||||
|
|
||||||
<!-- TextBoxes: InstallKey -->
|
<!-- TextBoxes: InstallKey -->
|
||||||
<TextBox
|
<TextBox
|
||||||
@@ -68,8 +82,8 @@
|
|||||||
Margin="3"
|
Margin="3"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Watermark="XXXXX-YYYYY-ZZZZZ-AAAAA-BBBBB"
|
Watermark="XXXXX-YYYYY-ZZZZZ-AAAAA-BBBBB"
|
||||||
TextChanged="TextBoxInstallKey_TextChanged"
|
Text="{Binding InstallKey}"
|
||||||
IsVisible="false"/>
|
IsVisible="{Binding CanUseExtrasKey}"/>
|
||||||
|
|
||||||
<!-- Button: SearchProfileFolder -->
|
<!-- Button: SearchProfileFolder -->
|
||||||
<pilz:ImageSplitButton
|
<pilz:ImageSplitButton
|
||||||
@@ -80,7 +94,28 @@
|
|||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
Text="{x:Static lang:GeneralLangRes.Select}"
|
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 -->
|
<!-- Button: CheckForUpdates -->
|
||||||
<pilz:ImageButton
|
<pilz:ImageButton
|
||||||
@@ -91,7 +126,8 @@
|
|||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
Text="{x:Static lang:GeneralLangRes.CheckForUpdates}"
|
Text="{x:Static lang:GeneralLangRes.CheckForUpdates}"
|
||||||
Click="ButtonCheckForUpdates_Click"/>
|
Command="{Binding CheckForUpdatesCommand}"
|
||||||
|
IsEnabled="{Binding CanUpdate}"/>
|
||||||
|
|
||||||
<!-- Button: Install -->
|
<!-- Button: Install -->
|
||||||
<pilz:ImageSplitButton
|
<pilz:ImageSplitButton
|
||||||
@@ -103,10 +139,12 @@
|
|||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
HorizontalContentAlignment="Center"
|
HorizontalContentAlignment="Center"
|
||||||
Text="{x:Static lang:GeneralLangRes.Install}"
|
Text="{x:Static lang:GeneralLangRes.Install}"
|
||||||
Click="ButtonInstall_Click">
|
Command="{Binding InstallCommand}"
|
||||||
|
IsEnabled="{Binding CanUpdate}">
|
||||||
|
|
||||||
<SplitButton.Flyout>
|
<SplitButton.Flyout>
|
||||||
<MenuFlyout>
|
<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>
|
</MenuFlyout>
|
||||||
</SplitButton.Flyout>
|
</SplitButton.Flyout>
|
||||||
</pilz:ImageSplitButton>
|
</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().ToShortString()})";
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,84 +1,140 @@
|
|||||||
using System.Diagnostics;
|
using System.Collections.ObjectModel;
|
||||||
using System.Reflection;
|
using System.Diagnostics;
|
||||||
|
using System.Windows.Input;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Platform.Storage;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using Avalonia.Threading;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using ModpackUpdater.Apps.Client.Gui.LangRes;
|
using ModpackUpdater.Apps.Client.Gui.LangRes;
|
||||||
using ModpackUpdater.Manager;
|
using ModpackUpdater.Manager;
|
||||||
using Pilz.Extensions;
|
|
||||||
using Pilz.Extensions.Collections;
|
using Pilz.Extensions.Collections;
|
||||||
using Pilz.UI.Symbols;
|
|
||||||
|
|
||||||
namespace ModpackUpdater.Apps.Client.Gui;
|
namespace ModpackUpdater.Apps.Client.Gui;
|
||||||
|
|
||||||
public partial class MainForm : Window
|
public partial class MainViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
private readonly MenuFlyout menuFlyoutSearchProfileFolder = new();
|
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 readonly UpdateCheckOptions updateOptions = new();
|
||||||
private ModpackInfo modpackInfo = new();
|
private ModpackInfo modpackInfo = new();
|
||||||
private ModpackConfig updateConfig = new();
|
private ModpackConfig updateConfig = new();
|
||||||
private ModpackFeatures? features;
|
private ModpackFeatures? features;
|
||||||
private UpdateCheckResult? lastUpdateCheckResult;
|
private UpdateCheckResult? lastUpdateCheckResult;
|
||||||
private bool currentUpdating;
|
|
||||||
private bool loadingData;
|
|
||||||
|
|
||||||
public MainForm()
|
[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()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
CheckForUpdatesCommand = new AsyncRelayCommand(async () => await CheckStatusAndUpdate(false));
|
||||||
|
InstallCommand = new AsyncRelayCommand(async () => await ExecuteUpdate(true, false));
|
||||||
|
RepairCommand = new AsyncRelayCommand(async () => await ExecuteUpdate(true, true));
|
||||||
|
}
|
||||||
|
|
||||||
Title = $"{Title} (v{Assembly.GetExecutingAssembly().GetAppVersion().ToShortString()})";
|
public async Task CheckForUpdates(Window parent)
|
||||||
ButtonSearchProfileFolder.Flyout = menuFlyoutSearchProfileFolder;
|
{
|
||||||
|
var updates = new AppUpdates("client", parent);
|
||||||
ButtonSearchProfileFolder.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.opened_folder);
|
updates.OnDownloadProgramUpdate += (_, _) => SetStatus(GeneralLangRes.DownloadProgramUpdate, AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer));
|
||||||
ButtonCheckForUpdates.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.update_done);
|
await updates.UpdateApp();
|
||||||
ButtonInstall.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer);
|
}
|
||||||
MenuItemRepair.Icon = AppGlobals.Symbols.GetImage(AppSymbols.wrench, SymbolSize.Small);
|
|
||||||
|
|
||||||
|
public async Task Initialize()
|
||||||
|
{
|
||||||
ClearStatus();
|
ClearStatus();
|
||||||
LoadProfileToUi();
|
LoadProfileToUi();
|
||||||
|
LoadRecentFilesToUi();
|
||||||
|
HasInitialized = true;
|
||||||
|
await CheckStatusAndUpdate(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Features
|
partial void OnMinecraftProfileFolderChanged(string? value)
|
||||||
|
|
||||||
private void SetStatus(string statusText, IImage? image)
|
|
||||||
{
|
{
|
||||||
TextStatus.Text = statusText;
|
if (!LoadingData)
|
||||||
ImageStatus.Source = image;
|
_ = CheckStatusAndUpdate(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearStatus()
|
partial void OnModpackConfigUrlChanged(string? value)
|
||||||
{
|
{
|
||||||
TextStatus.Text = "-";
|
if (!LoadingData)
|
||||||
ImageStatus.Source = null;
|
_ = CheckStatusAndUpdate(false);
|
||||||
}
|
}
|
||||||
private void LoadRecentFilesToUi()
|
|
||||||
|
partial void OnInstallKeyChanged(string? value)
|
||||||
{
|
{
|
||||||
menuFlyoutSearchProfileFolder.Items.Clear();
|
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 features != null && features.IsEnabled(ModpackFeatures.FeatureAllowExtas, new AllowExtrasFeatureContext(modpackInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
if (AppConfig.Instance.RecentMinecraftProfilePaths.Count == 0)
|
||||||
{
|
{
|
||||||
menuFlyoutSearchProfileFolder.Items.Add(new TextBlock
|
RecentMinecraftProfilePathItems.Add(new EmptyRecentFilesItem());
|
||||||
{
|
|
||||||
Text = GeneralLangRes.NoRecentProfilesAvailable,
|
|
||||||
FontStyle = FontStyle.Italic,
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppConfig.Instance.RecentMinecraftProfilePaths.ForEach(path =>
|
AppConfig.Instance.RecentMinecraftProfilePaths.ForEach(path =>
|
||||||
{
|
{
|
||||||
if (File.Exists(path))
|
if (Directory.Exists(path))
|
||||||
return;
|
RecentMinecraftProfilePathItems.Add(new RecentFilesItem(path));
|
||||||
|
|
||||||
var item = new MenuItem
|
|
||||||
{
|
|
||||||
Header = path.Length > 50 ? $"...{path[^50..]}" : path,
|
|
||||||
DataContext = path,
|
|
||||||
};
|
|
||||||
item.Click += MenuItemRecentMinecraftProfilePathItem_Click;
|
|
||||||
menuFlyoutSearchProfileFolder.Items.Add(item);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,26 +148,14 @@ public partial class MainForm : Window
|
|||||||
AppConfig.Instance.RecentMinecraftProfilePaths.Skip(10).ForEach(n => AppConfig.Instance.RecentMinecraftProfilePaths.Remove(n));
|
AppConfig.Instance.RecentMinecraftProfilePaths.Skip(10).ForEach(n => AppConfig.Instance.RecentMinecraftProfilePaths.Remove(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadProfileToUi()
|
[RelayCommand]
|
||||||
|
private void OpenRecentPath(string path)
|
||||||
{
|
{
|
||||||
loadingData = true;
|
if (!string.IsNullOrWhiteSpace(path))
|
||||||
|
MinecraftProfileFolder = path;
|
||||||
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()
|
public async Task CheckStatusAndUpdate(bool loadProfileToUi)
|
||||||
{
|
|
||||||
//foreach (var set in )
|
|
||||||
//{
|
|
||||||
// // ...
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void CheckStatusAndUpdate(bool loadProfileToUi)
|
|
||||||
{
|
{
|
||||||
if (!CheckStatus(loadProfileToUi))
|
if (!CheckStatus(loadProfileToUi))
|
||||||
return;
|
return;
|
||||||
@@ -124,9 +168,11 @@ public partial class MainForm : Window
|
|||||||
|
|
||||||
private bool CheckStatus(bool loadProfileToUi)
|
private bool CheckStatus(bool loadProfileToUi)
|
||||||
{
|
{
|
||||||
|
ClearStatus();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
modpackInfo = ModpackInfo.TryLoad(TextBoxMinecraftProfileFolder.Text?.Trim());
|
modpackInfo = ModpackInfo.TryLoad(MinecraftProfileFolder?.Trim());
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -138,7 +184,7 @@ public partial class MainForm : Window
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
updateConfig = ModpackConfig.LoadFromUrl(TextBoxModpackConfig.Text);
|
updateConfig = ModpackConfig.LoadFromUrl(ModpackConfigUrl);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -146,69 +192,68 @@ public partial class MainForm : Window
|
|||||||
}
|
}
|
||||||
|
|
||||||
features = new(updateConfig);
|
features = new(updateConfig);
|
||||||
modpackInfo.ExtrasKey = TextBoxInstallKey.Text?.Trim();
|
modpackInfo.ExtrasKey = InstallKey?.Trim();
|
||||||
if (!features.IsInvalid() && !string.IsNullOrWhiteSpace(TextBoxInstallKey.Text) && !AllowExtras())
|
if (!features.IsInvalid() && !string.IsNullOrWhiteSpace(modpackInfo.ExtrasKey) && !AllowExtras())
|
||||||
{
|
{
|
||||||
SetStatus(GeneralLangRes.InstallationKeyNotValid, AppGlobals.Symbols.GetImageSource(AppSymbols.general_warning_sign));
|
SetStatus(GeneralLangRes.InstallationKeyNotValid, AppGlobals.Symbols.GetImageSource(AppSymbols.general_warning_sign));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelInstallKey.IsVisible = TextBoxInstallKey.IsVisible = !string.IsNullOrWhiteSpace(updateConfig.UnleashApiUrl);
|
CanUseExtrasKey = CanUseExtrasKey = !string.IsNullOrWhiteSpace(updateConfig.UnleashApiUrl);
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(TextBoxMinecraftProfileFolder.Text) /*|| modpackInfo.Valid*/)
|
if (string.IsNullOrWhiteSpace(MinecraftProfileFolder) /*|| modpackInfo.Valid*/)
|
||||||
{
|
{
|
||||||
SetStatus(GeneralLangRes.MinecraftProfileFolderSeemsInvalid, AppGlobals.Symbols.GetImageSource(AppSymbols.general_warning_sign));
|
SetStatus(GeneralLangRes.MinecraftProfileFolderSeemsInvalid, AppGlobals.Symbols.GetImageSource(AppSymbols.general_warning_sign));
|
||||||
ButtonCheckForUpdates.IsEnabled = false;
|
CanUpdate = false;
|
||||||
ButtonInstall.IsEnabled = false;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (string.IsNullOrWhiteSpace(TextBoxModpackConfig.Text))
|
else if (string.IsNullOrWhiteSpace(ModpackConfigUrl))
|
||||||
{
|
{
|
||||||
SetStatus(GeneralLangRes.ConfigIncompleteOrNotLoaded, AppGlobals.Symbols.GetImageSource(AppSymbols.general_warning_sign));
|
SetStatus(GeneralLangRes.ConfigIncompleteOrNotLoaded, AppGlobals.Symbols.GetImageSource(AppSymbols.general_warning_sign));
|
||||||
ButtonCheckForUpdates.IsEnabled = false;
|
CanUpdate = false;
|
||||||
ButtonInstall.IsEnabled = false;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (updateConfig.Maintenance && !updateOptions.IgnoreMaintenance)
|
else if (updateConfig.Maintenance && !updateOptions.IgnoreMaintenance)
|
||||||
{
|
{
|
||||||
SetStatus(GeneralLangRes.UpdateServerInMaintenance, AppGlobals.Symbols.GetImageSource(AppSymbols.services));
|
SetStatus(GeneralLangRes.UpdateServerInMaintenance, AppGlobals.Symbols.GetImageSource(AppSymbols.services));
|
||||||
ButtonCheckForUpdates.IsEnabled = false;
|
CanUpdate = false;
|
||||||
ButtonInstall.IsEnabled = false;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LoadOptionsToUi();
|
LoadOptionsToUi();
|
||||||
ButtonCheckForUpdates.IsEnabled = true;
|
CanUpdate = true;
|
||||||
ButtonInstall.IsEnabled = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ExecuteUpdate(bool doInstall, bool repair)
|
private async Task ExecuteUpdate(bool doInstall, bool repair)
|
||||||
{
|
{
|
||||||
|
ClearStatus();
|
||||||
|
|
||||||
// Ensure set extras key
|
// Ensure set extras key
|
||||||
modpackInfo.ExtrasKey = TextBoxInstallKey.Text?.Trim();
|
modpackInfo.ExtrasKey = InstallKey?.Trim();
|
||||||
|
|
||||||
var updater = new ModpackInstaller(updateConfig, modpackInfo);
|
var updater = new ModpackInstaller(updateConfig, modpackInfo);
|
||||||
updater.InstallProgessUpdated += Update_InstallProgessUpdated;
|
updater.InstallProgessUpdated += Updater_InstallProgressUpdated;
|
||||||
updater.CheckingProgressUpdated += Updated_CheckingProgresssUpdated;
|
updater.CheckingProgressUpdated += Updater_CheckingProgressUpdated;
|
||||||
|
|
||||||
void error()
|
void error()
|
||||||
{
|
{
|
||||||
SetStatus(GeneralLangRes.ErrorOnUpdateCheckOrUpdating, AppGlobals.Symbols.GetImageSource(AppSymbols.close));
|
SetStatus(GeneralLangRes.ErrorOnUpdateCheckOrUpdating, AppGlobals.Symbols.GetImageSource(AppSymbols.close));
|
||||||
currentUpdating = false;
|
IsUpdating = false;
|
||||||
}
|
}
|
||||||
void installing()
|
void installing()
|
||||||
{
|
{
|
||||||
SetStatus(GeneralLangRes.Installing, AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer));
|
SetStatus(GeneralLangRes.Installing, AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer));
|
||||||
currentUpdating = true;
|
IsUpdating = true;
|
||||||
}
|
}
|
||||||
void updatesAvailable()
|
void updatesAvailable()
|
||||||
{
|
{
|
||||||
SetStatus(GeneralLangRes.AnUpdateIsAvailable, AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer));
|
SetStatus(GeneralLangRes.AnUpdateIsAvailable, AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer));
|
||||||
|
IsUpdating = false;
|
||||||
}
|
}
|
||||||
void everythingOk()
|
void everythingOk()
|
||||||
{
|
{
|
||||||
SetStatus(GeneralLangRes.EverythingIsRightAndUpToDate, AppGlobals.Symbols.GetImageSource(AppSymbols.done));
|
SetStatus(GeneralLangRes.EverythingIsRightAndUpToDate, AppGlobals.Symbols.GetImageSource(AppSymbols.done));
|
||||||
currentUpdating = false;
|
IsUpdating = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check only if not pressed "install", not really needed otherwise.
|
// Check only if not pressed "install", not really needed otherwise.
|
||||||
@@ -261,7 +306,7 @@ public partial class MainForm : Window
|
|||||||
|
|
||||||
// Install updates
|
// Install updates
|
||||||
installing();
|
installing();
|
||||||
currentUpdating = true;
|
IsUpdating = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Install
|
// Install
|
||||||
@@ -275,7 +320,7 @@ public partial class MainForm : Window
|
|||||||
lastUpdateCheckResult = null; // Reset last update check, a new one would be needed now.
|
lastUpdateCheckResult = null; // Reset last update check, a new one would be needed now.
|
||||||
everythingOk();
|
everythingOk();
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch
|
||||||
{
|
{
|
||||||
// Error
|
// Error
|
||||||
error();
|
error();
|
||||||
@@ -284,96 +329,14 @@ public partial class MainForm : Window
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool AllowExtras()
|
private void Updater_CheckingProgressUpdated(int toCheck, int processed)
|
||||||
{
|
|
||||||
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));
|
SetStatus(Math.Round(processed / (double)toCheck * 100d, 1) + "%", AppGlobals.Symbols.GetImageSource(AppSymbols.update_done));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Update_InstallProgessUpdated(UpdateCheckResult result, int processedSyncs)
|
private void Updater_InstallProgressUpdated(UpdateCheckResult result, int processedSyncs)
|
||||||
{
|
{
|
||||||
var actionCount = result.Actions.Count;
|
var actionCount = result.Actions.Count;
|
||||||
SetStatus(Math.Round(processedSyncs / (double)actionCount * 100d, 1) + "%", AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer));
|
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
|
|
||||||
}
|
}
|
||||||
@@ -66,6 +66,7 @@
|
|||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||||
<PackageReference Include="MessageBox.Avalonia" Version="3.3.0" />
|
<PackageReference Include="MessageBox.Avalonia" Version="3.3.0" />
|
||||||
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
|
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
|
|||||||
Reference in New Issue
Block a user