Bake setup step logic into the view models themselves instead of being in the setup wizard implementation

renamed view models to contexts (like TKMM), however the contexts here are actually of a unique base type; containing aforementioned setup step logic. if the return value is of an error state result, it will prompt a retry of the page.
This commit is contained in:
GreemDev
2025-11-23 19:56:52 -06:00
parent fd2ecee479
commit 133ac41425
13 changed files with 123 additions and 104 deletions

View File

@@ -5,7 +5,7 @@
xmlns:markup="clr-namespace:Ryujinx.Ava.Common.Markup"
xmlns:pages="clr-namespace:Ryujinx.Ava.UI.SetupWizard.Pages"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:DataType="pages:SetupFirmwarePageViewModel"
x:DataType="pages:SetupFirmwarePageContext"
x:Class="Ryujinx.Ava.UI.SetupWizard.Pages.SetupFirmwarePage">
<StackPanel>
<TextBlock Text="{markup:Locale SetupWizardFirmwarePageDescription}"/>

View File

@@ -2,7 +2,7 @@ using Ryujinx.Ava.UI.Controls;
namespace Ryujinx.Ava.UI.SetupWizard.Pages
{
public partial class SetupFirmwarePage : RyujinxControl<SetupFirmwarePageViewModel>
public partial class SetupFirmwarePage : RyujinxControl<SetupFirmwarePageContext>
{
public SetupFirmwarePage()
{

View File

@@ -3,16 +3,20 @@ using Avalonia.Platform.Storage;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Gommon;
using Ryujinx.Ava;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Systems.SetupWizard;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.Utilities;
using Ryujinx.Common.Configuration;
using Ryujinx.HLE.FileSystem;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.SetupWizard.Pages
{
public partial class SetupFirmwarePageViewModel : BaseModel
public partial class SetupFirmwarePageContext : SetupWizardPageContext
{
[ObservableProperty]
public partial string FirmwareSourcePath { get; set; }
@@ -65,5 +69,54 @@ namespace Ryujinx.Ava.UI.SetupWizard.Pages
tb.Text = firmwareFolder.TryGetLocalPath();
}
}
public override Result CompleteStep()
{
if (!Directory.Exists(FirmwareSourcePath))
return Result.Fail;
try
{
RyujinxApp.MainWindow.ContentManager.InstallFirmware(FirmwareSourcePath);
SystemVersion installedFwVer = RyujinxApp.MainWindow.ContentManager.GetCurrentFirmwareVersion();
if (installedFwVer != null)
{
NotificationHelper.ShowInformation(
"Firmware installed",
$"Installed firmware version {installedFwVer.VersionString}."
);
}
else
{
NotificationHelper.ShowError(
"Firmware not installed",
$"It seems some error occurred when trying to install the firmware at path '{FirmwareSourcePath}'." +
"\nDid that folder contain a firmware dump?"
);
}
RyujinxApp.MainWindow.ViewModel.RefreshFirmwareStatus(installedFwVer, allowNullVersion: true);
if (installedFwVer is null)
return Result.Fail;
// Purge Applet Cache.
DirectoryInfo miiEditorCacheFolder = new(
Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache")
);
if (miiEditorCacheFolder.Exists)
{
miiEditorCacheFolder.Delete(true);
}
}
catch (Exception e)
{
NotificationHelper.ShowError(e.Message, waitingExit: true);
return Result.Fail;
}
return Result.Success;
}
}
}

View File

@@ -6,7 +6,7 @@
xmlns:markup="clr-namespace:Ryujinx.Ava.Common.Markup"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Ryujinx.Ava.UI.SetupWizard.Pages.SetupKeysPage"
x:DataType="pages:SetupKeysPageViewModel">
x:DataType="pages:SetupKeysPageContext">
<StackPanel>
<TextBlock Text="{markup:Locale SetupWizardKeysPageDescription}" Margin="0,0,0,10"/>
<Grid ColumnDefinitions="*,Auto">

View File

@@ -2,7 +2,7 @@ using Ryujinx.Ava.UI.Controls;
namespace Ryujinx.Ava.UI.SetupWizard.Pages
{
public partial class SetupKeysPage : RyujinxControl<SetupKeysPageViewModel>
public partial class SetupKeysPage : RyujinxControl<SetupKeysPageContext>
{
public SetupKeysPage()
{

View File

@@ -0,0 +1,100 @@
using Avalonia.Controls;
using Avalonia.Platform.Storage;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using DynamicData;
using Gommon;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Systems.SetupWizard;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.Utilities;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.FileSystem;
using System;
using System.IO;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.SetupWizard.Pages
{
public partial class SetupKeysPageContext : SetupWizardPageContext
{
public override Result CompleteStep() =>
!Directory.Exists(KeysFolderPath)
? Result.Fail
: InstallKeys(KeysFolderPath);
[ObservableProperty]
public partial string KeysFolderPath { get; set; }
[RelayCommand]
private static async Task Browse(TextBox tb)
{
Optional<IStorageFolder> result = await RyujinxApp.MainWindow.ViewModel.StorageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions
{
Title = LocaleManager.Instance[LocaleKeys.SetupWizardKeysPageFolderPopupTitle]
});
if (result.TryGet(out IStorageFolder keyFolder))
{
tb.Text = keyFolder.TryGetLocalPath();
}
}
private static Result InstallKeys(string directory)
{
try
{
string systemDirectory = AppDataManager.KeysDirPath;
if (AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile &&
Directory.Exists(AppDataManager.KeysDirPathUser))
{
systemDirectory = AppDataManager.KeysDirPathUser;
}
Logger.Info?.Print(LogClass.Application, $"Installing keys from {directory}");
ContentManager.InstallKeys(directory, systemDirectory);
NotificationHelper.ShowInformation(
title: LocaleManager.Instance[LocaleKeys.RyujinxInfo],
text: LocaleManager.Instance[LocaleKeys.DialogKeysInstallerKeysInstallSuccessMessage]);
}
catch (InvalidFirmwarePackageException ifwpe)
{
NotificationHelper.ShowError(ifwpe.Message, waitingExit: true);
return Result.Failure(NoKeysFoundInFolder.Shared);
}
catch (MissingKeyException ex)
{
NotificationHelper.ShowError(ex.ToString(), waitingExit: true);
return Result.Failure(NoKeysFoundInFolder.Shared);
}
catch (Exception ex)
{
string message = ex.Message;
if (ex is FormatException)
{
message = LocaleManager.Instance.UpdateAndGetDynamicValue(
LocaleKeys.DialogKeysInstallerKeysNotFoundErrorMessage, directory);
}
NotificationHelper.ShowError(message, waitingExit: true);
return Result.Failure(new MessageError(message));
}
finally
{
RyujinxApp.MainWindow.VirtualFileSystem.ReloadKeySet();
}
return Result.Success;
}
}
public struct NoKeysFoundInFolder : IErrorState
{
public static readonly NoKeysFoundInFolder Shared = new();
}
}

View File

@@ -1,32 +0,0 @@
using Avalonia.Controls;
using Avalonia.Platform.Storage;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Gommon;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Utilities;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.SetupWizard.Pages
{
public partial class SetupKeysPageViewModel : BaseModel
{
[ObservableProperty]
public partial string KeysFolderPath { get; set; }
[RelayCommand]
private static async Task Browse(TextBox tb)
{
Optional<IStorageFolder> result = await RyujinxApp.MainWindow.ViewModel.StorageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions
{
Title = LocaleManager.Instance[LocaleKeys.SetupWizardKeysPageFolderPopupTitle]
});
if (result.TryGet(out IStorageFolder keyFolder))
{
tb.Text = keyFolder.TryGetLocalPath();
}
}
}
}