Merge changes from topic "ts-diff-host"
* changes: Convert gr-diff-host to typescript Rename files to preserve history
This commit is contained in:
		@@ -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',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
					 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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,
 | 
					 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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',
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
											
										
									
								
							
							
								
								
									
										1228
									
								
								polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1228
									
								
								polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -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) doesn’t seem
 | 
					    // Using JSON.stringify because Safari 9.1 (11601.5.17.1) doesn’t 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,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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.
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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>;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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.
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user