From 3202ec72b22c37fa6e4deed3cb1d3b8e5f0c4524 Mon Sep 17 00:00:00 2001 From: GreemDev Date: Sun, 23 Nov 2025 19:56:52 -0600 Subject: [PATCH] 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. --- Directory.Packages.props | 4 +- .../SetupWizard/SetupWizardPage.Builder.cs | 1 + .../Systems/SetupWizard/SetupWizardPage.cs | 2 + .../SetupWizard/SetupWizardPageContext.cs | 10 +++ .../SetupWizard/Pages/SetupFirmwarePage.axaml | 2 +- .../Pages/SetupFirmwarePage.axaml.cs | 2 +- ...ewModel.cs => SetupFirmwarePageContext.cs} | 59 +++++++++++++++- .../UI/SetupWizard/Pages/SetupKeysPage.axaml | 2 +- .../SetupWizard/Pages/SetupKeysPage.axaml.cs | 2 +- .../SetupKeysPageContext.cs} | 39 +++++++++-- .../Pages/SetupKeysPageViewModel.cs | 32 --------- .../UI/SetupWizard/RyujinxSetupWizard.cs | 67 +++---------------- .../RyujinxSetupWizardWindow.axaml.cs | 5 +- 13 files changed, 123 insertions(+), 104 deletions(-) create mode 100644 src/Ryujinx/Systems/SetupWizard/SetupWizardPageContext.cs rename src/Ryujinx/UI/SetupWizard/Pages/{SetupFirmwarePageViewModel.cs => SetupFirmwarePageContext.cs} (54%) rename src/Ryujinx/UI/SetupWizard/{RyujinxSetupWizard.Helpers.cs => Pages/SetupKeysPageContext.cs} (61%) delete mode 100644 src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPageViewModel.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index fd61602a8..bcce686ac 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,7 @@ - + @@ -59,4 +59,4 @@ - \ No newline at end of file + diff --git a/src/Ryujinx/Systems/SetupWizard/SetupWizardPage.Builder.cs b/src/Ryujinx/Systems/SetupWizard/SetupWizardPage.Builder.cs index e12df291b..27f516697 100644 --- a/src/Ryujinx/Systems/SetupWizard/SetupWizardPage.Builder.cs +++ b/src/Ryujinx/Systems/SetupWizard/SetupWizardPage.Builder.cs @@ -2,6 +2,7 @@ using Avalonia; using Avalonia.Controls; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Controls; +using Ryujinx.Ava.UI.SetupWizard; using Ryujinx.Ava.UI.ViewModels; namespace Ryujinx.Ava.Systems.SetupWizard diff --git a/src/Ryujinx/Systems/SetupWizard/SetupWizardPage.cs b/src/Ryujinx/Systems/SetupWizard/SetupWizardPage.cs index c96664fed..f01794d06 100644 --- a/src/Ryujinx/Systems/SetupWizard/SetupWizardPage.cs +++ b/src/Ryujinx/Systems/SetupWizard/SetupWizardPage.cs @@ -2,6 +2,8 @@ using Avalonia.Controls.Presenters; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Controls; +using Ryujinx.Ava.UI.SetupWizard; using Ryujinx.Ava.UI.ViewModels; using System.Threading; using System.Threading.Tasks; diff --git a/src/Ryujinx/Systems/SetupWizard/SetupWizardPageContext.cs b/src/Ryujinx/Systems/SetupWizard/SetupWizardPageContext.cs new file mode 100644 index 000000000..9107a8f0b --- /dev/null +++ b/src/Ryujinx/Systems/SetupWizard/SetupWizardPageContext.cs @@ -0,0 +1,10 @@ +using Gommon; +using Ryujinx.Ava.UI.ViewModels; + +namespace Ryujinx.Ava.Systems.SetupWizard +{ + public abstract class SetupWizardPageContext: BaseModel + { + public abstract Result CompleteStep(); + } +} diff --git a/src/Ryujinx/UI/SetupWizard/Pages/SetupFirmwarePage.axaml b/src/Ryujinx/UI/SetupWizard/Pages/SetupFirmwarePage.axaml index 870f9720a..1549cf17a 100644 --- a/src/Ryujinx/UI/SetupWizard/Pages/SetupFirmwarePage.axaml +++ b/src/Ryujinx/UI/SetupWizard/Pages/SetupFirmwarePage.axaml @@ -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"> diff --git a/src/Ryujinx/UI/SetupWizard/Pages/SetupFirmwarePage.axaml.cs b/src/Ryujinx/UI/SetupWizard/Pages/SetupFirmwarePage.axaml.cs index d5e02cfc9..5f2950bee 100644 --- a/src/Ryujinx/UI/SetupWizard/Pages/SetupFirmwarePage.axaml.cs +++ b/src/Ryujinx/UI/SetupWizard/Pages/SetupFirmwarePage.axaml.cs @@ -2,7 +2,7 @@ using Ryujinx.Ava.UI.Controls; namespace Ryujinx.Ava.UI.SetupWizard.Pages { - public partial class SetupFirmwarePage : RyujinxControl + public partial class SetupFirmwarePage : RyujinxControl { public SetupFirmwarePage() { diff --git a/src/Ryujinx/UI/SetupWizard/Pages/SetupFirmwarePageViewModel.cs b/src/Ryujinx/UI/SetupWizard/Pages/SetupFirmwarePageContext.cs similarity index 54% rename from src/Ryujinx/UI/SetupWizard/Pages/SetupFirmwarePageViewModel.cs rename to src/Ryujinx/UI/SetupWizard/Pages/SetupFirmwarePageContext.cs index 50f0d4627..0359fab88 100644 --- a/src/Ryujinx/UI/SetupWizard/Pages/SetupFirmwarePageViewModel.cs +++ b/src/Ryujinx/UI/SetupWizard/Pages/SetupFirmwarePageContext.cs @@ -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; + } } } diff --git a/src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPage.axaml b/src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPage.axaml index 702eb29d0..d24e86687 100644 --- a/src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPage.axaml +++ b/src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPage.axaml @@ -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"> diff --git a/src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPage.axaml.cs b/src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPage.axaml.cs index 78febdb7d..fdde29fd6 100644 --- a/src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPage.axaml.cs +++ b/src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPage.axaml.cs @@ -2,7 +2,7 @@ using Ryujinx.Ava.UI.Controls; namespace Ryujinx.Ava.UI.SetupWizard.Pages { - public partial class SetupKeysPage : RyujinxControl + public partial class SetupKeysPage : RyujinxControl { public SetupKeysPage() { diff --git a/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.Helpers.cs b/src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPageContext.cs similarity index 61% rename from src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.Helpers.cs rename to src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPageContext.cs index cdec13a4d..8b0543957 100644 --- a/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.Helpers.cs +++ b/src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPageContext.cs @@ -1,19 +1,48 @@ +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 +namespace Ryujinx.Ava.UI.SetupWizard.Pages { - public partial class RyujinxSetupWizard + public partial class SetupKeysPageContext : SetupWizardPageContext { - private Result InstallKeys(string directory) + 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 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 { @@ -53,11 +82,11 @@ namespace Ryujinx.Ava.UI.SetupWizard NotificationHelper.ShowError(message, waitingExit: true); - return Result.Failure(new MessageError(ex.Message)); + return Result.Failure(new MessageError(message)); } finally { - _mainWindow.VirtualFileSystem.ReloadKeySet(); + RyujinxApp.MainWindow.VirtualFileSystem.ReloadKeySet(); } return Result.Success; diff --git a/src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPageViewModel.cs b/src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPageViewModel.cs deleted file mode 100644 index 234b50d6e..000000000 --- a/src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPageViewModel.cs +++ /dev/null @@ -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 result = await RyujinxApp.MainWindow.ViewModel.StorageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions - { - Title = LocaleManager.Instance[LocaleKeys.SetupWizardKeysPageFolderPopupTitle] - }); - - if (result.TryGet(out IStorageFolder keyFolder)) - { - tb.Text = keyFolder.TryGetLocalPath(); - } - } - } -} diff --git a/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.cs b/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.cs index 30ebcb743..ef0ed6b55 100644 --- a/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.cs +++ b/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.cs @@ -1,25 +1,19 @@ -using Avalonia.Controls.Presenters; -using Gommon; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Systems.SetupWizard; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.SetupWizard.Pages; using Ryujinx.Ava.UI.Windows; -using Ryujinx.Common.Configuration; -using Ryujinx.HLE.FileSystem; -using System; -using System.IO; using System.Threading.Tasks; namespace Ryujinx.Ava.UI.SetupWizard { - public partial class RyujinxSetupWizard(RyujinxSetupWizardWindow wizardWindow) + public class RyujinxSetupWizard(RyujinxSetupWizardWindow wizardWindow) : BaseSetupWizard(wizardWindow.WizardPresenter) { private readonly MainWindow _mainWindow = RyujinxApp.MainWindow; - private bool _configWasModified = false; + private bool _configWasModified; public bool HasFirmware => _mainWindow.ContentManager.GetCurrentFirmwareVersion() != null; @@ -60,20 +54,14 @@ namespace Ryujinx.Ava.UI.SetupWizard Retry: bool result = await NextPage() .WithTitle(LocaleKeys.SetupWizardKeysPageTitle) - .WithContent(out SetupKeysPageViewModel kpvm) + .WithContent(out SetupKeysPageContext keyContext) .Show(); if (!result) return false; - if (!Directory.Exists(kpvm.KeysFolderPath)) + if (!keyContext.CompleteStep()) goto Retry; - - Result installResult = InstallKeys(kpvm.KeysFolderPath); - if (!installResult.IsSuccess) - { - goto Retry; - } } return true; @@ -92,55 +80,22 @@ namespace Ryujinx.Ava.UI.SetupWizard Retry: bool result = await NextPage() .WithTitle(LocaleKeys.SetupWizardFirmwarePageTitle) - .WithContent(out SetupFirmwarePageViewModel fwvm) + .WithContent(out SetupFirmwarePageContext fwContext) .Show(); if (!result) return false; - if (!Directory.Exists(fwvm.FirmwareSourcePath)) + if (!fwContext.CompleteStep()) goto Retry; - - try - { - _mainWindow.ContentManager.InstallFirmware(fwvm.FirmwareSourcePath); - SystemVersion installedFwVer = _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 '{fwvm.FirmwareSourcePath}'. " + - "\nPlease check the log or try again." - ); - } - _mainWindow.ViewModel.RefreshFirmwareStatus(installedFwVer, allowNullVersion: true); - - // 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); - goto Retry; - } } return true; } + + public void SignalConfigModified() + { + _configWasModified = true; + } } } diff --git a/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizardWindow.axaml.cs b/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizardWindow.axaml.cs index 71d0aa054..61a5295e1 100644 --- a/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizardWindow.axaml.cs +++ b/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizardWindow.axaml.cs @@ -1,4 +1,5 @@ using Avalonia.Controls; +using Gommon; using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Systems.SetupWizard; using Ryujinx.Ava.UI.Windows; @@ -28,10 +29,10 @@ namespace Ryujinx.Ava.UI.SetupWizard { if (!CanShowSetupWizard) return Task.CompletedTask; - + Task windowTask = ShowAsync( CreateWindow(out BaseSetupWizard wiz), - owner ?? RyujinxApp.MainWindow + owner ); _ = wiz.Start(); return windowTask;