Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data storage (i.e. extension and registered root path) for version control extensions inside Dev Home #2984

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions DevHome.sln
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileExplorerGitIntegration.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevHome.FileExplorerSourceControlIntegration", "tools\Customization\DevHome.FileExplorerSourceControlIntegration\DevHome.FileExplorerSourceControlIntegration.csproj", "{1237EEB8-F063-45E5-BD7C-534591C38464}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevHome.FileExplorerSourceControlIntegrationUnitTest", "tools\Customization\DevHome.FileExplorerSourceControlIntegrationUnitTest\DevHome.FileExplorerSourceControlIntegrationUnitTest.csproj", "{53759D96-E4DA-4478-B0E9-9FFBAA12ECA9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|arm64 = Debug|arm64
Expand Down Expand Up @@ -742,6 +744,18 @@ Global
{1237EEB8-F063-45E5-BD7C-534591C38464}.Release|x64.Build.0 = Release|x64
{1237EEB8-F063-45E5-BD7C-534591C38464}.Release|x86.ActiveCfg = Release|x86
{1237EEB8-F063-45E5-BD7C-534591C38464}.Release|x86.Build.0 = Release|x86
{53759D96-E4DA-4478-B0E9-9FFBAA12ECA9}.Debug|arm64.ActiveCfg = Debug|ARM64
{53759D96-E4DA-4478-B0E9-9FFBAA12ECA9}.Debug|arm64.Build.0 = Debug|ARM64
{53759D96-E4DA-4478-B0E9-9FFBAA12ECA9}.Debug|x64.ActiveCfg = Debug|x64
{53759D96-E4DA-4478-B0E9-9FFBAA12ECA9}.Debug|x64.Build.0 = Debug|x64
{53759D96-E4DA-4478-B0E9-9FFBAA12ECA9}.Debug|x86.ActiveCfg = Debug|x86
{53759D96-E4DA-4478-B0E9-9FFBAA12ECA9}.Debug|x86.Build.0 = Debug|x86
{53759D96-E4DA-4478-B0E9-9FFBAA12ECA9}.Release|arm64.ActiveCfg = Release|ARM64
{53759D96-E4DA-4478-B0E9-9FFBAA12ECA9}.Release|arm64.Build.0 = Release|ARM64
{53759D96-E4DA-4478-B0E9-9FFBAA12ECA9}.Release|x64.ActiveCfg = Release|x64
{53759D96-E4DA-4478-B0E9-9FFBAA12ECA9}.Release|x64.Build.0 = Release|x64
{53759D96-E4DA-4478-B0E9-9FFBAA12ECA9}.Release|x86.ActiveCfg = Release|x86
{53759D96-E4DA-4478-B0E9-9FFBAA12ECA9}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -800,6 +814,7 @@ Global
{58B09B74-90D2-4333-A3C2-F13C31376416} = {D42E6F59-E107-4F74-AEB9-BA0DC8A7ABD9}
{0E31B8E2-D36B-46A1-B0B4-498301159CE1} = {D42E6F59-E107-4F74-AEB9-BA0DC8A7ABD9}
{1237EEB8-F063-45E5-BD7C-534591C38464} = {623998FD-B0A6-4980-95D5-A5072301CA10}
{53759D96-E4DA-4478-B0E9-9FFBAA12ECA9} = {623998FD-B0A6-4980-95D5-A5072301CA10}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {030B5641-B206-46BB-BF71-36FF009088FA}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<ProjectReference Include="..\..\QuietBackgroundProcesses\DevHome.QuietBackgroundProcesses.UI\DevHome.QuietBackgroundProcesses.UI.csproj" />
<ProjectReference Include="..\..\SetupFlow\DevHome.SetupFlow.Common\DevHome.SetupFlow.Common.csproj" />
<ProjectReference Include="..\..\SetupFlow\DevHome.SetupFlow\DevHome.SetupFlow.csproj" />
<ProjectReference Include="..\DevHome.FileExplorerSourceControlIntegration\DevHome.FileExplorerSourceControlIntegration.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,4 +297,16 @@
<value>Windows developer settings</value>
<comment>The header for the Windows developer settings card</comment>
</data>
<data name="EnhanceRepository.Text" xml:space="preserve">
<value>Enhance a repository</value>
<comment>The title for adding a repository for source control integration</comment>
</data>
<data name="RootPathTextBox.Header" xml:space="preserve">
<value>Enter the root folder path:</value>
<comment>The header for adding a root path for source control integration</comment>
</data>
<data name="ErrorRootPathValidation.Title" xml:space="preserve">
<value>The root path provided is incorrect or is a UNC path. Please check for invalid characters and if the drive exists on the machine.</value>
<comment>The error message for root path validation</comment>
</data>
</root>
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using CommunityToolkit.Mvvm.ComponentModel;
using DevHome.Common.Extensions;
using DevHome.Common.Models;
using DevHome.Common.Services;
using DevHome.Customization.Models;
using DevHome.Customization.TelemetryEvents;
using DevHome.FileExplorerSourceControlIntegration.Services;
using Microsoft.Internal.Windows.DevHome.Helpers;
using Microsoft.UI.Xaml;

namespace DevHome.Customization.ViewModels;

Expand All @@ -17,6 +23,10 @@ public partial class FileExplorerViewModel : ObservableObject

public ObservableCollection<Breadcrumb> Breadcrumbs { get; }

public ObservableCollection<string> TrackedRepositories { get; } = new();

private RepositoryTracking RepoTracker { get; set; } = new(null);

public FileExplorerViewModel()
{
_shellSettings = new ShellSettings();
Expand All @@ -27,6 +37,27 @@ public FileExplorerViewModel()
new(stringResource.GetLocalized("MainPage_Header"), typeof(MainPageViewModel).FullName!),
new(stringResource.GetLocalized("FileExplorer_Header"), typeof(FileExplorerViewModel).FullName!)
];
RefreshTrackedRepositories();
}

public void RefreshTrackedRepositories()
{
var experimentationService = Application.Current.GetService<IExperimentationService>();
if (experimentationService.IsFeatureEnabled("FileExplorerSourceControlIntegration"))
{
TrackedRepositories.Clear();
var repoCollection = RepoTracker.GetAllTrackedRepositories();
foreach (KeyValuePair<string, string> data in repoCollection)
{
TrackedRepositories.Add(data.Key);
}
}
}

public void AddRepositoryPath(string extension, string rootPath)
{
var normalizedPath = rootPath.ToUpper(CultureInfo.InvariantCulture).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
RepoTracker.AddRepositoryPath(extension, normalizedPath);
}

public bool ShowFileExtensions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,42 @@
behaviors:NavigationViewHeaderBehavior.HeaderTemplate="{StaticResource BreadcrumbBarDataTemplate}"
behaviors:NavigationViewHeaderBehavior.HeaderContext="{x:Bind ViewModel}">

<Grid MaxWidth="{ThemeResource MaxPageContentWidth}" Margin="{ThemeResource ContentPageMargin}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ScrollViewer VerticalAlignment="Top" VerticalScrollBarVisibility="Auto">
<Grid MaxWidth="{ThemeResource MaxPageContentWidth}" Margin="{ThemeResource ContentPageMargin}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- This is initial experimental UI. The UI will be updated in future and is temporary -->
<StackPanel x:Name="TrackRepository" Visibility="Collapsed">
<views:FileExplorerView />
<TextBlock x:Uid="EnhanceRepository" Margin="0,20,0,0"/>
<StackPanel Orientation="Horizontal" Margin="0,20,0,20">
<TextBox x:Name="RootPathTextBox"
x:Uid="RootPathTextBox"
PlaceholderText="Path"
MinWidth="300"
HorizontalAlignment="Left"/>

<ScrollView Grid.Row="2" VerticalAlignment="Top">
<views:FileExplorerView />
</ScrollView>
</Grid>
<Button Content="Add"
Click="AddButton_Click"
Margin="10, 27, 0, 0"/>
<InfoBar x:Name="RootPathErrorBar"
x:Uid="ErrorRootPathValidation"
Title="Error"
Margin="10, 10, 10, 0"
IsOpen="False"
Severity="Error">
</InfoBar>
</StackPanel>
<StackPanel x:Name="DisplayTrackRepository" Visibility="Collapsed">
<TextBlock Text="Git" Style="{ThemeResource SettingsSectionHeaderTextBlockStyle}"/>
<ItemsRepeater ItemsSource="{x:Bind ViewModel.TrackedRepositories}"
HorizontalAlignment="Stretch"
VerticalAlignment="Center" />
</StackPanel>
</StackPanel>
</Grid>
</ScrollViewer>
</Page>
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.IO;
using DevHome.Common.Extensions;
using DevHome.Common.Services;
using DevHome.Customization.ViewModels;
using DevHome.FileExplorerSourceControlIntegration.Services;
using FileExplorerSourceControlIntegration;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Serilog;

namespace DevHome.Customization.Views;

Expand All @@ -15,9 +21,65 @@ public FileExplorerViewModel ViewModel
get;
}

private string? RootPath
{
get; set;
}

private static readonly System.Buffers.SearchValues<char> InvalidChars = System.Buffers.SearchValues.Create("<>*?|.");

private readonly Serilog.ILogger log = Log.ForContext("SourceContext", nameof(FileExplorerPage));

public FileExplorerPage()
{
ViewModel = Application.Current.GetService<FileExplorerViewModel>();
this.InitializeComponent();
var experimentationService = Application.Current.GetService<IExperimentationService>();
if (experimentationService.IsFeatureEnabled("FileExplorerSourceControlIntegration"))
{
TrackRepository.Visibility = Visibility.Visible;
DisplayTrackRepository.Visibility = Visibility.Visible;
}
}

public void AddButton_Click(object sender, RoutedEventArgs e)
{
RootPath = RootPathTextBox.Text;

if (ValidateRepositoryPath(RootPath))
{
// TODO: Determine if the extension GUID should be stored instead of name as it is more identifiable and/or determine any other unique property to use for
// mapping here
ViewModel.AddRepositoryPath("git", RootPath);
}

ViewModel.RefreshTrackedRepositories();
}

public bool ValidateRepositoryPath(string rootPath)
{
if (!Path.IsPathFullyQualified(rootPath))
{
log.Warning("Path is not fully qualified or maybe a UNC path.");
RootPathErrorBar.IsOpen = true;
return false;
}

if (rootPath.IndexOfAny(Path.GetInvalidPathChars()) != -1 ||
rootPath.AsSpan().IndexOfAny(InvalidChars) != -1)
{
log.Warning("Path contains invalid chars");
RootPathErrorBar.IsOpen = true;
return false;
}

if (!Array.Exists(Environment.GetLogicalDrives(), d => d.Equals(Path.GetPathRoot(rootPath), StringComparison.OrdinalIgnoreCase)))
{
log.Warning("Drive provided does not exist on users machine");
RootPathErrorBar.IsOpen = true;
return false;
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DevHome.FileExplorerSourceControlIntegration.Services;

public partial class RepoStoreOptions
{
private const string RepoStoreFileNameDefault = "TrackedRepositoryStore.json";

public string RepoStoreFileName { get; set; } = RepoStoreFileNameDefault;

private readonly string repoStoreFolderPathDefault = Path.Combine(Path.GetTempPath(), "FileExplorerSourceControlIntegration");

private string? repoStoreFolderPath;

public string RepoStoreFolderPath
{
get => repoStoreFolderPath is null ? repoStoreFolderPathDefault : repoStoreFolderPath;
set => repoStoreFolderPath = string.IsNullOrEmpty(value) ? repoStoreFolderPathDefault : value;
}
}
Loading
Loading