From 3ef26f2918977906ebe14e328f015ce4f1941dc3 Mon Sep 17 00:00:00 2001 From: rbalsleyMSFT <53497092+rbalsleyMSFT@users.noreply.github.com> Date: Tue, 9 Sep 2025 17:49:36 -0700 Subject: [PATCH] Adds auto-loading of previous configuration on startup Implements a new feature to automatically load the previously saved environment when the UI is launched. This improves user experience by restoring the last saved configuration, including selected applications and drivers, eliminating the need to manually reload them on each run. The process loads the main `FFUConfig.json` and then proceeds to load associated Winget, BYO App, and Driver lists if they are defined. UI elements and checkboxes are updated accordingly to reflect the loaded state. --- FFUDevelopment/BuildFFUVM_UI.ps1 | 8 + .../FFUUI.Core/FFUUI.Core.Config.psm1 | 242 ++++++++++++++++++ 2 files changed, 250 insertions(+) diff --git a/FFUDevelopment/BuildFFUVM_UI.ps1 b/FFUDevelopment/BuildFFUVM_UI.ps1 index 6f117f0..70bee77 100644 --- a/FFUDevelopment/BuildFFUVM_UI.ps1 +++ b/FFUDevelopment/BuildFFUVM_UI.ps1 @@ -126,6 +126,14 @@ $window.Add_Loaded({ Initialize-UIDefaults -State $script:uiState Initialize-DynamicUIElements -State $script:uiState Register-EventHandlers -State $script:uiState + + # Attempt automatic load of previous environment (silent) + try { + Invoke-AutoLoadPreviousEnvironment -State $script:uiState + } + catch { + WriteLog "Auto-load previous environment failed: $($_.Exception.Message)" + } }) diff --git a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Config.psm1 b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Config.psm1 index 2403c87..3666ee5 100644 --- a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Config.psm1 +++ b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Config.psm1 @@ -664,4 +664,246 @@ function Invoke-SaveConfiguration { } } +function Invoke-AutoLoadPreviousEnvironment { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [psobject]$State + ) + + try { + $ffuDevRoot = $State.FFUDevelopmentPath + if ([string]::IsNullOrWhiteSpace($ffuDevRoot)) { + WriteLog "AutoLoad: FFUDevelopmentPath not set; skipping." + return + } + + $configPath = Join-Path $ffuDevRoot "config\FFUConfig.json" + if (-not (Test-Path -LiteralPath $configPath)) { + WriteLog "AutoLoad: No existing FFUConfig.json found at $configPath." + return + } + + WriteLog "AutoLoad: Found config file at $configPath. Parsing..." + $configContent = $null + try { + $raw = Get-Content -Path $configPath -Raw -ErrorAction Stop + if ([string]::IsNullOrWhiteSpace($raw)) { + WriteLog "AutoLoad: Config file is empty; aborting auto-load." + return + } + $configContent = $raw | ConvertFrom-Json -ErrorAction Stop + } + catch { + WriteLog "AutoLoad: Failed to parse config JSON: $($_.Exception.Message)" + return + } + + if ($null -eq $configContent) { + WriteLog "AutoLoad: Parsed configContent is null; aborting." + return + } + + WriteLog "AutoLoad: Applying configuration to UI." + Update-UIFromConfig -ConfigContent $configContent -State $State + + # Track which supplemental assets we successfully load + $loadedWinget = $false + $loadedBYO = $false + $loadedDrivers = $false + + # --- Winget AppList --- + $appListPath = $null + if ($configContent.PSObject.Properties.Match('AppListPath').Count -gt 0) { + $appListPath = $configContent.AppListPath + } + if (-not [string]::IsNullOrWhiteSpace($appListPath) -and (Test-Path -LiteralPath $appListPath)) { + WriteLog "AutoLoad: Loading Winget AppList from $appListPath." + try { + $importedAppsData = Get-Content -Path $appListPath -Raw | ConvertFrom-Json -ErrorAction Stop + if ($null -ne $importedAppsData -and $null -ne $importedAppsData.apps) { + $defaultArch = $State.Controls.cmbWindowsArch.SelectedItem + $appsBuffer = [System.Collections.Generic.List[object]]::new() + foreach ($appInfo in $importedAppsData.apps) { + $arch = if ($appInfo.source -eq 'msstore') { 'NA' } else { + if ($appInfo.PSObject.Properties['architecture']) { $appInfo.architecture } else { $defaultArch } + } + $appsBuffer.Add([PSCustomObject]@{ + IsSelected = $true + Name = $appInfo.name + Id = $appInfo.id + Version = "" + Source = $appInfo.source + Architecture = $arch + DownloadStatus = "" + }) + } + $State.Controls.lstWingetResults.ItemsSource = $appsBuffer.ToArray() + $loadedWinget = $true + WriteLog "AutoLoad: Winget AppList loaded with $($appsBuffer.Count) entries." + } + else { + WriteLog "AutoLoad: AppList JSON did not contain an 'apps' array." + } + } + catch { + WriteLog "AutoLoad: Failed loading Winget AppList ($appListPath): $($_.Exception.Message)" + } + } + else { + WriteLog "AutoLoad: AppListPath not set or file missing." + } + + # --- BYO UserAppList (UserAppList.json) --- + $userAppListPath = $null + if ($configContent.PSObject.Properties.Match('UserAppListPath').Count -gt 0) { + $userAppListPath = $configContent.UserAppListPath + } + if (-not [string]::IsNullOrWhiteSpace($userAppListPath) -and (Test-Path -LiteralPath $userAppListPath)) { + WriteLog "AutoLoad: Loading UserAppList from $userAppListPath." + try { + $applications = Get-Content -Path $userAppListPath -Raw | ConvertFrom-Json -ErrorAction Stop + if ($applications) { + $listView = $State.Controls.lstApplications + $listView.Items.Clear() + $sortedApps = $applications | Sort-Object Priority + foreach ($app in $sortedApps) { + $ignoreNonZero = if ($app.PSObject.Properties['IgnoreNonZeroExitCodes']) { $app.IgnoreNonZeroExitCodes } else { $false } + $listView.Items.Add([PSCustomObject]@{ + IsSelected = $false + Priority = $app.Priority + Name = $app.Name + CommandLine = $app.CommandLine + Arguments = if ($app.PSObject.Properties['Arguments']) { $app.Arguments } else { "" } + Source = $app.Source + AdditionalExitCodes = if ($app.PSObject.Properties['AdditionalExitCodes']) { $app.AdditionalExitCodes } else { "" } + IgnoreNonZeroExitCodes = $ignoreNonZero + IgnoreExitCodes = if ($ignoreNonZero) { "Yes" } else { "No" } + CopyStatus = "" + }) + } + # Reorder priorities sequentially + if (Get-Command -Name Update-ListViewPriorities -ErrorAction SilentlyContinue) { + Update-ListViewPriorities -ListView $listView + } + if (Get-Command -Name Update-CopyButtonState -ErrorAction SilentlyContinue) { + Update-CopyButtonState -State $State + } + if (Get-Command -Name Update-BYOAppsActionButtonsState -ErrorAction SilentlyContinue) { + Update-BYOAppsActionButtonsState -State $State + } + $loadedBYO = $true + WriteLog "AutoLoad: UserAppList loaded with $($listView.Items.Count) entries." + } + else { + WriteLog "AutoLoad: UserAppList JSON empty or null." + } + } + catch { + WriteLog "AutoLoad: Failed loading UserAppList ($userAppListPath): $($_.Exception.Message)" + } + } + else { + WriteLog "AutoLoad: UserAppListPath not set or file missing." + } + + # --- Drivers (DriversJsonPath) --- + $driversJsonPath = $null + if ($configContent.PSObject.Properties.Match('DriversJsonPath').Count -gt 0) { + $driversJsonPath = $configContent.DriversJsonPath + } + if (-not [string]::IsNullOrWhiteSpace($driversJsonPath) -and (Test-Path -LiteralPath $driversJsonPath)) { + WriteLog "AutoLoad: Loading Drivers JSON from $driversJsonPath." + try { + $rawDrivers = Get-Content -Path $driversJsonPath -Raw | ConvertFrom-Json -ErrorAction Stop + if ($rawDrivers -and $rawDrivers.PSObject.Properties.Count -gt 0) { + $State.Data.allDriverModels.Clear() + foreach ($makeProp in $rawDrivers.PSObject.Properties) { + $makeName = $makeProp.Name + $makeObject = $makeProp.Value + if ($null -eq $makeObject -or -not ($makeObject.PSObject.Properties['Models'])) { + continue + } + $models = $makeObject.Models + if ($models -and ($models -is [System.Collections.IEnumerable])) { + foreach ($modelEntry in $models) { + if ($null -eq $modelEntry -or -not ($modelEntry.PSObject.Properties['Name'])) { continue } + $modelName = $modelEntry.Name + if ([string]::IsNullOrWhiteSpace($modelName)) { continue } + + $driverObj = [PSCustomObject]@{ + IsSelected = $true + Make = $makeName + Model = $modelName + DownloadStatus = if ($modelEntry.PSObject.Properties['DownloadStatus']) { $modelEntry.DownloadStatus } else { "" } + Link = if ($modelEntry.PSObject.Properties['Link']) { $modelEntry.Link } else { $null } + ProductName = if ($modelEntry.PSObject.Properties['ProductName']) { $modelEntry.ProductName } else { $null } + MachineType = if ($modelEntry.PSObject.Properties['MachineType']) { $modelEntry.MachineType } else { $null } + Id = if ($modelEntry.PSObject.Properties['Id']) { $modelEntry.Id } else { $null } + } + + $State.Data.allDriverModels.Add($driverObj) + } + } + } + $State.Controls.lstDriverModels.ItemsSource = $State.Data.allDriverModels + if (Get-Command -Name Update-SelectAllHeaderCheckBoxState -ErrorAction SilentlyContinue) { + $headerChk = $State.Controls.chkSelectAllDriverModels + if ($null -ne $headerChk) { + Update-SelectAllHeaderCheckBoxState -ListView $State.Controls.lstDriverModels -HeaderCheckBox $headerChk + } + } + $loadedDrivers = $true + WriteLog "AutoLoad: Loaded $($State.Data.allDriverModels.Count) driver model entries." + } + else { + WriteLog "AutoLoad: Drivers JSON empty or did not contain expected structure." + } + } + catch { + WriteLog "AutoLoad: Failed loading Drivers JSON ($driversJsonPath): $($_.Exception.Message)" + } + } + else { + WriteLog "AutoLoad: DriversJsonPath not set or file missing." + } + + # Set checkboxes based on what was loaded + if ($loadedWinget -or $loadedBYO) { + $State.Controls.chkInstallApps.IsChecked = $true + } + if ($loadedWinget) { + $State.Controls.chkInstallWingetApps.IsChecked = $true + } + if ($loadedBYO) { + $State.Controls.chkBringYourOwnApps.IsChecked = $true + } + if ($loadedDrivers) { + $State.Controls.chkDownloadDrivers.IsChecked = $true + } + + # Re-run panel visibility/state helpers + if (Get-Command -Name Update-ApplicationPanelVisibility -ErrorAction SilentlyContinue) { + Update-ApplicationPanelVisibility -State $State -TriggeringControlName 'AutoLoad' + } + if (Get-Command -Name Update-DriverDownloadPanelVisibility -ErrorAction SilentlyContinue) { + Update-DriverDownloadPanelVisibility -State $State + } + if (Get-Command -Name Update-DriverCheckboxStates -ErrorAction SilentlyContinue) { + Update-DriverCheckboxStates -State $State + } + if (Get-Command -Name Update-OfficePanelVisibility -ErrorAction SilentlyContinue) { + Update-OfficePanelVisibility -State $State + } + if (Get-Command -Name Update-CopyButtonState -ErrorAction SilentlyContinue) { + Update-CopyButtonState -State $State + } + + WriteLog "AutoLoad: Completed. Loaded Config=$true; Winget=$loadedWinget; BYO=$loadedBYO; Drivers=$loadedDrivers." + } + catch { + WriteLog "AutoLoad: Unexpected failure: $($_.Exception.ToString())" + } +} + Export-ModuleMember -Function * \ No newline at end of file