343 lines
11 KiB
C#
343 lines
11 KiB
C#
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 ModpackFeatures? features;
|
|
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 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)
|
|
{
|
|
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
|
|
}
|
|
|
|
features = new(updateConfig);
|
|
modpackInfo.ExtrasKey = InstallKey?.Trim();
|
|
if (!features.IsInvalid() && !string.IsNullOrWhiteSpace(modpackInfo.ExtrasKey) && !AllowExtras())
|
|
{
|
|
SetStatus(GeneralLangRes.InstallationKeyNotValid, AppGlobals.Symbols.GetImageSource(AppSymbols.general_warning_sign));
|
|
return false;
|
|
}
|
|
|
|
CanUseExtrasKey = CanUseExtrasKey = !string.IsNullOrWhiteSpace(updateConfig.UnleashApiUrl);
|
|
|
|
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));
|
|
}
|
|
}
|