diff --git a/tools/SetupFlow/DevHome.SetupFlow/Models/ISetupTaskGroup.cs b/tools/SetupFlow/DevHome.SetupFlow/Models/ISetupTaskGroup.cs
index 8a6dacd1e..e91a53569 100644
--- a/tools/SetupFlow/DevHome.SetupFlow/Models/ISetupTaskGroup.cs
+++ b/tools/SetupFlow/DevHome.SetupFlow/Models/ISetupTaskGroup.cs
@@ -29,10 +29,18 @@ public interface ISetupTaskGroup
public ReviewTabViewModelBase GetReviewTabViewModel();
///
- /// Gets all the individual setup tasks that make up this group
+ /// Gets all the setup tasks that make up this group
///
public IEnumerable SetupTasks
{
get;
}
+
+ ///
+ /// Gets all the DSC tasks that make up this group
+ ///
+ public IEnumerable DSCTasks
+ {
+ get;
+ }
}
diff --git a/tools/SetupFlow/DevHome.SetupFlow/Models/WingetConfigure/WinGetDscSettings.cs b/tools/SetupFlow/DevHome.SetupFlow/Models/WingetConfigure/WinGetDscSettings.cs
index cd30b5508..8086b0b5b 100644
--- a/tools/SetupFlow/DevHome.SetupFlow/Models/WingetConfigure/WinGetDscSettings.cs
+++ b/tools/SetupFlow/DevHome.SetupFlow/Models/WingetConfigure/WinGetDscSettings.cs
@@ -1,6 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+using YamlDotNet.Core;
+using YamlDotNet.Serialization;
+
namespace DevHome.SetupFlow.Models.WingetConfigure;
///
@@ -9,6 +12,7 @@ namespace DevHome.SetupFlow.Models.WingetConfigure;
///
public class WinGetDscSettings : WinGetConfigSettingsBase
{
+ [YamlMember(ScalarStyle = ScalarStyle.DoubleQuoted)]
public string Id { get; set; }
public string Source { get; set; }
diff --git a/tools/SetupFlow/DevHome.SetupFlow/Services/ConfigurationFileBuilder.cs b/tools/SetupFlow/DevHome.SetupFlow/Services/ConfigurationFileBuilder.cs
index 0faf1612b..21c498c6d 100644
--- a/tools/SetupFlow/DevHome.SetupFlow/Services/ConfigurationFileBuilder.cs
+++ b/tools/SetupFlow/DevHome.SetupFlow/Services/ConfigurationFileBuilder.cs
@@ -8,6 +8,7 @@
using DevHome.SetupFlow.Models;
using DevHome.SetupFlow.Models.WingetConfigure;
using DevHome.SetupFlow.TaskGroups;
+using Serilog;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
@@ -21,13 +22,8 @@ public enum ConfigurationFileKind
public class ConfigurationFileBuilder
{
- private readonly SetupFlowOrchestrator _orchestrator;
-
- public ConfigurationFileBuilder(SetupFlowOrchestrator orchestrator)
- {
- _orchestrator = orchestrator;
- }
-
+ private readonly ILogger _log = Log.ForContext("SourceContext", nameof(ConfigurationFileBuilder));
+
///
/// Builds an object that represents a config file that can be used by WinGet Configure to install
/// apps and clone repositories.This is already formatted as valid yaml and can be written
@@ -36,21 +32,33 @@ public ConfigurationFileBuilder(SetupFlowOrchestrator orchestrator)
/// The config file object representing the yaml file.
public WinGetConfigFile BuildConfigFileObjectFromTaskGroups(IList taskGroups, ConfigurationFileKind configurationFileKind)
{
- var listOfResources = new List();
-
+ List repoResources = [];
+ List appResources = [];
foreach (var taskGroup in taskGroups)
{
if (taskGroup is RepoConfigTaskGroup repoConfigGroup)
{
// Add the GitDSC resource blocks to yaml
- listOfResources.AddRange(GetResourcesForCloneTaskGroup(repoConfigGroup, configurationFileKind));
+ repoResources.AddRange(GetResourcesForCloneTaskGroup(repoConfigGroup, configurationFileKind));
}
else if (taskGroup is AppManagementTaskGroup appManagementGroup)
{
// Add the WinGetDsc resource blocks to yaml
- listOfResources.AddRange(GetResourcesForAppManagementTaskGroup(appManagementGroup, configurationFileKind));
+ appResources.AddRange(GetResourcesForAppManagementTaskGroup(appManagementGroup, configurationFileKind));
}
}
+
+ // If Git is not added to the apps to install and there are
+ // repositories to clone, add Git as a pre-requisite
+ var isGitAdded = appResources
+ .Select(r => r.Settings as WinGetDscSettings)
+ .Any(s => s.Id == DscHelpers.GitWinGetPackageId);
+ if (!isGitAdded && repoResources.Count > 0)
+ {
+ appResources.Add(CreateWinGetInstallForGitPreReq());
+ }
+
+ List listOfResources = [..appResources, ..repoResources];
if (listOfResources.Count == 0)
{
@@ -114,22 +122,24 @@ public string SerializeWingetFileObjectToString(WinGetConfigFile configFile)
private List GetResourcesForCloneTaskGroup(RepoConfigTaskGroup repoConfigGroup, ConfigurationFileKind configurationFileKind)
{
var listOfResources = new List();
- var repoConfigTasks = repoConfigGroup.SetupTasks
+ var repoConfigTasks = repoConfigGroup.DSCTasks
.Where(task => task is CloneRepoTask)
.Select(task => task as CloneRepoTask)
.ToList();
- if (repoConfigTasks.Count != 0)
- {
- listOfResources.Add(CreateWinGetInstallForGitPreReq());
- }
-
foreach (var repoConfigTask in repoConfigTasks)
{
- if (repoConfigTask.RepositoryToClone is GenericRepository genericRepository)
- {
- listOfResources.Add(CreateResourceFromTaskForGitDsc(repoConfigTask, genericRepository.RepoUri, configurationFileKind));
- }
+ try
+ {
+ if (!repoConfigTask.RepositoryToClone.IsPrivate)
+ {
+ listOfResources.Add(CreateResourceFromTaskForGitDsc(repoConfigTask, repoConfigTask.RepositoryToClone.RepoUri, configurationFileKind));
+ }
+ }
+ catch (Exception e)
+ {
+ _log.Error($"Error creating a repository resource entry", e);
+ }
}
return listOfResources;
@@ -143,7 +153,7 @@ private List GetResourcesForCloneTaskGroup(RepoConfigTaskG
private List GetResourcesForAppManagementTaskGroup(AppManagementTaskGroup appManagementGroup, ConfigurationFileKind configurationFileKind)
{
var listOfResources = new List();
- var installList = appManagementGroup.SetupTasks
+ var installList = appManagementGroup.DSCTasks
.Where(task => task is InstallPackageTask)
.Select(task => task as InstallPackageTask)
.ToList();
@@ -177,8 +187,16 @@ private WinGetConfigResource CreateResourceFromTaskForWinGetDsc(InstallPackageTa
{
Resource = DscHelpers.WinGetDscResource,
Id = id,
- Directives = new() { AllowPrerelease = true, Description = $"Installing {arguments.PackageId}" },
- Settings = new WinGetDscSettings() { Id = arguments.PackageId, Source = DscHelpers.DscSourceNameForWinGet },
+ Directives = new()
+ {
+ AllowPrerelease = true,
+ Description = $"Installing {arguments.PackageId}",
+ },
+ Settings = new WinGetDscSettings()
+ {
+ Id = arguments.PackageId,
+ Source = arguments.CatalogName,
+ },
};
}
@@ -190,16 +208,13 @@ private WinGetConfigResource CreateResourceFromTaskForWinGetDsc(InstallPackageTa
/// The WinGetConfigResource object that represents the block of yaml needed by GitDsc to clone the repository.
private WinGetConfigResource CreateResourceFromTaskForGitDsc(CloneRepoTask task, Uri webAddress, ConfigurationFileKind configurationFileKind)
{
- // For normal cases, the Id will be null. This can be changed in the future when a use case for this Dsc File builder is needed outside the setup
- // setup target flow. We can likely drop the if statement and just use whats in its body.
- string id = null;
+ // WinGet configure uses the Id property to uniquely identify a resource and also to display the resource status in the UI.
+ // So we add a description to the Id to make it more readable in the UI. These do not need to be localized.
+ var id = $"Clone {task.RepositoryName}: {task.CloneLocation.FullName}";
var gitDependsOnId = DscHelpers.GitWinGetPackageId;
if (configurationFileKind == ConfigurationFileKind.SetupTarget)
{
- // WinGet configure uses the Id property to uniquely identify a resource and also to display the resource status in the UI.
- // So we add a description to the Id to make it more readable in the UI. These do not need to be localized.
- id = $"Clone {task.RepositoryName}" + ": " + task.CloneLocation.FullName;
gitDependsOnId = $"{DscHelpers.GitWinGetPackageId} | Install: {DscHelpers.GitName}";
}
@@ -223,7 +238,7 @@ private WinGetConfigResource CreateWinGetInstallForGitPreReq()
return new WinGetConfigResource()
{
Resource = DscHelpers.WinGetDscResource,
- Id = $"{DscHelpers.GitWinGetPackageId} | Install: {DscHelpers.GitName}",
+ Id = DscHelpers.GitWinGetPackageId,
Directives = new() { AllowPrerelease = true, Description = $"Installing {DscHelpers.GitName}" },
Settings = new WinGetDscSettings() { Id = DscHelpers.GitWinGetPackageId, Source = DscHelpers.DscSourceNameForWinGet },
};
diff --git a/tools/SetupFlow/DevHome.SetupFlow/Services/PackageProvider.cs b/tools/SetupFlow/DevHome.SetupFlow/Services/PackageProvider.cs
index bb2193809..e4d6f44eb 100644
--- a/tools/SetupFlow/DevHome.SetupFlow/Services/PackageProvider.cs
+++ b/tools/SetupFlow/DevHome.SetupFlow/Services/PackageProvider.cs
@@ -73,7 +73,7 @@ private sealed class PackageCache
///
/// Occurs when a package selection has changed
///
- public event EventHandler PackageSelectionChanged;
+ public event EventHandler SelectedPackagesItemChanged;
public PackageProvider(PackageViewModelFactory packageViewModelFactory)
{
@@ -107,7 +107,7 @@ public PackageViewModel CreateOrGet(IWinGetPackage package, bool cachePermanentl
_log.Debug($"Creating view model for package [{package.Id}]");
var viewModel = _packageViewModelFactory(package);
viewModel.SelectionChanged += OnPackageSelectionChanged;
- viewModel.SelectionChanged += (sender, package) => PackageSelectionChanged?.Invoke(sender, package);
+ viewModel.VersionChanged += OnSelectedPackageVersionChanged;
// Cache if requested
if (cachePermanently)
@@ -122,10 +122,25 @@ public PackageViewModel CreateOrGet(IWinGetPackage package, bool cachePermanentl
return viewModel;
}
+ }
+
+ private void OnSelectedPackageVersionChanged(object sender, string version)
+ {
+ var packageViewModel = sender as PackageViewModel;
+ if (packageViewModel?.IsSelected == true)
+ {
+ // Notify subscribers that an item in the list of selected packages has changed
+ SelectedPackagesItemChanged?.Invoke(packageViewModel, EventArgs.Empty);
+ }
}
- public void OnPackageSelectionChanged(object sender, PackageViewModel packageViewModel)
- {
+ private void OnPackageSelectionChanged(object sender, bool isSelected)
+ {
+ if (sender is not PackageViewModel packageViewModel)
+ {
+ return;
+ }
+
lock (_lock)
{
if (packageViewModel.IsSelected)
@@ -154,12 +169,17 @@ public void OnPackageSelectionChanged(object sender, PackageViewModel packageVie
{
_log.Debug($"Removing package [{packageViewModel.Package.Id}] from cache");
_packageViewModelCache.Remove(packageViewModel.UniqueKey);
+ packageViewModel.SelectionChanged -= OnPackageSelectionChanged;
+ packageViewModel.VersionChanged -= OnSelectedPackageVersionChanged;
}
// Remove from the selected package collection
_selectedPackages.Remove(packageViewModel);
}
}
+
+ // Notify subscribers that an item in the list of selected packages has changed
+ SelectedPackagesItemChanged?.Invoke(packageViewModel, EventArgs.Empty);
}
///
diff --git a/tools/SetupFlow/DevHome.SetupFlow/Services/StringResourceKey.cs b/tools/SetupFlow/DevHome.SetupFlow/Services/StringResourceKey.cs
index 912253ccc..a1d3cb4f0 100644
--- a/tools/SetupFlow/DevHome.SetupFlow/Services/StringResourceKey.cs
+++ b/tools/SetupFlow/DevHome.SetupFlow/Services/StringResourceKey.cs
@@ -54,6 +54,7 @@ public static class StringResourceKey
public static readonly string EditClonePathDialog = nameof(EditClonePathDialog);
public static readonly string EditClonePathDialogUncheckCheckMark = nameof(EditClonePathDialogUncheckCheckMark);
public static readonly string FilePickerFileTypeOption = nameof(FilePickerFileTypeOption);
+ public static readonly string FilePickerSingleFileTypeOption = nameof(FilePickerSingleFileTypeOption);
public static readonly string FileTypeNotSupported = nameof(FileTypeNotSupported);
public static readonly string InstalledPackage = nameof(InstalledPackage);
public static readonly string InstalledPackageReboot = nameof(InstalledPackageReboot);
diff --git a/tools/SetupFlow/DevHome.SetupFlow/Strings/en-us/Resources.resw b/tools/SetupFlow/DevHome.SetupFlow/Strings/en-us/Resources.resw
index 5dddf68b4..2f00a48b7 100644
--- a/tools/SetupFlow/DevHome.SetupFlow/Strings/en-us/Resources.resw
+++ b/tools/SetupFlow/DevHome.SetupFlow/Strings/en-us/Resources.resw
@@ -323,7 +323,11 @@
{0} files
- Dropdown option for a file picker. {0} is replaced by a file type (e.g. JSON, YAML, etc ...)
+ {Locked="{0}"}Dropdown option for a file picker. {0} is replaced by a file type (e.g. JSON, YAML, etc ...)
+
+
+ {0} file
+ {Locked="{0}"}Dropdown option for a file picker. {0} is replaced by a file type (e.g. JSON, YAML, etc ...)File type not supported
@@ -522,7 +526,7 @@
Header text for a group of controls giving multiple choices for configuring the machine, but not a full setup flow
- Clone repositories and install applications at once
+ Clone repositories, install applications, and generate Winget Configuration files togetherBody text description for a card than when clicked takes the user to a multi-step flow for setting up their machine
@@ -561,6 +565,10 @@
Remove allLabel for removing all items from selection
+
+ Applications that have been previously installed cannot be installed again. They will be included in your generated configuration files.
+ Message displayed when a user selects an application that is already installed
+
RemoveText announced when screen readers focus on the 'Remove' button. The 'Remove' button allows users to remove an application from their cart
@@ -633,6 +641,14 @@
RestoreLabel for restore button
+
+ Generate Configuration file
+ Text for a generating configuration file button
+
+
+ Generate a WinGet Configuration file (.winget) to repeat this set up in the future or share it with others.
+ {Locked="WinGet",".winget"}Tooltip text about the generated configuration file
+
Set up detailsHeader for a section detailing the set up steps to be performed. "Set up" is the noun
diff --git a/tools/SetupFlow/DevHome.SetupFlow/Styles/AppManagement_ThemeResources.xaml b/tools/SetupFlow/DevHome.SetupFlow/Styles/AppManagement_ThemeResources.xaml
index 0a50176ff..d8072694f 100644
--- a/tools/SetupFlow/DevHome.SetupFlow/Styles/AppManagement_ThemeResources.xaml
+++ b/tools/SetupFlow/DevHome.SetupFlow/Styles/AppManagement_ThemeResources.xaml
@@ -40,7 +40,6 @@
x:Uid="ms-resource:///DevHome.SetupFlow/Resources/Installed"/>
-
@@ -50,7 +49,6 @@
-
@@ -93,7 +91,6 @@
SelectedItem="{Binding SelectedVersion, Mode=TwoWay}"
ItemsSource="{Binding AvailableVersions}" />
public IEnumerable SetupTasks => CloneTasks;
+ public IEnumerable DSCTasks => SetupTasks;
+
///
/// Gets all tasks that need to be ran.
///
diff --git a/tools/SetupFlow/DevHome.SetupFlow/TaskGroups/SetupTargetTaskGroup.cs b/tools/SetupFlow/DevHome.SetupFlow/TaskGroups/SetupTargetTaskGroup.cs
index df87d8420..4bd1bf848 100644
--- a/tools/SetupFlow/DevHome.SetupFlow/TaskGroups/SetupTargetTaskGroup.cs
+++ b/tools/SetupFlow/DevHome.SetupFlow/TaskGroups/SetupTargetTaskGroup.cs
@@ -45,6 +45,8 @@ public SetupTargetTaskGroup(
public IEnumerable SetupTasks => new List() { _setupTargetTaskGroup };
+ public IEnumerable DSCTasks => SetupTasks;
+
public SetupPageViewModelBase GetSetupPageViewModel() => _setupTargetViewModel;
public ReviewTabViewModelBase GetReviewTabViewModel() => _setupTargetReviewViewModel;
diff --git a/tools/SetupFlow/DevHome.SetupFlow/ViewModels/AppManagementViewModel.cs b/tools/SetupFlow/DevHome.SetupFlow/ViewModels/AppManagementViewModel.cs
index d9bee99c1..c19498b6b 100644
--- a/tools/SetupFlow/DevHome.SetupFlow/ViewModels/AppManagementViewModel.cs
+++ b/tools/SetupFlow/DevHome.SetupFlow/ViewModels/AppManagementViewModel.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
@@ -8,7 +9,6 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using DevHome.Common.Extensions;
-using DevHome.Common.Services;
using DevHome.SetupFlow.Services;
using Microsoft.Extensions.Hosting;
using Serilog;
@@ -21,9 +21,7 @@ public partial class AppManagementViewModel : SetupPageViewModelBase
private readonly ShimmerSearchViewModel _shimmerSearchViewModel;
private readonly SearchViewModel _searchViewModel;
private readonly PackageCatalogListViewModel _packageCatalogListViewModel;
- private readonly IWindowsPackageManager _wpm;
private readonly PackageProvider _packageProvider;
- private readonly IScreenReaderService _screenReaderService;
///
/// Current view to display in the main content control
@@ -31,6 +29,9 @@ public partial class AppManagementViewModel : SetupPageViewModelBase
[ObservableProperty]
private ObservableObject _currentView;
+ [ObservableProperty]
+ private bool _showInstalledPackageWarning;
+
public ReadOnlyObservableCollection SelectedPackages => _packageProvider.SelectedPackages;
public string ApplicationsAddedText => SelectedPackages.Count == 1 ?
@@ -43,19 +44,13 @@ public AppManagementViewModel(
ISetupFlowStringResource stringResource,
SetupFlowOrchestrator orchestrator,
IHost host,
- IWindowsPackageManager wpm,
PackageProvider packageProvider)
: base(stringResource, orchestrator)
{
- _wpm = wpm;
_packageProvider = packageProvider;
_searchViewModel = host.GetService();
_shimmerSearchViewModel = host.GetService();
_packageCatalogListViewModel = host.GetService();
- _screenReaderService = host.GetService();
-
- _packageProvider.PackageSelectionChanged += (_, _) => OnPropertyChanged(nameof(ApplicationsAddedText));
- _packageProvider.PackageSelectionChanged += (_, _) => OnPropertyChanged(nameof(EnableRemoveAll));
PageTitle = StringResource.GetLocalized(StringResourceKey.ApplicationsPageTitle);
@@ -110,4 +105,26 @@ private void RemoveAllPackages()
package.IsSelected = false;
}
}
+
+ [RelayCommand]
+ private void OnLoaded()
+ {
+ _packageProvider.SelectedPackagesItemChanged += OnPackageSelectionChanged;
+ }
+
+ [RelayCommand]
+ private void OnUnloaded()
+ {
+ _packageProvider.SelectedPackagesItemChanged -= OnPackageSelectionChanged;
+ }
+
+ private void OnPackageSelectionChanged(object sender, EventArgs args)
+ {
+ // Notify UI to update
+ OnPropertyChanged(nameof(ApplicationsAddedText));
+ OnPropertyChanged(nameof(EnableRemoveAll));
+
+ // Show warning if any selected package is installed
+ ShowInstalledPackageWarning = SelectedPackages.Any(p => !p.CanInstall);
+ }
}
diff --git a/tools/SetupFlow/DevHome.SetupFlow/ViewModels/PackageViewModel.cs b/tools/SetupFlow/DevHome.SetupFlow/ViewModels/PackageViewModel.cs
index b5159f4f5..6008195d6 100644
--- a/tools/SetupFlow/DevHome.SetupFlow/ViewModels/PackageViewModel.cs
+++ b/tools/SetupFlow/DevHome.SetupFlow/ViewModels/PackageViewModel.cs
@@ -3,16 +3,13 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
-using DevHome.Common.Extensions;
using DevHome.Common.Services;
using DevHome.Contracts.Services;
using DevHome.SetupFlow.Models;
using DevHome.SetupFlow.Services;
-using Microsoft.Extensions.Hosting;
using Microsoft.Internal.Windows.DevHome.Helpers.Restore;
using Microsoft.UI.Xaml.Media.Imaging;
using Windows.Storage.Streams;
@@ -39,19 +36,23 @@ public partial class PackageViewModel : ObservableObject
private readonly Lazy _packageDarkThemeIcon;
private readonly Lazy _packageLightThemeIcon;
- private readonly Lazy _installPackageTask;
private readonly ISetupFlowStringResource _stringResource;
private readonly IWinGetPackage _package;
private readonly IWindowsPackageManager _wpm;
private readonly IThemeSelectorService _themeSelector;
- private readonly IScreenReaderService _screenReaderService;
- private readonly SetupFlowOrchestrator _setupFlowOrchestrator;
+ private readonly IScreenReaderService _screenReaderService;
+ private readonly SetupFlowOrchestrator _orchestrator;
///
/// Occurs after the package selection changes
///
- public event EventHandler SelectionChanged;
+ public event EventHandler SelectionChanged;
+
+ ///
+ /// Occurs after the package version has changed
+ ///
+ public event EventHandler VersionChanged;
///
/// Indicates if a package is selected
@@ -63,11 +64,8 @@ public partial class PackageViewModel : ObservableObject
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(TooltipVersion))]
[NotifyPropertyChangedFor(nameof(PackageFullDescription))]
- [NotifyPropertyChangedFor(nameof(CanSelect))]
private string _selectedVersion;
- public bool CanSelect => IsSelectable();
-
public bool ShowVersionList => IsVersioningSupported();
public PackageViewModel(
@@ -76,22 +74,21 @@ public PackageViewModel(
IWinGetPackage package,
IThemeSelectorService themeSelector,
IScreenReaderService screenReaderService,
- IHost host,
SetupFlowOrchestrator orchestrator)
{
_stringResource = stringResource;
_wpm = wpm;
_package = package;
_themeSelector = themeSelector;
- _screenReaderService = screenReaderService;
- _setupFlowOrchestrator = orchestrator;
+ _screenReaderService = screenReaderService;
+ _orchestrator = orchestrator;
// Lazy-initialize optional or expensive view model members
_packageDarkThemeIcon = new Lazy(() => GetIconByTheme(RestoreApplicationIconTheme.Dark));
_packageLightThemeIcon = new Lazy(() => GetIconByTheme(RestoreApplicationIconTheme.Light));
- _installPackageTask = new Lazy(() => CreateInstallTask(host.GetService().ActivityId));
SelectedVersion = GetDefaultSelectedVersion();
+ InstallPackageTask = CreateInstallTask();
}
public PackageUniqueKey UniqueKey => _package.UniqueKey;
@@ -106,8 +103,7 @@ public PackageViewModel(
public IReadOnlyList AvailableVersions => _package.AvailableVersions;
- // When in setup target flow don't disable installed packaged.
- public bool IsInstalled => _setupFlowOrchestrator.IsSettingUpATargetMachine ? false : _package.IsInstalled;
+ public bool IsInstalled => _package.IsInstalled;
public string CatalogName => _package.CatalogName;
@@ -130,12 +126,14 @@ public PackageViewModel(
public string TooltipSource => _stringResource.GetLocalized(StringResourceKey.PackageSourceTooltip, CatalogName);
public string TooltipPublisher => _stringResource.GetLocalized(StringResourceKey.PackagePublisherNameTooltip, PublisherName);
+
+ public bool CanInstall => _orchestrator.IsSettingUpATargetMachine || !IsInstalled || _package.InstalledVersion != SelectedVersion;
public string ButtonAutomationName => IsSelected ?
_stringResource.GetLocalized(StringResourceKey.RemoveApplication) :
_stringResource.GetLocalized(StringResourceKey.AddApplication);
- public InstallPackageTask InstallPackageTask => _installPackageTask.Value;
+ public InstallPackageTask InstallPackageTask { get; private set; }
///
/// Gets the URI for the "Learn more" button
@@ -171,16 +169,14 @@ public Uri GetLearnMoreUri()
return new Uri("https://github.com/microsoft/winget-pkgs");
}
- partial void OnIsSelectedChanged(bool value) => SelectionChanged?.Invoke(null, this);
-
+ partial void OnIsSelectedChanged(bool value) => SelectionChanged?.Invoke(this, value);
+
partial void OnSelectedVersionChanged(string value)
{
- // If the selected version changed to a version that cannot be selected
- // (e.g. installed version) then unselect the package
- if (IsSelected && !IsSelectable())
- {
- IsSelected = false;
- }
+ // Update the install task with the new selected version
+ InstallPackageTask = CreateInstallTask();
+
+ VersionChanged?.Invoke(this, SelectedVersion);
}
///
@@ -225,9 +221,9 @@ private BitmapImage CreateBitmapImage(IRandomAccessStream stream)
return bitmapImage;
}
- private InstallPackageTask CreateInstallTask(Guid activityId)
+ private InstallPackageTask CreateInstallTask()
{
- return _package.CreateInstallTask(_wpm, _stringResource, SelectedVersion, activityId);
+ return _package.CreateInstallTask(_wpm, _stringResource, SelectedVersion, _orchestrator.ActivityId);
}
private string GetPackageShortDescription()
@@ -276,28 +272,6 @@ private bool IsVersioningSupported()
return !_wpm.IsMsStorePackage(_package);
}
- ///
- /// Checks if the package is selectable
- ///
- /// True if the package is selectable
- /// Allow selecting a different version to install if the package is installed
- private bool IsSelectable()
- {
- if (!IsInstalled)
- {
- return true;
- }
-
- if (!IsVersioningSupported())
- {
- return false;
- }
-
- var isValidSelectedVersion = AvailableVersions.Contains(SelectedVersion);
- var isNotInstalledVersion = SelectedVersion != InstalledVersion;
- return isValidSelectedVersion && isNotInstalledVersion;
- }
-
///
/// Get the default selected version
///
diff --git a/tools/SetupFlow/DevHome.SetupFlow/ViewModels/ReviewViewModel.cs b/tools/SetupFlow/DevHome.SetupFlow/ViewModels/ReviewViewModel.cs
index e9d36e972..7f9ef63fa 100644
--- a/tools/SetupFlow/DevHome.SetupFlow/ViewModels/ReviewViewModel.cs
+++ b/tools/SetupFlow/DevHome.SetupFlow/ViewModels/ReviewViewModel.cs
@@ -5,15 +5,18 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
+using DevHome.Common.Extensions;
+using DevHome.Common.Windows.FileDialog;
using DevHome.SetupFlow.Models;
using DevHome.SetupFlow.Services;
using DevHome.SetupFlow.TaskGroups;
-using Microsoft.Extensions.Hosting;
using Serilog;
+using WinUIEx;
namespace DevHome.SetupFlow.ViewModels;
@@ -21,9 +24,9 @@ public partial class ReviewViewModel : SetupPageViewModelBase
{
private readonly ILogger _log = Log.ForContext("SourceContext", nameof(ReviewViewModel));
- private readonly IHost _host;
-
private readonly SetupFlowOrchestrator _setupFlowOrchestrator;
+ private readonly ConfigurationFileBuilder _configFileBuilder;
+ private readonly WindowEx _mainWindow;
[ObservableProperty]
private IList _reviewTabs;
@@ -74,18 +77,21 @@ public bool CanSetupTarget
public bool HasTasksToSetUp => Orchestrator.TaskGroups.Any(g => g.SetupTasks.Any());
+ public bool HasDSCTasksToDownload => Orchestrator.TaskGroups.Any(g => g.DSCTasks.Any());
+
public ReviewViewModel(
ISetupFlowStringResource stringResource,
SetupFlowOrchestrator orchestrator,
- IHost host)
+ ConfigurationFileBuilder configFileBuilder,
+ WindowEx mainWindow)
: base(stringResource, orchestrator)
{
- _host = host;
-
NextPageButtonText = StringResource.GetLocalized(StringResourceKey.SetUpButton);
PageTitle = StringResource.GetLocalized(StringResourceKey.ReviewPageTitle);
_setupFlowOrchestrator = orchestrator;
+ _configFileBuilder = configFileBuilder;
+ _mainWindow = mainWindow;
}
protected async override Task OnEachNavigateToAsync()
@@ -138,5 +144,29 @@ private async Task OnSetUpAsync()
{
_log.Error($"Failed to initialize elevated process.", e);
}
- }
+ }
+
+ [RelayCommand(CanExecute = nameof(HasDSCTasksToDownload))]
+ private async Task DownloadConfigurationAsync()
+ {
+ try
+ {
+ // Show the save file dialog
+ using var fileDialog = new WindowSaveFileDialog();
+ fileDialog.AddFileType(StringResource.GetLocalized(StringResourceKey.FilePickerSingleFileTypeOption, "YAML"), ".winget");
+ fileDialog.AddFileType(StringResource.GetLocalized(StringResourceKey.FilePickerSingleFileTypeOption, "YAML"), ".dsc.yaml");
+ var fileName = fileDialog.Show(_mainWindow);
+
+ // If the user selected a file, write the configuration to it
+ if (!string.IsNullOrEmpty(fileName))
+ {
+ var configFile = _configFileBuilder.BuildConfigFileStringFromTaskGroups(Orchestrator.TaskGroups, ConfigurationFileKind.Normal);
+ await File.WriteAllTextAsync(fileName, configFile);
+ }
+ }
+ catch (Exception e)
+ {
+ _log.Error($"Failed to download configuration file.", e);
+ }
+ }
}
diff --git a/tools/SetupFlow/DevHome.SetupFlow/ViewModels/SummaryViewModel.cs b/tools/SetupFlow/DevHome.SetupFlow/ViewModels/SummaryViewModel.cs
index 4b552efce..884831843 100644
--- a/tools/SetupFlow/DevHome.SetupFlow/ViewModels/SummaryViewModel.cs
+++ b/tools/SetupFlow/DevHome.SetupFlow/ViewModels/SummaryViewModel.cs
@@ -100,7 +100,7 @@ public ObservableCollection AppsDownloaded
get
{
var packagesInstalled = new ObservableCollection();
- var packages = _packageProvider.SelectedPackages.Where(sp => sp.InstallPackageTask.WasInstallSuccessful == true).ToList();
+ var packages = _packageProvider.SelectedPackages.Where(sp => sp.CanInstall && sp.InstallPackageTask.WasInstallSuccessful).ToList();
packages.ForEach(p => packagesInstalled.Add(p));
var localizedHeader = (packagesInstalled.Count == 1) ? StringResourceKey.SummaryPageOneApplicationInstalled : StringResourceKey.SummaryPageAppsDownloadedCount;
ApplicationsClonedText = StringResource.GetLocalized(localizedHeader);
@@ -275,7 +275,7 @@ private async Task ReloadCatalogsAsync()
// After installing packages, reconnect to catalogs to
// reflect the latest changes when new Package COM objects are created
_log.Information($"Checking if a new catalog connections should be established");
- if (_packageProvider.SelectedPackages.Any(package => package.InstallPackageTask.WasInstallSuccessful))
+ if (_packageProvider.SelectedPackages.Any(package => package.CanInstall && package.InstallPackageTask.WasInstallSuccessful))
{
await _appManagementInitializer.ReinitializeAsync();
}
diff --git a/tools/SetupFlow/DevHome.SetupFlow/Views/AppManagementView.xaml b/tools/SetupFlow/DevHome.SetupFlow/Views/AppManagementView.xaml
index a87cb0358..f4552ef5c 100644
--- a/tools/SetupFlow/DevHome.SetupFlow/Views/AppManagementView.xaml
+++ b/tools/SetupFlow/DevHome.SetupFlow/Views/AppManagementView.xaml
@@ -28,6 +28,12 @@
+
+
+
+
+
+
@@ -38,7 +44,7 @@
-
@@ -55,7 +61,7 @@
HorizontalAlignment="Left"
Width="400"
Margin="0,0,0,20"
- x:Uid="ms-resource:///DevHome.SetupFlow/Resources/SearchBox">
+ x:Uid="SearchBox">
@@ -99,43 +105,59 @@
-
+
+
-
+
+
+
+
+
+
-
+
-
-
+
+
-
+
+
+
+
+
+
-
+
-
+
diff --git a/tools/SetupFlow/DevHome.SetupFlow/Views/PackageView.xaml b/tools/SetupFlow/DevHome.SetupFlow/Views/PackageView.xaml
index 4f36487af..210a2d76c 100644
--- a/tools/SetupFlow/DevHome.SetupFlow/Views/PackageView.xaml
+++ b/tools/SetupFlow/DevHome.SetupFlow/Views/PackageView.xaml
@@ -15,7 +15,6 @@
-
@@ -46,7 +45,6 @@
-
@@ -74,7 +72,6 @@