From 1fbadf0623152acb73d32e7eb26c63ebeebc9fa6 Mon Sep 17 00:00:00 2001 From: Davide Giacometti <25966642+davidegiacometti@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:35:56 +0200 Subject: [PATCH] Navigation from experimental feature to the related page (#3595) --- common/Models/ExperimentalFeature.cs | 19 ++++++++++++++- .../Views/ExperimentalFeaturesPage.xaml | 23 +++++++++++++++++-- src/Helpers/NavConfig.cs | 12 ++++++++++ src/NavConfig.jsonc | 22 ++++++++++++++---- src/Services/PageService.cs | 4 +++- .../ViewModels/MainPageViewModel.cs | 2 +- .../Views/MainPage.xaml.cs | 17 ++++++++++++++ .../ViewModels/MainPageViewModel.cs | 2 +- .../ViewModels/SetupFlowViewModel.cs | 13 ++++++++++- 9 files changed, 103 insertions(+), 11 deletions(-) diff --git a/common/Models/ExperimentalFeature.cs b/common/Models/ExperimentalFeature.cs index aeb3bc6f75..6ee56923a1 100644 --- a/common/Models/ExperimentalFeature.cs +++ b/common/Models/ExperimentalFeature.cs @@ -6,9 +6,11 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using DevHome.Common.Contracts; +using DevHome.Common.Extensions; using DevHome.Common.Services; using DevHome.Common.TelemetryEvents; using DevHome.Telemetry; +using Microsoft.UI.Xaml; namespace DevHome.Common.Models; @@ -21,6 +23,10 @@ public partial class ExperimentalFeature : ObservableObject public string Id { get; init; } + public string OpenPageKey { get; init; } + + public string OpenPageParameter { get; init; } + public bool NeedsFeaturePresenceCheck { get; init; } public bool IsVisible { get; init; } @@ -29,9 +35,11 @@ public partial class ExperimentalFeature : ObservableObject public static IQuickstartSetupService? QuickstartSetupService { get; set; } - public ExperimentalFeature(string id, bool enabledByDefault, bool needsFeaturePresenceCheck, bool visible = true) + public ExperimentalFeature(string id, bool enabledByDefault, bool needsFeaturePresenceCheck, string openPageKey, string openPageParameter, bool visible = true) { Id = id; + OpenPageKey = openPageKey; + OpenPageParameter = openPageParameter; _isEnabledByDefault = enabledByDefault; NeedsFeaturePresenceCheck = needsFeaturePresenceCheck; IsVisible = visible; @@ -88,4 +96,13 @@ public async Task OnToggledAsync() } } } + + [RelayCommand] + public void Open() + { + if (OpenPageKey != null) + { + Application.Current.GetService().NavigateTo(OpenPageKey, OpenPageParameter); + } + } } diff --git a/settings/DevHome.Settings/Views/ExperimentalFeaturesPage.xaml b/settings/DevHome.Settings/Views/ExperimentalFeaturesPage.xaml index e976c7165e..48eb1502e7 100644 --- a/settings/DevHome.Settings/Views/ExperimentalFeaturesPage.xaml +++ b/settings/DevHome.Settings/Views/ExperimentalFeaturesPage.xaml @@ -10,6 +10,7 @@ xmlns:views="using:DevHome.Common.Views" xmlns:models="using:DevHome.Common.Models" xmlns:behaviors="using:DevHome.Common.Behaviors" + xmlns:ui="using:CommunityToolkit.WinUI" behaviors:NavigationViewHeaderBehavior.HeaderTemplate="{StaticResource BreadcrumbBarDataTemplate}" behaviors:NavigationViewHeaderBehavior.HeaderContext="{x:Bind ViewModel}"> @@ -27,12 +28,30 @@ Header="{x:Bind Name, Mode=OneWay}" Description="{x:Bind Description, Mode=OneWay}" Margin="{ThemeResource SettingsCardMargin}" - Visibility="{x:Bind IsVisible, Mode=OneWay}"> - + Visibility="{x:Bind IsVisible, Mode=OneWay}" + IsClickEnabled="{x:Bind IsEnabled, Mode=OneWay}" + Command="{x:Bind OpenCommand}"> + + + + + + + diff --git a/src/Helpers/NavConfig.cs b/src/Helpers/NavConfig.cs index 59ba31bcee..bfebc2cdd1 100644 --- a/src/Helpers/NavConfig.cs +++ b/src/Helpers/NavConfig.cs @@ -68,6 +68,9 @@ internal sealed class ExperimentalFeatures [JsonPropertyName("buildTypeOverrides")] public BuildTypeOverrides[] BuildTypeOverrides { get; set; } + + [JsonPropertyName("openPage")] + public OpenPage OpenPage { get; set; } } internal sealed class BuildTypeOverrides @@ -82,6 +85,15 @@ internal sealed class BuildTypeOverrides public bool Visible { get; set; } } +internal sealed class OpenPage +{ + [JsonPropertyName("key")] + public string Key { get; set; } + + [JsonPropertyName("parameter")] + public string Parameter { get; set; } +} + // Uses .NET's JSON source generator support for serializing / deserializing NavConfig to get some perf gains at startup. [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(NavConfig))] diff --git a/src/NavConfig.jsonc b/src/NavConfig.jsonc index d4ac3d7159..ba6f52150e 100644 --- a/src/NavConfig.jsonc +++ b/src/NavConfig.jsonc @@ -66,7 +66,10 @@ "enabledByDefault": false, "visible": true } - ] + ], + "openPage": { + "key": "DevHome.Customization.ViewModels.MainPageViewModel" + } }, { "identity": "FileExplorerSourceControlIntegration", @@ -87,7 +90,11 @@ "enabledByDefault": false, "visible": false } - ] + ], + "openPage": { + "key": "DevHome.Customization.ViewModels.MainPageViewModel", + "parameter": "ShowFileExplorer" + } }, { "identity": "QuickstartPlayground", @@ -108,7 +115,11 @@ "enabledByDefault": false, "visible": true } - ] + ], + "openPage": { + "key": "DevHome.SetupFlow.ViewModels.SetupFlowViewModel", + "parameter": "StartQuickstartPlayground" + } }, { "identity": "ProjectIronsidesExperiment", @@ -129,7 +140,10 @@ "enabledByDefault": false, "visible": true } - ] + ], + "openPage": { + "key": "DevHome.Utilities.ViewModels.UtilitiesMainPageViewModel" + } } ] } diff --git a/src/Services/PageService.cs b/src/Services/PageService.cs index de52c4b427..fecad33543 100644 --- a/src/Services/PageService.cs +++ b/src/Services/PageService.cs @@ -57,6 +57,8 @@ where assembly.GetName().Name == tool.Assembly { var enabledByDefault = experimentalFeature.EnabledByDefault; var needsFeaturePresenceCheck = experimentalFeature.NeedsFeaturePresenceCheck; + var openPageKey = experimentalFeature.OpenPage.Key; + var openPageParameter = experimentalFeature.OpenPage.Parameter; var isVisible = true; foreach (var buildTypeOverride in experimentalFeature.BuildTypeOverrides ?? Array.Empty()) { @@ -68,7 +70,7 @@ where assembly.GetName().Name == tool.Assembly } } - experimentationService.AddExperimentalFeature(new ExperimentalFeature(experimentalFeature.Identity, enabledByDefault, needsFeaturePresenceCheck, isVisible)); + experimentationService.AddExperimentalFeature(new ExperimentalFeature(experimentalFeature.Identity, enabledByDefault, needsFeaturePresenceCheck, openPageKey, openPageParameter, isVisible)); } } diff --git a/tools/Customization/DevHome.Customization/ViewModels/MainPageViewModel.cs b/tools/Customization/DevHome.Customization/ViewModels/MainPageViewModel.cs index 21ab9c3923..26827a0977 100644 --- a/tools/Customization/DevHome.Customization/ViewModels/MainPageViewModel.cs +++ b/tools/Customization/DevHome.Customization/ViewModels/MainPageViewModel.cs @@ -61,7 +61,7 @@ private async Task LaunchWindowsDeveloperSettings() } [RelayCommand] - private void NavigateToFileExplorerPage() + public void NavigateToFileExplorerPage() { _navigationService.NavigateTo(typeof(FileExplorerViewModel).FullName!); } diff --git a/tools/Customization/DevHome.Customization/Views/MainPage.xaml.cs b/tools/Customization/DevHome.Customization/Views/MainPage.xaml.cs index 9900d419c3..da150141f1 100644 --- a/tools/Customization/DevHome.Customization/Views/MainPage.xaml.cs +++ b/tools/Customization/DevHome.Customization/Views/MainPage.xaml.cs @@ -1,10 +1,13 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; using DevHome.Common.Extensions; using DevHome.Common.Views; using DevHome.Customization.ViewModels; +using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Navigation; namespace DevHome.Customization.Views; @@ -20,4 +23,18 @@ public MainPage() ViewModel = Application.Current.GetService(); InitializeComponent(); } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + base.OnNavigatedTo(e); + + if (e.NavigationMode != NavigationMode.Back) + { + var parameter = e.Parameter?.ToString(); + if (parameter != null && parameter.Equals("ShowFileExplorer", StringComparison.OrdinalIgnoreCase)) + { + Application.Current.GetService().TryEnqueue(ViewModel.NavigateToFileExplorerPage); + } + } + } } diff --git a/tools/SetupFlow/DevHome.SetupFlow/ViewModels/MainPageViewModel.cs b/tools/SetupFlow/DevHome.SetupFlow/ViewModels/MainPageViewModel.cs index e204853b68..0a910dfd84 100644 --- a/tools/SetupFlow/DevHome.SetupFlow/ViewModels/MainPageViewModel.cs +++ b/tools/SetupFlow/DevHome.SetupFlow/ViewModels/MainPageViewModel.cs @@ -278,7 +278,7 @@ private void StartRepoConfig(string flowTitle) } [RelayCommand] - private void StartQuickstart(string flowTitle) + public void StartQuickstart(string flowTitle) { _log.Information("Starting flow for developer quickstart playground"); StartSetupFlowForTaskGroups(flowTitle, "DeveloperQuickstartPlayground", _host.GetService()); diff --git a/tools/SetupFlow/DevHome.SetupFlow/ViewModels/SetupFlowViewModel.cs b/tools/SetupFlow/DevHome.SetupFlow/ViewModels/SetupFlowViewModel.cs index 8d7e720b44..33bab8ad53 100644 --- a/tools/SetupFlow/DevHome.SetupFlow/ViewModels/SetupFlowViewModel.cs +++ b/tools/SetupFlow/DevHome.SetupFlow/ViewModels/SetupFlowViewModel.cs @@ -25,12 +25,13 @@ public partial class SetupFlowViewModel : ObservableObject { private readonly ILogger _log = Log.ForContext("SourceContext", nameof(SetupFlowViewModel)); private readonly IHost _host; + private readonly ISetupFlowStringResource _stringResource; private readonly MainPageViewModel _mainPageViewModel; private readonly PackageProvider _packageProvider; private readonly string _creationFlowNavigationParameter = "StartCreationFlow"; - private readonly string _configurationFlowNavigationParameter = "StartConfigurationFlow"; + private readonly string _quickstartNavigationParameter = "StartQuickstartPlayground"; public SetupFlowOrchestrator Orchestrator { get; } @@ -38,10 +39,12 @@ public partial class SetupFlowViewModel : ObservableObject public SetupFlowViewModel( IHost host, + ISetupFlowStringResource stringResource, SetupFlowOrchestrator orchestrator, PackageProvider packageProvider) { _host = host; + _stringResource = stringResource; Orchestrator = orchestrator; _packageProvider = packageProvider; @@ -158,6 +161,14 @@ public void OnNavigatedTo(NavigationEventArgs args) Cancel(); StartCreationFlow(originPage: parameters[1]); } + else if ((!string.IsNullOrEmpty(parameter)) && + parameter.Contains(_quickstartNavigationParameter, StringComparison.OrdinalIgnoreCase)) + { + Cancel(); + Orchestrator.FlowPages = [_mainPageViewModel]; + var flowTitle = _stringResource.GetLocalized("MainPage_QuickstartPlayground/Header"); + _mainPageViewModel.StartQuickstart(flowTitle); + } else if (args.Parameter is object[] configObjs && configObjs.Length == 3) { if (configObjs[0] is string configObj && configObj.Equals(_configurationFlowNavigationParameter, StringComparison.OrdinalIgnoreCase))