diff --git a/src/GitHub.Exports/Settings/Guids.cs b/src/GitHub.Exports/Settings/Guids.cs index 2e225c7038..81562cdaff 100644 --- a/src/GitHub.Exports/Settings/Guids.cs +++ b/src/GitHub.Exports/Settings/Guids.cs @@ -13,6 +13,7 @@ public static class Guids public const string StartPagePackageId = "3b764d23-faf7-486f-94c7-b3accc44a70e"; public const string CodeContainerProviderId = "6CE146CB-EF57-4F2C-A93F-5BA685317660"; public const string InlineReviewsPackageId = "248325BE-4A2D-4111-B122-E7D59BF73A35"; + public const string PullRequestStatusPackageId = "5121BEC6-1088-4553-8453-0DDC7C8E2238"; public const string TeamExplorerWelcomeMessage = "C529627F-8AA6-4FDB-82EB-4BFB7DB753C3"; public const string LoginManagerId = "7BA2071A-790A-4F95-BE4A-0EEAA5928AAF"; diff --git a/src/GitHub.InlineReviews/GitHub.InlineReviews.csproj b/src/GitHub.InlineReviews/GitHub.InlineReviews.csproj index d7e0f3d91f..ced6cf829e 100644 --- a/src/GitHub.InlineReviews/GitHub.InlineReviews.csproj +++ b/src/GitHub.InlineReviews/GitHub.InlineReviews.csproj @@ -88,10 +88,13 @@ + + + @@ -124,6 +127,7 @@ + DiffCommentThreadView.xaml @@ -161,6 +165,9 @@ CommentView.xaml + + PullRequestStatusView.xaml + @@ -447,6 +454,10 @@ MSBuild:Compile Designer + + Designer + MSBuild:Compile + diff --git a/src/GitHub.InlineReviews/InlineReviewsPackage.cs b/src/GitHub.InlineReviews/InlineReviewsPackage.cs index 9271fbd299..5d06a63d87 100644 --- a/src/GitHub.InlineReviews/InlineReviewsPackage.cs +++ b/src/GitHub.InlineReviews/InlineReviewsPackage.cs @@ -12,7 +12,7 @@ namespace GitHub.InlineReviews [Guid(Guids.InlineReviewsPackageId)] [ProvideAutoLoad(UIContextGuids80.SolutionExists)] [ProvideMenuResource("Menus.ctmenu", 1)] - [ProvideToolWindow(typeof(PullRequestCommentsPane), DocumentLikeTool=true)] + [ProvideToolWindow(typeof(PullRequestCommentsPane), DocumentLikeTool = true)] public class InlineReviewsPackage : Package { protected override void Initialize() diff --git a/src/GitHub.InlineReviews/PullRequestStatusPackage.cs b/src/GitHub.InlineReviews/PullRequestStatusPackage.cs new file mode 100644 index 0000000000..bf9caf9075 --- /dev/null +++ b/src/GitHub.InlineReviews/PullRequestStatusPackage.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.ComponentModelHost; +using GitHub.VisualStudio; +using GitHub.InlineReviews.Services; + +namespace GitHub.InlineReviews +{ + [PackageRegistration(UseManagedResourcesOnly = true)] + [Guid(Guids.PullRequestStatusPackageId)] + [ProvideAutoLoad(UIContextGuids80.SolutionExists)] + public class PullRequestStatusPackage : Package + { + protected override void Initialize() + { + var componentModel = (IComponentModel)GetService(typeof(SComponentModel)); + var exportProvider = componentModel.DefaultExportProvider; + var pullRequestStatusManager = exportProvider.GetExportedValue(); + pullRequestStatusManager.Initialize(); + } + } +} diff --git a/src/GitHub.InlineReviews/Services/IPullRequestStatusManager.cs b/src/GitHub.InlineReviews/Services/IPullRequestStatusManager.cs new file mode 100644 index 0000000000..d355b68312 --- /dev/null +++ b/src/GitHub.InlineReviews/Services/IPullRequestStatusManager.cs @@ -0,0 +1,7 @@ +namespace GitHub.InlineReviews.Services +{ + public interface IPullRequestStatusManager + { + void Initialize(); + } +} diff --git a/src/GitHub.InlineReviews/Services/PullRequestStatusManager.cs b/src/GitHub.InlineReviews/Services/PullRequestStatusManager.cs new file mode 100644 index 0000000000..d2d3f118a9 --- /dev/null +++ b/src/GitHub.InlineReviews/Services/PullRequestStatusManager.cs @@ -0,0 +1,149 @@ +using System; +using System.Windows; +using System.Windows.Input; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.ComponentModel; +using System.ComponentModel.Composition; +using GitHub.InlineReviews.Views; +using GitHub.InlineReviews.ViewModels; +using GitHub.Services; +using GitHub.VisualStudio; +using Microsoft.VisualStudio.Shell; +using GitHub.Models; +using GitHub.Logging; +using Serilog; + +namespace GitHub.InlineReviews.Services +{ + [Export(typeof(IPullRequestStatusManager))] + public class PullRequestStatusManager : IPullRequestStatusManager + { + static readonly ILogger log = LogManager.ForContext(); + const string StatusBarPartName = "PART_SccStatusBarHost"; + + readonly SVsServiceProvider serviceProvider; + readonly Window mainWindow; + readonly IPullRequestSessionManager pullRequestSessionManager; + + [ImportingConstructor] + public PullRequestStatusManager(SVsServiceProvider serviceProvider, IPullRequestSessionManager pullRequestSessionManager) + : this() + { + this.serviceProvider = serviceProvider; + this.pullRequestSessionManager = pullRequestSessionManager; + } + + public PullRequestStatusManager() + { + mainWindow = Application.Current.MainWindow; + } + + public void Initialize() + { + RefreshCurrentSession(); + pullRequestSessionManager.PropertyChanged += PullRequestSessionManager_PropertyChanged; + } + + void PullRequestSessionManager_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(PullRequestSessionManager.CurrentSession)) + { + RefreshCurrentSession(); + } + } + + void RefreshCurrentSession() + { + var pullRequest = pullRequestSessionManager.CurrentSession?.PullRequest; + var viewModel = pullRequest != null ? CreatePullRequestStatusViewModel(serviceProvider, pullRequest) : null; + ShowStatus(viewModel); + } + + static PullRequestStatusViewModel CreatePullRequestStatusViewModel(IServiceProvider serviceProvider, IPullRequestModel pullRequest) + { + var command = new RaiseVsCommand(serviceProvider, Guids.guidGitHubCmdSetString, PkgCmdIDList.showCurrentPullRequestCommand); + var pullRequestStatusViewModel = new PullRequestStatusViewModel(command); + pullRequestStatusViewModel.Number = pullRequest.Number; + pullRequestStatusViewModel.Title = pullRequest.Title; + return pullRequestStatusViewModel; + } + + void ShowStatus(PullRequestStatusViewModel pullRequestStatusViewModel = null) + { + var statusBar = FindSccStatusBar(); + if (statusBar != null) + { + var githubStatusBar = Find(statusBar); + if (githubStatusBar != null) + { + // Replace to ensure status shows up. + statusBar.Items.Remove(githubStatusBar); + } + + if (pullRequestStatusViewModel != null) + { + githubStatusBar = new PullRequestStatusView { DataContext = pullRequestStatusViewModel }; + statusBar.Items.Insert(0, githubStatusBar); + } + } + } + + static T Find(StatusBar statusBar) + { + foreach (var item in statusBar.Items) + { + if (item is T) + { + return (T)item; + } + } + + return default(T); + } + + StatusBar FindSccStatusBar() + { + var contentControl = mainWindow?.Template?.FindName(StatusBarPartName, mainWindow) as ContentControl; + return contentControl?.Content as StatusBar; + } + + class RaiseVsCommand : ICommand + { + readonly IServiceProvider serviceProvider; + readonly string guid; + readonly int id; + + internal RaiseVsCommand(IServiceProvider serviceProvider, string guid, int id) + { + this.serviceProvider = serviceProvider; + this.guid = guid; + this.id = id; + } + + public bool CanExecute(object parameter) => true; + + public void Execute(object parameter) + { + try + { + var dte = serviceProvider.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE; + object customIn = null; + object customOut = null; + dte.Commands.Raise(guid, id, ref customIn, ref customOut); + } + catch (Exception e) + { + log.Error(e, "Couldn't raise {Guid}:{ID}", guid, id); + System.Diagnostics.Trace.WriteLine(e); + } + } + + public event EventHandler CanExecuteChanged + { + add { } + remove { } + } + } + } +} diff --git a/src/GitHub.InlineReviews/ViewModels/PullRequestStatusViewModel.cs b/src/GitHub.InlineReviews/ViewModels/PullRequestStatusViewModel.cs new file mode 100644 index 0000000000..ee7b1e285e --- /dev/null +++ b/src/GitHub.InlineReviews/ViewModels/PullRequestStatusViewModel.cs @@ -0,0 +1,47 @@ +using System; +using System.Windows.Input; +using System.ComponentModel; + +namespace GitHub.InlineReviews.ViewModels +{ + public class PullRequestStatusViewModel : INotifyPropertyChanged + { + int? number; + string title; + + public PullRequestStatusViewModel(ICommand showCurrentPullRequestCommand) + { + ShowCurrentPullRequestCommand = showCurrentPullRequestCommand; + } + + public int? Number + { + get { return number; } + set + { + if (number != value) + { + number = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Number))); + } + } + } + + public string Title + { + get { return title; } + set + { + if (title != value) + { + title = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Title))); + } + } + } + + public ICommand ShowCurrentPullRequestCommand { get; } + + public event PropertyChangedEventHandler PropertyChanged; + } +} diff --git a/src/GitHub.InlineReviews/Views/PullRequestStatusView.xaml b/src/GitHub.InlineReviews/Views/PullRequestStatusView.xaml new file mode 100644 index 0000000000..98d48646f0 --- /dev/null +++ b/src/GitHub.InlineReviews/Views/PullRequestStatusView.xaml @@ -0,0 +1,18 @@ + + + + + + + + + diff --git a/src/GitHub.InlineReviews/Views/PullRequestStatusView.xaml.cs b/src/GitHub.InlineReviews/Views/PullRequestStatusView.xaml.cs new file mode 100644 index 0000000000..535830924a --- /dev/null +++ b/src/GitHub.InlineReviews/Views/PullRequestStatusView.xaml.cs @@ -0,0 +1,13 @@ +using System; +using System.Windows.Controls; + +namespace GitHub.InlineReviews.Views +{ + public partial class PullRequestStatusView : UserControl + { + public PullRequestStatusView() + { + InitializeComponent(); + } + } +}