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

Commit 6da33e6

Browse files
committed
Implement closing/reopening a PR.
This state is not synced with the PR details pane or the PR list currently though.
1 parent 996aac3 commit 6da33e6

File tree

12 files changed

+151
-42
lines changed

12 files changed

+151
-42
lines changed

src/GitHub.App/SampleData/Documents/IssueishCommentThreadViewModelDesigner.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ public class IssueishCommentThreadViewModelDesigner : ViewModelBase, IIssueishCo
1414
public Task DeleteComment(ICommentViewModel comment) => Task.CompletedTask;
1515
public Task EditComment(ICommentViewModel comment) => Task.CompletedTask;
1616
public Task PostComment(ICommentViewModel comment) => Task.CompletedTask;
17-
public Task CloseIssueish(ICommentViewModel comment) => Task.CompletedTask;
17+
public Task CloseOrReopen(ICommentViewModel comment) => Task.CompletedTask;
1818
}
1919
}

src/GitHub.App/Services/IssueishService.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,55 @@
22
using System.Collections.Generic;
33
using System.Threading.Tasks;
44
using GitHub.Api;
5+
using GitHub.Factories;
56
using GitHub.Models;
67
using GitHub.Primitives;
8+
using Octokit;
79
using Octokit.GraphQL;
810
using Octokit.GraphQL.Model;
911
using static Octokit.GraphQL.Variable;
1012

1113
namespace GitHub.Services
1214
{
15+
/// <summary>
16+
/// Base class for issue and pull request services.
17+
/// </summary>
1318
public abstract class IssueishService : IIssueishService
1419
{
1520
static ICompiledQuery<CommentModel> postComment;
21+
readonly IApiClientFactory apiClientFactory;
1622
readonly IGraphQLClientFactory graphqlFactory;
1723

18-
public IssueishService(IGraphQLClientFactory graphqlFactory)
24+
/// <summary>
25+
/// Initializes a new instance of the <see cref="IssueishService"/> class.
26+
/// </summary>
27+
/// <param name="apiClientFactory">The API client factory.</param>
28+
/// <param name="graphqlFactory">The GraphQL client factory.</param>
29+
public IssueishService(
30+
IApiClientFactory apiClientFactory,
31+
IGraphQLClientFactory graphqlFactory)
1932
{
33+
this.apiClientFactory = apiClientFactory;
2034
this.graphqlFactory = graphqlFactory;
2135
}
2236

37+
/// <inheritdoc/>
38+
public async Task CloseIssueish(HostAddress address, string owner, string repository, int number)
39+
{
40+
var client = await apiClientFactory.CreateGitHubClient(address).ConfigureAwait(false);
41+
var update = new IssueUpdate { State = ItemState.Closed };
42+
await client.Issue.Update(owner, repository, number, update).ConfigureAwait(false);
43+
}
44+
45+
/// <inheritdoc/>
46+
public async Task ReopenIssueish(HostAddress address, string owner, string repository, int number)
47+
{
48+
var client = await apiClientFactory.CreateGitHubClient(address).ConfigureAwait(false);
49+
var update = new IssueUpdate { State = ItemState.Open };
50+
await client.Issue.Update(owner, repository, number, update).ConfigureAwait(false);
51+
}
52+
53+
/// <inheritdoc/>
2354
public async Task<CommentModel> PostComment(HostAddress address, string issueishId, string body)
2455
{
2556
var input = new AddCommentInput

src/GitHub.App/Services/PullRequestService.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using GitHub.Api;
1616
using GitHub.App.Services;
1717
using GitHub.Extensions;
18+
using GitHub.Factories;
1819
using GitHub.Logging;
1920
using GitHub.Models;
2021
using GitHub.Primitives;
@@ -63,10 +64,11 @@ public PullRequestService(
6364
IGitClient gitClient,
6465
IGitService gitService,
6566
IVSGitExt gitExt,
67+
IApiClientFactory apiClientFactory,
6668
IGraphQLClientFactory graphqlFactory,
6769
IOperatingSystem os,
6870
IUsageTracker usageTracker)
69-
: base(graphqlFactory)
71+
: base(apiClientFactory, graphqlFactory)
7072
{
7173
this.gitClient = gitClient;
7274
this.gitService = gitService;

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Reactive;
1+
using System;
2+
using System.Reactive;
23
using System.Threading.Tasks;
34
using GitHub.Models;
45
using ReactiveUI;
@@ -8,7 +9,7 @@ namespace GitHub.ViewModels.Documents
89
/// <summary>
910
/// View model for comments on an issue or pull request.
1011
/// </summary>
11-
public interface IIssueishCommentViewModel : ICommentViewModel
12+
public interface IIssueishCommentViewModel : ICommentViewModel, IDisposable
1213
{
1314
/// <summary>
1415
/// Gets a value indicating whether the comment will show a button for
@@ -36,16 +37,19 @@ public interface IIssueishCommentViewModel : ICommentViewModel
3637
/// <param name="isPullRequest">
3738
/// true if the comment is on a pull request, false if the comment is on an issue.
3839
/// </param>
39-
/// <param name="isOpen">Whether the issue or pull request is open.</param>
4040
/// <param name="canCloseOrReopen">
4141
/// Whether the user can close or reopen the pull request from this comment.
4242
/// </param>
43+
/// <param name="isOpen">
44+
/// An observable tracking whether the issue or pull request is open. Can be null if
45+
/// <paramref name="canCloseOrReopen"/> is false.
46+
/// </param>
4347
Task InitializeAsync(
4448
IIssueishCommentThreadViewModel thread,
4549
ActorModel currentUser,
4650
CommentModel comment,
4751
bool isPullRequest,
48-
bool isOpen,
49-
bool canCloseOrReopen);
52+
bool canCloseOrReopen,
53+
IObservable<bool> isOpen = null);
5054
}
5155
}

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

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ namespace GitHub.ViewModels.Documents
1414
/// </summary>
1515
[Export(typeof(IIssueishCommentViewModel))]
1616
[PartCreationPolicy(CreationPolicy.NonShared)]
17-
public class IssueishCommentViewModel : CommentViewModel, IIssueishCommentViewModel
17+
public sealed class IssueishCommentViewModel : CommentViewModel, IIssueishCommentViewModel
1818
{
19+
bool canCloseOrReopen;
1920
ObservableAsPropertyHelper<string> closeOrReopenCaption;
2021

2122
/// <summary>
@@ -29,10 +30,15 @@ public IssueishCommentViewModel(ICommentService commentService)
2930
CloseOrReopen = ReactiveCommand.CreateFromTask(
3031
DoCloseOrReopen,
3132
this.WhenAnyValue(x => x.CanCloseOrReopen));
33+
AddErrorHandler(CloseOrReopen);
3234
}
3335

3436
/// <inheritdoc/>
35-
public bool CanCloseOrReopen { get; private set; }
37+
public bool CanCloseOrReopen
38+
{
39+
get => canCloseOrReopen;
40+
private set => this.RaiseAndSetIfChanged(ref canCloseOrReopen, value);
41+
}
3642

3743
/// <inheritdoc/>
3844
public string CloseOrReopenCaption => closeOrReopenCaption?.Value;
@@ -46,8 +52,8 @@ public async Task InitializeAsync(
4652
ActorModel currentUser,
4753
CommentModel comment,
4854
bool isPullRequest,
49-
bool isOpen,
50-
bool canCloseOrReopen)
55+
bool canCloseOrReopen,
56+
IObservable<bool> isOpen = null)
5157
{
5258
await base.InitializeAsync(
5359
thread,
@@ -59,25 +65,39 @@ await base.InitializeAsync(
5965
CanCloseOrReopen = canCloseOrReopen;
6066
closeOrReopenCaption?.Dispose();
6167

62-
if (canCloseOrReopen)
68+
if (canCloseOrReopen && isOpen != null)
6369
{
64-
var caption = isPullRequest ?
65-
isOpen ?
66-
(Resources.ClosePullRequest, Resources.CloseAndComment) :
67-
(Resources.ReopenPullRequest, Resources.ReopenAndComment) :
68-
isOpen ?
69-
(Resources.CloseIssue, Resources.CloseAndComment) :
70-
(Resources.ReopenIssue, Resources.ReopenAndComment);
71-
72-
closeOrReopenCaption = this.WhenAnyValue(x => x.Body)
73-
.Select(x => string.IsNullOrWhiteSpace(x) ? caption.Item1 : caption.Item2)
70+
closeOrReopenCaption =
71+
this.WhenAnyValue(x => x.Body)
72+
.CombineLatest(isOpen, (body, open) => GetCloseOrReopenCaption(isPullRequest, open, body))
7473
.ToProperty(this, x => x.CloseOrReopenCaption);
7574
}
7675
}
7776

78-
Task DoCloseOrReopen()
77+
public void Dispose() => closeOrReopenCaption.Dispose();
78+
79+
async Task DoCloseOrReopen()
7980
{
80-
return Task.CompletedTask;
81+
await ((IIssueishCommentThreadViewModel)Thread).CloseOrReopen(this).ConfigureAwait(true);
82+
}
83+
84+
static string GetCloseOrReopenCaption(bool isPullRequest, bool isOpen, string body)
85+
{
86+
if (string.IsNullOrEmpty(body))
87+
{
88+
if (isPullRequest)
89+
{
90+
return isOpen ? Resources.ClosePullRequest : Resources.ReopenPullRequest;
91+
}
92+
else
93+
{
94+
return isOpen ? Resources.CloseIssue: Resources.ReopenIssue;
95+
}
96+
}
97+
else
98+
{
99+
return isOpen ? Resources.CloseAndComment : Resources.ReopenAndComment;
100+
}
81101
}
82102
}
83103
}

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

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class PullRequestPageViewModel : PullRequestViewModelBase, IPullRequestPa
2424
readonly IPullRequestSessionManager sessionManager;
2525
readonly ITeamExplorerServices teServices;
2626
ActorModel currentUserModel;
27-
ReactiveList<IViewModel> timeline;
27+
ReactiveList<IViewModel> timeline = new ReactiveList<IViewModel>();
2828

2929
/// <summary>
3030
/// Initializes a new instance of the <see cref="PullRequestPageViewModel"/> class.
@@ -47,6 +47,8 @@ public PullRequestPageViewModel(
4747
this.sessionManager = sessionManager;
4848
this.teServices = teServices;
4949

50+
timeline.ItemsRemoved.Subscribe(TimelineItemRemoved);
51+
5052
ShowCommit = ReactiveCommand.CreateFromTask<string>(DoShowCommit);
5153
}
5254

@@ -71,10 +73,10 @@ public async Task InitializeAsync(
7173
{
7274
await base.InitializeAsync(repository, localRepository, model).ConfigureAwait(true);
7375

76+
timeline.Clear();
7477
CommitCount = 0;
7578
currentUserModel = currentUser;
7679
CurrentUser = new ActorViewModel(currentUser);
77-
timeline = new ReactiveList<IViewModel>();
7880

7981
var commits = new List<CommitSummaryViewModel>();
8082

@@ -106,6 +108,31 @@ public async Task InitializeAsync(
106108
await AddPlaceholder().ConfigureAwait(true);
107109
}
108110

111+
/// <inheritdoc/>
112+
public async Task CloseOrReopen(ICommentViewModel comment)
113+
{
114+
var address = HostAddress.Create(Repository.CloneUrl);
115+
116+
if (State == PullRequestState.Open)
117+
{
118+
await service.CloseIssueish(
119+
address,
120+
Repository.Owner,
121+
Repository.Name,
122+
Number).ConfigureAwait(true);
123+
State = PullRequestState.Closed;
124+
}
125+
else
126+
{
127+
await service.ReopenIssueish(
128+
address,
129+
Repository.Owner,
130+
Repository.Name,
131+
Number).ConfigureAwait(true);
132+
State = PullRequestState.Open;
133+
}
134+
}
135+
109136
/// <inheritdoc/>
110137
public async Task PostComment(ICommentViewModel comment)
111138
{
@@ -126,11 +153,6 @@ Task ICommentThreadViewModel.EditComment(ICommentViewModel comment)
126153
throw new NotImplementedException();
127154
}
128155

129-
Task IIssueishCommentThreadViewModel.CloseIssueish(ICommentViewModel comment)
130-
{
131-
throw new NotImplementedException();
132-
}
133-
134156
async Task AddComment(CommentModel comment)
135157
{
136158
var vm = factory.CreateViewModel<IIssueishCommentViewModel>();
@@ -139,7 +161,6 @@ await vm.InitializeAsync(
139161
currentUserModel,
140162
comment,
141163
true,
142-
State == PullRequestState.Open,
143164
false).ConfigureAwait(true);
144165
timeline.Add(vm);
145166
}
@@ -152,8 +173,8 @@ await placeholder.InitializeAsync(
152173
currentUserModel,
153174
null,
154175
true,
155-
State == PullRequestState.Open,
156-
true).ConfigureAwait(true);
176+
true,
177+
this.WhenAnyValue(x => x.State, x => x == PullRequestState.Open)).ConfigureAwait(true);
157178
timeline.Add(placeholder);
158179
}
159180

@@ -162,5 +183,10 @@ async Task DoShowCommit(string oid)
162183
await service.FetchCommit(LocalRepository, Repository, oid).ConfigureAwait(true);
163184
teServices.ShowCommitDetails(oid);
164185
}
186+
187+
void TimelineItemRemoved(IViewModel item)
188+
{
189+
(item as IDisposable)?.Dispose();
190+
}
165191
}
166192
}

src/GitHub.App/ViewModels/PullRequestViewModelBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public PullRequestViewModelBase()
3232
public PullRequestState State
3333
{
3434
get => state;
35-
private set => this.RaiseAndSetIfChanged(ref state, value);
35+
protected set => this.RaiseAndSetIfChanged(ref state, value);
3636
}
3737

3838
public string SourceBranchDisplayName

src/GitHub.Exports.Reactive/Services/IIssueishService.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,24 @@ namespace GitHub.Services
1010
/// </summary>
1111
public interface IIssueishService
1212
{
13+
/// <summary>
14+
/// Closes an issue or pull request.
15+
/// </summary>
16+
/// <param name="address">The address of the server.</param>
17+
/// <param name="owner">The repository owner.</param>
18+
/// <param name="repository">The repository name.</param>
19+
/// <param name="number">The issue or pull request number.</param>
20+
Task CloseIssueish(HostAddress address, string owner, string repository, int number);
21+
22+
/// <summary>
23+
/// Reopens an issue or pull request.
24+
/// </summary>
25+
/// <param name="address">The address of the server.</param>
26+
/// <param name="owner">The repository owner.</param>
27+
/// <param name="repository">The repository name.</param>
28+
/// <param name="number">The issue or pull request number.</param>
29+
Task ReopenIssueish(HostAddress address, string owner, string repository, int number);
30+
1331
/// <summary>
1432
/// Posts an issue or pull request comment.
1533
/// </summary>

src/GitHub.Exports.Reactive/ViewModels/Documents/IIssueishCommentThreadViewModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ public interface IIssueishCommentThreadViewModel : ICommentThreadViewModel
1212
/// Called by a comment in the thread to close the issue or pull request.
1313
/// </summary>
1414
/// <param name="body">The comment requesting the close.</param>
15-
Task CloseIssueish(ICommentViewModel comment);
15+
Task CloseOrReopen(ICommentViewModel comment);
1616
}
1717
}

0 commit comments

Comments
 (0)