Merge changes from topic "ts-diff-host"

* changes:
  Convert gr-diff-host to typescript
  Rename files to preserve history
This commit is contained in:
Ben Rohlfs
2020-09-30 10:38:40 +00:00
committed by Gerrit Code Review
29 changed files with 1628 additions and 1485 deletions

View File

@@ -27,6 +27,7 @@ import {runA11yAudit} from '../../../test/a11y-test-utils.js';
import {html} from '@polymer/polymer/lib/utils/html-tag.js'; import {html} from '@polymer/polymer/lib/utils/html-tag.js';
import {TestKeyboardShortcutBinder} from '../../../test/test-utils.js'; import {TestKeyboardShortcutBinder} from '../../../test/test-utils.js';
import {Shortcut} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js'; import {Shortcut} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js';
import {ChangeComments} from '../../diff/gr-comment-api/gr-comment-api.js';
const commentApiMock = createCommentApiMockWithTemplateElement( const commentApiMock = createCommentApiMockWithTemplateElement(
'gr-file-list-comment-api-mock', html` 'gr-file-list-comment-api-mock', html`
@@ -351,7 +352,7 @@ suite('gr-file-list tests', () => {
}); });
test('comment filtering', () => { test('comment filtering', () => {
element.changeComments._comments = { const comments = {
'/COMMIT_MSG': [ '/COMMIT_MSG': [
{patch_set: 1, message: 'Done', updated: '2017-02-08 16:40:49'}, {patch_set: 1, message: 'Done', updated: '2017-02-08 16:40:49'},
{patch_set: 1, message: 'oh hay', updated: '2017-02-09 16:40:49'}, {patch_set: 1, message: 'oh hay', updated: '2017-02-09 16:40:49'},
@@ -387,7 +388,7 @@ suite('gr-file-list tests', () => {
}, },
], ],
}; };
element.changeComments._drafts = { const drafts = {
'/COMMIT_MSG': [ '/COMMIT_MSG': [
{ {
patch_set: 1, patch_set: 1,
@@ -414,6 +415,7 @@ suite('gr-file-list tests', () => {
}, },
], ],
}; };
element.changeComments = new ChangeComments(comments, {}, drafts, 123);
const parentTo1 = { const parentTo1 = {
basePatchNum: 'PARENT', basePatchNum: 'PARENT',

View File

@@ -97,11 +97,8 @@ import {
PolymerSplice, PolymerSplice,
PolymerSpliceChange, PolymerSpliceChange,
} from '@polymer/polymer/interfaces'; } from '@polymer/polymer/interfaces';
import {assertNever, hasOwnProperty} from '../../../utils/common-util'; import {assertNever} from '../../../utils/common-util';
import { import {CommentThread, isDraft} from '../../diff/gr-comment-api/gr-comment-api';
CommentThread,
HumanCommentInfoWithPath,
} from '../../diff/gr-comment-api/gr-comment-api';
import {GrTextarea} from '../../shared/gr-textarea/gr-textarea'; import {GrTextarea} from '../../shared/gr-textarea/gr-textarea';
import {GrAccountChip} from '../../shared/gr-account-chip/gr-account-chip'; import {GrAccountChip} from '../../shared/gr-account-chip/gr-account-chip';
import {GrOverlay} from '../../shared/gr-overlay/gr-overlay'; import {GrOverlay} from '../../shared/gr-overlay/gr-overlay';
@@ -1029,10 +1026,7 @@ export class GrReplyDialog extends KeyboardShortcutMixin(
_containsNewCommentThread(threads: CommentThread[]) { _containsNewCommentThread(threads: CommentThread[]) {
return threads.some( return threads.some(
thread => thread =>
!!thread.comments && !!thread.comments && !!thread.comments[0] && isDraft(thread.comments[0])
!!thread.comments[0] &&
hasOwnProperty(thread.comments[0], '__draft') &&
!!(thread.comments[0] as HumanCommentInfoWithPath).__draft
); );
} }

View File

@@ -26,9 +26,12 @@ import {parseDate} from '../../../utils/date-util';
import {NO_THREADS_MSG} from '../../../constants/messages'; import {NO_THREADS_MSG} from '../../../constants/messages';
import {CommentSide, SpecialFilePath} from '../../../constants/constants'; import {CommentSide, SpecialFilePath} from '../../../constants/constants';
import {customElement, property, observe} from '@polymer/decorators'; import {customElement, observe, property} from '@polymer/decorators';
import {CommentThread} from '../../diff/gr-comment-api/gr-comment-api'; import {
import {Comment, RobotComment} from '../../shared/gr-comment/gr-comment'; CommentThread,
isDraft,
UIRobot,
} from '../../diff/gr-comment-api/gr-comment-api';
import {PolymerSpliceChange} from '@polymer/polymer/interfaces'; import {PolymerSpliceChange} from '@polymer/polymer/interfaces';
import {ChangeInfo} from '../../../types/common'; import {ChangeInfo} from '../../../types/common';
@@ -303,27 +306,32 @@ export class GrThreadList extends GestureEventListeners(
_getThreadWithStatusInfo(thread: CommentThread): CommentThreadWithInfo { _getThreadWithStatusInfo(thread: CommentThread): CommentThreadWithInfo {
const comments = thread.comments; const comments = thread.comments;
const lastComment = (comments[comments.length - 1] || {}) as Comment; const lastComment = comments.length
? comments[comments.length - 1]
: undefined;
let hasRobotComment = false; let hasRobotComment = false;
let hasHumanReplyToRobotComment = false; let hasHumanReplyToRobotComment = false;
comments.forEach(comment => { comments.forEach(comment => {
if ((comment as RobotComment).robot_id) { if ((comment as UIRobot).robot_id) {
hasRobotComment = true; hasRobotComment = true;
} else if (hasRobotComment) { } else if (hasRobotComment) {
hasHumanReplyToRobotComment = true; hasHumanReplyToRobotComment = true;
} }
}); });
let updated = undefined;
if (lastComment) {
if (isDraft(lastComment)) updated = lastComment.__date;
if (lastComment.updated) updated = parseDate(lastComment.updated);
}
return { return {
thread, thread,
hasRobotComment, hasRobotComment,
hasHumanReplyToRobotComment, hasHumanReplyToRobotComment,
unresolved: !!lastComment.unresolved, unresolved: !!lastComment && !!lastComment.unresolved,
isEditing: !!lastComment.__editing, isEditing: !!lastComment && !!lastComment.__editing,
hasDraft: !!lastComment.__draft, hasDraft: !!lastComment && isDraft(lastComment),
updated: lastComment.updated updated,
? parseDate(lastComment.updated)
: lastComment.__date,
}; };
} }

View File

@@ -154,7 +154,7 @@ suite('gr-thread-list tests', () => {
message: 'resolved draft', message: 'resolved draft',
unresolved: false, unresolved: false,
__draft: true, __draft: true,
__draftID: '0.m683trwff68', __draftID: '0.m683trwff69',
__editing: false, __editing: false,
patch_set: '2', patch_set: '2',
}, },

View File

@@ -38,11 +38,9 @@ import {
RobotId, RobotId,
} from '../../../types/common'; } from '../../../types/common';
import {GrOverlay} from '../../shared/gr-overlay/gr-overlay'; import {GrOverlay} from '../../shared/gr-overlay/gr-overlay';
import { import {CommentEventDetail} from '../../shared/gr-comment/gr-comment';
CommentEventDetail,
isRobotComment,
} from '../../shared/gr-comment/gr-comment';
import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api'; import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api';
import {isRobot} from '../gr-comment-api/gr-comment-api';
export interface GrApplyFixDialog { export interface GrApplyFixDialog {
$: { $: {
@@ -115,7 +113,7 @@ export class GrApplyFixDialog extends GestureEventListeners(
open(e: CustomEvent<CommentEventDetail>) { open(e: CustomEvent<CommentEventDetail>) {
const detail = e.detail; const detail = e.detail;
const comment = detail.comment; const comment = detail.comment;
if (!detail.patchNum || !comment || !isRobotComment(comment)) { if (!detail.patchNum || !comment || !isRobot(comment)) {
return Promise.resolve(); return Promise.resolve();
} }
this._patchNum = detail.patchNum; this._patchNum = detail.patchNum;

View File

@@ -27,12 +27,12 @@ import {
} from '../../../utils/patch-set-util'; } from '../../../utils/patch-set-util';
import {customElement, property} from '@polymer/decorators'; import {customElement, property} from '@polymer/decorators';
import { import {
CommentBasics,
CommentInfo, CommentInfo,
ConfigInfo, ConfigInfo,
ParentPatchSetNum, ParentPatchSetNum,
PatchRange, PatchRange,
PatchSetNum, PatchSetNum,
PathToCommentsInfoMap,
PathToRobotCommentsInfoMap, PathToRobotCommentsInfoMap,
RobotCommentInfo, RobotCommentInfo,
Timestamp, Timestamp,
@@ -40,36 +40,61 @@ import {
NumericChangeId, NumericChangeId,
} from '../../../types/common'; } from '../../../types/common';
import {hasOwnProperty} from '../../../utils/common-util'; import {hasOwnProperty} from '../../../utils/common-util';
import {CommentSide} from '../../../constants/constants'; import {CommentSide, Side} from '../../../constants/constants';
import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api'; import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api';
export interface HumanCommentInfoWithPath extends CommentInfo { export interface DraftCommentProps {
path: string;
__draft?: boolean; __draft?: boolean;
__draftID?: string;
__date?: Date; __date?: Date;
} }
export interface RobotCommentInfoWithPath extends RobotCommentInfo { export type DraftInfo = CommentBasics & DraftCommentProps;
path: string;
/**
* Each of the type implements or extends CommentBasics.
*/
export type Comment = DraftInfo | CommentInfo | RobotCommentInfo;
export interface UIStateCommentProps {
// The `side` of the comment is PARENT or REVISION, but this is LEFT or RIGHT.
// TODO(TS): Remove the naming confusion of commentSide being of type of Side,
// but side being of type CommentSide. :-)
__commentSide?: Side;
// TODO(TS): Remove this. Seems to be exactly the same as `path`??
__path?: string;
collapsed?: boolean;
// TODO(TS): Consider allowing this only for drafts.
__editing?: boolean;
__otherEditing?: boolean;
} }
export type CommentInfoWithPath = export type UIDraft = DraftInfo & UIStateCommentProps;
| HumanCommentInfoWithPath
| RobotCommentInfoWithPath;
// TODO(TS): Can be removed, CommentInfoWithTwoPaths already has a path export type UIHuman = CommentInfo & UIStateCommentProps;
export type CommentInfoWithTwoPaths = CommentInfoWithPath & {__path: string};
export type PathToCommentsInfoWithPathMap = { export type UIRobot = RobotCommentInfo & UIStateCommentProps;
[path: string]: CommentInfoWithPath[];
}; export type UIComment = UIHuman | UIRobot | UIDraft;
export type CommentMap = {[path: string]: boolean}; export type CommentMap = {[path: string]: boolean};
export function isRobot<T extends CommentInfo>(
x: T | DraftInfo | RobotCommentInfo | undefined
): x is RobotCommentInfo {
return !!x && !!(x as RobotCommentInfo).robot_id;
}
export function isDraft<T extends CommentInfo>(
x: T | UIDraft | undefined
): x is UIDraft {
return !!x && !!(x as UIDraft).__draft;
}
export interface PatchSetFile { export interface PatchSetFile {
path: string; path: string;
basePath?: string; basePath?: string;
patchNum: PatchSetNum; patchNum?: PatchSetNum;
} }
export interface PatchNumOnly { export interface PatchNumOnly {
@@ -107,9 +132,13 @@ export function sortComments<T extends SortableComment>(comments: T[]): T[] {
} }
export interface CommentThread { export interface CommentThread {
comments: CommentInfoWithTwoPaths[]; comments: UIComment[];
patchNum?: PatchSetNum; patchNum?: PatchSetNum;
path: string; path: string;
// TODO(TS): It would be nice to use LineNumber here, but the comment thread
// element actually relies on line to be undefined for file comments. Be
// aware of element attribute getters and setters, if you try to refactor
// this. :-) Still worthwhile to do ...
line?: number; line?: number;
rootId: UrlEncodedCommentId; rootId: UrlEncodedCommentId;
commentSide?: CommentSide; commentSide?: CommentSide;
@@ -119,7 +148,7 @@ export type CommentIdToCommentThreadMap = {
[urlEncodedCommentId: string]: CommentThread; [urlEncodedCommentId: string]: CommentThread;
}; };
interface TwoSidesComments { export interface TwoSidesComments {
// TODO(TS): remove meta - it is not used anywhere // TODO(TS): remove meta - it is not used anywhere
meta: { meta: {
changeNum: NumericChangeId; changeNum: NumericChangeId;
@@ -127,16 +156,16 @@ interface TwoSidesComments {
patchRange: PatchRange; patchRange: PatchRange;
projectConfig?: ConfigInfo; projectConfig?: ConfigInfo;
}; };
left: CommentInfoWithPath[]; left: UIComment[];
right: CommentInfoWithPath[]; right: UIComment[];
} }
export class ChangeComments { export class ChangeComments {
private readonly _comments: PathToCommentsInfoWithPathMap; private readonly _comments: {[path: string]: UIHuman[]};
private readonly _robotComments: PathToCommentsInfoWithPathMap; private readonly _robotComments: {[path: string]: UIRobot[]};
private readonly _drafts: PathToCommentsInfoWithPathMap; private readonly _drafts: {[path: string]: UIDraft[]};
private readonly _changeNum: NumericChangeId; private readonly _changeNum: NumericChangeId;
@@ -145,9 +174,9 @@ export class ChangeComments {
* elements of that which uses the gr-comment-api. * elements of that which uses the gr-comment-api.
*/ */
constructor( constructor(
comments: PathToCommentsInfoMap | undefined, comments: {[path: string]: UIHuman[]} | undefined,
robotComments: PathToRobotCommentsInfoMap | undefined, robotComments: {[path: string]: UIRobot[]} | undefined,
drafts: PathToCommentsInfoMap | undefined, drafts: {[path: string]: UIDraft[]} | undefined,
changeNum: NumericChangeId changeNum: NumericChangeId
) { ) {
this._comments = this._addPath(comments); this._comments = this._addPath(comments);
@@ -164,10 +193,10 @@ export class ChangeComments {
* TODO(taoalpha): should consider changing BE to send path * TODO(taoalpha): should consider changing BE to send path
* back within CommentInfo * back within CommentInfo
*/ */
_addPath( _addPath<T>(
comments: PathToCommentsInfoMap = {} comments: {[path: string]: T[]} = {}
): PathToCommentsInfoWithPathMap { ): {[path: string]: Array<T & {path: string}>} {
const updatedComments: PathToCommentsInfoWithPathMap = {}; const updatedComments: {[path: string]: Array<T & {path: string}>} = {};
for (const filePath of Object.keys(comments)) { for (const filePath of Object.keys(comments)) {
const allCommentsForPath = comments[filePath] || []; const allCommentsForPath = comments[filePath] || [];
if (allCommentsForPath.length) { if (allCommentsForPath.length) {
@@ -191,10 +220,8 @@ export class ChangeComments {
return this._robotComments; return this._robotComments;
} }
findCommentById( findCommentById(commentId: UrlEncodedCommentId): Comment | undefined {
commentId: UrlEncodedCommentId const findComment = (comments: {[path: string]: CommentBasics[]}) => {
): HumanCommentInfoWithPath | RobotCommentInfoWithPath | undefined {
const findComment = (comments: PathToCommentsInfoWithPathMap) => {
let comment; let comment;
for (const path of Object.keys(comments)) { for (const path of Object.keys(comments)) {
comment = comment || comments[path].find(c => c.id === commentId); comment = comment || comments[path].find(c => c.id === commentId);
@@ -216,7 +243,11 @@ export class ChangeComments {
* patchNum and basePatchNum properties to represent the range. * patchNum and basePatchNum properties to represent the range.
*/ */
getPaths(patchRange?: PatchRange): CommentMap { getPaths(patchRange?: PatchRange): CommentMap {
const responses = [this.comments, this.drafts, this.robotComments]; const responses: {[path: string]: UIComment[]}[] = [
this.comments,
this.drafts,
this.robotComments,
];
const commentMap: CommentMap = {}; const commentMap: CommentMap = {};
for (const response of responses) { for (const response of responses) {
for (const path in response) { for (const path in response) {
@@ -240,9 +271,7 @@ export class ChangeComments {
/** /**
* Gets all the comments and robot comments for the given change. * Gets all the comments and robot comments for the given change.
*/ */
getAllPublishedComments( getAllPublishedComments(patchNum?: PatchSetNum) {
patchNum?: PatchSetNum
): PathToCommentsInfoWithPathMap {
return this.getAllComments(false, patchNum); return this.getAllComments(false, patchNum);
} }
@@ -263,12 +292,9 @@ export class ChangeComments {
/** /**
* Gets all the comments and robot comments for the given change. * Gets all the comments and robot comments for the given change.
*/ */
getAllComments( getAllComments(includeDrafts?: boolean, patchNum?: PatchSetNum) {
includeDrafts?: boolean,
patchNum?: PatchSetNum
): PathToCommentsInfoWithPathMap {
const paths = this.getPaths(); const paths = this.getPaths();
const publishedComments: PathToCommentsInfoWithPathMap = {}; const publishedComments: {[path: string]: CommentBasics[]} = {};
for (const path of Object.keys(paths)) { for (const path of Object.keys(paths)) {
publishedComments[path] = this.getAllCommentsForPath( publishedComments[path] = this.getAllCommentsForPath(
path, path,
@@ -282,9 +308,9 @@ export class ChangeComments {
/** /**
* Gets all the drafts for the given change. * Gets all the drafts for the given change.
*/ */
getAllDrafts(patchNum?: PatchSetNum): PathToCommentsInfoWithPathMap { getAllDrafts(patchNum?: PatchSetNum) {
const paths = this.getPaths(); const paths = this.getPaths();
const drafts: PathToCommentsInfoWithPathMap = {}; const drafts: {[path: string]: UIDraft[]} = {};
for (const path of Object.keys(paths)) { for (const path of Object.keys(paths)) {
drafts[path] = this.getAllDraftsForPath(path, patchNum); drafts[path] = this.getAllDraftsForPath(path, patchNum);
} }
@@ -302,8 +328,8 @@ export class ChangeComments {
path: string, path: string,
patchNum?: PatchSetNum, patchNum?: PatchSetNum,
includeDrafts?: boolean includeDrafts?: boolean
): CommentInfoWithPath[] { ): Comment[] {
const comments = this._comments[path] || []; const comments: Comment[] = this._comments[path] || [];
const robotComments = this._robotComments[path] || []; const robotComments = this._robotComments[path] || [];
let allComments = comments.concat(robotComments); let allComments = comments.concat(robotComments);
if (includeDrafts) { if (includeDrafts) {
@@ -347,10 +373,7 @@ export class ChangeComments {
* This will return a shallow copy of all drafts every time, * This will return a shallow copy of all drafts every time,
* so changes on any copy will not affect other copies. * so changes on any copy will not affect other copies.
*/ */
getAllDraftsForPath( getAllDraftsForPath(path: string, patchNum?: PatchSetNum): Comment[] {
path: string,
patchNum?: PatchSetNum
): CommentInfoWithPath[] {
let comments = this._drafts[path] || []; let comments = this._drafts[path] || [];
if (patchNum) { if (patchNum) {
comments = comments.filter(c => patchNumEquals(c.patch_set, patchNum)); comments = comments.filter(c => patchNumEquals(c.patch_set, patchNum));
@@ -365,7 +388,7 @@ export class ChangeComments {
* *
* // TODO(taoalpha): maybe merge in *ForPath * // TODO(taoalpha): maybe merge in *ForPath
*/ */
getAllDraftsForFile(file: PatchSetFile): CommentInfoWithPath[] { getAllDraftsForFile(file: PatchSetFile): Comment[] {
let allDrafts = this.getAllDraftsForPath(file.path, file.patchNum); let allDrafts = this.getAllDraftsForPath(file.path, file.patchNum);
if (file.basePath) { if (file.basePath) {
allDrafts = allDrafts.concat( allDrafts = allDrafts.concat(
@@ -390,9 +413,9 @@ export class ChangeComments {
patchRange: PatchRange, patchRange: PatchRange,
projectConfig?: ConfigInfo projectConfig?: ConfigInfo
): TwoSidesComments { ): TwoSidesComments {
let comments: CommentInfoWithPath[] = []; let comments: Comment[] = [];
let drafts: CommentInfoWithPath[] = []; let drafts: DraftInfo[] = [];
let robotComments: CommentInfoWithPath[] = []; let robotComments: RobotCommentInfo[] = [];
if (this.comments && this.comments[path]) { if (this.comments && this.comments[path]) {
comments = this.comments[path]; comments = this.comments[path];
} }
@@ -404,11 +427,10 @@ export class ChangeComments {
} }
drafts.forEach(d => { drafts.forEach(d => {
// drafts don't include robot comments d.__draft = true;
(d as HumanCommentInfoWithPath).__draft = true;
}); });
const all = comments const all: Comment[] = comments
.concat(drafts) .concat(drafts)
.concat(robotComments) .concat(robotComments)
.map(c => { .map(c => {
@@ -450,7 +472,7 @@ export class ChangeComments {
file: PatchSetFile, file: PatchSetFile,
patchRange: PatchRange, patchRange: PatchRange,
projectConfig?: ConfigInfo projectConfig?: ConfigInfo
) { ): TwoSidesComments {
const comments = this.getCommentsBySideForPath( const comments = this.getCommentsBySideForPath(
file.path, file.path,
patchRange, patchRange,
@@ -476,24 +498,22 @@ export class ChangeComments {
* also includes the file that it was left on, which was the key of the * also includes the file that it was left on, which was the key of the
* originall object. * originall object.
*/ */
_commentObjToArrayWithFile( _commentObjToArrayWithFile<T>(comments: {
comments: PathToCommentsInfoWithPathMap [path: string]: T[];
): CommentInfoWithTwoPaths[] { }): Array<T & {__path: string}> {
let commentArr: CommentInfoWithTwoPaths[] = []; let commentArr: Array<T & {__path: string}> = [];
for (const file of Object.keys(comments)) { for (const file of Object.keys(comments)) {
const commentsForFile = []; const commentsForFile: Array<T & {__path: string}> = [];
for (const comment of comments[file]) { for (const comment of comments[file]) {
commentsForFile.push({__path: file, ...comment}); commentsForFile.push({...comment, __path: file});
} }
commentArr = commentArr.concat(commentsForFile); commentArr = commentArr.concat(commentsForFile);
} }
return commentArr; return commentArr;
} }
_commentObjToArray( _commentObjToArray<T>(comments: {[path: string]: T[]}): T[] {
comments: PathToCommentsInfoWithPathMap let commentArr: T[] = [];
): CommentInfoWithPath[] {
let commentArr: CommentInfoWithPath[] = [];
for (const file of Object.keys(comments)) { for (const file of Object.keys(comments)) {
commentArr = commentArr.concat(comments[file]); commentArr = commentArr.concat(comments[file]);
} }
@@ -527,8 +547,8 @@ export class ChangeComments {
* Computes a number of unresolved comment threads in a given file and path. * Computes a number of unresolved comment threads in a given file and path.
*/ */
computeUnresolvedNum(file: PatchSetFile | PatchNumOnly) { computeUnresolvedNum(file: PatchSetFile | PatchNumOnly) {
let comments: CommentInfoWithPath[] = []; let comments: Comment[] = [];
let drafts: CommentInfoWithPath[] = []; let drafts: Comment[] = [];
if (isPatchSetFile(file)) { if (isPatchSetFile(file)) {
comments = this.getAllCommentsForFile(file); comments = this.getAllCommentsForFile(file);
@@ -541,12 +561,7 @@ export class ChangeComments {
comments = comments.concat(drafts); comments = comments.concat(drafts);
// TODO(TS): the 'as CommentInfoWithTwoPaths[]' is completely wrong below const threads = this.getCommentThreads(sortComments(comments));
// However, this doesn't affect the final result of computeUnresolvedNum
// This should be fixed by removing CommentInfoWithTwoPaths later
const threads = this.getCommentThreads(
sortComments(comments) as CommentInfoWithTwoPaths[]
);
const unresolvedThreads = threads.filter( const unresolvedThreads = threads.filter(
thread => thread =>
@@ -568,7 +583,7 @@ export class ChangeComments {
* *
* @param comments sorted by updated timestamp. * @param comments sorted by updated timestamp.
*/ */
getCommentThreads(comments: CommentInfoWithTwoPaths[]) { getCommentThreads(comments: UIComment[]) {
const threads: CommentThread[] = []; const threads: CommentThread[] = [];
const idThreadMap: CommentIdToCommentThreadMap = {}; const idThreadMap: CommentIdToCommentThreadMap = {};
for (const comment of comments) { for (const comment of comments) {
@@ -585,10 +600,13 @@ export class ChangeComments {
} }
// Otherwise, this comment starts its own thread. // Otherwise, this comment starts its own thread.
if (!comment.__path && !comment.path) {
throw new Error('Comment missing required "path".');
}
const newThread: CommentThread = { const newThread: CommentThread = {
comments: [comment], comments: [comment],
patchNum: comment.patch_set, patchNum: comment.patch_set,
path: comment.__path, path: comment.__path || comment.path!,
line: comment.line, line: comment.line,
rootId: comment.id, rootId: comment.id,
}; };
@@ -605,7 +623,7 @@ export class ChangeComments {
* Whether the given comment should be included in the base side of the * Whether the given comment should be included in the base side of the
* given patch range. * given patch range.
*/ */
_isInBaseOfPatchRange(comment: CommentInfo, range: PatchRange) { _isInBaseOfPatchRange(comment: CommentBasics, range: PatchRange) {
// If the base of the patch range is a parent of a merge, and the comment // If the base of the patch range is a parent of a merge, and the comment
// appears on a specific parent then only show the comment if the parent // appears on a specific parent then only show the comment if the parent
// index of the comment matches that of the range. // index of the comment matches that of the range.
@@ -636,7 +654,7 @@ export class ChangeComments {
* Whether the given comment should be included in the revision side of the * Whether the given comment should be included in the revision side of the
* given patch range. * given patch range.
*/ */
_isInRevisionOfPatchRange(comment: CommentInfo, range: PatchRange) { _isInRevisionOfPatchRange(comment: CommentBasics, range: PatchRange) {
return ( return (
comment.side !== CommentSide.PARENT && comment.side !== CommentSide.PARENT &&
patchNumEquals(comment.patch_set, range.patchNum) patchNumEquals(comment.patch_set, range.patchNum)
@@ -646,7 +664,7 @@ export class ChangeComments {
/** /**
* Whether the given comment should be included in the given patch range. * Whether the given comment should be included in the given patch range.
*/ */
_isInPatchRange(comment: CommentInfo, range: PatchRange): boolean { _isInPatchRange(comment: CommentBasics, range: PatchRange): boolean {
return ( return (
this._isInBaseOfPatchRange(comment, range) || this._isInBaseOfPatchRange(comment, range) ||
this._isInRevisionOfPatchRange(comment, range) this._isInRevisionOfPatchRange(comment, range)

View File

@@ -17,6 +17,7 @@
import '../../../test/common-test-setup-karma.js'; import '../../../test/common-test-setup-karma.js';
import './gr-comment-api.js'; import './gr-comment-api.js';
import {ChangeComments} from './gr-comment-api.js';
const basicFixture = fixtureFromElement('gr-comment-api'); const basicFixture = fixtureFromElement('gr-comment-api');
@@ -216,7 +217,7 @@ suite('gr-comment-api tests', () => {
} }
setup(() => { setup(() => {
element._changeComments._drafts = { const drafts = {
'file/one': [ 'file/one': [
{ {
id: '11', id: '11',
@@ -244,7 +245,7 @@ suite('gr-comment-api tests', () => {
}, },
], ],
}; };
element._changeComments._robotComments = { const robotComments = {
'file/one': [ 'file/one': [
{ {
id: '01', id: '01',
@@ -268,7 +269,7 @@ suite('gr-comment-api tests', () => {
}, },
], ],
}; };
element._changeComments._comments = { const comments = {
'file/one': [ 'file/one': [
{ {
id: '03', id: '03',
@@ -305,6 +306,8 @@ suite('gr-comment-api tests', () => {
{id: '10', patch_set: 5, line: 1, updated: makeTime(1)}, {id: '10', patch_set: 5, line: 1, updated: makeTime(1)},
], ],
}; };
element._changeComments =
new ChangeComments(comments, robotComments, drafts, 1234);
}); });
test('getPaths', () => { test('getPaths', () => {
@@ -405,9 +408,7 @@ suite('gr-comment-api tests', () => {
}); });
test('computeUnresolvedNum w/ non-linear thread', () => { test('computeUnresolvedNum w/ non-linear thread', () => {
element._changeComments._drafts = {}; const comments = {
element._changeComments._robotComments = {};
element._changeComments._comments = {
path: [{ path: [{
id: '9c6ba3c6_28b7d467', id: '9c6ba3c6_28b7d467',
patch_set: 1, patch_set: 1,
@@ -433,6 +434,7 @@ suite('gr-comment-api tests', () => {
unresolved: false, unresolved: false,
}], }],
}; };
element._changeComments = new ChangeComments(comments, {}, {}, 1234);
assert.equal( assert.equal(
element._changeComments.computeUnresolvedNum(1, 'path'), 0); element._changeComments.computeUnresolvedNum(1, 'path'), 0);
}); });
@@ -523,6 +525,7 @@ suite('gr-comment-api tests', () => {
end_line: 2, end_line: 2,
end_character: 2, end_character: 2,
}, },
path: 'file/one',
__path: 'file/one', __path: 'file/one',
}, },
], ],
@@ -538,6 +541,7 @@ suite('gr-comment-api tests', () => {
patch_set: 2, patch_set: 2,
side: 'PARENT', side: 'PARENT',
line: 2, line: 2,
path: 'file/one',
__path: 'file/one', __path: 'file/one',
updated: '2013-02-26 15:01:43.986000000', updated: '2013-02-26 15:01:43.986000000',
}, },
@@ -553,6 +557,7 @@ suite('gr-comment-api tests', () => {
id: '04', id: '04',
patch_set: 2, patch_set: 2,
line: 1, line: 1,
path: 'file/one',
__path: 'file/one', __path: 'file/one',
updated: '2013-02-26 15:01:43.986000000', updated: '2013-02-26 15:01:43.986000000',
}, },
@@ -562,6 +567,7 @@ suite('gr-comment-api tests', () => {
patch_set: 2, patch_set: 2,
unresolved: true, unresolved: true,
line: 1, line: 1,
path: 'file/one',
__path: 'file/one', __path: 'file/one',
updated: '2013-02-26 15:03:43.986000000', updated: '2013-02-26 15:03:43.986000000',
}, },
@@ -570,6 +576,7 @@ suite('gr-comment-api tests', () => {
in_reply_to: '04', in_reply_to: '04',
patch_set: 2, patch_set: 2,
line: 1, line: 1,
path: 'file/one',
__path: 'file/one', __path: 'file/one',
__draft: true, __draft: true,
updated: '2013-02-26 15:02:43.986000000', updated: '2013-02-26 15:02:43.986000000',
@@ -585,6 +592,7 @@ suite('gr-comment-api tests', () => {
id: '05', id: '05',
patch_set: 2, patch_set: 2,
line: 2, line: 2,
path: 'file/two',
__path: 'file/two', __path: 'file/two',
updated: '2013-02-26 15:01:43.986000000', updated: '2013-02-26 15:01:43.986000000',
}, },
@@ -599,6 +607,7 @@ suite('gr-comment-api tests', () => {
id: '06', id: '06',
patch_set: 3, patch_set: 3,
line: 2, line: 2,
path: 'file/two',
__path: 'file/two', __path: 'file/two',
updated: '2013-02-26 15:01:43.986000000', updated: '2013-02-26 15:01:43.986000000',
}, },
@@ -615,6 +624,7 @@ suite('gr-comment-api tests', () => {
side: 'PARENT', side: 'PARENT',
unresolved: true, unresolved: true,
line: 1, line: 1,
path: 'file/three',
__path: 'file/three', __path: 'file/three',
updated: '2013-02-26 15:01:43.986000000', updated: '2013-02-26 15:01:43.986000000',
}, },
@@ -630,6 +640,7 @@ suite('gr-comment-api tests', () => {
id: '08', id: '08',
patch_set: 3, patch_set: 3,
line: 1, line: 1,
path: 'file/three',
__path: 'file/three', __path: 'file/three',
updated: '2013-02-26 15:01:43.986000000', updated: '2013-02-26 15:01:43.986000000',
}, },
@@ -645,6 +656,7 @@ suite('gr-comment-api tests', () => {
patch_set: 5, patch_set: 5,
side: 'PARENT', side: 'PARENT',
line: 1, line: 1,
path: 'file/four',
__path: 'file/four', __path: 'file/four',
updated: '2013-02-26 15:01:43.986000000', updated: '2013-02-26 15:01:43.986000000',
}, },
@@ -660,6 +672,7 @@ suite('gr-comment-api tests', () => {
id: '10', id: '10',
patch_set: 5, patch_set: 5,
line: 1, line: 1,
path: 'file/four',
__path: 'file/four', __path: 'file/four',
updated: '2013-02-26 15:01:43.986000000', updated: '2013-02-26 15:01:43.986000000',
}, },
@@ -674,6 +687,7 @@ suite('gr-comment-api tests', () => {
id: '05', id: '05',
patch_set: 3, patch_set: 3,
line: 1, line: 1,
path: 'file/two',
__path: 'file/two', __path: 'file/two',
__draft: true, __draft: true,
updated: '2013-02-26 15:03:43.986000000', updated: '2013-02-26 15:03:43.986000000',
@@ -690,6 +704,7 @@ suite('gr-comment-api tests', () => {
patch_set: 2, patch_set: 2,
side: 'PARENT', side: 'PARENT',
line: 1, line: 1,
path: 'file/one',
__path: 'file/one', __path: 'file/one',
__draft: true, __draft: true,
updated: '2013-02-26 15:03:43.986000000', updated: '2013-02-26 15:03:43.986000000',
@@ -710,6 +725,7 @@ suite('gr-comment-api tests', () => {
let expectedComments = [ let expectedComments = [
{ {
__path: 'file/one', __path: 'file/one',
path: 'file/one',
id: '04', id: '04',
patch_set: 2, patch_set: 2,
line: 1, line: 1,
@@ -717,6 +733,7 @@ suite('gr-comment-api tests', () => {
}, },
{ {
__path: 'file/one', __path: 'file/one',
path: 'file/one',
id: '02', id: '02',
in_reply_to: '04', in_reply_to: '04',
patch_set: 2, patch_set: 2,
@@ -726,6 +743,7 @@ suite('gr-comment-api tests', () => {
}, },
{ {
__path: 'file/one', __path: 'file/one',
path: 'file/one',
__draft: true, __draft: true,
id: '12', id: '12',
in_reply_to: '04', in_reply_to: '04',
@@ -742,6 +760,7 @@ suite('gr-comment-api tests', () => {
patch_set: 2, patch_set: 2,
side: 'PARENT', side: 'PARENT',
line: 1, line: 1,
path: 'file/one',
__path: 'file/one', __path: 'file/one',
__draft: true, __draft: true,
updated: '2013-02-26 15:03:43.986000000', updated: '2013-02-26 15:03:43.986000000',

View File

@@ -18,7 +18,7 @@ import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-l
import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin'; import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin';
import {PolymerElement} from '@polymer/polymer/polymer-element'; import {PolymerElement} from '@polymer/polymer/polymer-element';
import {htmlTemplate} from './gr-coverage-layer_html'; import {htmlTemplate} from './gr-coverage-layer_html';
import {CoverageType, DiffLayer} from '../../../types/types'; import {CoverageRange, CoverageType, DiffLayer} from '../../../types/types';
import {customElement, property} from '@polymer/decorators'; import {customElement, property} from '@polymer/decorators';
declare global { declare global {
@@ -45,11 +45,9 @@ export class GrCoverageLayer
/** /**
* Must be sorted by code_range.start_line. * Must be sorted by code_range.start_line.
* Must only contain ranges that match the side. * Must only contain ranges that match the side.
*
*/ */
// TODO(TS): convert into AnnotationLayer once gr-diff-is converted
@property({type: Array}) @property({type: Array})
coverageRanges: any[] = []; coverageRanges: CoverageRange[] = [];
@property({type: String}) @property({type: String})
side?: string; side?: string;

View File

@@ -317,9 +317,7 @@ export class GrDiffBuilderElement extends GestureEventListeners(
} }
getSideByLineEl(lineEl: Element) { getSideByLineEl(lineEl: Element) {
return lineEl.classList.contains(GrDiffBuilder.Side.RIGHT) return lineEl.classList.contains(Side.RIGHT) ? Side.RIGHT : Side.LEFT;
? Side.RIGHT
: Side.LEFT;
} }
emitGroup(group: GrDiffGroup, sectionEl: HTMLElement) { emitGroup(group: GrDiffGroup, sectionEl: HTMLElement) {

View File

@@ -24,6 +24,7 @@ import {
} from '../gr-diff/gr-diff-group'; } from '../gr-diff/gr-diff-group';
import {BlameInfo, DiffInfo, DiffPreferencesInfo} from '../../../types/common'; import {BlameInfo, DiffInfo, DiffPreferencesInfo} from '../../../types/common';
import {Side} from '../../../constants/constants'; import {Side} from '../../../constants/constants';
import {DiffLayer} from '../../../types/types';
/** /**
* In JS, unicode code points above 0xFFFF occupy two elements of a string. * In JS, unicode code points above 0xFFFF occupy two elements of a string.
@@ -86,8 +87,7 @@ export abstract class GrDiffBuilder {
diff: DiffInfo, diff: DiffInfo,
prefs: DiffPreferencesInfo, prefs: DiffPreferencesInfo,
outputEl: HTMLElement, outputEl: HTMLElement,
// TODO(TS): Replace any by a layer interface. readonly layers: DiffLayer[] = []
readonly layers: any[] = []
) { ) {
this._diff = diff; this._diff = diff;
this._numLinesLeft = this._diff.content this._numLinesLeft = this._diff.content
@@ -142,12 +142,6 @@ export abstract class GrDiffBuilder {
REMOVED: 'edit_a', REMOVED: 'edit_a',
}; };
// TODO(TS): Convert to enum.
static readonly Side = {
LEFT: 'left',
RIGHT: 'right',
};
// TODO(TS): Replace usages with ContextButtonType enum. // TODO(TS): Replace usages with ContextButtonType enum.
static readonly ContextButtonType = { static readonly ContextButtonType = {
ABOVE: 'above', ABOVE: 'above',
@@ -480,10 +474,14 @@ export abstract class GrDiffBuilder {
contentText.setAttribute('data-side', side); contentText.setAttribute('data-side', side);
} }
for (const layer of this.layers) { if (lineNumberEl) {
if (typeof layer.annotate === 'function') { for (const layer of this.layers) {
layer.annotate(contentText, lineNumberEl, line); if (typeof layer.annotate === 'function') {
layer.annotate(contentText, lineNumberEl, line);
}
} }
} else {
console.error('The lineNumberEl is null, skipping layer annotations.');
} }
td.appendChild(contentText); td.appendChild(contentText);

View File

@@ -26,9 +26,8 @@ import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-l
import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin'; import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin';
import {PolymerElement} from '@polymer/polymer/polymer-element'; import {PolymerElement} from '@polymer/polymer/polymer-element';
import {htmlTemplate} from './gr-diff-cursor_html'; import {htmlTemplate} from './gr-diff-cursor_html';
import {ScrollMode} from '../../../constants/constants'; import {ScrollMode, Side} from '../../../constants/constants';
import {customElement, property, observe} from '@polymer/decorators'; import {customElement, property, observe} from '@polymer/decorators';
import {DiffSide} from '../gr-diff/gr-diff-utils';
import {GrDiffLineType} from '../gr-diff/gr-diff-line'; import {GrDiffLineType} from '../gr-diff/gr-diff-line';
import {PolymerSpliceChange} from '@polymer/polymer/interfaces'; import {PolymerSpliceChange} from '@polymer/polymer/interfaces';
import {PolymerDomWrapper} from '../../../types/types'; import {PolymerDomWrapper} from '../../../types/types';
@@ -83,7 +82,7 @@ export class GrDiffCursor extends GestureEventListeners(
private _lastDisplayedNavigateToNextFileToast: number | null = null; private _lastDisplayedNavigateToNextFileToast: number | null = null;
@property({type: String}) @property({type: String})
side = DiffSide.RIGHT; side = Side.RIGHT;
@property({type: Object, notify: true, observer: '_rowChanged'}) @property({type: Object, notify: true, observer: '_rowChanged'})
diffRow?: HTMLElement; diffRow?: HTMLElement;
@@ -162,14 +161,14 @@ export class GrDiffCursor extends GestureEventListeners(
} }
moveLeft() { moveLeft() {
this.side = DiffSide.LEFT; this.side = Side.LEFT;
if (this._isTargetBlank()) { if (this._isTargetBlank()) {
this.moveUp(); this.moveUp();
} }
} }
moveRight() { moveRight() {
this.side = DiffSide.RIGHT; this.side = Side.RIGHT;
if (this._isTargetBlank()) { if (this._isTargetBlank()) {
this.moveUp(); this.moveUp();
} }
@@ -272,7 +271,7 @@ export class GrDiffCursor extends GestureEventListeners(
this._fixSide(); this._fixSide();
} }
moveToLineNumber(number: number, side: DiffSide, path?: string) { moveToLineNumber(number: number, side: Side, path?: string) {
const row = this._findRowByNumberAndFile(number, side, path); const row = this._findRowByNumberAndFile(number, side, path);
if (row) { if (row) {
this.side = side; this.side = side;
@@ -291,7 +290,7 @@ export class GrDiffCursor extends GestureEventListeners(
} }
if (this._getViewMode() === DiffViewMode.SIDE_BY_SIDE) { if (this._getViewMode() === DiffViewMode.SIDE_BY_SIDE) {
lineElSelector += this.side === DiffSide.LEFT ? '.left' : '.right'; lineElSelector += this.side === Side.LEFT ? '.left' : '.right';
} }
return this.diffRow.querySelector(lineElSelector); return this.diffRow.querySelector(lineElSelector);
@@ -452,7 +451,7 @@ export class GrDiffCursor extends GestureEventListeners(
_rowHasSide(row: Element) { _rowHasSide(row: Element) {
const selector = const selector =
(this.side === DiffSide.LEFT ? '.left' : '.right') + ' + .content'; (this.side === Side.LEFT ? '.left' : '.right') + ' + .content';
return !!row.querySelector(selector); return !!row.querySelector(selector);
} }
@@ -478,7 +477,7 @@ export class GrDiffCursor extends GestureEventListeners(
this._getViewMode() === DiffViewMode.SIDE_BY_SIDE && this._getViewMode() === DiffViewMode.SIDE_BY_SIDE &&
this._isTargetBlank() this._isTargetBlank()
) { ) {
this.side = this.side === DiffSide.LEFT ? DiffSide.RIGHT : DiffSide.LEFT; this.side = this.side === Side.LEFT ? Side.RIGHT : Side.LEFT;
} }
} }
@@ -489,8 +488,8 @@ export class GrDiffCursor extends GestureEventListeners(
const actions = this._getActionsForRow(); const actions = this._getActionsForRow();
return ( return (
(this.side === DiffSide.LEFT && !actions.left) || (this.side === Side.LEFT && !actions.left) ||
(this.side === DiffSide.RIGHT && !actions.right) (this.side === Side.RIGHT && !actions.right)
); );
} }
@@ -506,16 +505,8 @@ export class GrDiffCursor extends GestureEventListeners(
if (!this.diffRow) { if (!this.diffRow) {
return; return;
} }
this.toggleClass( this.toggleClass(LEFT_SIDE_CLASS, this.side === Side.LEFT, this.diffRow);
LEFT_SIDE_CLASS, this.toggleClass(RIGHT_SIDE_CLASS, this.side === Side.RIGHT, this.diffRow);
this.side === DiffSide.LEFT,
this.diffRow
);
this.toggleClass(
RIGHT_SIDE_CLASS,
this.side === DiffSide.RIGHT,
this.diffRow
);
} }
_isActionType(type: GrDiffRowType) { _isActionType(type: GrDiffRowType) {
@@ -601,7 +592,7 @@ export class GrDiffCursor extends GestureEventListeners(
_findRowByNumberAndFile( _findRowByNumberAndFile(
targetNumber: number, targetNumber: number,
side: DiffSide, side: Side,
path?: string path?: string
): HTMLElement | undefined { ): HTMLElement | undefined {
let stops; let stops;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -20,8 +20,8 @@ import './gr-diff-host.js';
import {GrDiffBuilderImage} from '../gr-diff-builder/gr-diff-builder-image.js'; import {GrDiffBuilderImage} from '../gr-diff-builder/gr-diff-builder-image.js';
import {GerritNav} from '../../core/gr-navigation/gr-navigation.js'; import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js'; import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
import {DiffSide} from '../gr-diff/gr-diff-utils.js';
import {sortComments} from '../gr-comment-api/gr-comment-api.js'; import {sortComments} from '../gr-comment-api/gr-comment-api.js';
import {Side} from '../../../constants/constants.js';
const basicFixture = fixtureFromElement('gr-diff-host'); const basicFixture = fixtureFromElement('gr-diff-host');
@@ -36,6 +36,8 @@ suite('gr-diff-host tests', () => {
async getLoggedIn() { return getLoggedIn; }, async getLoggedIn() { return getLoggedIn; },
}); });
element = basicFixture.instantiate(); element = basicFixture.instantiate();
element.changeNum = 123;
element.path = 'some/path';
sinon.stub(element.reporting, 'time'); sinon.stub(element.reporting, 'time');
sinon.stub(element.reporting, 'timeEnd'); sinon.stub(element.reporting, 'timeEnd');
}); });
@@ -47,6 +49,8 @@ suite('gr-diff-host tests', () => {
getDiffLayers() { return pluginLayers; }, getDiffLayers() { return pluginLayers; },
}); });
element = basicFixture.instantiate(); element = basicFixture.instantiate();
element.changeNum = 123;
element.path = 'some/path';
}); });
test('plugin layers requested', () => { test('plugin layers requested', () => {
element.patchRange = {}; element.patchRange = {};
@@ -172,7 +176,6 @@ suite('gr-diff-host tests', () => {
], ],
}; };
element._removeComment({});
// Using JSON.stringify because Safari 9.1 (11601.5.17.1) doesnt seem // Using JSON.stringify because Safari 9.1 (11601.5.17.1) doesnt seem
// to believe that one object deepEquals another even when they do :-/. // to believe that one object deepEquals another even when they do :-/.
assert.equal(JSON.stringify(element.comments), JSON.stringify({ assert.equal(JSON.stringify(element.comments), JSON.stringify({
@@ -249,12 +252,20 @@ suite('gr-diff-host tests', () => {
}); });
test('thread-discard handling', () => { test('thread-discard handling', () => {
const threads = [ const threads = element._createThreads([
{comments: [{id: 4711}]}, {
{comments: [{id: 42}]}, id: 4711,
]; __commentSide: 'left',
updated: '2015-12-20 15:01:20.396000000',
},
{
id: 42,
__commentSide: 'left',
updated: '2017-12-20 15:01:20.396000000',
},
]);
element._parentIndex = 1; element._parentIndex = 1;
element.changeNum = '2'; element.changeNum = 2;
element.path = 'some/path'; element.path = 'some/path';
element.projectName = 'Some project'; element.projectName = 'Some project';
const threadEls = threads.map( const threadEls = threads.map(
@@ -268,8 +279,8 @@ suite('gr-diff-host tests', () => {
return threadEl; return threadEl;
}); });
assert.equal(threadEls.length, 2); assert.equal(threadEls.length, 2);
assert.equal(threadEls[0].rootId, 4711); assert.equal(threadEls[0].comments[0].id, 4711);
assert.equal(threadEls[1].rootId, 42); assert.equal(threadEls[1].comments[0].id, 42);
for (const threadEl of threadEls) { for (const threadEl of threadEls) {
element.appendChild(threadEl); element.appendChild(threadEl);
} }
@@ -279,7 +290,7 @@ suite('gr-diff-host tests', () => {
const attachedThreads = element.queryAllEffectiveChildren( const attachedThreads = element.queryAllEffectiveChildren(
'gr-comment-thread'); 'gr-comment-thread');
assert.equal(attachedThreads.length, 1); assert.equal(attachedThreads.length, 1);
assert.equal(attachedThreads[0].rootId, 42); assert.equal(attachedThreads[0].comments[0].id, 42);
}); });
suite('render reporting', () => { suite('render reporting', () => {
@@ -393,6 +404,8 @@ suite('gr-diff-host tests', () => {
setup(() => { setup(() => {
getLoggedIn = false; getLoggedIn = false;
element = basicFixture.instantiate(); element = basicFixture.instantiate();
element.changeNum = 123;
element.path = 'some/path';
}); });
test('reload() loads files weblinks', () => { test('reload() loads files weblinks', () => {
@@ -852,6 +865,8 @@ suite('gr-diff-host tests', () => {
suite('blame', () => { suite('blame', () => {
setup(() => { setup(() => {
element = basicFixture.instantiate(); element = basicFixture.instantiate();
element.changeNum = 123;
element.path = 'some/path';
}); });
test('clearBlame', () => { test('clearBlame', () => {
@@ -933,9 +948,8 @@ suite('gr-diff-host tests', () => {
}); });
test('passes in changeNum', () => { test('passes in changeNum', () => {
const value = '12345'; element.changeNum = 12345;
element.changeNum = value; assert.equal(element.$.diff.changeNum, 12345);
assert.equal(element.$.diff.changeNum, value);
}); });
test('passes in noAutoRender', () => { test('passes in noAutoRender', () => {
@@ -963,9 +977,8 @@ suite('gr-diff-host tests', () => {
}); });
test('passes in changeNum', () => { test('passes in changeNum', () => {
const value = '12345'; element.changeNum = 12345;
element.changeNum = value; assert.equal(element.$.diff.changeNum, 12345);
assert.equal(element.$.diff.changeNum, value);
}); });
test('passes in projectName', () => { test('passes in projectName', () => {
@@ -1016,6 +1029,7 @@ suite('gr-diff-host tests', () => {
setup(() => { setup(() => {
element = basicFixture.instantiate(); element = basicFixture.instantiate();
element.changeNum = 123;
element.path = 'file.txt'; element.path = 'file.txt';
element.patchRange = {basePatchNum: 1}; element.patchRange = {basePatchNum: 1};
reportStub = sinon.stub(element.reporting, 'reportInteraction'); reportStub = sinon.stub(element.reporting, 'reportInteraction');
@@ -1167,23 +1181,17 @@ suite('gr-diff-host tests', () => {
assert.equal(actualThreads.length, 2); assert.equal(actualThreads.length, 2);
assert.equal(
actualThreads[0].start_datetime, '2015-12-23 15:00:20.396000000');
assert.equal(actualThreads[0].commentSide, 'left'); assert.equal(actualThreads[0].commentSide, 'left');
assert.equal(actualThreads[0].comments.length, 2); assert.equal(actualThreads[0].comments.length, 2);
assert.deepEqual(actualThreads[0].comments[0], comments[0]); assert.deepEqual(actualThreads[0].comments[0], comments[0]);
assert.deepEqual(actualThreads[0].comments[1], comments[1]); assert.deepEqual(actualThreads[0].comments[1], comments[1]);
assert.equal(actualThreads[0].patchNum, undefined); assert.equal(actualThreads[0].patchNum, undefined);
assert.equal(actualThreads[0].rootId, 'sallys_confession');
assert.equal(actualThreads[0].lineNum, 1); assert.equal(actualThreads[0].lineNum, 1);
assert.equal(
actualThreads[1].start_datetime, '2015-12-20 15:01:20.396000000');
assert.equal(actualThreads[1].commentSide, 'left'); assert.equal(actualThreads[1].commentSide, 'left');
assert.equal(actualThreads[1].comments.length, 1); assert.equal(actualThreads[1].comments.length, 1);
assert.deepEqual(actualThreads[1].comments[0], comments[2]); assert.deepEqual(actualThreads[1].comments[0], comments[2]);
assert.equal(actualThreads[1].patchNum, undefined); assert.equal(actualThreads[1].patchNum, undefined);
assert.equal(actualThreads[1].rootId, 'new_draft');
assert.equal(actualThreads[1].lineNum, undefined); assert.equal(actualThreads[1].lineNum, undefined);
}); });
@@ -1205,7 +1213,6 @@ suite('gr-diff-host tests', () => {
const expectedThreads = [ const expectedThreads = [
{ {
start_datetime: '2015-12-24 15:00:10.396000000',
commentSide: 'left', commentSide: 'left',
comments: [{ comments: [{
id: 'betsys_confession', id: 'betsys_confession',
@@ -1222,7 +1229,6 @@ suite('gr-diff-host tests', () => {
line: 1, line: 1,
}], }],
patchNum: 5, patchNum: 5,
rootId: 'betsys_confession',
range: { range: {
start_line: 1, start_line: 1,
start_character: 1, start_character: 1,
@@ -1264,14 +1270,12 @@ suite('gr-diff-host tests', () => {
id: 'sallys_confession', id: 'sallys_confession',
message: 'i like you, jack', message: 'i like you, jack',
updated: '2015-12-23 15:00:20.396000000', updated: '2015-12-23 15:00:20.396000000',
// line: 1, __commentSide: 'left',
// __commentSide: 'left',
}, { }, {
id: 'jacks_reply', id: 'jacks_reply',
message: 'i like you, too', message: 'i like you, too',
updated: '2015-12-24 15:01:20.396000000', updated: '2015-12-24 15:01:20.396000000',
// __commentSide: 'left', __commentSide: 'left',
// line: 1,
in_reply_to: 'sallys_confession', in_reply_to: 'sallys_confession',
}, },
]; ];
@@ -1375,9 +1379,9 @@ suite('gr-diff-host tests', () => {
const threads = []; const threads = [];
assert.deepEqual(element._filterThreadElsForLocation(threads, line), []); assert.deepEqual(element._filterThreadElsForLocation(threads, line), []);
assert.deepEqual(element._filterThreadElsForLocation(threads, line, assert.deepEqual(element._filterThreadElsForLocation(threads, line,
DiffSide.LEFT), []); Side.LEFT), []);
assert.deepEqual(element._filterThreadElsForLocation(threads, line, assert.deepEqual(element._filterThreadElsForLocation(threads, line,
DiffSide.RIGHT), []); Side.RIGHT), []);
}); });
test('_filterThreadElsForLocation for line comments', () => { test('_filterThreadElsForLocation for line comments', () => {
@@ -1403,9 +1407,9 @@ suite('gr-diff-host tests', () => {
assert.deepEqual(element._filterThreadElsForLocation(threadEls, line), assert.deepEqual(element._filterThreadElsForLocation(threadEls, line),
[l3, r5]); [l3, r5]);
assert.deepEqual(element._filterThreadElsForLocation(threadEls, line, assert.deepEqual(element._filterThreadElsForLocation(threadEls, line,
DiffSide.LEFT), [l3]); Side.LEFT), [l3]);
assert.deepEqual(element._filterThreadElsForLocation(threadEls, line, assert.deepEqual(element._filterThreadElsForLocation(threadEls, line,
DiffSide.RIGHT), [r5]); Side.RIGHT), [r5]);
}); });
test('_filterThreadElsForLocation for file comments', () => { test('_filterThreadElsForLocation for file comments', () => {
@@ -1423,11 +1427,11 @@ suite('gr-diff-host tests', () => {
assert.deepEqual(element._filterThreadElsForLocation(threadEls, line), assert.deepEqual(element._filterThreadElsForLocation(threadEls, line),
[l, r]); [l, r]);
assert.deepEqual(element._filterThreadElsForLocation(threadEls, line, assert.deepEqual(element._filterThreadElsForLocation(threadEls, line,
DiffSide.BOTH), [l, r]); Side.BOTH), [l, r]);
assert.deepEqual(element._filterThreadElsForLocation(threadEls, line, assert.deepEqual(element._filterThreadElsForLocation(threadEls, line,
DiffSide.LEFT), [l]); Side.LEFT), [l]);
assert.deepEqual(element._filterThreadElsForLocation(threadEls, line, assert.deepEqual(element._filterThreadElsForLocation(threadEls, line,
DiffSide.RIGHT), [r]); Side.RIGHT), [r]);
}); });
suite('syntax layer with syntax_highlighting on', () => { suite('syntax layer with syntax_highlighting on', () => {
@@ -1441,6 +1445,8 @@ suite('gr-diff-host tests', () => {
}; };
element.patchRange = {}; element.patchRange = {};
element.prefs = prefs; element.prefs = prefs;
element.changeNum = 123;
element.path = 'some/path';
}); });
test('gr-diff-host provides syntax highlighting layer to gr-diff', () => { test('gr-diff-host provides syntax highlighting layer to gr-diff', () => {
@@ -1551,6 +1557,8 @@ suite('gr-diff-host tests', () => {
}, },
}); });
element = basicFixture.instantiate(); element = basicFixture.instantiate();
element.changeNum = 123;
element.path = 'some/path';
const prefs = { const prefs = {
line_length: 10, line_length: 10,
show_tabs: true, show_tabs: true,

View File

@@ -31,7 +31,7 @@ import {
import {CancelablePromise, util} from '../../../scripts/util'; import {CancelablePromise, util} from '../../../scripts/util';
import {customElement, property} from '@polymer/decorators'; import {customElement, property} from '@polymer/decorators';
import {DiffContent} from '../../../types/common'; import {DiffContent} from '../../../types/common';
import {DiffSide} from '../gr-diff/gr-diff-utils'; import {Side} from '../../../constants/constants';
const WHOLE_FILE = -1; const WHOLE_FILE = -1;
@@ -567,8 +567,8 @@ export class GrDiffProcessor extends GestureEventListeners(
for (let i = 0; i < numLines; i++) { for (let i = 0; i < numLines; i++) {
// If this line should not be collapsed. // If this line should not be collapsed.
if ( if (
this.keyLocations[DiffSide.LEFT][leftOffset + i] || this.keyLocations[Side.LEFT][leftOffset + i] ||
this.keyLocations[DiffSide.RIGHT][rightOffset + i] this.keyLocations[Side.RIGHT][rightOffset + i]
) { ) {
// If any lines have been accumulated into the chunk leading up to // If any lines have been accumulated into the chunk leading up to
// this non-collapse line, then add them as a chunk and start a new // this non-collapse line, then add them as a chunk and start a new

View File

@@ -18,17 +18,12 @@
import {CommentRange} from '../../../types/common'; import {CommentRange} from '../../../types/common';
import {FILE, LineNumber} from './gr-diff-line'; import {FILE, LineNumber} from './gr-diff-line';
export enum DiffSide {
LEFT = 'left',
RIGHT = 'right',
}
/** /**
* Compare two ranges. Either argument may be falsy, but will only return * Compare two ranges. Either argument may be falsy, but will only return
* true if both are falsy or if neither are falsy and have the same position * true if both are falsy or if neither are falsy and have the same position
* values. * values.
*/ */
export function rangesEqual(a: CommentRange, b: CommentRange): boolean { export function rangesEqual(a?: CommentRange, b?: CommentRange): boolean {
if (!a && !b) { if (!a && !b) {
return true; return true;
} }

View File

@@ -27,7 +27,7 @@ import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-l
import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin'; import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin';
import {htmlTemplate} from './gr-diff_html'; import {htmlTemplate} from './gr-diff_html';
import {FILE, LineNumber} from './gr-diff-line'; import {FILE, LineNumber} from './gr-diff-line';
import {DiffSide, getLineNumber, rangesEqual} from './gr-diff-utils'; import {getLineNumber, rangesEqual} from './gr-diff-utils';
import {getHiddenScroll} from '../../../scripts/hiddenscroll'; import {getHiddenScroll} from '../../../scripts/hiddenscroll';
import {isMergeParent, patchNumEquals} from '../../../utils/patch-set-util'; import {isMergeParent, patchNumEquals} from '../../../utils/patch-set-util';
import {customElement, observe, property} from '@polymer/decorators'; import {customElement, observe, property} from '@polymer/decorators';
@@ -95,7 +95,7 @@ const COMMIT_MSG_LINE_LENGTH = 72;
const RENDER_DIFF_TABLE_DEBOUNCE_NAME = 'renderDiffTable'; const RENDER_DIFF_TABLE_DEBOUNCE_NAME = 'renderDiffTable';
interface LineOfInterest { export interface LineOfInterest {
number: number; number: number;
leftSide: boolean; leftSide: boolean;
} }
@@ -534,7 +534,7 @@ export class GrDiff extends GestureEventListeners(
this.dispatchEvent( this.dispatchEvent(
new CustomEvent('line-selected', { new CustomEvent('line-selected', {
detail: { detail: {
side: el.classList.contains('left') ? DiffSide.LEFT : DiffSide.RIGHT, side: el.classList.contains('left') ? Side.LEFT : Side.RIGHT,
number: el.getAttribute('data-value'), number: el.getAttribute('data-value'),
path: this.path, path: this.path,
}, },
@@ -614,7 +614,7 @@ export class GrDiff extends GestureEventListeners(
); );
return false; return false;
} }
const patchNum = el.classList.contains(DiffSide.LEFT) const patchNum = el.classList.contains(Side.LEFT)
? this.patchRange.basePatchNum ? this.patchRange.basePatchNum
: this.patchRange.patchNum; : this.patchRange.patchNum;
@@ -717,7 +717,7 @@ export class GrDiff extends GestureEventListeners(
let patchNum = this.patchRange.patchNum; let patchNum = this.patchRange.patchNum;
if ( if (
(lineEl.classList.contains(DiffSide.LEFT) || (lineEl.classList.contains(Side.LEFT) ||
contentEl.classList.contains('remove')) && contentEl.classList.contains('remove')) &&
this.patchRange.basePatchNum !== 'PARENT' && this.patchRange.basePatchNum !== 'PARENT' &&
!isMergeParent(this.patchRange.basePatchNum) !isMergeParent(this.patchRange.basePatchNum)
@@ -730,7 +730,7 @@ export class GrDiff extends GestureEventListeners(
_getIsParentCommentByLineAndContent(lineEl: Element, contentEl: Element) { _getIsParentCommentByLineAndContent(lineEl: Element, contentEl: Element) {
if (!this.patchRange) throw Error('patch range not set'); if (!this.patchRange) throw Error('patch range not set');
return ( return (
(lineEl.classList.contains(DiffSide.LEFT) || (lineEl.classList.contains(Side.LEFT) ||
contentEl.classList.contains('remove')) && contentEl.classList.contains('remove')) &&
(this.patchRange.basePatchNum === 'PARENT' || (this.patchRange.basePatchNum === 'PARENT' ||
isMergeParent(this.patchRange.basePatchNum)) isMergeParent(this.patchRange.basePatchNum))
@@ -740,7 +740,7 @@ export class GrDiff extends GestureEventListeners(
_getCommentSideByLineAndContent(lineEl: Element, contentEl: Element): Side { _getCommentSideByLineAndContent(lineEl: Element, contentEl: Element): Side {
let side = Side.RIGHT; let side = Side.RIGHT;
if ( if (
lineEl.classList.contains(DiffSide.LEFT) || lineEl.classList.contains(Side.LEFT) ||
contentEl.classList.contains('remove') contentEl.classList.contains('remove')
) { ) {
side = Side.LEFT; side = Side.LEFT;

View File

@@ -51,7 +51,7 @@ import {
DropDownValueChangeEvent, DropDownValueChangeEvent,
GrDropdownList, GrDropdownList,
} from '../../shared/gr-dropdown-list/gr-dropdown-list'; } from '../../shared/gr-dropdown-list/gr-dropdown-list';
import {WebLink} from '../../core/gr-navigation/gr-navigation'; import {GeneratedWebLink} from '../../core/gr-navigation/gr-navigation';
// Maximum length for patch set descriptions. // Maximum length for patch set descriptions.
const PATCH_DESC_MAX_LENGTH = 500; const PATCH_DESC_MAX_LENGTH = 500;
@@ -63,9 +63,9 @@ export interface PatchRangeChangeDetail {
export type PatchRangeChangeEvent = CustomEvent<PatchRangeChangeDetail>; export type PatchRangeChangeEvent = CustomEvent<PatchRangeChangeDetail>;
interface FilesWebLinks { export interface FilesWebLinks {
meta_a: WebLink[]; meta_a: GeneratedWebLink[];
meta_b: WebLink[]; meta_b: GeneratedWebLink[];
} }
export interface GrPatchRangeSelect { export interface GrPatchRangeSelect {

View File

@@ -25,6 +25,7 @@ import {RevisionInfo} from '../../shared/revision-info/revision-info.js';
import {createCommentApiMockWithTemplateElement} from '../../../test/mocks/comment-api'; import {createCommentApiMockWithTemplateElement} from '../../../test/mocks/comment-api';
import {html} from '@polymer/polymer/lib/utils/html-tag.js'; import {html} from '@polymer/polymer/lib/utils/html-tag.js';
import {SPECIAL_PATCH_SET_NUM} from '../../../utils/patch-set-util.js'; import {SPECIAL_PATCH_SET_NUM} from '../../../utils/patch-set-util.js';
import {ChangeComments} from '../gr-comment-api/gr-comment-api.js';
const commentApiMockElement = createCommentApiMockWithTemplateElement( const commentApiMockElement = createCommentApiMockWithTemplateElement(
'gr-patch-range-select-comment-api-mock', html` 'gr-patch-range-select-comment-api-mock', html`
@@ -355,7 +356,7 @@ suite('gr-patch-range-select tests', () => {
test('_computePatchSetCommentsString', () => { test('_computePatchSetCommentsString', () => {
// Test string with unresolved comments. // Test string with unresolved comments.
element.changeComments._comments = { const comments = {
foo: [{ foo: [{
id: '27dcee4d_f7b77cfa', id: '27dcee4d_f7b77cfa',
message: 'test', message: 'test',
@@ -377,6 +378,7 @@ suite('gr-patch-range-select tests', () => {
}], }],
abc: [], abc: [],
}; };
element.changeComments = new ChangeComments(comments, {}, {}, 123);
assert.equal(element._computePatchSetCommentsString( assert.equal(element._computePatchSetCommentsString(
element.changeComments, 1), ' (3 comments, 1 unresolved)'); element.changeComments, 1), ' (3 comments, 1 unresolved)');

View File

@@ -27,10 +27,17 @@ import {
CustomKeyboardEvent, CustomKeyboardEvent,
KeyboardShortcutMixin, KeyboardShortcutMixin,
} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin'; } from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
import {sortComments} from '../../diff/gr-comment-api/gr-comment-api'; import {
isDraft,
isRobot,
sortComments,
UIComment,
UIDraft,
UIRobot,
} from '../../diff/gr-comment-api/gr-comment-api';
import {GerritNav} from '../../core/gr-navigation/gr-navigation'; import {GerritNav} from '../../core/gr-navigation/gr-navigation';
import {appContext} from '../../../services/app-context'; import {appContext} from '../../../services/app-context';
import {CommentSide, SpecialFilePath} from '../../../constants/constants'; import {CommentSide, Side, SpecialFilePath} from '../../../constants/constants';
import {computeDisplayPath} from '../../../utils/path-list-util'; import {computeDisplayPath} from '../../../utils/path-list-util';
import {customElement, observe, property} from '@polymer/decorators'; import {customElement, observe, property} from '@polymer/decorators';
import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api'; import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api';
@@ -42,7 +49,7 @@ import {
RepoName, RepoName,
UrlEncodedCommentId, UrlEncodedCommentId,
} from '../../../types/common'; } from '../../../types/common';
import {Comment, GrComment, RobotComment} from '../gr-comment/gr-comment'; import {GrComment} from '../gr-comment/gr-comment';
import {PolymerDeepPropertyChange} from '@polymer/polymer/interfaces'; import {PolymerDeepPropertyChange} from '@polymer/polymer/interfaces';
import {GrStorage, StorageLocation} from '../gr-storage/gr-storage'; import {GrStorage, StorageLocation} from '../gr-storage/gr-storage';
@@ -102,7 +109,7 @@ export class GrCommentThread extends KeyboardShortcutMixin(
changeNum?: NumericChangeId; changeNum?: NumericChangeId;
@property({type: Array}) @property({type: Array})
comments: Comment[] = []; comments: UIComment[] = [];
@property({type: Object, reflectToAttribute: true}) @property({type: Object, reflectToAttribute: true})
range?: CommentRange; range?: CommentRange;
@@ -111,7 +118,7 @@ export class GrCommentThread extends KeyboardShortcutMixin(
keyEventTarget: HTMLElement = document.body; keyEventTarget: HTMLElement = document.body;
@property({type: String, reflectToAttribute: true}) @property({type: String, reflectToAttribute: true})
commentSide?: string; commentSide?: Side;
@property({type: String}) @property({type: String})
patchNum?: PatchSetNum; patchNum?: PatchSetNum;
@@ -151,10 +158,10 @@ export class GrCommentThread extends KeyboardShortcutMixin(
_showActions?: boolean; _showActions?: boolean;
@property({type: Object}) @property({type: Object})
_lastComment?: Comment; _lastComment?: UIComment;
@property({type: Array}) @property({type: Array})
_orderedComments: Comment[] = []; _orderedComments: UIComment[] = [];
@property({type: Object}) @property({type: Object})
_projectConfig?: ConfigInfo; _projectConfig?: ConfigInfo;
@@ -197,7 +204,7 @@ export class GrCommentThread extends KeyboardShortcutMixin(
addOrEditDraft(lineNum?: number, rangeParam?: CommentRange) { addOrEditDraft(lineNum?: number, rangeParam?: CommentRange) {
const lastComment = this.comments[this.comments.length - 1] || {}; const lastComment = this.comments[this.comments.length - 1] || {};
if (lastComment.__draft) { if (isDraft(lastComment)) {
const commentEl = this._commentElWithDraftID( const commentEl = this._commentElWithDraftID(
lastComment.id || lastComment.__draftID lastComment.id || lastComment.__draftID
); );
@@ -237,7 +244,7 @@ export class GrCommentThread extends KeyboardShortcutMixin(
_getDiffUrlForPath(path: string) { _getDiffUrlForPath(path: string) {
if (!this.changeNum) throw new Error('changeNum is missing'); if (!this.changeNum) throw new Error('changeNum is missing');
if (!this.projectName) throw new Error('projectName is missing'); if (!this.projectName) throw new Error('projectName is missing');
if (this.comments[0].__draft) { if (isDraft(this.comments[0])) {
return GerritNav.getUrlForDiffById( return GerritNav.getUrlForDiffById(
this.changeNum, this.changeNum,
this.projectName, this.projectName,
@@ -251,14 +258,15 @@ export class GrCommentThread extends KeyboardShortcutMixin(
} }
_getDiffUrlForComment( _getDiffUrlForComment(
projectName: RepoName, projectName?: RepoName,
changeNum: NumericChangeId, changeNum?: NumericChangeId,
path: string, path?: string,
patchNum: PatchSetNum patchNum?: PatchSetNum
) { ) {
if (!projectName || !changeNum || !path) return undefined;
if ( if (
(this.comments.length && this.comments[0].side === 'PARENT') || (this.comments.length && this.comments[0].side === 'PARENT') ||
this.comments[0].__draft isDraft(this.comments[0])
) { ) {
return GerritNav.getUrlForDiffById( return GerritNav.getUrlForDiffById(
changeNum, changeNum,
@@ -271,9 +279,7 @@ export class GrCommentThread extends KeyboardShortcutMixin(
} }
const id = this.comments[0].id; const id = this.comments[0].id;
if (!id) throw new Error('A published comment is missing the id.'); if (!id) throw new Error('A published comment is missing the id.');
if (!this.changeNum) throw new Error('changeNum is missing'); return GerritNav.getUrlForComment(changeNum, projectName, id);
if (!this.projectName) throw new Error('projectName is missing');
return GerritNav.getUrlForComment(this.changeNum, this.projectName, id);
} }
_isPatchsetLevelComment(path: string) { _isPatchsetLevelComment(path: string) {
@@ -315,19 +321,19 @@ export class GrCommentThread extends KeyboardShortcutMixin(
if (this._orderedComments.length) { if (this._orderedComments.length) {
this._lastComment = this._getLastComment(); this._lastComment = this._getLastComment();
this.unresolved = this._lastComment.unresolved; this.unresolved = this._lastComment.unresolved;
this.hasDraft = this._lastComment.__draft; this.hasDraft = isDraft(this._lastComment);
this.isRobotComment = !!(this._lastComment as RobotComment).robot_id; this.isRobotComment = isRobot(this._lastComment);
} }
} }
_shouldDisableAction(_showActions?: boolean, _lastComment?: Comment) { _shouldDisableAction(_showActions?: boolean, _lastComment?: UIComment) {
return !_showActions || !_lastComment || !!_lastComment.__draft; return !_showActions || !_lastComment || isDraft(_lastComment);
} }
_hideActions(_showActions?: boolean, _lastComment?: Comment) { _hideActions(_showActions?: boolean, _lastComment?: UIComment) {
return ( return (
this._shouldDisableAction(_showActions, _lastComment) || this._shouldDisableAction(_showActions, _lastComment) ||
!!(_lastComment as RobotComment).robot_id isRobot(_lastComment)
); );
} }
@@ -371,7 +377,7 @@ export class GrCommentThread extends KeyboardShortcutMixin(
if (this._orderedComments) { if (this._orderedComments) {
for (let i = 0; i < this._orderedComments.length; i++) { for (let i = 0; i < this._orderedComments.length; i++) {
const comment = this._orderedComments[i]; const comment = this._orderedComments[i];
const isRobotComment = !!(comment as RobotComment).robot_id; const isRobotComment = !!(comment as UIRobot).robot_id;
// False if it's an unresolved comment under UNRESOLVED_EXPAND_COUNT. // False if it's an unresolved comment under UNRESOLVED_EXPAND_COUNT.
const resolvedThread = const resolvedThread =
!this.unresolved || !this.unresolved ||
@@ -417,8 +423,8 @@ export class GrCommentThread extends KeyboardShortcutMixin(
} }
} }
_isDraft(comment: Comment) { _isDraft(comment: UIComment) {
return !!comment.__draft; return isDraft(comment);
} }
_processCommentReply(quote?: boolean) { _processCommentReply(quote?: boolean) {
@@ -463,9 +469,9 @@ export class GrCommentThread extends KeyboardShortcutMixin(
const els = this.root?.querySelectorAll('gr-comment'); const els = this.root?.querySelectorAll('gr-comment');
if (!els) return null; if (!els) return null;
for (const el of els) { for (const el of els) {
if (el.comment?.id === id || el.comment?.__draftID === id) { const c = el.comment;
return el; if (isRobot(c)) continue;
} if (c?.id === id || (isDraft(c) && c?.__draftID === id)) return el;
} }
return null; return null;
} }
@@ -487,7 +493,7 @@ export class GrCommentThread extends KeyboardShortcutMixin(
} }
_newDraft(lineNum?: number, range?: CommentRange) { _newDraft(lineNum?: number, range?: CommentRange) {
const d: Comment = { const d: UIDraft = {
__draft: true, __draft: true,
__draftID: Math.random().toString(36), __draftID: Math.random().toString(36),
__date: new Date(), __date: new Date(),
@@ -529,14 +535,16 @@ export class GrCommentThread extends KeyboardShortcutMixin(
return isOnParent ? CommentSide.PARENT : CommentSide.REVISION; return isOnParent ? CommentSide.PARENT : CommentSide.REVISION;
} }
_computeRootId(comments: PolymerDeepPropertyChange<Comment[], unknown>) { _computeRootId(comments: PolymerDeepPropertyChange<UIComment[], unknown>) {
// Keep the root ID even if the comment was removed, so that notification // Keep the root ID even if the comment was removed, so that notification
// to sync will know which thread to remove. // to sync will know which thread to remove.
if (!comments.base.length) { if (!comments.base.length) {
return this.rootId; return this.rootId;
} }
const rootComment = comments.base[0]; const rootComment = comments.base[0];
return rootComment.id || rootComment.__draftID; if (rootComment.id) return rootComment.id;
if (isDraft(rootComment)) return rootComment.__draftID;
throw new Error('Missing id in root comment.');
} }
_handleCommentDiscard(e: Event) { _handleCommentDiscard(e: Event) {
@@ -599,12 +607,12 @@ export class GrCommentThread extends KeyboardShortcutMixin(
this.updateThreadProperties(); this.updateThreadProperties();
} }
_indexOf(comment: Comment | undefined, arr: Comment[]) { _indexOf(comment: UIComment | undefined, arr: UIComment[]) {
if (!comment) return -1; if (!comment) return -1;
for (let i = 0; i < arr.length; i++) { for (let i = 0; i < arr.length; i++) {
const c = arr[i]; const c = arr[i];
if ( if (
(c.__draftID && c.__draftID === comment.__draftID) || (isDraft(c) && isDraft(comment) && c.__draftID === comment.__draftID) ||
(c.id && c.id === comment.id) (c.id && c.id === comment.id)
) { ) {
return i; return i;

View File

@@ -38,22 +38,27 @@ import {htmlTemplate} from './gr-comment_html';
import {KeyboardShortcutMixin} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin'; import {KeyboardShortcutMixin} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
import {getRootElement} from '../../../scripts/rootElement'; import {getRootElement} from '../../../scripts/rootElement';
import {appContext} from '../../../services/app-context'; import {appContext} from '../../../services/app-context';
import {customElement, property, observe} from '@polymer/decorators'; import {customElement, observe, property} from '@polymer/decorators';
import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api'; import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api';
import {GrTextarea} from '../gr-textarea/gr-textarea'; import {GrTextarea} from '../gr-textarea/gr-textarea';
import {GrStorage, StorageLocation} from '../gr-storage/gr-storage'; import {GrStorage, StorageLocation} from '../gr-storage/gr-storage';
import {GrOverlay} from '../gr-overlay/gr-overlay'; import {GrOverlay} from '../gr-overlay/gr-overlay';
import { import {
RobotCommentInfo,
PatchSetNum,
CommentInfo,
ConfigInfo,
AccountDetailInfo, AccountDetailInfo,
NumericChangeId, NumericChangeId,
ConfigInfo,
PatchSetNum,
} from '../../../types/common'; } from '../../../types/common';
import {GrButton} from '../gr-button/gr-button'; import {GrButton} from '../gr-button/gr-button';
import {GrConfirmDeleteCommentDialog} from '../gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog'; import {GrConfirmDeleteCommentDialog} from '../gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog';
import {GrDialog} from '../gr-dialog/gr-dialog'; import {GrDialog} from '../gr-dialog/gr-dialog';
import {Side} from '../../../constants/constants';
import {
isDraft,
UIComment,
UIDraft,
UIRobot,
} from '../../diff/gr-comment-api/gr-comment-api';
const STORAGE_DEBOUNCE_INTERVAL = 400; const STORAGE_DEBOUNCE_INTERVAL = 400;
const TOAST_DEBOUNCE_INTERVAL = 200; const TOAST_DEBOUNCE_INTERVAL = 200;
@@ -84,23 +89,6 @@ const RESPECTFUL_REVIEW_TIPS = [
'When disagreeing, explain the advantage of your approach.', 'When disagreeing, explain the advantage of your approach.',
]; ];
export interface Draft {
collapsed?: boolean;
__editing?: boolean;
__otherEditing?: boolean;
__draft?: boolean;
__draftID?: string;
__commentSide?: string;
__date?: Date;
}
export type Comment = Draft & CommentInfo;
export type RobotComment = Draft & RobotCommentInfo;
export function isRobotComment(c: Comment | RobotComment): c is RobotComment {
return (c as RobotComment).robot_id !== undefined;
}
interface CommentOverlays { interface CommentOverlays {
confirmDelete?: GrOverlay | null; confirmDelete?: GrOverlay | null;
confirmDiscard?: GrOverlay | null; confirmDiscard?: GrOverlay | null;
@@ -117,7 +105,7 @@ export interface GrComment {
export interface CommentEventDetail { export interface CommentEventDetail {
patchNum?: PatchSetNum; patchNum?: PatchSetNum;
comment?: Comment | RobotComment; comment?: UIComment;
} }
@customElement('gr-comment') @customElement('gr-comment')
@@ -174,10 +162,10 @@ export class GrComment extends KeyboardShortcutMixin(
changeNum?: NumericChangeId; changeNum?: NumericChangeId;
@property({type: Object, notify: true, observer: '_commentChanged'}) @property({type: Object, notify: true, observer: '_commentChanged'})
comment?: Comment | RobotComment; comment?: UIComment | UIRobot;
@property({type: Array}) @property({type: Array})
comments?: (Comment | RobotComment)[]; comments?: (UIComment | UIRobot)[];
@property({type: Boolean, reflectToAttribute: true}) @property({type: Boolean, reflectToAttribute: true})
isRobotComment = false; isRobotComment = false;
@@ -235,7 +223,7 @@ export class GrComment extends KeyboardShortcutMixin(
_messageText = ''; _messageText = '';
@property({type: String}) @property({type: String})
commentSide?: string; commentSide?: Side;
@property({type: String}) @property({type: String})
side?: string; side?: string;
@@ -311,7 +299,7 @@ export class GrComment extends KeyboardShortcutMixin(
} }
} }
_getAuthor(comment: Comment) { _getAuthor(comment: UIComment) {
return comment.author || this._selfAccount; return comment.author || this._selfAccount;
} }
@@ -410,7 +398,7 @@ export class GrComment extends KeyboardShortcutMixin(
} }
@observe('comment') @observe('comment')
_isRobotComment(comment: RobotComment) { _isRobotComment(comment: UIRobot) {
this.isRobotComment = !!comment.robot_id; this.isRobotComment = !!comment.robot_id;
} }
@@ -433,7 +421,7 @@ export class GrComment extends KeyboardShortcutMixin(
return 'DRAFT' + (unableToSave ? '(Failed to save)' : ''); return 'DRAFT' + (unableToSave ? '(Failed to save)' : '');
} }
save(opt_comment?: Comment) { save(opt_comment?: UIComment) {
let comment = opt_comment; let comment = opt_comment;
if (!comment) { if (!comment) {
comment = this.comment; comment = this.comment;
@@ -456,7 +444,8 @@ export class GrComment extends KeyboardShortcutMixin(
this._eraseDraftComment(); this._eraseDraftComment();
return this.$.restAPI.getResponseObject(response).then(obj => { return this.$.restAPI.getResponseObject(response).then(obj => {
const resComment = (obj as unknown) as Comment; const resComment = (obj as unknown) as UIDraft;
if (!isDraft(this.comment)) throw new Error('Can only save drafts.');
resComment.__draft = true; resComment.__draft = true;
// Maintain the ephemeral draft ID for identification by other // Maintain the ephemeral draft ID for identification by other
// elements. // elements.
@@ -495,7 +484,7 @@ export class GrComment extends KeyboardShortcutMixin(
}); });
} }
_commentChanged(comment: Comment) { _commentChanged(comment: UIComment) {
this.editing = !!comment.__editing; this.editing = !!comment.__editing;
this.resolved = !comment.unresolved; this.resolved = !comment.unresolved;
if (this.editing) { if (this.editing) {
@@ -513,7 +502,7 @@ export class GrComment extends KeyboardShortcutMixin(
c => c =>
c.in_reply_to && c.in_reply_to &&
c.in_reply_to === comment.id && c.in_reply_to === comment.id &&
!(c as RobotComment).robot_id !(c as UIRobot).robot_id
); );
} }
@@ -587,7 +576,7 @@ export class GrComment extends KeyboardShortcutMixin(
_computeSaveDisabled( _computeSaveDisabled(
draft: string, draft: string,
comment: Comment | undefined, comment: UIComment | undefined,
resolved?: boolean resolved?: boolean
) { ) {
// If resolved state has changed and a msg exists, save should be enabled. // If resolved state has changed and a msg exists, save should be enabled.
@@ -753,8 +742,8 @@ export class GrComment extends KeyboardShortcutMixin(
); );
} }
_hasNoFix(comment: Comment) { _hasNoFix(comment: UIComment) {
return !comment || !(comment as RobotComment).fix_suggestions; return !comment || !(comment as UIRobot).fix_suggestions;
} }
_handleDiscard(e: Event) { _handleDiscard(e: Event) {
@@ -785,7 +774,7 @@ export class GrComment extends KeyboardShortcutMixin(
_discardDraft() { _discardDraft() {
if (!this.comment) return Promise.reject(new Error('undefined comment')); if (!this.comment) return Promise.reject(new Error('undefined comment'));
if (!this.comment.__draft) { if (!isDraft(this.comment)) {
return Promise.reject(new Error('Cannot discard a non-draft comment.')); return Promise.reject(new Error('Cannot discard a non-draft comment.'));
} }
this.discarding = true; this.discarding = true;
@@ -883,7 +872,7 @@ export class GrComment extends KeyboardShortcutMixin(
this._handleFailedDraftRequest(); this._handleFailedDraftRequest();
} }
_saveDraft(draft?: Comment) { _saveDraft(draft?: UIComment) {
if (!draft || this.changeNum === undefined || this.patchNum === undefined) { if (!draft || this.changeNum === undefined || this.patchNum === undefined) {
throw new Error('undefined draft or changeNum or patchNum'); throw new Error('undefined draft or changeNum or patchNum');
} }
@@ -907,7 +896,7 @@ export class GrComment extends KeyboardShortcutMixin(
}); });
} }
_deleteDraft(draft: Comment) { _deleteDraft(draft: UIComment) {
if (this.changeNum === undefined || this.patchNum === undefined) { if (this.changeNum === undefined || this.patchNum === undefined) {
throw new Error('undefined changeNum or patchNum'); throw new Error('undefined changeNum or patchNum');
} }
@@ -937,7 +926,7 @@ export class GrComment extends KeyboardShortcutMixin(
_loadLocalDraft( _loadLocalDraft(
changeNum: number, changeNum: number,
patchNum?: PatchSetNum, patchNum?: PatchSetNum,
comment?: Comment comment?: UIComment
) { ) {
// Polymer 2: check for undefined // Polymer 2: check for undefined
if ([changeNum, patchNum, comment].includes(undefined)) { if ([changeNum, patchNum, comment].includes(undefined)) {
@@ -1012,7 +1001,7 @@ export class GrComment extends KeyboardShortcutMixin(
return overlay.open(); return overlay.open();
} }
_computeHideRunDetails(comment: RobotComment, collapsed: boolean) { _computeHideRunDetails(comment: UIRobot, collapsed: boolean) {
if (!comment) return true; if (!comment) return true;
return !(comment.robot_id && comment.url && !collapsed); return !(comment.robot_id && comment.url && !collapsed);
} }

View File

@@ -15,19 +15,18 @@
* limitations under the License. * limitations under the License.
*/ */
import {GrAnnotationActionsContext} from './gr-annotation-actions-context'; import {GrAnnotationActionsContext} from './gr-annotation-actions-context';
import {GrDiffLine, LineNumber} from '../../diff/gr-diff/gr-diff-line'; import {GrDiffLine} from '../../diff/gr-diff/gr-diff-line';
import {CoverageRange} from '../../../types/types'; import {
CoverageRange,
DiffLayer,
DiffLayerListener,
} from '../../../types/types';
import {Side} from '../../../constants/constants'; import {Side} from '../../../constants/constants';
import {PluginApi} from '../../plugins/gr-plugin-types'; import {PluginApi} from '../../plugins/gr-plugin-types';
import {NumericChangeId} from '../../../types/common';
type AddLayerFunc = (ctx: GrAnnotationActionsContext) => void; type AddLayerFunc = (ctx: GrAnnotationActionsContext) => void;
type LayerUpdateListener = (
start: LineNumber,
end: LineNumber,
side: Side
) => void;
type NotifyFunc = ( type NotifyFunc = (
path: string, path: string,
start: number, start: number,
@@ -36,10 +35,10 @@ type NotifyFunc = (
) => void; ) => void;
export type CoverageProvider = ( export type CoverageProvider = (
changeNum: number, changeNum: NumericChangeId,
path: string, path: string,
basePatchNum: number, basePatchNum?: number,
patchNum: number patchNum?: number
) => Promise<Array<CoverageRange>>; ) => Promise<Array<CoverageRange>>;
export class GrAnnotationActionsInterface { export class GrAnnotationActionsInterface {
@@ -205,8 +204,8 @@ export class GrAnnotationActionsInterface {
} }
} }
export class AnnotationLayer { export class AnnotationLayer implements DiffLayer {
private listeners: LayerUpdateListener[] = []; private listeners: DiffLayerListener[] = [];
/** /**
* Used to create an instance of the Annotation Layer interface. * Used to create an instance of the Annotation Layer interface.
@@ -232,12 +231,12 @@ export class AnnotationLayer {
* Should accept as arguments the line numbers for the start and end of * Should accept as arguments the line numbers for the start and end of
* the update and the side as a string. * the update and the side as a string.
*/ */
addListener(fn: () => void) { addListener(listener: DiffLayerListener) {
this.listeners.push(fn); this.listeners.push(listener);
} }
removeListener(fn: () => void) { removeListener(listener: DiffLayerListener) {
this.listeners = this.listeners.filter(f => f !== fn); this.listeners = this.listeners.filter(f => f !== listener);
} }
/** /**
@@ -271,7 +270,7 @@ export class AnnotationLayer {
* @param end The line where the update ends. * @param end The line where the update ends.
* @param side The side of the update. ('left' or 'right') * @param side The side of the update. ('left' or 'right')
*/ */
notifyListeners(start: LineNumber, end: LineNumber, side: Side) { notifyListeners(start: number, end: number, side: Side) {
for (const listener of this.listeners) { for (const listener of this.listeners) {
listener(start, end, side); listener(start, end, side);
} }

View File

@@ -21,10 +21,7 @@ import {getPluginLoader} from './gr-plugin-loader';
import {patchNumEquals} from '../../../utils/patch-set-util'; import {patchNumEquals} from '../../../utils/patch-set-util';
import {customElement} from '@polymer/decorators'; import {customElement} from '@polymer/decorators';
import {ChangeInfo, RevisionInfo} from '../../../types/common'; import {ChangeInfo, RevisionInfo} from '../../../types/common';
import { import {GrAnnotationActionsInterface} from './gr-annotation-actions-js-api';
CoverageProvider,
GrAnnotationActionsInterface,
} from './gr-annotation-actions-js-api';
import {GrAdminApi} from '../../plugins/gr-admin-api/gr-admin-api'; import {GrAdminApi} from '../../plugins/gr-admin-api/gr-admin-api';
import { import {
JsApiService, JsApiService,
@@ -33,7 +30,7 @@ import {
ShowRevisionActionsDetail, ShowRevisionActionsDetail,
} from './gr-js-api-types'; } from './gr-js-api-types';
import {EventType, TargetElement} from '../../plugins/gr-plugin-types'; import {EventType, TargetElement} from '../../plugins/gr-plugin-types';
import {HighlightJS} from '../../../types/types'; import {DiffLayer, HighlightJS} from '../../../types/types';
const elements: {[key: string]: HTMLElement} = {}; const elements: {[key: string]: HTMLElement} = {};
const eventCallbacks: {[key: string]: EventCallback[]} = {}; const eventCallbacks: {[key: string]: EventCallback[]} = {};
@@ -248,7 +245,7 @@ export class GrJsApiInterface
} }
getDiffLayers(path: string, changeNum: number) { getDiffLayers(path: string, changeNum: number) {
const layers = []; const layers: DiffLayer[] = [];
for (const cb of this._getEventCallbacks(EventType.ANNOTATE_DIFF)) { for (const cb of this._getEventCallbacks(EventType.ANNOTATE_DIFF)) {
const annotationApi = (cb as unknown) as GrAnnotationActionsInterface; const annotationApi = (cb as unknown) as GrAnnotationActionsInterface;
try { try {
@@ -279,7 +276,9 @@ export class GrJsApiInterface
* provider, the first one is returned. If no plugin offers a coverage provider, * provider, the first one is returned. If no plugin offers a coverage provider,
* will resolve to null. * will resolve to null.
*/ */
getCoverageAnnotationApi(): Promise<CoverageProvider | undefined> { getCoverageAnnotationApi(): Promise<
GrAnnotationActionsInterface | undefined
> {
return getPluginLoader() return getPluginLoader()
.awaitPluginsLoaded() .awaitPluginsLoaded()
.then( .then(
@@ -287,7 +286,7 @@ export class GrJsApiInterface
this._getEventCallbacks(EventType.ANNOTATE_DIFF).find(cb => { this._getEventCallbacks(EventType.ANNOTATE_DIFF).find(cb => {
const annotationApi = (cb as unknown) as GrAnnotationActionsInterface; const annotationApi = (cb as unknown) as GrAnnotationActionsInterface;
return annotationApi.getCoverageProvider(); return annotationApi.getCoverageProvider();
}) as CoverageProvider | undefined }) as GrAnnotationActionsInterface | undefined
); );
} }

View File

@@ -16,6 +16,8 @@
*/ */
import {ActionInfo, ChangeInfo, PatchSetNum} from '../../../types/common'; import {ActionInfo, ChangeInfo, PatchSetNum} from '../../../types/common';
import {EventType, TargetElement} from '../../plugins/gr-plugin-types'; import {EventType, TargetElement} from '../../plugins/gr-plugin-types';
import {DiffLayer} from '../../../types/types';
import {GrAnnotationActionsInterface} from './gr-annotation-actions-js-api';
export interface ShowChangeDetail { export interface ShowChangeDetail {
change: ChangeInfo; change: ChangeInfo;
@@ -45,5 +47,8 @@ export interface JsApiService {
origMsg: string origMsg: string
): string; ): string;
addElement(key: TargetElement, el: HTMLElement): void; addElement(key: TargetElement, el: HTMLElement): void;
getDiffLayers(path: string, changeNum: number): DiffLayer[];
disposeDiffLayers(path: string): void;
getCoverageAnnotationApi(): Promise<GrAnnotationActionsInterface | undefined>;
// TODO(TS): Add more methods when needed for the TS conversion. // TODO(TS): Add more methods when needed for the TS conversion.
} }

View File

@@ -133,6 +133,7 @@ import {
FixId, FixId,
FilePathToDiffInfoMap, FilePathToDiffInfoMap,
ChangeViewChangeInfo, ChangeViewChangeInfo,
BlameInfo,
} from '../../../types/common'; } from '../../../types/common';
import { import {
CancelConditionCallback, CancelConditionCallback,
@@ -2524,7 +2525,7 @@ export class GrRestApiInterface
req.fetchOptions.headers.append('Cache-Control', 'no-cache'); req.fetchOptions.headers.append('Cache-Control', 'no-cache');
} }
return this._getChangeURLAndFetch(req); return this._getChangeURLAndFetch(req) as Promise<DiffInfo | undefined>;
} }
getDiffComments( getDiffComments(
@@ -2639,11 +2640,9 @@ export class GrRestApiInterface
_setRanges(comments?: CommentInfo[]) { _setRanges(comments?: CommentInfo[]) {
comments = comments || []; comments = comments || [];
comments.sort((a, b) => { comments.sort(
if (!a.updated) return 1; (a, b) => parseDate(a.updated).valueOf() - parseDate(b.updated).valueOf()
if (!b.updated) return -1; );
return parseDate(a.updated).valueOf() - parseDate(b.updated).valueOf();
});
for (const comment of comments) { for (const comment of comments) {
this._setRange(comments, comment); this._setRange(comments, comment);
} }
@@ -3472,7 +3471,7 @@ export class GrRestApiInterface
patchNum, patchNum,
params: base ? {base: 't'} : undefined, params: base ? {base: 't'} : undefined,
anonymizedEndpoint: '/files/*/blame', anonymizedEndpoint: '/files/*/blame',
}); }) as Promise<BlameInfo[] | undefined>;
} }
/** /**

View File

@@ -87,9 +87,13 @@ import {
EmailAddress, EmailAddress,
FixId, FixId,
FilePathToDiffInfoMap, FilePathToDiffInfoMap,
DiffInfo,
BlameInfo,
PatchRange,
ImagesForDiff,
} from '../../../types/common'; } from '../../../types/common';
import {ParsedChangeInfo} from '../../../elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser'; import {ParsedChangeInfo} from '../../../elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser';
import {HttpMethod} from '../../../constants/constants'; import {HttpMethod, IgnoreWhitespaceType} from '../../../constants/constants';
export type ErrorCallback = (response?: Response | null, err?: Error) => void; export type ErrorCallback = (response?: Response | null, err?: Error) => void;
export type CancelConditionCallback = () => boolean; export type CancelConditionCallback = () => boolean;
@@ -735,4 +739,26 @@ export interface RestApiService {
patchNum: PatchSetNum, patchNum: PatchSetNum,
fixId: string fixId: string
): Promise<Response>; ): Promise<Response>;
getDiff(
changeNum: NumericChangeId,
basePatchNum: PatchSetNum,
patchNum: PatchSetNum,
path: string,
whitespace?: IgnoreWhitespaceType,
errFn?: ErrorCallback
): Promise<DiffInfo | undefined>;
getBlame(
changeNum: NumericChangeId,
patchNum: PatchSetNum,
path: string,
base?: boolean
): Promise<BlameInfo[] | undefined>;
getImagesForDiff(
changeNum: NumericChangeId,
diff: DiffInfo,
patchRange: PatchRange
): Promise<ImagesForDiff>;
} }

View File

@@ -1078,7 +1078,7 @@ export interface UserConfigInfo {
*/ */
export interface CommentInfo extends CommentInput { export interface CommentInfo extends CommentInput {
patch_set?: PatchSetNum; patch_set?: PatchSetNum;
id?: UrlEncodedCommentId; id: UrlEncodedCommentId;
path?: string; path?: string;
side?: CommentSide; side?: CommentSide;
parent?: number; parent?: number;
@@ -1086,7 +1086,7 @@ export interface CommentInfo extends CommentInput {
range?: CommentRange; range?: CommentRange;
in_reply_to?: UrlEncodedCommentId; in_reply_to?: UrlEncodedCommentId;
message?: string; message?: string;
updated?: Timestamp; updated: Timestamp;
author?: AccountInfo; author?: AccountInfo;
tag?: string; tag?: string;
unresolved?: boolean; unresolved?: boolean;
@@ -1876,6 +1876,18 @@ export type RecipientTypeToNotifyInfoMap = {
*/ */
export type RobotCommentInput = RobotCommentInfo; export type RobotCommentInput = RobotCommentInfo;
/**
* This is what human, robot and draft comments can agree upon.
*
* Human, robot and saved draft comments all have a required id, but unsaved
* drafts do not. That is why the id is omitted from CommentInfo, such that it
* can be optional in Draft, but required in CommentInfo and RobotCommentInfo.
*/
export interface CommentBasics extends Omit<CommentInfo, 'id' | 'updated'> {
id?: UrlEncodedCommentId;
updated?: Timestamp;
}
/** /**
* The RobotCommentInfo entity contains information about a robot inline comment * The RobotCommentInfo entity contains information about a robot inline comment
* https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#robot-comment-info * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#robot-comment-info

View File

@@ -19,6 +19,7 @@ import {IronA11yAnnouncer} from '@polymer/iron-a11y-announcer/iron-a11y-announce
import {GrDiffLine} from '../elements/diff/gr-diff/gr-diff-line'; import {GrDiffLine} from '../elements/diff/gr-diff/gr-diff-line';
import {FlattenedNodesObserver} from '@polymer/polymer/lib/utils/flattened-nodes-observer'; import {FlattenedNodesObserver} from '@polymer/polymer/lib/utils/flattened-nodes-observer';
import {PaperInputElement} from '@polymer/paper-input/paper-input'; import {PaperInputElement} from '@polymer/paper-input/paper-input';
import {CommitId} from './common';
export function notUndefined<T>(x: T | undefined): x is T { export function notUndefined<T>(x: T | undefined): x is T {
return x !== undefined; return x !== undefined;
@@ -28,6 +29,11 @@ export interface FixIronA11yAnnouncer extends IronA11yAnnouncer {
requestAvailability(): void; requestAvailability(): void;
} }
export interface CommitRange {
baseCommit: CommitId;
commit: CommitId;
}
export interface CoverageRange { export interface CoverageRange {
type: CoverageType; type: CoverageType;
side: Side; side: Side;

View File

@@ -3,6 +3,7 @@ import {
ChangeInfo, ChangeInfo,
PatchSetNum, PatchSetNum,
EditPatchSetNum, EditPatchSetNum,
BrandType,
} from '../types/common'; } from '../types/common';
import {RestApiService} from '../services/services/gr-rest-api/gr-rest-api'; import {RestApiService} from '../services/services/gr-rest-api/gr-rest-api';
import {ParsedChangeInfo} from '../elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser'; import {ParsedChangeInfo} from '../elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser';
@@ -81,6 +82,12 @@ export function isMergeParent(n: PatchSetNum) {
return `${n}`[0] === '-'; return `${n}`[0] === '-';
} }
export function isNumber(
psn: PatchSetNum
): psn is BrandType<number, '_patchSet'> {
return typeof psn === 'number';
}
/** /**
* Given an object of revisions, get a particular revision based on patch * Given an object of revisions, get a particular revision based on patch
* num. * num.