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

Comment Reaction API #74930

Merged
merged 1 commit into from
Jun 10, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/vs/vscode.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9054,6 +9054,31 @@ declare module 'vscode' {
iconPath?: Uri;
}

/**
* Reactions of a [comment](#Comment)
*/
export interface CommentReaction {
/**
* The human-readable label for the reaction
*/
readonly label: string;

/**
* Icon for the reaction shown in UI.
*/
readonly iconPath: string | Uri;

/**
* The number of users who have reacted to this reaction
*/
readonly count: number;

/**
* Whether the [author](CommentAuthorInformation) of the comment has reacted to this reaction
*/
readonly authorHasReacted: boolean;
}

/**
* A comment is displayed within the editor or the Comments Panel, depending on how it is provided.
*/
Expand Down Expand Up @@ -9093,6 +9118,11 @@ declare module 'vscode' {
*/
contextValue?: string;

/**
* Optional reactions of the [comment](#Comment)
*/
reactions?: CommentReaction[];

/**
* Optional label describing the [Comment](#Comment)
* Label will be rendered next to authorName if exists.
Expand Down Expand Up @@ -9157,6 +9187,11 @@ declare module 'vscode' {
*/
createCommentThread(uri: Uri, range: Range, comments: Comment[]): CommentThread;

/**
* Optional reaction handler for creating and deleting reactions on a [comment](#Comment).
*/
reactionHandler?: (comment: Comment, reaction: CommentReaction) => Promise<void>;

/**
* Dispose this comment controller.
*
Expand Down
3 changes: 0 additions & 3 deletions src/vs/vscode.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -851,9 +851,6 @@ declare module 'vscode' {
* Stay in proposed.
*/
interface CommentReaction {
readonly label?: string;
readonly iconPath?: string | Uri;
count?: number;
readonly hasReacted?: boolean;
}

Expand Down
3 changes: 3 additions & 0 deletions src/vs/workbench/api/browser/mainThreadComments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,9 @@ export class MainThreadCommentController {
private readonly _threads: Map<number, MainThreadCommentThread> = new Map<number, MainThreadCommentThread>();
public activeCommentThread?: MainThreadCommentThread;

get features(): CommentProviderFeatures {
return this._features;
}

constructor(
private readonly _proxy: ExtHostCommentsShape,
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export interface CommentProviderFeatures {
finishDraftLabel?: string;
reactionGroup?: modes.CommentReaction[];
commentThreadTemplate?: CommentThreadTemplate;
reactionHandler?: boolean;
}

export interface MainThreadCommentsShape extends IDisposable {
Expand Down
50 changes: 39 additions & 11 deletions src/vs/workbench/api/common/extHostComments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ export class ExtHostComments implements ExtHostCommentsShape {
const document = this._documents.getDocument(URI.revive(uri));
const commentController = this._commentControllers.get(commentControllerHandle);

if (!commentController || !commentController.reactionProvider || !commentController.reactionProvider.toggleReaction) {
if (!commentController || !((commentController.reactionProvider && commentController.reactionProvider.toggleReaction) || commentController.reactionHandler)) {
return Promise.resolve(undefined);
}

Expand All @@ -219,9 +219,16 @@ export class ExtHostComments implements ExtHostCommentsShape {
if (commentThread) {
const vscodeComment = commentThread.getComment(comment.commentId);

if (commentController !== undefined && commentController.reactionProvider && commentController.reactionProvider.toggleReaction && vscodeComment) {
return commentController.reactionProvider.toggleReaction(document, vscodeComment, convertFromReaction(reaction));
if (commentController !== undefined && vscodeComment) {
if (commentController.reactionHandler) {
return commentController.reactionHandler(vscodeComment, convertFromReaction(reaction));
}

if (commentController.reactionProvider && commentController.reactionProvider.toggleReaction) {
return commentController.reactionProvider.toggleReaction(document, vscodeComment, convertFromReaction(reaction));
}
}

}

return Promise.resolve(undefined);
Expand Down Expand Up @@ -635,7 +642,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
}

getComment(commentId: string): vscode.Comment | undefined {
const comments = this._comments.filter(comment => comment.commentId === commentId);
const comments = this._comments.filter(comment => (comment.commentId === commentId || comment.id === commentId));

if (comments && comments.length) {
return comments[0];
Expand Down Expand Up @@ -707,6 +714,9 @@ export class ExtHostCommentInputBox implements vscode.CommentInputBox {
this._value = input;
}
}

type ReactionHandler = (comment: vscode.Comment, reaction: vscode.CommentReaction) => Promise<void>;

class ExtHostCommentController implements vscode.CommentController {
get id(): string {
return this._id;
Expand Down Expand Up @@ -740,6 +750,18 @@ class ExtHostCommentController implements vscode.CommentController {
}
}

private _reactionHandler?: ReactionHandler;

get reactionHandler(): ReactionHandler | undefined {
return this._reactionHandler;
}

set reactionHandler(handler: ReactionHandler | undefined) {
this._reactionHandler = handler;

this._proxy.$updateCommentControllerFeatures(this.handle, { reactionHandler: !!handler });
}

constructor(
_extension: IExtensionDescription,
private _handle: number,
Expand Down Expand Up @@ -861,9 +883,12 @@ function convertFromComment(comment: modes.Comment): vscode.Comment {
isDraft: comment.isDraft,
commentReactions: comment.commentReactions ? comment.commentReactions.map(reaction => {
return {
label: reaction.label,
count: reaction.count,
hasReacted: reaction.hasReacted
label: reaction.label || '',
count: reaction.count || 0,
iconPath: reaction.iconPath ? URI.revive(reaction.iconPath) : '',
hasReacted: reaction.hasReacted,
authorHasReacted: reaction.hasReacted || false

};
}) : undefined,
mode: comment.mode ? comment.mode : modes.CommentMode.Preview
Expand All @@ -878,6 +903,7 @@ function convertToModeComment2(thread: ExtHostCommentThread, commentController:
}

const iconPath = vscodeComment.author && vscodeComment.author.iconPath ? vscodeComment.author.iconPath.toString() : undefined;
const reactions = vscodeComment.reactions || vscodeComment.commentReactions;

return {
commentId: vscodeComment.id || vscodeComment.commentId,
Expand All @@ -892,7 +918,7 @@ function convertToModeComment2(thread: ExtHostCommentThread, commentController:
editCommand: vscodeComment.editCommand ? commandsConverter.toInternal(vscodeComment.editCommand) : undefined,
deleteCommand: vscodeComment.deleteCommand ? commandsConverter.toInternal(vscodeComment.deleteCommand) : undefined,
label: vscodeComment.label,
commentReactions: vscodeComment.commentReactions ? vscodeComment.commentReactions.map(reaction => convertToReaction2(commentController.reactionProvider, reaction)) : undefined
commentReactions: reactions ? reactions.map(reaction => convertToReaction2(commentController.reactionProvider, reaction)) : undefined
};
}

Expand Down Expand Up @@ -939,9 +965,11 @@ function convertToReaction2(provider: vscode.CommentReactionProvider | undefined

function convertFromReaction(reaction: modes.CommentReaction): vscode.CommentReaction {
return {
label: reaction.label,
count: reaction.count,
hasReacted: reaction.hasReacted
label: reaction.label || '',
count: reaction.count || 0,
iconPath: reaction.iconPath ? URI.revive(reaction.iconPath) : '',
hasReacted: reaction.hasReacted,
authorHasReacted: reaction.hasReacted || false
};
}

Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/browser/web.simpleservices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,7 @@ export class SimpleCommentService implements ICommentService {
addReaction: any;
deleteReaction: any;
getReactionGroup: any;
hasReactionHandler: any;
toggleReaction: any;
setActiveCommentThread: any;
}
Expand Down
59 changes: 36 additions & 23 deletions src/vs/workbench/contrib/comments/browser/commentNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export class CommentNode extends Disposable {
this._md = this.markdownRenderer.render(comment.body).element;
this._body.appendChild(this._md);

if (this.comment.commentReactions && this.comment.commentReactions.length) {
if (this.comment.commentReactions && this.comment.commentReactions.length && this.comment.commentReactions.filter(reaction => !!reaction.count).length) {
this.createReactionsContainer(this._commentDetailsContainer);
}

Expand Down Expand Up @@ -154,15 +154,23 @@ export class CommentNode extends Disposable {
private createActionsToolbar() {
const actions: IAction[] = [];

let reactionGroup = this.commentService.getReactionGroup(this.owner);
if (reactionGroup && reactionGroup.length) {
let commentThread = this.commentThread as modes.CommentThread2;
if (commentThread.commentThreadHandle !== undefined) {
let toggleReactionAction = this.createReactionPicker2();
actions.push(toggleReactionAction);
} else {
let toggleReactionAction = this.createReactionPicker();
actions.push(toggleReactionAction);
let hasReactionHandler = this.commentService.hasReactionHandler(this.owner);

if (hasReactionHandler) {
let toggleReactionAction = this.createReactionPicker2(this.comment.commentReactions || []);
actions.push(toggleReactionAction);
} else {
let reactionGroup = this.commentService.getReactionGroup(this.owner);
if (reactionGroup && reactionGroup.length) {
let commentThread = this.commentThread as modes.CommentThread2;
if (commentThread.commentThreadHandle !== undefined) {
let reactionGroup = this.commentService.getReactionGroup(this.owner);
let toggleReactionAction = this.createReactionPicker2(reactionGroup || []);
actions.push(toggleReactionAction);
} else {
let toggleReactionAction = this.createReactionPicker();
actions.push(toggleReactionAction);
}
}
}

Expand Down Expand Up @@ -241,7 +249,7 @@ export class CommentNode extends Disposable {
}
}

private createReactionPicker2(): ToggleReactionsAction {
private createReactionPicker2(reactionGroup: modes.CommentReaction[]): ToggleReactionsAction {
let toggleReactionActionViewItem: DropdownMenuActionViewItem;
let toggleReactionAction = this._register(new ToggleReactionsAction(() => {
if (toggleReactionActionViewItem) {
Expand All @@ -250,7 +258,6 @@ export class CommentNode extends Disposable {
}, nls.localize('commentToggleReaction', "Toggle Reaction")));

let reactionMenuActions: Action[] = [];
let reactionGroup = this.commentService.getReactionGroup(this.owner);
if (reactionGroup && reactionGroup.length) {
reactionMenuActions = reactionGroup.map((reaction) => {
return new Action(`reaction.command.${reaction.label}`, `${reaction.label}`, '', true, async () => {
Expand Down Expand Up @@ -356,8 +363,9 @@ export class CommentNode extends Disposable {
});
this._register(this._reactionsActionBar);

this.comment.commentReactions!.map(reaction => {
let action = new ReactionAction(`reaction.${reaction.label}`, `${reaction.label}`, reaction.hasReacted && reaction.canEdit ? 'active' : '', reaction.canEdit, async () => {
let hasReactionHandler = this.commentService.hasReactionHandler(this.owner);
this.comment.commentReactions!.filter(reaction => !!reaction.count).map(reaction => {
let action = new ReactionAction(`reaction.${reaction.label}`, `${reaction.label}`, reaction.hasReacted && (reaction.canEdit || hasReactionHandler) ? 'active' : '', (reaction.canEdit || hasReactionHandler), async () => {
try {
let commentThread = this.commentThread as modes.CommentThread2;
if (commentThread.commentThreadHandle !== undefined) {
Expand Down Expand Up @@ -390,15 +398,20 @@ export class CommentNode extends Disposable {
}
});

let reactionGroup = this.commentService.getReactionGroup(this.owner);
if (reactionGroup && reactionGroup.length) {
let commentThread = this.commentThread as modes.CommentThread2;
if (commentThread.commentThreadHandle !== undefined) {
let toggleReactionAction = this.createReactionPicker2();
this._reactionsActionBar.push(toggleReactionAction, { label: false, icon: true });
} else {
let toggleReactionAction = this.createReactionPicker();
this._reactionsActionBar.push(toggleReactionAction, { label: false, icon: true });
if (hasReactionHandler) {
let toggleReactionAction = this.createReactionPicker2(this.comment.commentReactions || []);
this._reactionsActionBar.push(toggleReactionAction, { label: false, icon: true });
} else {
let reactionGroup = this.commentService.getReactionGroup(this.owner);
if (reactionGroup && reactionGroup.length) {
let commentThread = this.commentThread as modes.CommentThread2;
if (commentThread.commentThreadHandle !== undefined) {
let toggleReactionAction = this.createReactionPicker2(reactionGroup || []);
this._reactionsActionBar.push(toggleReactionAction, { label: false, icon: true });
} else {
let toggleReactionAction = this.createReactionPicker();
this._reactionsActionBar.push(toggleReactionAction, { label: false, icon: true });
}
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/vs/workbench/contrib/comments/browser/commentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export interface ICommentService {
addReaction(owner: string, resource: URI, comment: Comment, reaction: CommentReaction): Promise<void>;
deleteReaction(owner: string, resource: URI, comment: Comment, reaction: CommentReaction): Promise<void>;
getReactionGroup(owner: string): CommentReaction[] | undefined;
hasReactionHandler(owner: string): boolean;
toggleReaction(owner: string, resource: URI, thread: CommentThread2, comment: Comment, reaction: CommentReaction): Promise<void>;
setActiveCommentThread(commentThread: CommentThread | null): void;
}
Expand Down Expand Up @@ -304,6 +305,16 @@ export class CommentService extends Disposable implements ICommentService {
return undefined;
}

hasReactionHandler(owner: string): boolean {
const commentProvider = this._commentControls.get(owner);

if (commentProvider) {
return !!commentProvider.features.reactionHandler;
}

return false;
}

getStartDraftLabel(owner: string): string | undefined {
const commentProvider = this._commentProviders.get(owner);

Expand Down