Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit d246ad2

Browse files
committed
Make PR conversation tool windows multi-instance.
1 parent a8d4da6 commit d246ad2

File tree

7 files changed

+89
-19
lines changed

7 files changed

+89
-19
lines changed

src/GitHub.App/ViewModels/Documents/IssueishPaneViewModel.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ public IViewModel Content
3636
private set => this.RaiseAndSetIfChanged(ref content, value);
3737
}
3838

39+
public bool IsInitialized => content != null;
40+
3941
public string PaneCaption
4042
{
4143
get => paneCaption;

src/GitHub.Exports/Services/IGitHubToolWindowManager.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Runtime.InteropServices;
44
using GitHub.ViewModels.GitHubPane;
55
using GitHub.ViewModels.Documents;
6+
using GitHub.Primitives;
67

78
namespace GitHub.Services
89
{
@@ -20,9 +21,19 @@ public interface IGitHubToolWindowManager
2021
Task<IGitHubPaneViewModel> ShowGitHubPane();
2122

2223
/// <summary>
23-
/// Opens a new issue or pull request document pane.
24+
/// Shows a document-like tool window pane for an issue or pull request.
2425
/// </summary>
25-
/// <returns>>The view model for the document pane.</returns>
26-
Task<IIssueishPaneViewModel> OpenIssueishDocumentPane();
26+
/// <param name="address">
27+
/// The host address of the server that hosts the issue or pull request.
28+
/// </param>
29+
/// <param name="owner">The repository owner.</param>
30+
/// <param name="repository">The repository name.</param>
31+
/// <param name="number">The issue or pull request number.</param>
32+
/// <returns>The view model for the document pane.</returns>
33+
Task<IIssueishPaneViewModel> ShowIssueishDocumentPane(
34+
HostAddress address,
35+
string owner,
36+
string repository,
37+
int number);
2738
}
2839
}

src/GitHub.Exports/ViewModels/Documents/IIssueishPaneViewModel.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ public interface IIssueishPaneViewModel : IPaneViewModel
1414
/// </summary>
1515
IViewModel Content { get; }
1616

17+
/// <summary>
18+
/// Gets a value indicating whether <see cref="Load(IConnection, string, string, int)"/>
19+
/// has been called on the view model.
20+
/// </summary>
21+
bool IsInitialized { get; }
22+
1723
/// <summary>
1824
/// Loads an issue or pull request into the view model.
1925
/// </summary>

src/GitHub.VisualStudio/Commands/OpenIssueishDocumentCommand.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,13 @@ public override async Task Execute(OpenIssueishParams p)
4747
try
4848
{
4949
var m = serviceProvider.GetService<IGitHubToolWindowManager>();
50-
var vm = await m.OpenIssueishDocumentPane();
51-
var connection = await connectionManager.GetConnection(p.Address);
52-
await vm.Load(connection, p.Owner, p.Repository, p.Number);
50+
var vm = await m.ShowIssueishDocumentPane(p.Address, p.Owner, p.Repository, p.Number);
51+
52+
if (!vm.IsInitialized)
53+
{
54+
var connection = await connectionManager.GetConnection(p.Address);
55+
await vm.Load(connection, p.Owner, p.Repository, p.Number);
56+
}
5357
}
5458
catch (Exception ex)
5559
{

src/GitHub.VisualStudio/GitHubPackage.cs

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
using System;
2-
using System.Threading;
3-
using System.Threading.Tasks;
4-
using System.ComponentModel.Design;
2+
using System.Collections.Generic;
53
using System.ComponentModel.Composition;
4+
using System.ComponentModel.Design;
65
using System.Runtime.InteropServices;
6+
using System.Threading;
7+
using System.Threading.Tasks;
78
using GitHub.Api;
89
using GitHub.Commands;
9-
using GitHub.Info;
1010
using GitHub.Exports;
11+
using GitHub.Info;
1112
using GitHub.Logging;
13+
using GitHub.Primitives;
1214
using GitHub.Services;
13-
using GitHub.Settings;
14-
using GitHub.VisualStudio.Helpers;
15-
using GitHub.VisualStudio.Commands;
1615
using GitHub.Services.Vssdk.Commands;
16+
using GitHub.Settings;
17+
using GitHub.ViewModels.Documents;
1718
using GitHub.ViewModels.GitHubPane;
19+
using GitHub.VisualStudio.Commands;
20+
using GitHub.VisualStudio.Helpers;
1821
using GitHub.VisualStudio.Settings;
1922
using GitHub.VisualStudio.UI;
2023
using Microsoft.VisualStudio;
@@ -23,7 +26,7 @@
2326
using Microsoft.VisualStudio.Shell.Interop;
2427
using Serilog;
2528
using Task = System.Threading.Tasks.Task;
26-
using GitHub.ViewModels.Documents;
29+
using static System.FormattableString;
2730

2831
namespace GitHub.VisualStudio
2932
{
@@ -162,7 +165,7 @@ public ServiceProviderExports([Import(typeof(SVsServiceProvider))] IServiceProvi
162165
[ProvideService(typeof(IUsageService), IsAsyncQueryable = true)]
163166
[ProvideService(typeof(IVSGitExt), IsAsyncQueryable = true)]
164167
[ProvideService(typeof(IGitHubToolWindowManager))]
165-
[ProvideToolWindow(typeof(IssueishDocumentPane), DocumentLikeTool = true)]
168+
[ProvideToolWindow(typeof(IssueishDocumentPane), DocumentLikeTool = true, MultiInstances = true)]
166169
[Guid(ServiceProviderPackageId)]
167170
public sealed class ServiceProviderPackage : AsyncPackage, IServiceProviderPackage, IGitHubToolWindowManager
168171
{
@@ -217,10 +220,43 @@ public async Task<IGitHubPaneViewModel> ShowGitHubPane()
217220
return await gitHubPane.GetViewModelAsync();
218221
}
219222

220-
public async Task<IIssueishPaneViewModel> OpenIssueishDocumentPane()
223+
public async Task<IIssueishPaneViewModel> ShowIssueishDocumentPane(
224+
HostAddress address,
225+
string owner,
226+
string repository,
227+
int number)
228+
{
229+
var id = Invariant($"{address.WebUri}|{owner}/{repository}#{number}");
230+
var pane = GetOrCreateToolWindow<IssueishDocumentPane>(id);
231+
232+
if (pane != null && pane.Frame is IVsWindowFrame frame)
233+
{
234+
ErrorHandler.ThrowOnFailure(frame.Show());
235+
return await pane.GetViewModelAsync();
236+
}
237+
238+
return null;
239+
}
240+
241+
T GetOrCreateToolWindow<T>(string id) where T : AsyncPaneBase
221242
{
222-
var pane = (IssueishDocumentPane)ShowToolWindow(new Guid(IssueishDocumentPane.IssueishDocumentPaneGuid));
223-
return await pane.GetViewModelAsync();
243+
for (var i = 0; i < int.MaxValue; ++i)
244+
{
245+
var result = (T)FindToolWindow(typeof(T), i, false);
246+
247+
if (result != null && result.Id == id)
248+
{
249+
return result;
250+
}
251+
else if (result == null)
252+
{
253+
result = (T)FindToolWindow(typeof(T), i, true);
254+
result.Id = id;
255+
return result;
256+
}
257+
}
258+
259+
return null;
224260
}
225261

226262
static ToolWindowPane ShowToolWindow(Guid windowGuid)

src/GitHub.VisualStudio/UI/AsyncPaneBase.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,15 @@
1212

1313
namespace GitHub.VisualStudio.UI
1414
{
15-
public class AsyncPaneBase<TViewModel> : ToolWindowPane
15+
public class AsyncPaneBase : ToolWindowPane
16+
{
17+
/// <summary>
18+
/// Gets or sets an ID string that identifies the content of the pane.
19+
/// </summary>
20+
public string Id { get; set; }
21+
}
22+
23+
public class AsyncPaneBase<TViewModel> : AsyncPaneBase
1624
where TViewModel : IPaneViewModel
1725
{
1826
readonly ContentPresenter contentPresenter;

src/GitHub.VisualStudio/UI/IssueishDocumentPane.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ namespace GitHub.VisualStudio.UI
1111
[Guid(IssueishDocumentPaneGuid)]
1212
public class IssueishDocumentPane : AsyncPaneBase<IIssueishPaneViewModel>
1313
{
14+
/// <summary>
15+
/// The guid of the document pane.
16+
/// </summary>
1417
public const string IssueishDocumentPaneGuid = "9506846C-4CEC-4DDA-87E7-A99CDCD4E35B";
1518
}
1619
}

0 commit comments

Comments
 (0)