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

Save drafts of comments #1994

Merged
merged 21 commits into from Oct 29, 2018
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions codecov.yml
Expand Up @@ -32,3 +32,4 @@ ignore:
- "*.xaml"
- "*.xaml.cs"
- "**/SampleData/*"
- "src/GitHub.App/sqlite-net/*"
15 changes: 15 additions & 0 deletions src/GitHub.App/Models/Drafts/CommentDraft.cs
@@ -0,0 +1,15 @@
using GitHub.ViewModels;

namespace GitHub.Models.Drafts
{
/// <summary>
/// Stores a draft for a <see cref="CommentViewModel"/>
/// </summary>
public class CommentDraft
{
/// <summary>
/// Gets or sets the draft comment body.
/// </summary>
public string Body { get; set; }
}
}
20 changes: 20 additions & 0 deletions src/GitHub.App/Models/Drafts/PullRequestDraft.cs
@@ -0,0 +1,20 @@
using GitHub.ViewModels.GitHubPane;

namespace GitHub.Models.Drafts
{
/// <summary>
/// Stores a draft for a <see cref="PullRequestCreationViewModel"/>.
/// </summary>
public class PullRequestDraft
{
/// <summary>
/// Gets or sets the draft pull request title.
/// </summary>
public string Title { get; set; }

/// <summary>
/// Gets or sets the draft pull request body.
/// </summary>
public string Body { get; set; }
}
}
21 changes: 21 additions & 0 deletions src/GitHub.App/Models/Drafts/PullRequestReviewCommentDraft.cs
@@ -0,0 +1,21 @@
using System;
using GitHub.ViewModels;

namespace GitHub.Models.Drafts
{
/// <summary>
/// Stores a draft for a <see cref="PullRequestReviewCommentViewModel"/>
/// </summary>
public class PullRequestReviewCommentDraft : CommentDraft
{
/// <summary>
/// Gets or sets the side of the diff that the draft comment was left on.
/// </summary>
public DiffSide Side { get; set; }

/// <summary>
/// Gets or sets the time that the draft was last updated.
/// </summary>
public DateTimeOffset UpdatedAt { get; set; }
}
}
15 changes: 15 additions & 0 deletions src/GitHub.App/Models/Drafts/PullRequestReviewDraft.cs
@@ -0,0 +1,15 @@
using GitHub.ViewModels.GitHubPane;

namespace GitHub.Models.Drafts
{
/// <summary>
/// Stores a draft for a <see cref="PullRequestReviewAuthoringViewModel"/>.
/// </summary>
public class PullRequestReviewDraft
{
/// <summary>
/// Gets or sets the draft review body.
/// </summary>
public string Body { get; set; }
}
}
6 changes: 3 additions & 3 deletions src/GitHub.App/SampleData/CommentThreadViewModelDesigner.cs
Expand Up @@ -14,8 +14,8 @@ public class CommentThreadViewModelDesigner : ViewModelBase, ICommentThreadViewM
public IActorViewModel CurrentUser { get; set; }
= new ActorViewModel { Login = "shana" };

public Task DeleteComment(int pullRequestId, int commentId) => Task.CompletedTask;
public Task EditComment(string id, string body) => Task.CompletedTask;
public Task PostComment(string body) => Task.CompletedTask;
public Task DeleteComment(ICommentViewModel comment) => Task.CompletedTask;
public Task EditComment(ICommentViewModel comment) => Task.CompletedTask;
public Task PostComment(ICommentViewModel comment) => Task.CompletedTask;
}
}
Expand Up @@ -2,30 +2,25 @@
using System.ComponentModel.Composition;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using GitHub.Api;
using GitHub.Extensions;
using GitHub.Factories;
using GitHub.InlineReviews.Peek;
using GitHub.InlineReviews.Tags;
using GitHub.Models;
using GitHub.Primitives;
using GitHub.Services;
using GitHub.ViewModels;
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Differencing;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Outlining;
using Microsoft.VisualStudio.Text.Projection;

namespace GitHub.InlineReviews.Services
namespace GitHub.Services
{
/// <summary>
/// Shows inline comments in a peek view.
/// </summary>
[Export(typeof(IInlineCommentPeekService))]
class InlineCommentPeekService : IInlineCommentPeekService
{
const string relationship = "GitHubCodeReview";
readonly IOutliningManagerService outliningService;
readonly IPeekBroker peekBroker;
readonly IUsageTracker usageTracker;
Expand Down Expand Up @@ -90,69 +85,46 @@ public void Hide(ITextView textView)
}

/// <inheritdoc/>
public ITrackingPoint Show(ITextView textView, AddInlineCommentTag tag)
public ITrackingPoint Show(ITextView textView, DiffSide side, int lineNumber)
{
Guard.ArgumentNotNull(tag, nameof(tag));

var lineAndtrackingPoint = GetLineAndTrackingPoint(textView, tag);
var lineAndtrackingPoint = GetLineAndTrackingPoint(textView, side, lineNumber);
var line = lineAndtrackingPoint.Item1;
var trackingPoint = lineAndtrackingPoint.Item2;
var options = new PeekSessionCreationOptions(
textView,
InlineCommentPeekRelationship.Instance.Name,
relationship,
trackingPoint,
defaultHeight: 0);

ExpandCollapsedRegions(textView, line.Extent);

var session = peekBroker.TriggerPeekSession(options);
var item = session.PeekableItems.OfType<InlineCommentPeekableItem>().FirstOrDefault();
item?.ViewModel.Close.Take(1).Subscribe(_ => session.Dismiss());

return trackingPoint;
}

/// <inheritdoc/>
public ITrackingPoint Show(ITextView textView, ShowInlineCommentTag tag)
{
Guard.ArgumentNotNull(textView, nameof(textView));
Guard.ArgumentNotNull(tag, nameof(tag));

var lineAndtrackingPoint = GetLineAndTrackingPoint(textView, tag);
var line = lineAndtrackingPoint.Item1;
var trackingPoint = lineAndtrackingPoint.Item2;
var options = new PeekSessionCreationOptions(
textView,
InlineCommentPeekRelationship.Instance.Name,
trackingPoint,
defaultHeight: 0);

ExpandCollapsedRegions(textView, line.Extent);

var session = peekBroker.TriggerPeekSession(options);
var item = session.PeekableItems.OfType<InlineCommentPeekableItem>().FirstOrDefault();
item?.ViewModel.Close.Take(1).Subscribe(_ => session.Dismiss());

var item = session.PeekableItems.OfType<IClosable>().FirstOrDefault();
item?.Closed.Take(1).Subscribe(_ => session.Dismiss());

return trackingPoint;
}

Tuple<ITextSnapshotLine, ITrackingPoint> GetLineAndTrackingPoint(ITextView textView, InlineCommentTag tag)
Tuple<ITextSnapshotLine, ITrackingPoint> GetLineAndTrackingPoint(
ITextView textView,
DiffSide side,
int lineNumber)
{
var diffModel = (textView as IWpfTextView)?.TextViewModel as IDifferenceTextViewModel;
var snapshot = textView.TextSnapshot;

if (diffModel?.ViewType == DifferenceViewType.InlineView)
{
snapshot = tag.DiffChangeType == DiffChangeType.Delete ?
snapshot = side == DiffSide.Left ?
diffModel.Viewer.DifferenceBuffer.LeftBuffer.CurrentSnapshot :
diffModel.Viewer.DifferenceBuffer.RightBuffer.CurrentSnapshot;
}

var line = snapshot.GetLineFromLineNumber(tag.LineNumber);
var line = snapshot.GetLineFromLineNumber(lineNumber);
var trackingPoint = snapshot.CreateTrackingPoint(line.Start.Position, PointTrackingMode.Positive);

ExpandCollapsedRegions(textView, line.Extent);
peekBroker.TriggerPeekSession(textView, trackingPoint, InlineCommentPeekRelationship.Instance.Name);
peekBroker.TriggerPeekSession(textView, trackingPoint, relationship);

usageTracker.IncrementCounter(x => x.NumberOfPRReviewDiffViewInlineCommentOpen).Forget();

Expand Down
53 changes: 50 additions & 3 deletions src/GitHub.App/Services/PullRequestEditorService.cs
Expand Up @@ -5,9 +5,12 @@
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using EnvDTE;
using GitHub.Commands;
using GitHub.Extensions;
using GitHub.Models;
using GitHub.Models.Drafts;
using GitHub.ViewModels;
using GitHub.VisualStudio;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Editor;
Expand All @@ -18,7 +21,6 @@
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Projection;
using Microsoft.VisualStudio.TextManager.Interop;
using EnvDTE;
using Task = System.Threading.Tasks.Task;

namespace GitHub.Services
Expand All @@ -39,6 +41,8 @@ public class PullRequestEditorService : IPullRequestEditorService
readonly IStatusBarNotificationService statusBar;
readonly IGoToSolutionOrPullRequestFileCommand goToSolutionOrPullRequestFileCommand;
readonly IEditorOptionsFactoryService editorOptionsFactoryService;
readonly IMessageDraftStore draftStore;
readonly IInlineCommentPeekService peekService;
readonly IUsageTracker usageTracker;

[ImportingConstructor]
Expand All @@ -49,6 +53,8 @@ public class PullRequestEditorService : IPullRequestEditorService
IStatusBarNotificationService statusBar,
IGoToSolutionOrPullRequestFileCommand goToSolutionOrPullRequestFileCommand,
IEditorOptionsFactoryService editorOptionsFactoryService,
IMessageDraftStore draftStore,
IInlineCommentPeekService peekService,
IUsageTracker usageTracker)
{
Guard.ArgumentNotNull(serviceProvider, nameof(serviceProvider));
Expand All @@ -58,13 +64,17 @@ public class PullRequestEditorService : IPullRequestEditorService
Guard.ArgumentNotNull(goToSolutionOrPullRequestFileCommand, nameof(goToSolutionOrPullRequestFileCommand));
Guard.ArgumentNotNull(goToSolutionOrPullRequestFileCommand, nameof(editorOptionsFactoryService));
Guard.ArgumentNotNull(usageTracker, nameof(usageTracker));
Guard.ArgumentNotNull(peekService, nameof(peekService));
Guard.ArgumentNotNull(draftStore, nameof(draftStore));

this.serviceProvider = serviceProvider;
this.pullRequestService = pullRequestService;
this.vsEditorAdaptersFactory = vsEditorAdaptersFactory;
this.statusBar = statusBar;
this.goToSolutionOrPullRequestFileCommand = goToSolutionOrPullRequestFileCommand;
this.editorOptionsFactoryService = editorOptionsFactoryService;
this.draftStore = draftStore;
this.peekService = peekService;
this.usageTracker = usageTracker;
}

Expand Down Expand Up @@ -129,7 +139,7 @@ public class PullRequestEditorService : IPullRequestEditorService
}

/// <inheritdoc/>
public async Task<IDifferenceViewer> OpenDiff(IPullRequestSession session, string relativePath, string headSha, bool scrollToFirstDiff)
public async Task<IDifferenceViewer> OpenDiff(IPullRequestSession session, string relativePath, string headSha, bool scrollToFirstDraftOrDiff)
{
Guard.ArgumentNotNull(session, nameof(session));
Guard.ArgumentNotEmptyString(relativePath, nameof(relativePath));
Expand Down Expand Up @@ -168,12 +178,37 @@ public async Task<IDifferenceViewer> OpenDiff(IPullRequestSession session, strin
var caption = $"Diff - {Path.GetFileName(file.RelativePath)}";
var options = __VSDIFFSERVICEOPTIONS.VSDIFFOPT_DetectBinaryFiles |
__VSDIFFSERVICEOPTIONS.VSDIFFOPT_LeftFileIsTemporary;
var openThread = (line: -1, side: DiffSide.Left);
var scrollToFirstDiff = false;

if (!workingDirectory)
{
options |= __VSDIFFSERVICEOPTIONS.VSDIFFOPT_RightFileIsTemporary;
}

if (scrollToFirstDraftOrDiff)
{
var (key, _) = PullRequestReviewCommentThreadViewModel.GetDraftKeys(
session.LocalRepository.CloneUrl.WithOwner(session.RepositoryOwner),
session.PullRequest.Number,
relativePath,
0);
var drafts = (await draftStore.GetDrafts<PullRequestReviewCommentDraft>(key)
.ConfigureAwait(true))
.OrderByDescending(x => x.data.UpdatedAt)
.ToList();

if (drafts.Count > 0 && int.TryParse(drafts[0].secondaryKey, out var line))
{
openThread = (line, drafts[0].data.Side);
scrollToFirstDiff = false;
}
else
{
scrollToFirstDiff = true;
}
}

IVsWindowFrame frame;
using (OpenWithOption(DifferenceViewerOptions.ScrollToFirstDiffName, scrollToFirstDiff))
using (OpenInProvisionalTab())
Expand Down Expand Up @@ -228,6 +263,18 @@ public async Task<IDifferenceViewer> OpenDiff(IPullRequestSession session, strin
else
await usageTracker.IncrementCounter(x => x.NumberOfPRDetailsViewChanges);

if (openThread.line != -1)
{
var view = diffViewer.ViewMode == DifferenceViewMode.Inline ?
diffViewer.InlineView :
openThread.side == DiffSide.Left ? diffViewer.LeftView : diffViewer.RightView;

// HACK: We need to wait here for the view to initialize or the peek session won't appear.
// There must be a better way of doing this.
await Task.Delay(1500).ConfigureAwait(true);
peekService.Show(view, openThread.side, openThread.line);
}

return diffViewer;
}
catch (Exception e)
Expand All @@ -247,7 +294,7 @@ public async Task<IDifferenceViewer> OpenDiff(IPullRequestSession session, strin
Guard.ArgumentNotEmptyString(relativePath, nameof(relativePath));
Guard.ArgumentNotNull(thread, nameof(thread));

var diffViewer = await OpenDiff(session, relativePath, thread.CommitSha, scrollToFirstDiff: false);
var diffViewer = await OpenDiff(session, relativePath, thread.CommitSha, scrollToFirstDraftOrDiff: false);

var param = (object)new InlineCommentNavigationParams
{
Expand Down