Convert gr-change-view to typescript
The change converts the following files to typescript:
* elements/change/gr-change-view/gr-change-view.ts
Change-Id: I32a2ca2683757c8922e298b3c2d4336d3eb40dcf
diff --git a/polygerrit-ui/app/constants/constants.ts b/polygerrit-ui/app/constants/constants.ts
index 8e8eaf3..807adef 100644
--- a/polygerrit-ui/app/constants/constants.ts
+++ b/polygerrit-ui/app/constants/constants.ts
@@ -190,6 +190,17 @@
INHERIT = 'INHERIT',
}
+/**
+ * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#mergeable-info
+ */
+export enum MergeStrategy {
+ RECURSIVE = 'recursive',
+ RESOLVE = 'resolve',
+ SIMPLE_TWO_WAY_IN_CORE = 'simple-two-way-in-core',
+ OURS = 'ours',
+ THEIRS = 'theirs',
+}
+
/*
* Enum for possible configured value in InheritedBooleanInfo.
* https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#inherited-boolean-info
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
index 20fe1a6..68fb622 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
@@ -14,53 +14,56 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import '@polymer/paper-tabs/paper-tabs.js';
-import '../../../styles/shared-styles.js';
-import '../../diff/gr-comment-api/gr-comment-api.js';
-import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.js';
-import '../../plugins/gr-endpoint-param/gr-endpoint-param.js';
-import '../../shared/gr-account-link/gr-account-link.js';
-import '../../shared/gr-button/gr-button.js';
-import '../../shared/gr-change-star/gr-change-star.js';
-import '../../shared/gr-change-status/gr-change-status.js';
-import '../../shared/gr-date-formatter/gr-date-formatter.js';
-import '../../shared/gr-editable-content/gr-editable-content.js';
-import '../../shared/gr-js-api-interface/gr-js-api-interface.js';
-import '../../shared/gr-linked-text/gr-linked-text.js';
-import '../../shared/gr-overlay/gr-overlay.js';
-import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
-import '../../shared/gr-tooltip-content/gr-tooltip-content.js';
-import '../../shared/revision-info/revision-info.js';
-import '../gr-change-actions/gr-change-actions.js';
-import '../gr-change-metadata/gr-change-metadata.js';
-import '../../shared/gr-icons/gr-icons.js';
-import '../gr-commit-info/gr-commit-info.js';
-import '../gr-download-dialog/gr-download-dialog.js';
-import '../gr-file-list-header/gr-file-list-header.js';
-import '../gr-included-in-dialog/gr-included-in-dialog.js';
-import '../gr-messages-list/gr-messages-list.js';
-import '../gr-related-changes-list/gr-related-changes-list.js';
-import '../../diff/gr-apply-fix-dialog/gr-apply-fix-dialog.js';
-import '../gr-reply-dialog/gr-reply-dialog.js';
-import '../gr-thread-list/gr-thread-list.js';
-import '../gr-upload-help-dialog/gr-upload-help-dialog.js';
-import {flush} from '@polymer/polymer/lib/legacy/polymer.dom.js';
-import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
-import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
-import {PolymerElement} from '@polymer/polymer/polymer-element.js';
-import {htmlTemplate} from './gr-change-view_html.js';
-import {KeyboardShortcutMixin, Shortcut} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js';
-import {GrEditConstants} from '../../edit/gr-edit-constants.js';
-import {GrCountStringFormatter} from '../../shared/gr-count-string-formatter/gr-count-string-formatter.js';
-import {getComputedStyleValue} from '../../../utils/dom-util.js';
-import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
-import {getPluginEndpoints} from '../../shared/gr-js-api-interface/gr-plugin-endpoints.js';
-import {getPluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader.js';
-import {RevisionInfo} from '../../shared/revision-info/revision-info.js';
-import {PrimaryTab, SecondaryTab} from '../../../constants/constants.js';
-import {NO_ROBOT_COMMENTS_THREADS_MSG} from '../../../constants/messages.js';
-import {appContext} from '../../../services/app-context.js';
-import {ChangeStatus} from '../../../constants/constants.js';
+import '@polymer/paper-tabs/paper-tabs';
+import '../../../styles/shared-styles';
+import '../../diff/gr-comment-api/gr-comment-api';
+import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator';
+import '../../plugins/gr-endpoint-param/gr-endpoint-param';
+import '../../shared/gr-account-link/gr-account-link';
+import '../../shared/gr-button/gr-button';
+import '../../shared/gr-change-star/gr-change-star';
+import '../../shared/gr-change-status/gr-change-status';
+import '../../shared/gr-date-formatter/gr-date-formatter';
+import '../../shared/gr-editable-content/gr-editable-content';
+import '../../shared/gr-js-api-interface/gr-js-api-interface';
+import '../../shared/gr-linked-text/gr-linked-text';
+import '../../shared/gr-overlay/gr-overlay';
+import '../../shared/gr-rest-api-interface/gr-rest-api-interface';
+import '../../shared/gr-tooltip-content/gr-tooltip-content';
+import '../gr-change-actions/gr-change-actions';
+import '../gr-change-metadata/gr-change-metadata';
+import '../../shared/gr-icons/gr-icons';
+import '../gr-commit-info/gr-commit-info';
+import '../gr-download-dialog/gr-download-dialog';
+import '../gr-file-list-header/gr-file-list-header';
+import '../gr-included-in-dialog/gr-included-in-dialog';
+import '../gr-messages-list/gr-messages-list';
+import '../gr-related-changes-list/gr-related-changes-list';
+import '../../diff/gr-apply-fix-dialog/gr-apply-fix-dialog';
+import '../gr-reply-dialog/gr-reply-dialog';
+import '../gr-thread-list/gr-thread-list';
+import '../gr-upload-help-dialog/gr-upload-help-dialog';
+import {flush} from '@polymer/polymer/lib/legacy/polymer.dom';
+import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners';
+import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin';
+import {PolymerElement} from '@polymer/polymer/polymer-element';
+import {htmlTemplate} from './gr-change-view_html';
+import {
+ KeyboardShortcutMixin,
+ Shortcut,
+ CustomKeyboardEvent,
+} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
+import {GrEditConstants} from '../../edit/gr-edit-constants';
+import {GrCountStringFormatter} from '../../shared/gr-count-string-formatter/gr-count-string-formatter';
+import {getComputedStyleValue} from '../../../utils/dom-util';
+import {GerritNav, GerritView} from '../../core/gr-navigation/gr-navigation';
+import {getPluginEndpoints} from '../../shared/gr-js-api-interface/gr-plugin-endpoints';
+import {getPluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader';
+import {RevisionInfo as RevisionInfoClass} from '../../shared/revision-info/revision-info';
+import {PrimaryTab, SecondaryTab} from '../../../constants/constants';
+import {NO_ROBOT_COMMENTS_THREADS_MSG} from '../../../constants/messages';
+import {appContext} from '../../../services/app-context';
+import {ChangeStatus} from '../../../constants/constants';
import {
computeAllPatchSets,
computeLatestPatchNum,
@@ -68,18 +71,81 @@
hasEditBasedOnCurrentPatchSet,
hasEditPatchsetLoaded,
patchNumEquals,
- SPECIAL_PATCH_SET_NUM,
-} from '../../../utils/patch-set-util.js';
-import {changeStatuses, changeStatusString} from '../../../utils/change-util.js';
-import {EventType} from '../../plugins/gr-plugin-types.js';
-import {DEFAULT_NUM_FILES_SHOWN} from '../gr-file-list/gr-file-list.js';
+ PatchSet,
+} from '../../../utils/patch-set-util';
+import {changeStatuses, changeStatusString} from '../../../utils/change-util';
+import {EventType} from '../../plugins/gr-plugin-types';
+import {customElement, property, observe} from '@polymer/decorators';
+import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api';
+import {GrJsApiInterface} from '../../shared/gr-js-api-interface/gr-js-api-interface-element';
+import {GrApplyFixDialog} from '../../diff/gr-apply-fix-dialog/gr-apply-fix-dialog';
+import {GrFileListHeader} from '../gr-file-list-header/gr-file-list-header';
+import {GrEditableContent} from '../../shared/gr-editable-content/gr-editable-content';
+import {GrOverlay} from '../../shared/gr-overlay/gr-overlay';
+import {GrRelatedChangesList} from '../gr-related-changes-list/gr-related-changes-list';
+import {GrChangeStar} from '../../shared/gr-change-star/gr-change-star';
+import {GrChangeActions} from '../gr-change-actions/gr-change-actions';
+import {
+ AccountDetailInfo,
+ ChangeInfo,
+ NumericChangeId,
+ PatchRange,
+ ActionNameToActionInfoMap,
+ CommitId,
+ PatchSetNum,
+ ParentPatchSetNum,
+ EditPatchSetNum,
+ ServerInfo,
+ ConfigInfo,
+ PreferencesInfo,
+ CommitInfo,
+ DiffPreferencesInfo,
+ RevisionInfo,
+ EditInfo,
+ LabelNameToInfoMap,
+ UrlEncodedCommentId,
+ QuickLabelInfo,
+ ApprovalInfo,
+ ElementPropertyDeepChange,
+} from '../../../types/common';
+import {GrReplyDialog, FocusTarget} from '../gr-reply-dialog/gr-reply-dialog';
+import {GrIncludedInDialog} from '../gr-included-in-dialog/gr-included-in-dialog';
+import {CommentEventDetail} from '../../shared/gr-comment/gr-comment';
+import {GrDownloadDialog} from '../gr-download-dialog/gr-download-dialog';
+import {GrChangeMetadata} from '../gr-change-metadata/gr-change-metadata';
+import {
+ GrCommentApi,
+ ChangeComments,
+} from '../../diff/gr-comment-api/gr-comment-api';
+import {hasOwnProperty} from '../../../utils/common-util';
+import {GrEditControls} from '../../edit/gr-edit-controls/gr-edit-controls';
+import {
+ CommentThread,
+ UIDraft,
+ DraftInfo,
+ isDraftThread,
+ isRobot,
+} from '../../../utils/comment-util';
+import {
+ PolymerDeepPropertyChange,
+ PolymerSpliceChange,
+ PolymerSplice,
+} from '@polymer/polymer/interfaces';
+import {AppElementChangeViewParams} from '../../gr-app-types';
+import {DropdownLink} from '../../shared/gr-dropdown/gr-dropdown';
+import {PaperTabsElement} from '@polymer/paper-tabs/paper-tabs';
+import {ParsedChangeInfo} from '../../shared/gr-rest-api-interface/gr-reviewer-updates-parser';
+import {
+ GrFileList,
+ DEFAULT_NUM_FILES_SHOWN,
+} from '../gr-file-list/gr-file-list';
+import {isPolymerSpliceChange} from '../../../types/types';
const CHANGE_ID_ERROR = {
MISMATCH: 'mismatch',
MISSING: 'missing',
};
-const CHANGE_ID_REGEX_PATTERN =
- /^(Change-Id\:\s|Link:.*\/id\/)(I[0-9a-f]{8,40})/gm;
+const CHANGE_ID_REGEX_PATTERN = /^(Change-Id:\s|Link:.*\/id\/)(I[0-9a-f]{8,40})/gm;
const MIN_LINES_FOR_COMMIT_COLLAPSE = 30;
@@ -111,10 +177,10 @@
NEW_MESSAGE: 'There are new messages on this change',
};
-const DiffViewMode = {
- SIDE_BY_SIDE: 'SIDE_BY_SIDE',
- UNIFIED: 'UNIFIED_DIFF',
-};
+enum DiffViewMode {
+ SIDE_BY_SIDE = 'SIDE_BY_SIDE',
+ UNIFIED = 'UNIFIED_DIFF',
+}
const CHANGE_DATA_TIMING_LABEL = 'ChangeDataLoaded';
const CHANGE_RELOAD_TIMING_LABEL = 'ChangeReloaded';
@@ -122,24 +188,62 @@
// Making the tab names more unique in case a plugin adds one with same name
const ROBOT_COMMENTS_LIMIT = 10;
-// types used in this file
-/**
- * Type for the custom event to switch tab.
- *
- * @typedef {Object} SwitchTabEventDetail
- * @property {?string} tab - name of the tab to set as active, from custom event
- * @property {?boolean} scrollIntoView - scroll into the tab afterwards, from custom event
- * @property {?number} value - index of tab to set as active, from paper-tabs event
- */
+// Type for the custom event to switch tab.
+interface SwitchTabEventDetail {
+ // name of the tab to set as active, from custom event
+ tab?: string;
+ // index of tab to set as active, from paper-tabs event
+ value?: number;
+ // scroll into the tab afterwards, from custom event
+ scrollIntoView?: boolean;
+}
-/**
- * @extends PolymerElement
- */
-class GrChangeView extends KeyboardShortcutMixin(
- GestureEventListeners(LegacyElementMixin(PolymerElement))) {
- static get template() { return htmlTemplate; }
+export interface ChangeViewState {
+ diffMode?: DiffViewMode;
+ scrollTop?: number;
+ showDownloadDialog?: boolean;
+ showReplyDialog?: boolean;
+ changeNum?: NumericChangeId;
+ numFilesShown?: number;
+ patchRange?: PatchRange;
+ diffViewMode?: boolean;
+}
- static get is() { return 'gr-change-view'; }
+export interface GrChangeView {
+ $: {
+ restAPI: RestApiService & Element;
+ jsAPI: GrJsApiInterface;
+ commentAPI: GrCommentApi;
+ applyFixDialog: GrApplyFixDialog;
+ fileList: GrFileList & Element;
+ fileListHeader: GrFileListHeader;
+ commitMessageEditor: GrEditableContent;
+ includedInOverlay: GrOverlay;
+ includedInDialog: GrIncludedInDialog;
+ downloadOverlay: GrOverlay;
+ downloadDialog: GrDownloadDialog;
+ uploadHelpOverlay: GrOverlay;
+ replyOverlay: GrOverlay;
+ replyDialog: GrReplyDialog;
+ mainContent: HTMLDivElement;
+ relatedChanges: GrRelatedChangesList;
+ changeStar: GrChangeStar;
+ actions: GrChangeActions;
+ commitMessage: HTMLDivElement;
+ commitAndRelated: HTMLDivElement;
+ metadata: GrChangeMetadata;
+ relatedChangesToggle: HTMLDivElement;
+ mainChangeInfo: HTMLDivElement;
+ };
+}
+@customElement('gr-change-view')
+export class GrChangeView extends KeyboardShortcutMixin(
+ GestureEventListeners(LegacyElementMixin(PolymerElement))
+) {
+ static get template() {
+ return htmlTemplate;
+ }
+
/**
* Fired when the title of the page should change.
*
@@ -158,271 +262,276 @@
* @event show-auth-required
*/
- static get properties() {
- return {
- /**
- * URL params passed from the router.
- */
- params: {
- type: Object,
- observer: '_paramsChanged',
- },
- /** @type {?} */
- viewState: {
- type: Object,
- notify: true,
- value() { return {}; },
- observer: '_viewStateChanged',
- },
- backPage: String,
- hasParent: Boolean,
- keyEventTarget: {
- type: Object,
- value() { return document.body; },
- },
- disableEdit: {
- type: Boolean,
- value: false,
- },
- disableDiffPrefs: {
- type: Boolean,
- value: false,
- },
- _diffPrefsDisabled: {
- type: Boolean,
- computed: '_computeDiffPrefsDisabled(disableDiffPrefs, _loggedIn)',
- },
- _commentThreads: Array,
- // TODO(taoalpha): Consider replacing diffDrafts
- // with _draftCommentThreads everywhere, currently only
- // replaced in reply-dialoig
- _draftCommentThreads: {
- type: Array,
- },
- _robotCommentThreads: {
- type: Array,
- computed: '_computeRobotCommentThreads(_commentThreads,'
- + ' _currentRobotCommentsPatchSet, _showAllRobotComments)',
- },
- /** @type {?} */
- _serverConfig: {
- type: Object,
- observer: '_startUpdateCheckTimer',
- },
- _diffPrefs: Object,
- _numFilesShown: {
- type: Number,
- value: DEFAULT_NUM_FILES_SHOWN,
- observer: '_numFilesShownChanged',
- },
- _account: {
- type: Object,
- value: {},
- },
- _prefs: Object,
- /** @type {?} */
- _changeComments: Object,
- _canStartReview: {
- type: Boolean,
- computed: '_computeCanStartReview(_change)',
- },
- /** @type {?} */
- _change: {
- type: Object,
- observer: '_changeChanged',
- },
- _revisionInfo: {
- type: Object,
- computed: '_getRevisionInfo(_change)',
- },
- /** @type {?} */
- _commitInfo: Object,
- _currentRevision: {
- type: Object,
- computed: '_computeCurrentRevision(_change.current_revision, ' +
- '_change.revisions)',
- observer: '_handleCurrentRevisionUpdate',
- },
- _files: Object,
- _changeNum: String,
- _diffDrafts: {
- type: Object,
- value() { return {}; },
- },
- _editingCommitMessage: {
- type: Boolean,
- value: false,
- },
- _hideEditCommitMessage: {
- type: Boolean,
- computed: '_computeHideEditCommitMessage(_loggedIn, ' +
- '_editingCommitMessage, _change, _editMode, _commitCollapsed, ' +
- '_commitCollapsible)',
- },
- _diffAgainst: String,
- /** @type {?string} */
- _latestCommitMessage: {
- type: String,
- value: '',
- },
- _constants: {
- type: Object,
- value: {
- SecondaryTab,
- PrimaryTab,
- },
- },
- _messages: {
- type: Object,
- value: {
- NO_ROBOT_COMMENTS_THREADS_MSG,
- },
- },
- _lineHeight: Number,
- _changeIdCommitMessageError: {
- type: String,
- computed:
- '_computeChangeIdCommitMessageError(_latestCommitMessage, _change)',
- },
- /** @type {?} */
- _patchRange: {
- type: Object,
- },
- _filesExpanded: String,
- _basePatchNum: String,
- _selectedRevision: Object,
- _currentRevisionActions: Object,
- _allPatchSets: {
- type: Array,
- computed: '_computeAllPatchSets(_change, _change.revisions.*)',
- },
- _loggedIn: {
- type: Boolean,
- value: false,
- },
- _loading: Boolean,
- /** @type {?} */
- _projectConfig: Object,
- _replyButtonLabel: {
- type: String,
- value: 'Reply',
- computed: '_computeReplyButtonLabel(_diffDrafts.*, _canStartReview)',
- },
- _selectedPatchSet: String,
- _shownFileCount: Number,
- _initialLoadComplete: {
- type: Boolean,
- value: false,
- },
- _replyDisabled: {
- type: Boolean,
- value: true,
- computed: '_computeReplyDisabled(_serverConfig)',
- },
- _changeStatus: {
- type: String,
- computed: '_changeStatusString(_change)',
- },
- _changeStatuses: {
- type: String,
- computed:
- '_computeChangeStatusChips(_change, _mergeable, _submitEnabled)',
- },
- /** If false, then the "Show more" button was used to expand. */
- _commitCollapsed: {
- type: Boolean,
- value: true,
- },
- /** Is the "Show more/less" button visible? */
- _commitCollapsible: {
- type: Boolean,
- computed: '_computeCommitCollapsible(_latestCommitMessage)',
- },
- _relatedChangesCollapsed: {
- type: Boolean,
- value: true,
- },
- /** @type {?number} */
- _updateCheckTimerHandle: Number,
- _editMode: {
- type: Boolean,
- computed: '_computeEditMode(_patchRange.*, params.*)',
- },
- _showRelatedToggle: {
- type: Boolean,
- value: false,
- observer: '_updateToggleContainerClass',
- },
- _parentIsCurrent: {
- type: Boolean,
- computed: '_isParentCurrent(_currentRevisionActions)',
- },
- _submitEnabled: {
- type: Boolean,
- computed: '_isSubmitEnabled(_currentRevisionActions)',
- },
+ reporting = appContext.reportingService;
- /** @type {?} */
- _mergeable: {
- type: Boolean,
- value: undefined,
- },
- _showFileTabContent: {
- type: Boolean,
- value: true,
- },
- /** @type {Array<string>} */
- _dynamicTabHeaderEndpoints: {
- type: Array,
- },
- /** @type {Array<string>} */
- _dynamicTabContentEndpoints: {
- type: Array,
- },
- // The dynamic content of the plugin added tab
- _selectedTabPluginEndpoint: {
- type: String,
- },
- // The dynamic heading of the plugin added tab
- _selectedTabPluginHeader: {
- type: String,
- },
- _robotCommentsPatchSetDropdownItems: {
- type: Array,
- value() { return []; },
- computed: '_computeRobotCommentsPatchSetDropdownItems(_change, ' +
- '_commentThreads)',
- },
- _currentRobotCommentsPatchSet: {
- type: Number,
- },
+ /**
+ * URL params passed from the router.
+ */
+ @property({type: Object, observer: '_paramsChanged'})
+ params?: AppElementChangeViewParams;
- /**
- * @type {Array<string>} this is a two-element tuple to always
- * hold the current active tab for both primary and secondary tabs
- */
- _activeTabs: {
- type: Array,
- value: [PrimaryTab.FILES, SecondaryTab.CHANGE_LOG],
- },
- _showAllRobotComments: {
- type: Boolean,
- value: false,
- },
- _showRobotCommentsButton: {
- type: Boolean,
- value: false,
- },
- };
- }
+ @property({type: Object, notify: true, observer: '_viewStateChanged'})
+ viewState: ChangeViewState = {};
- static get observers() {
- return [
- '_labelsChanged(_change.labels.*)',
- '_paramsAndChangeChanged(params, _change)',
- '_patchNumChanged(_patchRange.patchNum)',
- ];
- }
+ @property({type: String})
+ backPage?: string;
+
+ @property({type: Boolean})
+ hasParent?: boolean;
+
+ @property({type: Object})
+ keyEventTarget = document.body;
+
+ @property({type: Boolean})
+ disableEdit = false;
+
+ @property({type: Boolean})
+ disableDiffPrefs = false;
+
+ @property({
+ type: Boolean,
+ computed: '_computeDiffPrefsDisabled(disableDiffPrefs, _loggedIn)',
+ })
+ _diffPrefsDisabled?: boolean;
+
+ @property({type: Array})
+ _commentThreads?: CommentThread[];
+
+ // TODO(taoalpha): Consider replacing diffDrafts
+ // with _draftCommentThreads everywhere, currently only
+ // replaced in reply-dialog
+ @property({type: Array})
+ _draftCommentThreads?: CommentThread[];
+
+ @property({
+ type: Array,
+ computed:
+ '_computeRobotCommentThreads(_commentThreads,' +
+ ' _currentRobotCommentsPatchSet, _showAllRobotComments)',
+ })
+ _robotCommentThreads?: CommentThread[];
+
+ @property({type: Object, observer: '_startUpdateCheckTimer'})
+ _serverConfig?: ServerInfo;
+
+ @property({type: Object})
+ _diffPrefs?: DiffPreferencesInfo;
+
+ @property({type: Number, observer: '_numFilesShownChanged'})
+ _numFilesShown = DEFAULT_NUM_FILES_SHOWN;
+
+ @property({type: Object})
+ _account?: AccountDetailInfo;
+
+ @property({type: Object})
+ _prefs?: PreferencesInfo;
+
+ @property({type: Object})
+ _changeComments?: ChangeComments;
+
+ @property({type: Boolean, computed: '_computeCanStartReview(_change)'})
+ _canStartReview?: boolean;
+
+ @property({type: Object, observer: '_changeChanged'})
+ _change?: ChangeInfo | ParsedChangeInfo;
+
+ @property({type: Object, computed: '_getRevisionInfo(_change)'})
+ _revisionInfo?: RevisionInfoClass;
+
+ @property({type: Object})
+ _commitInfo?: CommitInfo;
+
+ @property({
+ type: Object,
+ computed:
+ '_computeCurrentRevision(_change.current_revision, ' +
+ '_change.revisions)',
+ observer: '_handleCurrentRevisionUpdate',
+ })
+ _currentRevision?: RevisionInfo;
+
+ @property({type: String})
+ _changeNum?: NumericChangeId;
+
+ @property({type: Object})
+ _diffDrafts?: {[path: string]: UIDraft[]} = {};
+
+ @property({type: Boolean})
+ _editingCommitMessage = false;
+
+ @property({
+ type: Boolean,
+ computed:
+ '_computeHideEditCommitMessage(_loggedIn, ' +
+ '_editingCommitMessage, _change, _editMode, _commitCollapsed, ' +
+ '_commitCollapsible)',
+ })
+ _hideEditCommitMessage?: boolean;
+
+ @property({type: String})
+ _diffAgainst?: string;
+
+ @property({type: String})
+ _latestCommitMessage: string | null = '';
+
+ @property({type: Object})
+ _constants = {
+ SecondaryTab,
+ PrimaryTab,
+ };
+
+ @property({type: Object})
+ _messages = NO_ROBOT_COMMENTS_THREADS_MSG;
+
+ @property({type: Number})
+ _lineHeight?: number;
+
+ @property({
+ type: String,
+ computed:
+ '_computeChangeIdCommitMessageError(_latestCommitMessage, _change)',
+ })
+ _changeIdCommitMessageError?: string;
+
+ @property({type: Object})
+ _patchRange?: PatchRange;
+
+ @property({type: String})
+ _filesExpanded?: string;
+
+ @property({type: String})
+ _basePatchNum?: string;
+
+ @property({type: Object})
+ _selectedRevision?: RevisionInfo;
+
+ @property({type: Object})
+ _currentRevisionActions?: ActionNameToActionInfoMap;
+
+ @property({
+ type: Array,
+ computed: '_computeAllPatchSets(_change, _change.revisions.*)',
+ })
+ _allPatchSets?: PatchSet[];
+
+ @property({type: Boolean})
+ _loggedIn = false;
+
+ @property({type: Boolean})
+ _loading?: boolean;
+
+ @property({type: Object})
+ _projectConfig?: ConfigInfo;
+
+ @property({
+ type: String,
+ computed: '_computeReplyButtonLabel(_diffDrafts.*, _canStartReview)',
+ })
+ _replyButtonLabel = 'Reply';
+
+ @property({type: String})
+ _selectedPatchSet?: string;
+
+ @property({type: Number})
+ _shownFileCount?: number;
+
+ @property({type: Boolean})
+ _initialLoadComplete = false;
+
+ @property({type: Boolean})
+ _replyDisabled = true;
+
+ @property({type: String, computed: '_changeStatusString(_change)'})
+ _changeStatus?: string;
+
+ @property({
+ type: String,
+ computed: '_computeChangeStatusChips(_change, _mergeable, _submitEnabled)',
+ })
+ _changeStatuses?: string;
+
+ /** If false, then the "Show more" button was used to expand. */
+ @property({type: Boolean})
+ _commitCollapsed = true;
+
+ /** Is the "Show more/less" button visible? */
+ @property({
+ type: Boolean,
+ computed: '_computeCommitCollapsible(_latestCommitMessage)',
+ })
+ _commitCollapsible?: boolean;
+
+ @property({type: Boolean})
+ _relatedChangesCollapsed = true;
+
+ @property({type: Number})
+ _updateCheckTimerHandle?: number | null;
+
+ @property({
+ type: Boolean,
+ computed: '_computeEditMode(_patchRange.*, params.*)',
+ })
+ _editMode?: boolean;
+
+ @property({type: Boolean, observer: '_updateToggleContainerClass'})
+ _showRelatedToggle = false;
+
+ @property({
+ type: Boolean,
+ computed: '_isParentCurrent(_currentRevisionActions)',
+ })
+ _parentIsCurrent?: boolean;
+
+ @property({
+ type: Boolean,
+ computed: '_isSubmitEnabled(_currentRevisionActions)',
+ })
+ _submitEnabled?: boolean;
+
+ @property({type: Boolean})
+ _mergeable: boolean | null = null;
+
+ @property({type: Boolean})
+ _showFileTabContent = true;
+
+ @property({type: Array})
+ _dynamicTabHeaderEndpoints: string[] = [];
+
+ @property({type: Array})
+ _dynamicTabContentEndpoints: string[] = [];
+
+ @property({type: String})
+ // The dynamic content of the plugin added tab
+ _selectedTabPluginEndpoint?: string;
+
+ @property({type: String})
+ // The dynamic heading of the plugin added tab
+ _selectedTabPluginHeader?: string;
+
+ @property({
+ type: Array,
+ computed:
+ '_computeRobotCommentsPatchSetDropdownItems(_change, _commentThreads)',
+ })
+ _robotCommentsPatchSetDropdownItems: DropdownLink[] = [];
+
+ @property({type: Number})
+ _currentRobotCommentsPatchSet?: PatchSetNum;
+
+ /**
+ * this is a two-element tuple to always
+ * hold the current active tab for both primary and secondary tabs
+ */
+ @property({type: Array})
+ _activeTabs: string[] = [PrimaryTab.FILES, SecondaryTab.CHANGE_LOG];
+
+ @property({type: Boolean})
+ _showAllRobotComments = false;
+
+ @property({type: Boolean})
+ _showRobotCommentsButton = false;
+
+ _throttledToggleChangeStar?: EventListener;
keyboardShortcuts() {
return {
@@ -430,8 +539,7 @@
[Shortcut.EMOJI_DROPDOWN]: null, // DOC_ONLY binding
[Shortcut.REFRESH_CHANGE]: '_handleRefreshChange',
[Shortcut.OPEN_REPLY_DIALOG]: '_handleOpenReplyDialog',
- [Shortcut.OPEN_DOWNLOAD_DIALOG]:
- '_handleOpenDownloadDialogShortcut',
+ [Shortcut.OPEN_DOWNLOAD_DIALOG]: '_handleOpenDownloadDialogShortcut',
[Shortcut.TOGGLE_DIFF_MODE]: '_handleToggleDiffMode',
[Shortcut.TOGGLE_CHANGE_STAR]: '_throttledToggleChangeStar',
[Shortcut.UP_TO_DASHBOARD]: '_handleUpToDashboard',
@@ -443,48 +551,44 @@
[Shortcut.DIFF_AGAINST_BASE]: '_handleDiffAgainstBase',
[Shortcut.DIFF_AGAINST_LATEST]: '_handleDiffAgainstLatest',
[Shortcut.DIFF_BASE_AGAINST_LEFT]: '_handleDiffBaseAgainstLeft',
- [Shortcut.DIFF_RIGHT_AGAINST_LATEST]:
- '_handleDiffRightAgainstLatest',
- [Shortcut.DIFF_BASE_AGAINST_LATEST]:
- '_handleDiffBaseAgainstLatest',
+ [Shortcut.DIFF_RIGHT_AGAINST_LATEST]: '_handleDiffRightAgainstLatest',
+ [Shortcut.DIFF_BASE_AGAINST_LATEST]: '_handleDiffBaseAgainstLatest',
};
}
- constructor() {
- super();
- this.reporting = appContext.reportingService;
- }
-
+ /** @override */
connectedCallback() {
super.connectedCallback();
this._throttledToggleChangeStar = this._throttleWrap(e =>
- this._handleToggleChangeStar(e));
+ this._handleToggleChangeStar(e as CustomKeyboardEvent)
+ );
}
/** @override */
created() {
super.created();
- this.addEventListener('topic-changed',
- () => this._handleTopicChanged());
+ this.addEventListener('topic-changed', () => this._handleTopicChanged());
this.addEventListener(
- // When an overlay is opened in a mobile viewport, the overlay has a full
- // screen view. When it has a full screen view, we do not want the
- // background to be scrollable. This will eliminate background scroll by
- // hiding most of the contents on the screen upon opening, and showing
- // again upon closing.
- 'fullscreen-overlay-opened',
- () => this._handleHideBackgroundContent());
+ // When an overlay is opened in a mobile viewport, the overlay has a full
+ // screen view. When it has a full screen view, we do not want the
+ // background to be scrollable. This will eliminate background scroll by
+ // hiding most of the contents on the screen upon opening, and showing
+ // again upon closing.
+ 'fullscreen-overlay-opened',
+ () => this._handleHideBackgroundContent()
+ );
- this.addEventListener('fullscreen-overlay-closed',
- () => this._handleShowBackgroundContent());
+ this.addEventListener('fullscreen-overlay-closed', () =>
+ this._handleShowBackgroundContent()
+ );
- this.addEventListener('diff-comments-modified',
- () => this._handleReloadCommentThreads());
+ this.addEventListener('diff-comments-modified', () =>
+ this._handleReloadCommentThreads()
+ );
- this.addEventListener('open-reply-dialog',
- e => this._openReplyDialog());
+ this.addEventListener('open-reply-dialog', () => this._openReplyDialog());
}
/** @override */
@@ -492,6 +596,7 @@
super.attached();
this._getServerConfig().then(config => {
this._serverConfig = config;
+ this._replyDisabled = false;
});
this._getLoggedIn().then(loggedIn => {
@@ -504,44 +609,56 @@
this._setDiffViewMode();
});
- getPluginLoader().awaitPluginsLoaded()
- .then(() => {
- this._dynamicTabHeaderEndpoints =
- getPluginEndpoints().getDynamicEndpoints('change-view-tab-header');
- this._dynamicTabContentEndpoints =
- getPluginEndpoints().getDynamicEndpoints('change-view-tab-content');
- if (this._dynamicTabContentEndpoints.length !==
- this._dynamicTabHeaderEndpoints.length) {
- console.warn('Different number of tab headers and tab content.');
- }
- })
- .then(() => this._initActiveTabs(this.params));
+ getPluginLoader()
+ .awaitPluginsLoaded()
+ .then(() => {
+ this._dynamicTabHeaderEndpoints = getPluginEndpoints().getDynamicEndpoints(
+ 'change-view-tab-header'
+ );
+ this._dynamicTabContentEndpoints = getPluginEndpoints().getDynamicEndpoints(
+ 'change-view-tab-content'
+ );
+ if (
+ this._dynamicTabContentEndpoints.length !==
+ this._dynamicTabHeaderEndpoints.length
+ ) {
+ console.warn('Different number of tab headers and tab content.');
+ }
+ })
+ .then(() => this._initActiveTabs(this.params));
this.addEventListener('comment-save', e => this._handleCommentSave(e));
- this.addEventListener('comment-refresh', e => this._reloadDrafts(e));
- this.addEventListener('comment-discard',
- e => this._handleCommentDiscard(e));
- this.addEventListener('change-message-deleted',
- () => this._reload());
- this.addEventListener('editable-content-save',
- e => this._handleCommitMessageSave(e));
- this.addEventListener('editable-content-cancel',
- e => this._handleCommitMessageCancel(e));
- this.addEventListener('open-fix-preview',
- e => this._onOpenFixPreview(e));
- this.addEventListener('close-fix-preview',
- e => this._onCloseFixPreview(e));
+ this.addEventListener('comment-refresh', () => this._reloadDrafts());
+ this.addEventListener('comment-discard', e =>
+ this._handleCommentDiscard(e)
+ );
+ this.addEventListener('change-message-deleted', () => this._reload());
+ this.addEventListener('editable-content-save', e =>
+ this._handleCommitMessageSave(e as CustomEvent<{content: string}>)
+ );
+ this.addEventListener('editable-content-cancel', () =>
+ this._handleCommitMessageCancel()
+ );
+ this.addEventListener('open-fix-preview', e =>
+ this._onOpenFixPreview(e as CustomEvent<CommentEventDetail>)
+ );
+ this.addEventListener('close-fix-preview', () => this._onCloseFixPreview());
this.listen(window, 'scroll', '_handleScroll');
this.listen(document, 'visibilitychange', '_handleVisibilityChange');
- this.addEventListener('show-primary-tab',
- e => this._setActivePrimaryTab(e));
- this.addEventListener('show-secondary-tab',
- e => this._setActiveSecondaryTab(e));
+ this.addEventListener('show-primary-tab', e =>
+ this._setActivePrimaryTab(e as CustomEvent<SwitchTabEventDetail>)
+ );
+ this.addEventListener('show-secondary-tab', e =>
+ this._setActiveSecondaryTab(e as CustomEvent<SwitchTabEventDetail>)
+ );
this.addEventListener('reload', e => {
e.stopPropagation();
- this._reload(/* opt_isLocationChange= */false,
- /* opt_clearPatchset= */e.detail && e.detail.clearPatchset);
+ const evt = e as CustomEvent<{clearPatchset: boolean}>;
+ this._reload(
+ /* isLocationChange= */ false,
+ /* clearPatchset= */ evt.detail && evt.detail.clearPatchset
+ );
});
}
@@ -557,47 +674,47 @@
}
get messagesList() {
- return this.shadowRoot.querySelector('gr-messages-list');
+ return this.shadowRoot!.querySelector('gr-messages-list');
}
get threadList() {
- return this.shadowRoot.querySelector('gr-thread-list');
+ return this.shadowRoot!.querySelector('gr-thread-list');
}
- _changeStatusString(change) {
+ _changeStatusString(change: ChangeInfo) {
return changeStatusString(change);
}
- /**
- * @param {boolean=} opt_reset
- */
- _setDiffViewMode(opt_reset) {
- if (!opt_reset && this.viewState.diffViewMode) { return; }
+ _setDiffViewMode(opt_reset?: boolean) {
+ if (!opt_reset && this.viewState.diffViewMode) {
+ return;
+ }
return this._getPreferences()
- .then( prefs => {
- if (!this.viewState.diffMode) {
- this.set('viewState.diffMode', prefs.default_diff_view);
- }
- })
- .then(() => {
- if (!this.viewState.diffMode) {
- this.set('viewState.diffMode', 'SIDE_BY_SIDE');
- }
- });
+ .then(prefs => {
+ if (!this.viewState.diffMode && prefs) {
+ this.set('viewState.diffMode', prefs.default_diff_view);
+ }
+ })
+ .then(() => {
+ if (!this.viewState.diffMode) {
+ this.set('viewState.diffMode', 'SIDE_BY_SIDE');
+ }
+ });
}
- _onOpenFixPreview(e) {
+ _onOpenFixPreview(e: CustomEvent<CommentEventDetail>) {
this.$.applyFixDialog.open(e);
}
- _onCloseFixPreview(e) {
+ _onCloseFixPreview() {
this._reload();
}
- _handleToggleDiffMode(e) {
- if (this.shouldSuppressKeyboardShortcut(e) ||
- this.modifierPressed(e)) { return; }
+ _handleToggleDiffMode(e: CustomKeyboardEvent) {
+ if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ return;
+ }
e.preventDefault();
if (this.viewState.diffMode === DiffViewMode.SIDE_BY_SIDE) {
@@ -607,19 +724,27 @@
}
}
- _isTabActive(tab, activeTabs) {
+ _isTabActive(tab: string, activeTabs: string[]) {
return activeTabs.includes(tab);
}
/**
* Actual implementation of switching a tab
*
- * @param {!HTMLElement} paperTabs - the parent tabs container
- * @param {!SwitchTabEventDetail} activeDetails
+ * @param paperTabs - the parent tabs container
*/
- _setActiveTab(paperTabs, activeDetails) {
+ _setActiveTab(
+ paperTabs: PaperTabsElement,
+ activeDetails: {
+ activeTabName?: string;
+ activeTabIndex?: number;
+ scrollIntoView?: boolean;
+ }
+ ) {
const {activeTabName, activeTabIndex, scrollIntoView} = activeDetails;
- const tabs = paperTabs.querySelectorAll('paper-tab');
+ const tabs = paperTabs.querySelectorAll('paper-tab') as NodeListOf<
+ HTMLElement
+ >;
let activeIndex = -1;
if (activeTabIndex !== undefined) {
activeIndex = activeTabIndex;
@@ -649,11 +774,11 @@
/**
* Changes active primary tab.
- *
- * @param {CustomEvent<SwitchTabEventDetail>} e
*/
- _setActivePrimaryTab(e) {
- const primaryTabs = this.shadowRoot.querySelector('#primaryTabs');
+ _setActivePrimaryTab(e: CustomEvent<SwitchTabEventDetail>) {
+ const primaryTabs = this.shadowRoot!.querySelector(
+ '#primaryTabs'
+ ) as PaperTabsElement;
const activeTabName = this._setActiveTab(primaryTabs, {
activeTabName: e.detail.tab,
activeTabIndex: e.detail.value,
@@ -664,12 +789,15 @@
// update plugin endpoint if its a plugin tab
const pluginIndex = (this._dynamicTabHeaderEndpoints || []).indexOf(
- activeTabName);
+ activeTabName
+ );
if (pluginIndex !== -1) {
this._selectedTabPluginEndpoint = this._dynamicTabContentEndpoints[
- pluginIndex];
+ pluginIndex
+ ];
this._selectedTabPluginHeader = this._dynamicTabHeaderEndpoints[
- pluginIndex];
+ pluginIndex
+ ];
} else {
this._selectedTabPluginEndpoint = '';
this._selectedTabPluginHeader = '';
@@ -679,11 +807,11 @@
/**
* Changes active secondary tab.
- *
- * @param {CustomEvent<SwitchTabEventDetail>} e
*/
- _setActiveSecondaryTab(e) {
- const secondaryTabs = this.shadowRoot.querySelector('#secondaryTabs');
+ _setActiveSecondaryTab(e: CustomEvent<SwitchTabEventDetail>) {
+ const secondaryTabs = this.shadowRoot!.querySelector(
+ '#secondaryTabs'
+ ) as PaperTabsElement;
const activeTabName = this._setActiveTab(secondaryTabs, {
activeTabName: e.detail.tab,
activeTabIndex: e.detail.value,
@@ -699,45 +827,47 @@
this.$.commitMessageEditor.focusTextarea();
}
- _handleCommitMessageSave(e) {
+ _handleCommitMessageSave(e: CustomEvent<{content: string}>) {
+ if (!this._change) throw new Error('missing required change property');
+ if (!this._changeNum)
+ throw new Error('missing required changeNum property');
// Trim trailing whitespace from each line.
const message = e.detail.content.replace(TRAILING_WHITESPACE_REGEX, '');
this.$.jsAPI.handleCommitMessage(this._change, message);
this.$.commitMessageEditor.disabled = true;
- this.$.restAPI.putChangeCommitMessage(
- this._changeNum, message)
- .then(resp => {
- this.$.commitMessageEditor.disabled = false;
- if (!resp.ok) { return; }
+ this.$.restAPI
+ .putChangeCommitMessage(this._changeNum, message)
+ .then(resp => {
+ this.$.commitMessageEditor.disabled = false;
+ if (!resp.ok) {
+ return;
+ }
- this._latestCommitMessage = this._prepareCommitMsgForLinkify(
- message);
- this._editingCommitMessage = false;
- this._reloadWindow();
- })
- .catch(err => {
- this.$.commitMessageEditor.disabled = false;
- });
+ this._latestCommitMessage = this._prepareCommitMsgForLinkify(message);
+ this._editingCommitMessage = false;
+ this._reloadWindow();
+ })
+ .catch(() => {
+ this.$.commitMessageEditor.disabled = false;
+ });
}
_reloadWindow() {
window.location.reload();
}
- _handleCommitMessageCancel(e) {
+ _handleCommitMessageCancel() {
this._editingCommitMessage = false;
}
- _computeChangeStatusChips(change, mergeable, submitEnabled) {
- // Polymer 2: check for undefined
- if ([
- change,
- mergeable,
- ].includes(undefined)) {
- // To keep consistent with Polymer 1, we are returning undefined
- // if not all dependencies are defined
+ _computeChangeStatusChips(
+ change: ChangeInfo | undefined,
+ mergeable: boolean | null,
+ submitEnabled?: boolean
+ ) {
+ if (!change) {
return undefined;
}
@@ -755,63 +885,77 @@
}
_computeHideEditCommitMessage(
- loggedIn, editing, change, editMode, collapsed, collapsible) {
- if (!loggedIn || editing ||
- (change && change.status === ChangeStatus.MERGED) ||
- editMode ||
- (collapsed && collapsible)) {
+ loggedIn: boolean,
+ editing: boolean,
+ change: ChangeInfo,
+ editMode: boolean,
+ collapsed: boolean,
+ collapsible: boolean
+ ) {
+ if (
+ !loggedIn ||
+ editing ||
+ (change && change.status === ChangeStatus.MERGED) ||
+ editMode ||
+ (collapsed && collapsible)
+ ) {
return true;
}
return false;
}
- _robotCommentCountPerPatchSet(threads) {
+ _robotCommentCountPerPatchSet(threads: CommentThread[]) {
return threads.reduce((robotCommentCountMap, thread) => {
const comments = thread.comments;
- const robotCommentsCount = comments.reduce((acc, comment) =>
- (comment.robot_id ? acc + 1 : acc), 0);
- robotCommentCountMap[comments[0].patch_set] =
- (robotCommentCountMap[comments[0].patch_set] || 0) +
- robotCommentsCount;
+ const robotCommentsCount = comments.reduce(
+ (acc, comment) => (isRobot(comment) ? acc + 1 : acc),
+ 0
+ );
+ if (comments[0].patch_set)
+ robotCommentCountMap[`${comments[0].patch_set}`] =
+ (robotCommentCountMap[`${comments[0].patch_set}`] || 0) +
+ robotCommentsCount;
return robotCommentCountMap;
- }, {});
+ }, {} as {[patchset: string]: number});
}
- _computeText(patch, commentThreads) {
+ _computeText(patch: RevisionInfo, commentThreads: CommentThread[]) {
const commentCount = this._robotCommentCountPerPatchSet(commentThreads);
const commentCnt = commentCount[patch._number] || 0;
if (commentCnt === 0) return `Patchset ${patch._number}`;
const findingsText = commentCnt === 1 ? 'finding' : 'findings';
- return `Patchset ${patch._number}`
- + ` (${commentCnt} ${findingsText})`;
+ return `Patchset ${patch._number} (${commentCnt} ${findingsText})`;
}
- _computeRobotCommentsPatchSetDropdownItems(change, commentThreads) {
+ _computeRobotCommentsPatchSetDropdownItems(
+ change: ChangeInfo,
+ commentThreads: CommentThread[]
+ ) {
if (!change || !commentThreads || !change.revisions) return [];
return Object.values(change.revisions)
- .filter(patch => patch._number !== 'edit')
- .map(patch => {
- return {
- text: this._computeText(patch, commentThreads),
- value: patch._number,
- };
- })
- .sort((a, b) => b.value - a.value);
+ .filter(patch => patch._number !== 'edit')
+ .map(patch => {
+ return {
+ text: this._computeText(patch, commentThreads),
+ value: patch._number,
+ };
+ })
+ .sort((a, b) => (b.value as number) - (a.value as number));
}
- _handleCurrentRevisionUpdate(currentRevision) {
+ _handleCurrentRevisionUpdate(currentRevision: RevisionInfo) {
this._currentRobotCommentsPatchSet = currentRevision._number;
}
- _handleRobotCommentPatchSetChanged(e) {
- const patchSet = parseInt(e.detail.value);
+ _handleRobotCommentPatchSetChanged(e: CustomEvent<{value: string}>) {
+ const patchSet = parseInt(e.detail.value) as PatchSetNum;
if (patchSet === this._currentRobotCommentsPatchSet) return;
this._currentRobotCommentsPatchSet = patchSet;
}
- _computeShowText(showAllRobotComments) {
+ _computeShowText(showAllRobotComments: boolean) {
return showAllRobotComments ? 'Show Less' : 'Show more';
}
@@ -819,56 +963,79 @@
this._showAllRobotComments = !this._showAllRobotComments;
}
- _computeRobotCommentThreads(commentThreads, currentRobotCommentsPatchSet,
- showAllRobotComments) {
+ _computeRobotCommentThreads(
+ commentThreads: CommentThread[],
+ currentRobotCommentsPatchSet: PatchSetNum,
+ showAllRobotComments: boolean
+ ) {
if (!commentThreads || !currentRobotCommentsPatchSet) return [];
const threads = commentThreads.filter(thread => {
const comments = thread.comments || [];
- return comments.length && comments[0].robot_id && (comments[0].patch_set
- === currentRobotCommentsPatchSet);
+ return (
+ comments.length &&
+ isRobot(comments[0]) &&
+ comments[0].patch_set === currentRobotCommentsPatchSet
+ );
});
this._showRobotCommentsButton = threads.length > ROBOT_COMMENTS_LIMIT;
- return threads.slice(0, showAllRobotComments ? undefined :
- ROBOT_COMMENTS_LIMIT);
+ return threads.slice(
+ 0,
+ showAllRobotComments ? undefined : ROBOT_COMMENTS_LIMIT
+ );
}
_handleReloadCommentThreads() {
// Get any new drafts that have been saved in the diff view and show
// in the comment thread view.
this._reloadDrafts().then(() => {
- this._commentThreads = this._changeComments.getAllThreadsForChange();
+ this._commentThreads = this._changeComments?.getAllThreadsForChange();
flush();
});
}
- _handleReloadDiffComments(e) {
+ _handleReloadDiffComments(
+ e: CustomEvent<{rootId: UrlEncodedCommentId; path: string}>
+ ) {
// Keeps the file list counts updated.
this._reloadDrafts().then(() => {
// Get any new drafts that have been saved in the thread view and show
// in the diff view.
- this.$.fileList.reloadCommentsForThreadWithRootId(e.detail.rootId,
- e.detail.path);
+ this.$.fileList.reloadCommentsForThreadWithRootId(
+ e.detail.rootId,
+ e.detail.path
+ );
flush();
});
}
- _computeTotalCommentCounts(unresolvedCount, changeComments) {
+ _computeTotalCommentCounts(
+ unresolvedCount: number,
+ changeComments: ChangeComments
+ ) {
if (!changeComments) return undefined;
const draftCount = changeComments.computeDraftCount();
const unresolvedString = GrCountStringFormatter.computeString(
- unresolvedCount, 'unresolved');
+ unresolvedCount,
+ 'unresolved'
+ );
const draftString = GrCountStringFormatter.computePluralString(
- draftCount, 'draft');
+ draftCount,
+ 'draft'
+ );
- return unresolvedString +
- // Add a comma and space if both unresolved and draft comments exist.
- (unresolvedString && draftString ? ', ' : '') +
- draftString;
+ return (
+ unresolvedString +
+ // Add a comma and space if both unresolved and draft comments exist.
+ (unresolvedString && draftString ? ', ' : '') +
+ draftString
+ );
}
- _handleCommentSave(e) {
+ _handleCommentSave(e: CustomEvent<{comment: DraftInfo}>) {
const draft = e.detail.comment;
- if (!draft.__draft) { return; }
+ if (!draft.__draft || !draft.path) return;
+ if (!this._patchRange)
+ throw new Error('missing required _patchRange property');
draft.patch_set = draft.patch_set || this._patchRange.patchNum;
@@ -882,27 +1049,30 @@
this._diffDrafts = diffDrafts;
return;
}
- for (let i = 0; i < this._diffDrafts[draft.path].length; i++) {
- if (this._diffDrafts[draft.path][i].id === draft.id) {
+ for (let i = 0; i < diffDrafts[draft.path].length; i++) {
+ if (diffDrafts[draft.path][i].id === draft.id) {
diffDrafts[draft.path][i] = draft;
this._diffDrafts = diffDrafts;
return;
}
}
diffDrafts[draft.path].push(draft);
- diffDrafts[draft.path].sort((c1, c2) =>
- // No line number means that it’s a file comment. Sort it above the
- // others.
- (c1.line || -1) - (c2.line || -1)
+ diffDrafts[draft.path].sort(
+ (c1, c2) =>
+ // No line number means that it’s a file comment. Sort it above the
+ // others.
+ (c1.line || -1) - (c2.line || -1)
);
this._diffDrafts = diffDrafts;
}
- _handleCommentDiscard(e) {
+ _handleCommentDiscard(e: CustomEvent<{comment: DraftInfo}>) {
const draft = e.detail.comment;
- if (!draft.__draft) { return; }
+ if (!draft.__draft || !draft.path) {
+ return;
+ }
- if (!this._diffDrafts[draft.path]) {
+ if (!this._diffDrafts || !this._diffDrafts[draft.path]) {
return;
}
let index = -1;
@@ -918,6 +1088,8 @@
return;
}
+ if (!this._patchRange)
+ throw new Error('missing required _patchRange property');
draft.patch_set = draft.patch_set || this._patchRange.patchNum;
// The use of path-based notification helpers (set, push) can’t be used
@@ -932,7 +1104,7 @@
this._diffDrafts = diffDrafts;
}
- _handleReplyTap(e) {
+ _handleReplyTap(e: MouseEvent) {
e.preventDefault();
this._openReplyDialog(this.$.replyDialog.FocusTarget.ANY);
}
@@ -949,34 +1121,37 @@
this.$.includedInOverlay.open();
}
- _handleIncludedInDialogClose(e) {
+ _handleIncludedInDialogClose() {
this.$.includedInOverlay.close();
}
_handleOpenDownloadDialog() {
this.$.downloadOverlay.open().then(() => {
- this.$.downloadOverlay
- .setFocusStops(this.$.downloadDialog.getFocusStops());
+ this.$.downloadOverlay.setFocusStops(
+ this.$.downloadDialog.getFocusStops()
+ );
this.$.downloadDialog.focus();
});
}
- _handleDownloadDialogClose(e) {
+ _handleDownloadDialogClose() {
this.$.downloadOverlay.close();
}
- _handleOpenUploadHelpDialog(e) {
+ _handleOpenUploadHelpDialog() {
this.$.uploadHelpOverlay.open();
}
- _handleCloseUploadHelpDialog(e) {
+ _handleCloseUploadHelpDialog() {
this.$.uploadHelpOverlay.close();
}
- _handleMessageReply(e) {
- const msg = e.detail.message.message;
- const quoteStr = msg.split('\n').map(
- line => '> ' + line)
+ _handleMessageReply(e: CustomEvent<{message: {message: string}}>) {
+ const msg: string = e.detail.message.message;
+ const quoteStr =
+ msg
+ .split('\n')
+ .map(line => '> ' + line)
.join('\n') + '\n\n';
this.$.replyDialog.quote = quoteStr;
this._openReplyDialog(this.$.replyDialog.FocusTarget.BODY);
@@ -990,27 +1165,34 @@
this.$.mainContent.classList.remove('overlayOpen');
}
- _handleReplySent(e) {
- this.addEventListener('change-details-loaded',
- () => {
- this.reporting.timeEnd(SEND_REPLY_TIMING_LABEL);
- }, {once: true});
+ _handleReplySent() {
+ this.addEventListener(
+ 'change-details-loaded',
+ () => {
+ this.reporting.timeEnd(SEND_REPLY_TIMING_LABEL);
+ },
+ {once: true}
+ );
this.$.replyOverlay.close();
this._reload();
}
- _handleReplyCancel(e) {
+ _handleReplyCancel() {
this.$.replyOverlay.close();
}
- _handleReplyAutogrow(e) {
+ _handleReplyAutogrow() {
// If the textarea resizes, we need to re-fit the overlay.
- this.debounce('reply-overlay-refit', () => {
- this.$.replyOverlay.refit();
- }, REPLY_REFIT_DEBOUNCE_INTERVAL_MS);
+ this.debounce(
+ 'reply-overlay-refit',
+ () => {
+ this.$.replyOverlay.refit();
+ },
+ REPLY_REFIT_DEBOUNCE_INTERVAL_MS
+ );
}
- _handleShowReplyDialog(e) {
+ _handleShowReplyDialog(e: CustomEvent<{value: {ccsOnly: boolean}}>) {
let target = this.$.replyDialog.FocusTarget.REVIEWERS;
if (e.detail.value && e.detail.value.ccsOnly) {
target = this.$.replyDialog.FocusTarget.CCS;
@@ -1019,17 +1201,23 @@
}
_handleScroll() {
- this.debounce('scroll', () => {
- this.viewState.scrollTop = document.body.scrollTop;
- }, 150);
+ this.debounce(
+ 'scroll',
+ () => {
+ this.viewState.scrollTop = document.body.scrollTop;
+ },
+ 150
+ );
}
- _setShownFiles(e) {
+ _setShownFiles(e: CustomEvent<{length: number}>) {
this._shownFileCount = e.detail.length;
}
- _expandAllDiffs(e) {
- if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+ _expandAllDiffs(e: CustomKeyboardEvent) {
+ if (this.shouldSuppressKeyboardShortcut(e)) {
+ return;
+ }
this.$.fileList.expandAllDiffs();
}
@@ -1037,8 +1225,8 @@
this.$.fileList.collapseAllDiffs();
}
- _paramsChanged(value) {
- if (value.view !== GerritNav.View.CHANGE) {
+ _paramsChanged(value: AppElementChangeViewParams) {
+ if (value.view !== GerritView.CHANGE) {
this._initialLoadComplete = false;
return;
}
@@ -1047,9 +1235,11 @@
this.$.restAPI.setInProjectLookup(value.changeNum, value.project);
}
- const patchChanged = this._patchRange &&
- (value.patchNum !== undefined && value.basePatchNum !== undefined) &&
- (this._patchRange.patchNum !== value.patchNum ||
+ const patchChanged =
+ this._patchRange &&
+ value.patchNum !== undefined &&
+ value.basePatchNum !== undefined &&
+ (this._patchRange.patchNum !== value.patchNum ||
this._patchRange.basePatchNum !== value.basePatchNum);
const changeChanged = this._changeNum !== value.changeNum;
@@ -1059,19 +1249,20 @@
};
// TODO(TS): remove once proper type for patchRange is defined
if (!isNaN(Number(patchRange.patchNum))) {
- patchRange.patchNum = Number(patchRange.patchNum);
+ patchRange.patchNum = Number(patchRange.patchNum) as PatchSetNum;
}
if (!isNaN(Number(patchRange.basePatchNum))) {
- patchRange.basePatchNum = Number(patchRange.basePatchNum);
+ patchRange.basePatchNum = Number(patchRange.basePatchNum) as PatchSetNum;
}
this.$.fileList.collapseAllDiffs();
- this._patchRange = patchRange;
+ // TODO(TS): change patchRange to PatchRange.
+ this._patchRange = patchRange as PatchRange;
// If the change has already been loaded and the parameter change is only
// in the patch range, then don't do a full reload.
if (!changeChanged && patchChanged) {
- if (patchRange.patchNum == null) {
+ if (!patchRange.patchNum) {
patchRange.patchNum = computeLatestPatchNum(this._allPatchSets);
}
this._reloadPatchNumDependentResources().then(() => {
@@ -1088,30 +1279,37 @@
this._performPostLoadTasks();
});
- getPluginLoader().awaitPluginsLoaded()
- .then(() => {
- this._initActiveTabs(value);
- });
+ getPluginLoader()
+ .awaitPluginsLoaded()
+ .then(() => {
+ this._initActiveTabs(value);
+ });
}
- _initActiveTabs(params = {}) {
+ _initActiveTabs(params?: AppElementChangeViewParams) {
let primaryTab = PrimaryTab.FILES;
- if (params.queryMap && params.queryMap.has('tab')) {
- primaryTab = params.queryMap.get('tab');
+ if (params && params.queryMap && params.queryMap.has('tab')) {
+ primaryTab = params.queryMap.get('tab') as PrimaryTab;
}
- this._setActivePrimaryTab({
- detail: {
- tab: primaryTab,
- },
- });
- this._setActiveSecondaryTab({
- detail: {
- tab: SecondaryTab.CHANGE_LOG,
- },
- });
+ this._setActivePrimaryTab(
+ new CustomEvent('initActiveTab', {
+ detail: {
+ tab: primaryTab,
+ },
+ })
+ );
+ this._setActiveSecondaryTab(
+ new CustomEvent('initActiveTab', {
+ detail: {
+ tab: SecondaryTab.CHANGE_LOG,
+ },
+ })
+ );
}
_sendShowChangeEvent() {
+ if (!this._patchRange)
+ throw new Error('missing required _patchRange property');
this.$.jsAPI.handleEvent(EventType.SHOW_CHANGE, {
change: this._change,
patchNum: this._patchRange.patchNum,
@@ -1128,8 +1326,7 @@
this.async(() => {
if (this.viewState.scrollTop) {
- document.documentElement.scrollTop =
- document.body.scrollTop = this.viewState.scrollTop;
+ document.documentElement.scrollTop = document.body.scrollTop = this.viewState.scrollTop;
} else {
this._maybeScrollToMessage(window.location.hash);
}
@@ -1137,42 +1334,58 @@
});
}
- _paramsAndChangeChanged(value, change) {
+ @observe('params', '_change')
+ _paramsAndChangeChanged(
+ value?: AppElementChangeViewParams,
+ change?: ChangeInfo
+ ) {
// Polymer 2: check for undefined
- if ([value, change].includes(undefined)) {
+ if (!value || !change) {
return;
}
+ if (!this._patchRange)
+ throw new Error('missing required _patchRange property');
// If the change number or patch range is different, then reset the
// selected file index.
const patchRangeState = this.viewState.patchRange;
- if (this.viewState.changeNum !== this._changeNum ||
- !patchRangeState ||
- patchRangeState.basePatchNum !== this._patchRange.basePatchNum ||
- patchRangeState.patchNum !== this._patchRange.patchNum) {
+ if (
+ this.viewState.changeNum !== this._changeNum ||
+ !patchRangeState ||
+ patchRangeState.basePatchNum !== this._patchRange.basePatchNum ||
+ patchRangeState.patchNum !== this._patchRange.patchNum
+ ) {
this._resetFileListViewState();
}
}
- _viewStateChanged(viewState) {
- this._numFilesShown = viewState.numFilesShown ?
- viewState.numFilesShown : DEFAULT_NUM_FILES_SHOWN;
+ _viewStateChanged(viewState: ChangeViewState) {
+ this._numFilesShown = viewState.numFilesShown
+ ? viewState.numFilesShown
+ : DEFAULT_NUM_FILES_SHOWN;
}
- _numFilesShownChanged(numFilesShown) {
+ _numFilesShownChanged(numFilesShown: number) {
this.viewState.numFilesShown = numFilesShown;
}
- _handleMessageAnchorTap(e) {
+ _handleMessageAnchorTap(e: CustomEvent<{id: string}>) {
+ if (!this._change) throw new Error('missing required change property');
+ if (!this._patchRange)
+ throw new Error('missing required _patchRange property');
const hash = MSG_PREFIX + e.detail.id;
- const url = GerritNav.getUrlForChange(this._change,
- this._patchRange.patchNum, this._patchRange.basePatchNum,
- this._editMode, hash);
+ const url = GerritNav.getUrlForChange(
+ this._change,
+ this._patchRange.patchNum,
+ this._patchRange.basePatchNum,
+ this._editMode,
+ hash
+ );
history.replaceState(null, '', url);
}
- _maybeScrollToMessage(hash) {
- if (hash.startsWith(MSG_PREFIX)) {
+ _maybeScrollToMessage(hash: string) {
+ if (hash.startsWith(MSG_PREFIX) && this.messagesList) {
this.messagesList.scrollToMessage(hash.substr(MSG_PREFIX.length));
}
}
@@ -1182,12 +1395,12 @@
return window.location.search;
}
- _getUrlParameter(param) {
+ _getUrlParameter(param: string) {
const pageURL = this._getLocationSearch().substring(1);
const vars = pageURL.split('&');
for (let i = 0; i < vars.length; i++) {
const name = vars[i].split('=');
- if (name[0] == param) {
+ if (name[0] === param) {
return name[0];
}
}
@@ -1195,30 +1408,40 @@
}
_maybeShowRevertDialog() {
- getPluginLoader().awaitPluginsLoaded()
- .then(() => this._getLoggedIn())
- .then(loggedIn => {
- if (!loggedIn || !this._change ||
- this._change.status !== ChangeStatus.MERGED) {
+ getPluginLoader()
+ .awaitPluginsLoaded()
+ .then(() => this._getLoggedIn())
+ .then(loggedIn => {
+ if (
+ !loggedIn ||
+ !this._change ||
+ this._change.status !== ChangeStatus.MERGED
+ ) {
// Do not display dialog if not logged-in or the change is not
// merged.
- return;
- }
- if (this._getUrlParameter('revert')) {
- this.$.actions.showRevertDialog();
- }
- });
+ return;
+ }
+ if (this._getUrlParameter('revert')) {
+ this.$.actions.showRevertDialog();
+ }
+ });
}
_maybeShowReplyDialog() {
this._getLoggedIn().then(loggedIn => {
- if (!loggedIn) { return; }
+ if (!loggedIn) {
+ return;
+ }
if (this.viewState.showReplyDialog) {
this._openReplyDialog(this.$.replyDialog.FocusTarget.ANY);
// TODO(kaspern@): Find a better signal for when to call center.
- this.async(() => { this.$.replyOverlay.center(); }, 100);
- this.async(() => { this.$.replyOverlay.center(); }, 1000);
+ this.async(() => {
+ this.$.replyOverlay.center();
+ }, 100);
+ this.async(() => {
+ this.$.replyOverlay.center();
+ }, 1000);
this.set('viewState.showReplyDialog', false);
}
});
@@ -1234,8 +1457,10 @@
_resetFileListViewState() {
this.set('viewState.selectedFileIndex', 0);
this.set('viewState.scrollTop', 0);
- if (!!this.viewState.changeNum &&
- this.viewState.changeNum !== this._changeNum) {
+ if (
+ !!this.viewState.changeNum &&
+ this.viewState.changeNum !== this._changeNum
+ ) {
// Reset the diff mode to null when navigating from one change to
// another, so that the user's preference is restored.
this._setDiffViewMode(true);
@@ -1245,36 +1470,41 @@
this.set('viewState.patchRange', this._patchRange);
}
- _changeChanged(change) {
- if (!change || !this._patchRange || !this._allPatchSets) { return; }
+ _changeChanged(change?: ChangeInfo | ParsedChangeInfo) {
+ if (!change || !this._patchRange || !this._allPatchSets) {
+ return;
+ }
// We get the parent first so we keep the original value for basePatchNum
// and not the updated value.
const parent = this._getBasePatchNum(change, this._patchRange);
- this.set('_patchRange.patchNum', this._patchRange.patchNum ||
- computeLatestPatchNum(this._allPatchSets));
+ this.set(
+ '_patchRange.patchNum',
+ this._patchRange.patchNum || computeLatestPatchNum(this._allPatchSets)
+ );
this.set('_patchRange.basePatchNum', parent);
const title = change.subject + ' (' + change.change_id.substr(0, 9) + ')';
- this.dispatchEvent(new CustomEvent('title-change', {
- detail: {title},
- composed: true, bubbles: true,
- }));
+ this.dispatchEvent(
+ new CustomEvent('title-change', {
+ detail: {title},
+ composed: true,
+ bubbles: true,
+ })
+ );
}
/**
* Gets base patch number, if it is a parent try and decide from
* preference whether to default to `auto merge`, `Parent 1` or `PARENT`.
- *
- * @param {Object} change
- * @param {Object} patchRange
- * @return {number|string}
*/
- _getBasePatchNum(change, patchRange) {
- if (patchRange.basePatchNum &&
- patchRange.basePatchNum !== 'PARENT') {
+ _getBasePatchNum(
+ change: ChangeInfo | ParsedChangeInfo,
+ patchRange: PatchRange
+ ) {
+ if (patchRange.basePatchNum && patchRange.basePatchNum !== 'PARENT') {
return patchRange.basePatchNum;
}
@@ -1284,11 +1514,10 @@
const parentCounts = revisionInfo.getParentCountMap();
// check that there is at least 2 parents otherwise fall back to 1,
// which means there is only one parent.
- const parentCount = parentCounts.hasOwnProperty(1) ?
- parentCounts[1] : 1;
+ const parentCount = hasOwnProperty(parentCounts, 1) ? parentCounts[1] : 1;
- const preferFirst = this._prefs &&
- this._prefs.default_base_for_merges === 'FIRST_PARENT';
+ const preferFirst =
+ this._prefs && this._prefs.default_base_for_merges === 'FIRST_PARENT';
if (parentCount > 1 && preferFirst && !patchRange.patchNum) {
return -1;
@@ -1297,48 +1526,60 @@
return 'PARENT';
}
- _computeChangeUrl(change) {
+ _computeChangeUrl(change: ChangeInfo) {
return GerritNav.getUrlForChange(change);
}
- _computeShowCommitInfo(changeStatus, current_revision) {
+ _computeShowCommitInfo(changeStatus: string, current_revision: RevisionInfo) {
return changeStatus === 'Merged' && current_revision;
}
- _computeMergedCommitInfo(current_revision, revisions) {
+ _computeMergedCommitInfo(
+ current_revision: CommitId,
+ revisions: {[revisionId: string]: RevisionInfo}
+ ) {
const rev = revisions[current_revision];
- if (!rev || !rev.commit) { return {}; }
+ if (!rev || !rev.commit) {
+ return {};
+ }
// CommitInfo.commit is optional. Set commit in all cases to avoid error
// in <gr-commit-info>. @see Issue 5337
- if (!rev.commit.commit) { rev.commit.commit = current_revision; }
+ if (!rev.commit.commit) {
+ rev.commit.commit = current_revision;
+ }
return rev.commit;
}
- _computeChangeIdClass(displayChangeId) {
+ _computeChangeIdClass(displayChangeId: string) {
return displayChangeId === CHANGE_ID_ERROR.MISMATCH ? 'warning' : '';
}
- _computeTitleAttributeWarning(displayChangeId) {
+ _computeTitleAttributeWarning(displayChangeId: string) {
if (displayChangeId === CHANGE_ID_ERROR.MISMATCH) {
return 'Change-Id mismatch';
} else if (displayChangeId === CHANGE_ID_ERROR.MISSING) {
return 'No Change-Id in commit message';
}
+ return undefined;
}
- _computeChangeIdCommitMessageError(commitMessage, change) {
- // Polymer 2: check for undefined
- if ([commitMessage, change].includes(undefined)) {
+ _computeChangeIdCommitMessageError(
+ commitMessage?: string,
+ change?: ChangeInfo
+ ) {
+ if (change === undefined) {
return undefined;
}
- if (!commitMessage) { return CHANGE_ID_ERROR.MISSING; }
+ if (!commitMessage) {
+ return CHANGE_ID_ERROR.MISSING;
+ }
// Find the last match in the commit message:
let changeId;
let changeIdArr;
- while (changeIdArr = CHANGE_ID_REGEX_PATTERN.exec(commitMessage)) {
+ while ((changeIdArr = CHANGE_ID_REGEX_PATTERN.exec(commitMessage))) {
changeId = changeIdArr[2];
}
@@ -1356,62 +1597,42 @@
return CHANGE_ID_ERROR.MISSING;
}
- _computeLabelNames(labels) {
- return Object.keys(labels).sort();
- }
-
- _computeLabelValues(labelName, labels) {
- const result = [];
- const t = labels[labelName];
- if (!t) { return result; }
- const approvals = t.all || [];
- for (const label of approvals) {
- if (label.value && label.value != labels[labelName].default_value) {
- let labelClassName;
- let labelValPrefix = '';
- if (label.value > 0) {
- labelValPrefix = '+';
- labelClassName = 'approved';
- } else if (label.value < 0) {
- labelClassName = 'notApproved';
- }
- result.push({
- value: labelValPrefix + label.value,
- className: labelClassName,
- account: label,
- });
- }
- }
- return result;
- }
-
- _computeReplyButtonLabel(changeRecord, canStartReview) {
- // Polymer 2: check for undefined
- if ([changeRecord, canStartReview].includes(undefined)) {
+ _computeReplyButtonLabel(
+ changeRecord?: ElementPropertyDeepChange<
+ GrChangeView,
+ '_diffDrafts'
+ > | null,
+ canStartReview?: PolymerDeepPropertyChange<boolean, boolean>
+ ) {
+ if (changeRecord === undefined || canStartReview === undefined) {
return 'Reply';
}
const drafts = (changeRecord && changeRecord.base) || {};
- const draftCount = Object.keys(drafts)
- .reduce((count, file) => count + drafts[file].length, 0);
+ const draftCount = Object.keys(drafts).reduce(
+ (count, file) => count + drafts[file].length,
+ 0
+ );
let label = canStartReview ? 'Start Review' : 'Reply';
if (draftCount > 0) {
- label += ' (' + draftCount + ')';
+ label += ` (${draftCount})`;
}
return label;
}
- _handleOpenReplyDialog(e) {
- if (this.shouldSuppressKeyboardShortcut(e) ||
- this.modifierPressed(e)) {
+ _handleOpenReplyDialog(e: CustomKeyboardEvent) {
+ if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
return;
}
this._getLoggedIn().then(isLoggedIn => {
if (!isLoggedIn) {
- this.dispatchEvent(new CustomEvent('show-auth-required', {
- composed: true, bubbles: true,
- }));
+ this.dispatchEvent(
+ new CustomEvent('show-auth-required', {
+ composed: true,
+ bubbles: true,
+ })
+ );
return;
}
@@ -1420,144 +1641,203 @@
});
}
- _handleOpenDownloadDialogShortcut(e) {
- if (this.shouldSuppressKeyboardShortcut(e) ||
- this.modifierPressed(e)) { return; }
+ _handleOpenDownloadDialogShortcut(e: CustomKeyboardEvent) {
+ if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ return;
+ }
e.preventDefault();
this._handleOpenDownloadDialog();
}
- _handleEditTopic(e) {
- if (this.shouldSuppressKeyboardShortcut(e) ||
- this.modifierPressed(e)) { return; }
+ _handleEditTopic(e: CustomKeyboardEvent) {
+ if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ return;
+ }
e.preventDefault();
this.$.metadata.editTopic();
}
- _handleDiffAgainstBase(e) {
- if (this.shouldSuppressKeyboardShortcut(e)) { return; }
- if (patchNumEquals(this._patchRange.basePatchNum,
- SPECIAL_PATCH_SET_NUM.PARENT)) {
- this.dispatchEvent(new CustomEvent('show-alert', {
- detail: {
- message: 'Base is already selected.',
- },
- composed: true, bubbles: true,
- }));
+ _handleDiffAgainstBase(e: CustomKeyboardEvent) {
+ if (this.shouldSuppressKeyboardShortcut(e)) {
+ return;
+ }
+ if (!this._change) throw new Error('missing required change property');
+ if (!this._patchRange)
+ throw new Error('missing required _patchRange property');
+ if (patchNumEquals(this._patchRange.basePatchNum, ParentPatchSetNum)) {
+ this.dispatchEvent(
+ new CustomEvent('show-alert', {
+ detail: {
+ message: 'Base is already selected.',
+ },
+ composed: true,
+ bubbles: true,
+ })
+ );
return;
}
GerritNav.navigateToChange(this._change, this._patchRange.patchNum);
}
- _handleDiffBaseAgainstLeft(e) {
- if (this.shouldSuppressKeyboardShortcut(e)) { return; }
- if (patchNumEquals(this._patchRange.basePatchNum,
- SPECIAL_PATCH_SET_NUM.PARENT)) {
- this.dispatchEvent(new CustomEvent('show-alert', {
- detail: {
- message: 'Left is already base.',
- },
- composed: true, bubbles: true,
- }));
+ _handleDiffBaseAgainstLeft(e: CustomKeyboardEvent) {
+ if (this.shouldSuppressKeyboardShortcut(e)) {
+ return;
+ }
+ if (!this._change) throw new Error('missing required change property');
+ if (!this._patchRange)
+ throw new Error('missing required _patchRange property');
+ if (patchNumEquals(this._patchRange.basePatchNum, ParentPatchSetNum)) {
+ this.dispatchEvent(
+ new CustomEvent('show-alert', {
+ detail: {
+ message: 'Left is already base.',
+ },
+ composed: true,
+ bubbles: true,
+ })
+ );
return;
}
GerritNav.navigateToChange(this._change, this._patchRange.basePatchNum);
}
- _handleDiffAgainstLatest(e) {
- if (this.shouldSuppressKeyboardShortcut(e)) { return; }
- const latestPatchNum = computeLatestPatchNum(this._allPatchSets);
- if (patchNumEquals(this._patchRange.patchNum, latestPatchNum)) {
- this.dispatchEvent(new CustomEvent('show-alert', {
- detail: {
- message: 'Latest is already selected.',
- },
- composed: true, bubbles: true,
- }));
+ _handleDiffAgainstLatest(e: CustomKeyboardEvent) {
+ if (this.shouldSuppressKeyboardShortcut(e)) {
return;
}
- GerritNav.navigateToChange(this._change, latestPatchNum,
- this._patchRange.basePatchNum);
- }
-
- _handleDiffRightAgainstLatest(e) {
- if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+ if (!this._change) throw new Error('missing required change property');
+ if (!this._patchRange)
+ throw new Error('missing required _patchRange property');
const latestPatchNum = computeLatestPatchNum(this._allPatchSets);
if (patchNumEquals(this._patchRange.patchNum, latestPatchNum)) {
- this.dispatchEvent(new CustomEvent('show-alert', {
- detail: {
- message: 'Right is already latest.',
- },
- composed: true, bubbles: true,
- }));
+ this.dispatchEvent(
+ new CustomEvent('show-alert', {
+ detail: {
+ message: 'Latest is already selected.',
+ },
+ composed: true,
+ bubbles: true,
+ })
+ );
return;
}
- GerritNav.navigateToChange(this._change, latestPatchNum,
- this._patchRange.patchNum);
+ GerritNav.navigateToChange(
+ this._change,
+ latestPatchNum,
+ this._patchRange.basePatchNum
+ );
}
- _handleDiffBaseAgainstLatest(e) {
- if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+ _handleDiffRightAgainstLatest(e: CustomKeyboardEvent) {
+ if (this.shouldSuppressKeyboardShortcut(e)) {
+ return;
+ }
+ if (!this._change) throw new Error('missing required change property');
const latestPatchNum = computeLatestPatchNum(this._allPatchSets);
- if (patchNumEquals(this._patchRange.patchNum, latestPatchNum) &&
- patchNumEquals(this._patchRange.basePatchNum,
- SPECIAL_PATCH_SET_NUM.PARENT)) {
- this.dispatchEvent(new CustomEvent('show-alert', {
- detail: {
- message: 'Already diffing base against latest.',
- },
- composed: true, bubbles: true,
- }));
+ if (!this._patchRange)
+ throw new Error('missing required _patchRange property');
+ if (patchNumEquals(this._patchRange.patchNum, latestPatchNum)) {
+ this.dispatchEvent(
+ new CustomEvent('show-alert', {
+ detail: {
+ message: 'Right is already latest.',
+ },
+ composed: true,
+ bubbles: true,
+ })
+ );
+ return;
+ }
+ GerritNav.navigateToChange(
+ this._change,
+ latestPatchNum,
+ this._patchRange.patchNum
+ );
+ }
+
+ _handleDiffBaseAgainstLatest(e: CustomKeyboardEvent) {
+ if (this.shouldSuppressKeyboardShortcut(e)) {
+ return;
+ }
+ if (!this._change) throw new Error('missing required change property');
+ if (!this._patchRange)
+ throw new Error('missing required _patchRange property');
+ const latestPatchNum = computeLatestPatchNum(this._allPatchSets);
+ if (
+ patchNumEquals(this._patchRange.patchNum, latestPatchNum) &&
+ patchNumEquals(this._patchRange.basePatchNum, ParentPatchSetNum)
+ ) {
+ this.dispatchEvent(
+ new CustomEvent('show-alert', {
+ detail: {
+ message: 'Already diffing base against latest.',
+ },
+ composed: true,
+ bubbles: true,
+ })
+ );
return;
}
GerritNav.navigateToChange(this._change, latestPatchNum);
}
- _handleRefreshChange(e) {
- if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+ _handleRefreshChange(e: CustomKeyboardEvent) {
+ if (this.shouldSuppressKeyboardShortcut(e)) {
+ return;
+ }
e.preventDefault();
- this._reload(/* opt_isLocationChange= */false,
- /* opt_clearPatchset= */true);
+ this._reload(/* isLocationChange= */ false, /* clearPatchset= */ true);
}
- _handleToggleChangeStar(e) {
- if (this.shouldSuppressKeyboardShortcut(e) ||
- this.modifierPressed(e)) { return; }
+ _handleToggleChangeStar(e: CustomKeyboardEvent) {
+ if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ return;
+ }
e.preventDefault();
this.$.changeStar.toggleStar();
}
- _handleUpToDashboard(e) {
- if (this.shouldSuppressKeyboardShortcut(e) ||
- this.modifierPressed(e)) { return; }
+ _handleUpToDashboard(e: CustomKeyboardEvent) {
+ if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ return;
+ }
e.preventDefault();
this._determinePageBack();
}
- _handleExpandAllMessages(e) {
- if (this.shouldSuppressKeyboardShortcut(e) ||
- this.modifierPressed(e)) { return; }
+ _handleExpandAllMessages(e: CustomKeyboardEvent) {
+ if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ return;
+ }
e.preventDefault();
- this.messagesList.handleExpandCollapse(true);
+ if (this.messagesList) {
+ this.messagesList.handleExpandCollapse(true);
+ }
}
- _handleCollapseAllMessages(e) {
- if (this.shouldSuppressKeyboardShortcut(e) ||
- this.modifierPressed(e)) { return; }
+ _handleCollapseAllMessages(e: CustomKeyboardEvent) {
+ if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ return;
+ }
e.preventDefault();
- this.messagesList.handleExpandCollapse(false);
+ if (this.messagesList) {
+ this.messagesList.handleExpandCollapse(false);
+ }
}
- _handleOpenDiffPrefsShortcut(e) {
- if (this.shouldSuppressKeyboardShortcut(e) ||
- this.modifierPressed(e)) { return; }
+ _handleOpenDiffPrefsShortcut(e: CustomKeyboardEvent) {
+ if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
+ return;
+ }
- if (this._diffPrefsDisabled) { return; }
+ if (this._diffPrefsDisabled) {
+ return;
+ }
e.preventDefault();
this.$.fileList.openDiffPrefs();
@@ -1566,18 +1846,22 @@
_determinePageBack() {
// Default backPage to root if user came to change view page
// via an email link, etc.
- GerritNav.navigateToRelativeUrl(this.backPage ||
- GerritNav.getUrlForRoot());
+ GerritNav.navigateToRelativeUrl(this.backPage || GerritNav.getUrlForRoot());
}
- _handleLabelRemoved(splices, path) {
+ _handleLabelRemoved(
+ splices: Array<PolymerSplice<ApprovalInfo[]>>,
+ path: string
+ ) {
for (const splice of splices) {
for (const removed of splice.removed) {
const changePath = path.split('.');
const labelPath = changePath.splice(0, changePath.length - 2);
- const labelDict = this.get(labelPath);
- if (labelDict.approved &&
- labelDict.approved._account_id === removed._account_id) {
+ const labelDict = this.get(labelPath) as QuickLabelInfo;
+ if (
+ labelDict.approved &&
+ labelDict.approved._account_id === removed._account_id
+ ) {
this._reload();
return;
}
@@ -1585,35 +1869,45 @@
}
}
- _labelsChanged(changeRecord) {
- if (!changeRecord) { return; }
- if (changeRecord.value && changeRecord.value.indexSplices) {
- this._handleLabelRemoved(changeRecord.value.indexSplices,
- changeRecord.path);
+ @observe('_change.labels.*')
+ _labelsChanged(
+ changeRecord: PolymerDeepPropertyChange<
+ LabelNameToInfoMap,
+ PolymerSpliceChange<ApprovalInfo[]>
+ >
+ ) {
+ if (!changeRecord) {
+ return;
+ }
+ if (changeRecord.value && isPolymerSpliceChange(changeRecord.value)) {
+ this._handleLabelRemoved(
+ changeRecord.value.indexSplices,
+ changeRecord.path
+ );
}
this.$.jsAPI.handleEvent(EventType.LABEL_CHANGE, {
change: this._change,
});
}
- /**
- * @param {string=} opt_section
- */
- _openReplyDialog(opt_section) {
+ _openReplyDialog(section?: FocusTarget) {
this.$.replyOverlay.open().finally(() => {
// the following code should be executed no matter open succeed or not
this._resetReplyOverlayFocusStops();
- this.$.replyDialog.open(opt_section);
+ this.$.replyDialog.open(section);
flush();
this.$.replyOverlay.center();
});
}
- _handleGetChangeDetailError(response) {
- this.dispatchEvent(new CustomEvent('page-error', {
- detail: {response},
- composed: true, bubbles: true,
- }));
+ _handleGetChangeDetailError(response?: Response | null) {
+ this.dispatchEvent(
+ new CustomEvent('page-error', {
+ detail: {response},
+ composed: true,
+ bubbles: true,
+ })
+ );
}
_getLoggedIn() {
@@ -1625,18 +1919,19 @@
}
_getProjectConfig() {
- if (!this._change) return;
- return this.$.restAPI.getProjectConfig(this._change.project).then(
- config => {
- this._projectConfig = config;
- });
+ if (!this._change) throw new Error('missing required change property');
+ return this.$.restAPI
+ .getProjectConfig(this._change.project)
+ .then(config => {
+ this._projectConfig = config;
+ });
}
_getPreferences() {
return this.$.restAPI.getPreferences();
}
- _prepareCommitMsgForLinkify(msg) {
+ _prepareCommitMsgForLinkify(msg: string) {
// TODO(wyatta) switch linkify sequence, see issue 5526.
// This is a zero-with space. It is added to prevent the linkify library
// from including R= or CC= as part of the email address.
@@ -1646,99 +1941,126 @@
/**
* Utility function to make the necessary modifications to a change in the
* case an edit exists.
- *
- * @param {!Object} change
- * @param {?Object} edit
*/
- _processEdit(change, edit) {
- if (!edit) { return; }
- change.revisions[edit.commit.commit] = {
- _number: SPECIAL_PATCH_SET_NUM.EDIT,
- basePatchNum: edit.base_patch_set_number,
- commit: edit.commit,
- fetch: edit.fetch,
- };
+ _processEdit(change: ParsedChangeInfo, edit?: EditInfo | false) {
+ if (!edit) return;
+ if (!this._patchRange)
+ throw new Error('missing required _patchRange property');
+ if (!edit.commit.commit) throw new Error('undefined edit.commit.commit');
+ const changeWithEdit = change;
+ if (changeWithEdit.revisions)
+ changeWithEdit.revisions[edit.commit.commit] = {
+ _number: EditPatchSetNum,
+ basePatchNum: edit.base_patch_set_number,
+ commit: edit.commit,
+ fetch: edit.fetch,
+ } as RevisionInfo;
+
// If the edit is based on the most recent patchset, load it by
// default, unless another patch set to load was specified in the URL.
- if (!this._patchRange.patchNum &&
- change.current_revision === edit.base_revision) {
- change.current_revision = edit.commit.commit;
- this.set('_patchRange.patchNum', SPECIAL_PATCH_SET_NUM.EDIT);
+ if (
+ !this._patchRange.patchNum &&
+ changeWithEdit.current_revision === edit.base_revision
+ ) {
+ changeWithEdit.current_revision = edit.commit.commit;
+ this.set('_patchRange.patchNum', EditPatchSetNum);
// Because edits are fibbed as revisions and added to the revisions
// array, and revision actions are always derived from the 'latest'
// patch set, we must copy over actions from the patch set base.
// Context: Issue 7243
- change.revisions[edit.commit.commit].actions =
- change.revisions[edit.base_revision].actions;
+ if (changeWithEdit.revisions) {
+ changeWithEdit.revisions[edit.commit.commit].actions =
+ changeWithEdit.revisions[edit.base_revision].actions;
+ }
}
}
_getChangeDetail() {
- const detailCompletes = this.$.restAPI.getChangeDetail(
- this._changeNum, r => this._handleGetChangeDetailError(r));
+ if (!this._changeNum)
+ throw new Error('missing required changeNum property');
+ const detailCompletes = this.$.restAPI.getChangeDetail(this._changeNum, r =>
+ this._handleGetChangeDetailError(r)
+ );
const editCompletes = this._getEdit();
const prefCompletes = this._getPreferences();
- return Promise.all([detailCompletes, editCompletes, prefCompletes])
- .then(([change, edit, prefs]) => {
- this._prefs = prefs;
+ return Promise.all([detailCompletes, editCompletes, prefCompletes]).then(
+ ([change, edit, prefs]) => {
+ this._prefs = prefs;
- if (!change) {
- return '';
+ if (!change) {
+ return false;
+ }
+ this._processEdit(change, edit);
+ // Issue 4190: Coalesce missing topics to null.
+ // TODO(TS): code needs second thought,
+ // it might be that nulls were assigned to trigger some bindings
+ if (!change.topic) {
+ change.topic = (null as unknown) as undefined;
+ }
+ if (!change.reviewer_updates) {
+ change.reviewer_updates = (null as unknown) as undefined;
+ }
+ const latestRevisionSha = this._getLatestRevisionSHA(change);
+ if (!latestRevisionSha)
+ throw new Error('Could not find latest Revision Sha');
+ const currentRevision = change.revisions[latestRevisionSha];
+ if (currentRevision.commit && currentRevision.commit.message) {
+ this._latestCommitMessage = this._prepareCommitMsgForLinkify(
+ currentRevision.commit.message
+ );
+ } else {
+ this._latestCommitMessage = null;
+ }
+
+ const lineHeight = getComputedStyle(this).lineHeight;
+
+ // Slice returns a number as a string, convert to an int.
+ this._lineHeight = parseInt(
+ lineHeight.slice(0, lineHeight.length - 2),
+ 10
+ );
+
+ this._change = change;
+ if (
+ !this._patchRange ||
+ !this._patchRange.patchNum ||
+ patchNumEquals(this._patchRange.patchNum, currentRevision._number)
+ ) {
+ // CommitInfo.commit is optional, and may need patching.
+ if (currentRevision.commit && !currentRevision.commit.commit) {
+ currentRevision.commit.commit = latestRevisionSha as CommitId;
}
- this._processEdit(change, edit);
- // Issue 4190: Coalesce missing topics to null.
- if (!change.topic) { change.topic = null; }
- if (!change.reviewer_updates) {
- change.reviewer_updates = null;
- }
- const latestRevisionSha = this._getLatestRevisionSHA(change);
- const currentRevision = change.revisions[latestRevisionSha];
- if (currentRevision.commit && currentRevision.commit.message) {
- this._latestCommitMessage = this._prepareCommitMsgForLinkify(
- currentRevision.commit.message);
- } else {
- this._latestCommitMessage = null;
- }
-
- const lineHeight = getComputedStyle(this).lineHeight;
-
- // Slice returns a number as a string, convert to an int.
- this._lineHeight =
- parseInt(lineHeight.slice(0, lineHeight.length - 2), 10);
-
- this._change = change;
- if (!this._patchRange || !this._patchRange.patchNum ||
- patchNumEquals(this._patchRange.patchNum,
- currentRevision._number)) {
- // CommitInfo.commit is optional, and may need patching.
- if (!currentRevision.commit.commit) {
- currentRevision.commit.commit = latestRevisionSha;
+ this._commitInfo = currentRevision.commit;
+ this._selectedRevision = currentRevision;
+ // TODO: Fetch and process files.
+ } else {
+ if (!this._change?.revisions || !this._patchRange) return false;
+ this._selectedRevision = Object.values(this._change.revisions).find(
+ revision => {
+ // edit patchset is a special one
+ const thePatchNum = this._patchRange!.patchNum;
+ if (thePatchNum === 'edit') {
+ return revision._number === thePatchNum;
+ }
+ return revision._number === parseInt(`${thePatchNum}`, 10);
}
- this._commitInfo = currentRevision.commit;
- this._selectedRevision = currentRevision;
- // TODO: Fetch and process files.
- } else {
- this._selectedRevision =
- Object.values(this._change.revisions).find(
- revision => {
- // edit patchset is a special one
- const thePatchNum = this._patchRange.patchNum;
- if (thePatchNum === 'edit') {
- return revision._number === thePatchNum;
- }
- return revision._number === parseInt(thePatchNum, 10);
- });
- }
- });
+ );
+ }
+ return false;
+ }
+ );
}
- _isSubmitEnabled(revisionActions) {
- return !!(revisionActions && revisionActions.submit &&
- revisionActions.submit.enabled);
+ _isSubmitEnabled(revisionActions: ActionNameToActionInfoMap) {
+ return !!(
+ revisionActions &&
+ revisionActions.submit &&
+ revisionActions.submit.enabled
+ );
}
- _isParentCurrent(revisionActions) {
+ _isParentCurrent(revisionActions: ActionNameToActionInfoMap) {
if (revisionActions && revisionActions.rebase) {
return !revisionActions.rebase.enabled;
} else {
@@ -1747,28 +2069,39 @@
}
_getEdit() {
+ if (!this._changeNum)
+ return Promise.reject(new Error('missing required changeNum property'));
return this.$.restAPI.getChangeEdit(this._changeNum, true);
}
_getLatestCommitMessage() {
- return this.$.restAPI.getChangeCommitInfo(this._changeNum,
- computeLatestPatchNum(this._allPatchSets)).then(commitInfo => {
- if (!commitInfo) return Promise.resolve();
- this._latestCommitMessage =
- this._prepareCommitMsgForLinkify(commitInfo.message);
- });
+ if (!this._changeNum)
+ throw new Error('missing required changeNum property');
+ const lastpatchNum = computeLatestPatchNum(this._allPatchSets);
+ if (lastpatchNum === undefined)
+ throw new Error('missing lastPatchNum property');
+ return this.$.restAPI
+ .getChangeCommitInfo(this._changeNum, lastpatchNum)
+ .then(commitInfo => {
+ if (!commitInfo) return;
+ this._latestCommitMessage = this._prepareCommitMsgForLinkify(
+ commitInfo.message
+ );
+ });
}
- _getLatestRevisionSHA(change) {
+ _getLatestRevisionSHA(change: ChangeInfo | ParsedChangeInfo) {
if (change.current_revision) {
return change.current_revision;
}
// current_revision may not be present in the case where the latest rev is
// a draft and the user doesn’t have permission to view that rev.
let latestRev = null;
- let latestPatchNum = -1;
+ let latestPatchNum = -1 as PatchSetNum;
for (const rev in change.revisions) {
- if (!change.revisions.hasOwnProperty(rev)) { continue; }
+ if (!hasOwnProperty(change.revisions, rev)) {
+ continue;
+ }
if (change.revisions[rev]._number > latestPatchNum) {
latestRev = rev;
@@ -1779,14 +2112,20 @@
}
_getCommitInfo() {
- return this.$.restAPI.getChangeCommitInfo(
- this._changeNum, this._patchRange.patchNum).then(
- commitInfo => {
- this._commitInfo = commitInfo;
- });
+ if (!this._changeNum)
+ throw new Error('missing required changeNum property');
+ if (!this._patchRange)
+ throw new Error('missing required _patchRange property');
+ if (this._patchRange.patchNum === undefined)
+ throw new Error('missing required patchNum property');
+ return this.$.restAPI
+ .getChangeCommitInfo(this._changeNum, this._patchRange.patchNum)
+ .then(commitInfo => {
+ this._commitInfo = commitInfo;
+ });
}
- _reloadDraftsWithCallback(e) {
+ _reloadDraftsWithCallback(e: CustomEvent<{resolve: () => void}>) {
return this._reloadDrafts().then(() => e.detail.resolve());
}
@@ -1802,8 +2141,11 @@
this._diffDrafts = undefined;
this._draftCommentThreads = undefined;
this._robotCommentThreads = undefined;
- return this.$.commentAPI.loadAll(this._changeNum)
- .then(comments => this._recomputeComments(comments));
+ if (!this._changeNum)
+ throw new Error('missing required changeNum property');
+ return this.$.commentAPI
+ .loadAll(this._changeNum)
+ .then(comments => this._recomputeComments(comments));
}
/**
@@ -1815,41 +2157,47 @@
* without updating threads
*/
_reloadDrafts() {
- return this.$.commentAPI.reloadDrafts(this._changeNum)
- .then(comments => this._recomputeComments(comments));
+ if (!this._changeNum)
+ throw new Error('missing required changeNum property');
+ return this.$.commentAPI
+ .reloadDrafts(this._changeNum)
+ .then(comments => this._recomputeComments(comments));
}
- _recomputeComments(comments) {
+ _recomputeComments(comments: ChangeComments) {
this._changeComments = comments;
this._diffDrafts = {...this._changeComments.drafts};
this._commentThreads = this._changeComments.getAllThreadsForChange();
this._draftCommentThreads = this._commentThreads
- .filter(thread => thread.comments[thread.comments.length - 1].__draft)
- .map(thread => {
- const copiedThread = {...thread};
- // Make a hardcopy of all comments and collapse all but last one
- const commentsInThread = copiedThread.comments = thread.comments
- .map(comment => { return {...comment, collapsed: true}; });
- commentsInThread[commentsInThread.length - 1].collapsed = false;
- return copiedThread;
- });
+ .filter(isDraftThread)
+ .map(thread => {
+ const copiedThread = {...thread};
+ // Make a hardcopy of all comments and collapse all but last one
+ const commentsInThread = (copiedThread.comments = thread.comments.map(
+ comment => {
+ return {...comment, collapsed: true as boolean};
+ }
+ ));
+ commentsInThread[commentsInThread.length - 1].collapsed = false;
+ return copiedThread;
+ });
}
/**
* Reload the change.
*
- * @param {boolean=} opt_isLocationChange Reloads the related changes
- * when true and ends reporting events that started on location change.
- * @param {boolean=} opt_clearPatchset Reloads the related changes
- * ignoring any patchset choice made.
- * @return {Promise} A promise that resolves when the core data has loaded.
- * Some non-core data loading may still be in-flight when the core data
- * promise resolves.
+ * @param isLocationChange Reloads the related changes
+ * when true and ends reporting events that started on location change.
+ * @param clearPatchset Reloads the related changes
+ * ignoring any patchset choice made.
+ * @return A promise that resolves when the core data has loaded.
+ * Some non-core data loading may still be in-flight when the core data
+ * promise resolves.
*/
- _reload(opt_isLocationChange, opt_clearPatchset) {
- if (opt_clearPatchset) {
+ _reload(isLocationChange?: boolean, clearPatchset?: boolean) {
+ if (clearPatchset && this._change) {
GerritNav.navigateToChange(this._change);
- return;
+ return Promise.resolve([]);
}
this._loading = true;
this._relatedChangesCollapsed = true;
@@ -1857,7 +2205,7 @@
this.reporting.time(CHANGE_DATA_TIMING_LABEL);
// Array to house all promises related to data requests.
- const allDataPromises = [];
+ const allDataPromises: Promise<unknown>[] = [];
// Resolves when the change detail and the edit patch set (if available)
// are loaded.
@@ -1867,21 +2215,26 @@
// Resolves when the loading flag is set to false, meaning that some
// change content may start appearing.
const loadingFlagSet = detailCompletes
- .then(() => {
- this._loading = false;
- this.dispatchEvent(new CustomEvent('change-details-loaded',
- {bubbles: true, composed: true}));
- })
- .then(() => {
- this.reporting.timeEnd(CHANGE_RELOAD_TIMING_LABEL);
- if (opt_isLocationChange) {
- this.reporting.changeDisplayed();
- }
- });
+ .then(() => {
+ this._loading = false;
+ this.dispatchEvent(
+ new CustomEvent('change-details-loaded', {
+ bubbles: true,
+ composed: true,
+ })
+ );
+ })
+ .then(() => {
+ this.reporting.timeEnd(CHANGE_RELOAD_TIMING_LABEL);
+ if (isLocationChange) {
+ this.reporting.changeDisplayed();
+ }
+ });
// Resolves when the project config has loaded.
- const projectConfigLoaded = detailCompletes
- .then(() => this._getProjectConfig());
+ const projectConfigLoaded = detailCompletes.then(() =>
+ this._getProjectConfig()
+ );
allDataPromises.push(projectConfigLoaded);
// Resolves when change comments have loaded (comments, drafts and robot
@@ -1900,53 +2253,62 @@
// Promise resolves when the change detail and patch dependent resources
// have loaded.
- const detailAndPatchResourcesLoaded =
- Promise.all([patchResourcesLoaded, loadingFlagSet]);
+ const detailAndPatchResourcesLoaded = Promise.all([
+ patchResourcesLoaded,
+ loadingFlagSet,
+ ]);
// Promise resolves when mergeability information has loaded.
- const mergeabilityLoaded = detailAndPatchResourcesLoaded
- .then(() => this._getMergeability());
+ const mergeabilityLoaded = detailAndPatchResourcesLoaded.then(() =>
+ this._getMergeability()
+ );
allDataPromises.push(mergeabilityLoaded);
// Promise resovles when the change actions have loaded.
- const actionsLoaded = detailAndPatchResourcesLoaded
- .then(() => this.$.actions.reload());
+ const actionsLoaded = detailAndPatchResourcesLoaded.then(() =>
+ this.$.actions.reload()
+ );
allDataPromises.push(actionsLoaded);
// The core data is loaded when both mergeability and actions are known.
coreDataPromise = Promise.all([mergeabilityLoaded, actionsLoaded]);
} else {
// Resolves when the file list has loaded.
- const fileListReload = loadingFlagSet
- .then(() => this.$.fileList.reload());
+ const fileListReload = loadingFlagSet.then(() =>
+ this.$.fileList.reload()
+ );
allDataPromises.push(fileListReload);
const latestCommitMessageLoaded = loadingFlagSet.then(() => {
// If the latest commit message is known, there is nothing to do.
- if (this._latestCommitMessage) { return Promise.resolve(); }
+ if (this._latestCommitMessage) {
+ return Promise.resolve();
+ }
return this._getLatestCommitMessage();
});
allDataPromises.push(latestCommitMessageLoaded);
// Promise resolves when mergeability information has loaded.
- const mergeabilityLoaded = loadingFlagSet
- .then(() => this._getMergeability());
+ const mergeabilityLoaded = loadingFlagSet.then(() =>
+ this._getMergeability()
+ );
allDataPromises.push(mergeabilityLoaded);
// Core data is loaded when mergeability has been loaded.
- coreDataPromise = mergeabilityLoaded;
+ coreDataPromise = Promise.all([mergeabilityLoaded]);
}
- if (opt_isLocationChange) {
+ if (isLocationChange) {
this._editingCommitMessage = false;
- const relatedChangesLoaded = coreDataPromise
- .then(() => this.$.relatedChanges.reload());
+ const relatedChangesLoaded = coreDataPromise.then(() =>
+ this.$.relatedChanges.reload()
+ );
allDataPromises.push(relatedChangesLoaded);
}
Promise.all(allDataPromises).then(() => {
this.reporting.timeEnd(CHANGE_DATA_TIMING_LABEL);
- if (opt_isLocationChange) {
+ if (isLocationChange) {
this.reporting.changeFullyLoaded();
}
});
@@ -1959,10 +2321,7 @@
* (`this._patchRange`) being defined.
*/
_reloadPatchNumDependentResources() {
- return Promise.all([
- this._getCommitInfo(),
- this.$.fileList.reload(),
- ]);
+ return Promise.all([this._getCommitInfo(), this.$.fileList.reload()]);
}
_getMergeability() {
@@ -1973,38 +2332,51 @@
// If the change is closed, it is not mergeable. Note: already merged
// changes are obviously not mergeable, but the mergeability API will not
// answer for abandoned changes.
- if (this._change.status === ChangeStatus.MERGED ||
- this._change.status === ChangeStatus.ABANDONED) {
+ if (
+ this._change.status === ChangeStatus.MERGED ||
+ this._change.status === ChangeStatus.ABANDONED
+ ) {
this._mergeable = false;
return Promise.resolve();
}
+ if (!this._changeNum) {
+ return Promise.reject(new Error('missing required changeNum property'));
+ }
+
this._mergeable = null;
- return this.$.restAPI.getMergeable(this._changeNum).then(m => {
- this._mergeable = m.mergeable;
+ return this.$.restAPI.getMergeable(this._changeNum).then(mergableInfo => {
+ if (mergableInfo) {
+ this._mergeable = mergableInfo.mergeable;
+ }
});
}
- _computeCanStartReview(change) {
- return !!(change.actions && change.actions.ready &&
- change.actions.ready.enabled);
+ _computeCanStartReview(change: ChangeInfo) {
+ return !!(
+ change.actions &&
+ change.actions.ready &&
+ change.actions.ready.enabled
+ );
}
- _computeReplyDisabled() { return false; }
-
- _computeChangePermalinkAriaLabel(changeNum) {
- return 'Change ' + changeNum;
+ _computeReplyDisabled() {
+ return false;
}
- _computeCommitMessageCollapsed(collapsed, collapsible) {
+ _computeChangePermalinkAriaLabel(changeNum: NumericChangeId) {
+ return `Change ${changeNum}`;
+ }
+
+ _computeCommitMessageCollapsed(collapsed?: boolean, collapsible?: boolean) {
return collapsible && collapsed;
}
- _computeRelatedChangesClass(collapsed) {
+ _computeRelatedChangesClass(collapsed: boolean) {
return collapsed ? 'collapsed' : '';
}
- _computeCollapseText(collapsed) {
+ _computeCollapseText(collapsed: boolean) {
// Symbols are up and down triangles.
return collapsed ? '\u25bc Show more' : '\u25b2 Show less';
}
@@ -2012,13 +2384,13 @@
/**
* Returns the text to be copied when
* click the copy icon next to change subject
- *
- * @param {!Object} change
*/
- _computeCopyTextForTitle(change) {
- return `${change._number}: ${change.subject} | ` +
- `${location.protocol}//${location.host}` +
- `${this._computeChangeUrl(change)}`;
+ _computeCopyTextForTitle(change: ChangeInfo) {
+ return (
+ `${change._number}: ${change.subject} | ` +
+ `${location.protocol}//${location.host}` +
+ `${this._computeChangeUrl(change)}`
+ );
}
_toggleCommitCollapsed() {
@@ -2035,25 +2407,27 @@
}
}
- _computeCommitCollapsible(commitMessage) {
- if (!commitMessage) { return false; }
+ _computeCommitCollapsible(commitMessage?: string) {
+ if (!commitMessage) {
+ return false;
+ }
return commitMessage.split('\n').length >= MIN_LINES_FOR_COMMIT_COLLAPSE;
}
- _getOffsetHeight(element) {
+ _getOffsetHeight(element: HTMLElement) {
return element.offsetHeight;
}
- _getScrollHeight(element) {
+ _getScrollHeight(element: HTMLElement) {
return element.scrollHeight;
}
/**
* Get the line height of an element to the nearest integer.
*/
- _getLineHeight(element) {
+ _getLineHeight(element: Element) {
const lineHeightStr = getComputedStyle(element).lineHeight;
- return Math.round(lineHeightStr.slice(0, lineHeightStr.length - 2));
+ return Math.round(Number(lineHeightStr.slice(0, lineHeightStr.length - 2)));
}
/**
@@ -2066,21 +2440,23 @@
const EXTRA_HEIGHT = 30;
let newHeight;
- if (window.matchMedia(`(max-width: ${BREAKPOINT_RELATED_SMALL})`)
- .matches) {
+ if (window.matchMedia(`(max-width: ${BREAKPOINT_RELATED_SMALL})`).matches) {
// In a small (mobile) view, give the relation chain some space.
newHeight = SMALL_RELATED_HEIGHT;
- } else if (window.matchMedia(`(max-width: ${BREAKPOINT_RELATED_MED})`)
- .matches) {
+ } else if (
+ window.matchMedia(`(max-width: ${BREAKPOINT_RELATED_MED})`).matches
+ ) {
// Since related changes are below the commit message, but still next to
// metadata, the height should be the height of the metadata minus the
// height of the commit message to reduce jank. However, if that doesn't
// result in enough space, instead use the MINIMUM_RELATED_MAX_HEIGHT.
// Note: extraHeight is to take into account margin/padding.
const medRelatedHeight = Math.max(
- this._getOffsetHeight(this.$.mainChangeInfo) -
- this._getOffsetHeight(this.$.commitMessage) - 2 * EXTRA_HEIGHT,
- MINIMUM_RELATED_MAX_HEIGHT);
+ this._getOffsetHeight(this.$.mainChangeInfo) -
+ this._getOffsetHeight(this.$.commitMessage) -
+ 2 * EXTRA_HEIGHT,
+ MINIMUM_RELATED_MAX_HEIGHT
+ );
newHeight = medRelatedHeight;
} else {
if (this._commitCollapsible) {
@@ -2089,11 +2465,11 @@
// height.
newHeight = this._getOffsetHeight(this.$.commitMessage);
} else {
- newHeight = this._getOffsetHeight(this.$.commitAndRelated) -
- EXTRA_HEIGHT;
+ newHeight =
+ this._getOffsetHeight(this.$.commitAndRelated) - EXTRA_HEIGHT;
}
}
- const stylesToUpdate = {};
+ const stylesToUpdate: {[key: string]: string} = {};
// Get the line height of related changes, and convert it to the nearest
// integer.
@@ -2103,11 +2479,11 @@
const remainder = newHeight % lineHeight;
newHeight = newHeight - remainder;
- stylesToUpdate['--relation-chain-max-height'] = newHeight + 'px';
+ stylesToUpdate['--relation-chain-max-height'] = `${newHeight}px`;
// Update the max-height of the relation chain to this new height.
if (this._commitCollapsible) {
- stylesToUpdate['--related-change-btn-top-padding'] = remainder + 'px';
+ stylesToUpdate['--related-change-btn-top-padding'] = `${remainder}px`;
}
this.updateStyles(stylesToUpdate);
@@ -2122,18 +2498,20 @@
// Prevents showMore from showing when click on related change, since the
// line height would be positive, but related changes height is 0.
if (!this._getScrollHeight(this.$.relatedChanges)) {
- return this._showRelatedToggle = false;
+ return (this._showRelatedToggle = false);
}
- if (this._getScrollHeight(this.$.relatedChanges) >
- (this._getOffsetHeight(this.$.relatedChanges) +
- this._getLineHeight(this.$.relatedChanges))) {
- return this._showRelatedToggle = true;
+ if (
+ this._getScrollHeight(this.$.relatedChanges) >
+ this._getOffsetHeight(this.$.relatedChanges) +
+ this._getLineHeight(this.$.relatedChanges)
+ ) {
+ return (this._showRelatedToggle = true);
}
- this._showRelatedToggle = false;
+ return (this._showRelatedToggle = false);
}
- _updateToggleContainerClass(showRelatedToggle) {
+ _updateToggleContainerClass(showRelatedToggle: boolean) {
if (showRelatedToggle) {
this.$.relatedChangesToggle.classList.add('showToggle');
} else {
@@ -2142,14 +2520,17 @@
}
_startUpdateCheckTimer() {
- if (!this._serverConfig ||
- !this._serverConfig.change ||
- this._serverConfig.change.update_delay === undefined ||
- this._serverConfig.change.update_delay <= MIN_CHECK_INTERVAL_SECS) {
+ if (
+ !this._serverConfig ||
+ !this._serverConfig.change ||
+ this._serverConfig.change.update_delay === undefined ||
+ this._serverConfig.change.update_delay <= MIN_CHECK_INTERVAL_SECS
+ ) {
return;
}
this._updateCheckTimerHandle = this.async(() => {
+ if (!this._change) throw new Error('missing required change property');
const change = this._change;
fetchChangeUpdates(change, this.$.restAPI).then(result => {
let toastMessage = null;
@@ -2176,19 +2557,24 @@
}
this._cancelUpdateCheckTimer();
- this.dispatchEvent(new CustomEvent('show-alert', {
- detail: {
- message: toastMessage,
- // Persist this alert.
- dismissOnNavigation: true,
- action: 'Reload',
- callback: () => {
- this._reload(/* opt_isLocationChange= */false,
- /* opt_clearPatchset= */true);
+ this.dispatchEvent(
+ new CustomEvent('show-alert', {
+ detail: {
+ message: toastMessage,
+ // Persist this alert.
+ dismissOnNavigation: true,
+ action: 'Reload',
+ callback: () => {
+ this._reload(
+ /* isLocationChange= */ false,
+ /* clearPatchset= */ true
+ );
+ },
},
- },
- composed: true, bubbles: true,
- }));
+ composed: true,
+ bubbles: true,
+ })
+ );
});
}, this._serverConfig.change.update_delay * 1000);
}
@@ -2212,27 +2598,42 @@
this.$.relatedChanges.reload();
}
- _computeHeaderClass(editMode) {
+ _computeHeaderClass(editMode: boolean) {
const classes = ['header'];
- if (editMode) { classes.push('editMode'); }
+ if (editMode) {
+ classes.push('editMode');
+ }
return classes.join(' ');
}
- _computeEditMode(patchRangeRecord, paramsRecord) {
- if ([patchRangeRecord, paramsRecord].includes(undefined)) {
+ _computeEditMode(
+ patchRangeRecord: PolymerDeepPropertyChange<PatchRange, PatchRange>,
+ paramsRecord: PolymerDeepPropertyChange<
+ AppElementChangeViewParams,
+ AppElementChangeViewParams
+ >
+ ) {
+ if (!patchRangeRecord || !paramsRecord) {
return undefined;
}
- if (paramsRecord.base && paramsRecord.base.edit) { return true; }
+ if (paramsRecord.base && paramsRecord.base.edit) {
+ return true;
+ }
const patchRange = patchRangeRecord.base || {};
- return patchNumEquals(patchRange.patchNum, SPECIAL_PATCH_SET_NUM.EDIT);
+ return patchNumEquals(patchRange.patchNum, EditPatchSetNum);
}
- _handleFileActionTap(e) {
+ _handleFileActionTap(e: CustomEvent<{path: string; action: string}>) {
e.preventDefault();
- const controls = this.$.fileListHeader
- .shadowRoot.querySelector('#editControls');
+ const controls = this.$.fileListHeader.shadowRoot!.querySelector(
+ '#editControls'
+ ) as GrEditControls | null;
+ if (!controls) throw new Error('Missing edit controls');
+ if (!this._change) throw new Error('missing required change property');
+ if (!this._patchRange)
+ throw new Error('missing required _patchRange property');
const path = e.detail.path;
switch (e.detail.action) {
case GrEditConstants.Actions.DELETE.id:
@@ -2240,8 +2641,12 @@
break;
case GrEditConstants.Actions.OPEN.id:
GerritNav.navigateToRelativeUrl(
- GerritNav.getEditUrlForDiff(this._change, path,
- this._patchRange.patchNum));
+ GerritNav.getEditUrlForDiff(
+ this._change,
+ path,
+ this._patchRange.patchNum
+ )
+ );
break;
case GrEditConstants.Actions.RENAME.id:
controls.openRenameDialog(path);
@@ -2252,25 +2657,31 @@
}
}
- _computeCommitMessageKey(number, revision) {
+ _computeCommitMessageKey(number: NumericChangeId, revision: CommitId) {
return `c${number}_rev${revision}`;
}
- _patchNumChanged(patchNumStr) {
+ @observe('_patchRange.patchNum')
+ _patchNumChanged(patchNumStr: PatchSetNum) {
if (!this._selectedRevision) {
return;
}
+ if (!this._change) throw new Error('missing required change property');
- let patchNum = parseInt(patchNumStr, 10);
+ let patchNum: PatchSetNum;
if (patchNumStr === 'edit') {
- patchNum = patchNumStr;
+ patchNum = EditPatchSetNum;
+ } else {
+ patchNum = parseInt(`${patchNumStr}`, 10) as PatchSetNum;
}
if (patchNum === this._selectedRevision._number) {
return;
}
- this._selectedRevision = Object.values(this._change.revisions).find(
- revision => revision._number === patchNum);
+ if (this._change.revisions)
+ this._selectedRevision = Object.values(this._change.revisions).find(
+ revision => revision._number === patchNum
+ );
}
/**
@@ -2278,25 +2689,37 @@
* navigation API.
*/
_handleEditTap() {
- const editInfo = Object.values(this._change.revisions).find(info =>
- info._number === SPECIAL_PATCH_SET_NUM.EDIT);
+ if (!this._change || !this._change.revisions)
+ throw new Error('missing required change property');
+ const editInfo = Object.values(this._change.revisions).find(
+ info => info._number === EditPatchSetNum
+ );
if (editInfo) {
- GerritNav.navigateToChange(this._change, SPECIAL_PATCH_SET_NUM.EDIT);
+ GerritNav.navigateToChange(this._change, EditPatchSetNum);
return;
}
// Avoid putting patch set in the URL unless a non-latest patch set is
// selected.
+ if (!this._patchRange)
+ throw new Error('missing required _patchRange property');
let patchNum;
- if (!patchNumEquals(this._patchRange.patchNum,
- computeLatestPatchNum(this._allPatchSets))) {
+ if (
+ !patchNumEquals(
+ this._patchRange.patchNum,
+ computeLatestPatchNum(this._allPatchSets)
+ )
+ ) {
patchNum = this._patchRange.patchNum;
}
- GerritNav.navigateToChange(this._change, patchNum, null, true);
+ GerritNav.navigateToChange(this._change, patchNum, undefined, true);
}
_handleStopEditTap() {
+ if (!this._change) throw new Error('missing required change property');
+ if (!this._patchRange)
+ throw new Error('missing required _patchRange property');
GerritNav.navigateToChange(this._change, this._patchRange.patchNum);
}
@@ -2304,50 +2727,62 @@
this.$.replyOverlay.setFocusStops(this.$.replyDialog.getFocusStops());
}
- _handleToggleStar(e) {
- this.$.restAPI.saveChangeStarred(e.detail.change._number,
- e.detail.starred);
+ _handleToggleStar(e: CustomEvent<{change: ChangeInfo; starred: boolean}>) {
+ this.$.restAPI.saveChangeStarred(e.detail.change._number, e.detail.starred);
}
- _getRevisionInfo(change) {
- return new RevisionInfo(change);
+ _getRevisionInfo(change: ChangeInfo | ParsedChangeInfo) {
+ return new RevisionInfoClass(change);
}
- _computeCurrentRevision(currentRevision, revisions) {
+ _computeCurrentRevision(
+ currentRevision: CommitId,
+ revisions: {[revisionId: string]: RevisionInfo}
+ ) {
return currentRevision && revisions && revisions[currentRevision];
}
- _computeDiffPrefsDisabled(disableDiffPrefs, loggedIn) {
+ _computeDiffPrefsDisabled(disableDiffPrefs: boolean, loggedIn: boolean) {
return disableDiffPrefs || !loggedIn;
}
/**
* Wrapper for using in the element template and computed properties
*/
- _computeLatestPatchNum(allPatchSets) {
+ _computeLatestPatchNum(allPatchSets: PatchSet[]) {
return computeLatestPatchNum(allPatchSets);
}
/**
* Wrapper for using in the element template and computed properties
*/
- _hasEditBasedOnCurrentPatchSet(allPatchSets) {
+ _hasEditBasedOnCurrentPatchSet(allPatchSets: PatchSet[]) {
return hasEditBasedOnCurrentPatchSet(allPatchSets);
}
/**
* Wrapper for using in the element template and computed properties
*/
- _hasEditPatchsetLoaded(patchRangeRecord) {
- return hasEditPatchsetLoaded(patchRangeRecord);
+ _hasEditPatchsetLoaded(
+ patchRangeRecord: PolymerDeepPropertyChange<PatchRange, PatchRange>
+ ) {
+ const patchRange = patchRangeRecord.base;
+ if (!patchRange) {
+ return false;
+ }
+ return hasEditPatchsetLoaded(patchRange);
}
/**
* Wrapper for using in the element template and computed properties
*/
- _computeAllPatchSets(change) {
+ _computeAllPatchSets(change: ChangeInfo) {
return computeAllPatchSets(change);
}
}
-customElements.define(GrChangeView.is, GrChangeView);
+declare global {
+ interface HTMLElementTagNameMap {
+ 'gr-change-view': GrChangeView;
+ }
+}
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js
index 6170bea..4fcbc78 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js
@@ -300,6 +300,7 @@
_fetchSharedCacheURL() { return Promise.resolve({}); },
});
element = fixture.instantiate();
+ element._changeNum = '1';
sinon.stub(element.$.actions, 'reload').returns(Promise.resolve());
getPluginLoader().loadPlugins([]);
pluginApi.install(
@@ -333,6 +334,11 @@
basePatchNum: 'PARENT',
patchNum: 1,
};
+ element._change = {
+ _number: '1',
+ project: '',
+ change_id: '1',
+ };
const getUrlStub = sinon.stub(GerritNav, 'getUrlForChange');
const replaceStateStub = sinon.stub(history, 'replaceState');
element._handleMessageAnchorTap({detail: {id: 'a12345'}});
@@ -414,6 +420,7 @@
suite('plugins adding to file tab', () => {
setup(done => {
+ element._changeNum = '1';
// Resolving it here instead of during setup() as other tests depend
// on flush() not being called during setup.
flush(() => done());
@@ -459,6 +466,7 @@
queryMap.set('tab', PrimaryTab.FINDINGS);
// view is required
element.params = {
+ changeNum: '1',
view: GerritNav.View.CHANGE,
...element.params, queryMap};
flush(() => {
@@ -473,6 +481,7 @@
queryMap.set('tab', 'random');
// view is required
element.params = {
+ changeNum: '1',
view: GerritNav.View.CHANGE,
...element.params, queryMap};
flush(() => {
@@ -783,6 +792,7 @@
getAllThreadsForChange: () => ([]),
computeDraftCount: () => 1,
}));
+ element._changeNum = '1';
});
test('drafts are reloaded when reload-drafts fired', done => {
@@ -1415,6 +1425,7 @@
});
test('_handleCommitMessageSave trims trailing whitespace', () => {
+ element._change = {};
const putStub = sinon.stub(element.$.restAPI, 'putChangeCommitMessage')
.returns(Promise.resolve({}));
@@ -1609,14 +1620,16 @@
});
test('_openReplyDialog called with `ANY` when coming from tap event',
- () => {
- const openStub = sinon.stub(element, '_openReplyDialog');
- element._serverConfig = {};
- MockInteractions.tap(element.$.replyBtn);
- assert(openStub.lastCall.calledWithExactly(
- element.$.replyDialog.FocusTarget.ANY),
- '_openReplyDialog should have been passed ANY');
- assert.equal(openStub.callCount, 1);
+ done => {
+ flush(() => {
+ const openStub = sinon.stub(element, '_openReplyDialog');
+ MockInteractions.tap(element.$.replyBtn);
+ assert(openStub.lastCall.calledWithExactly(
+ element.$.replyDialog.FocusTarget.ANY),
+ '_openReplyDialog should have been passed ANY');
+ assert.equal(openStub.callCount, 1);
+ done();
+ });
});
test('_openReplyDialog called with `BODY` when coming from message reply' +
@@ -1806,10 +1819,13 @@
});
});
- test('reply button is disabled until server config is loaded', () => {
+ test('reply button is disabled until server config is loaded', done => {
assert.isTrue(element._replyDisabled);
- element._serverConfig = {};
- assert.isFalse(element._replyDisabled);
+ // fetches the server config on attached
+ flush(() => {
+ assert.isFalse(element._replyDisabled);
+ done();
+ });
});
suite('commit message expand/collapse', () => {
@@ -2189,6 +2205,11 @@
basePatchNum: 'PARENT',
patchNum: 1,
};
+ element._change = {
+ _number: '1',
+ project: '',
+ change_id: '1',
+ };
const fileList = element.$.fileList;
const Actions = GrEditConstants.Actions;
element.$.fileListHeader.editMode = true;
@@ -2371,6 +2392,11 @@
});
test('_handleStopEditTap', done => {
+ element._change = {
+ _number: '1',
+ project: '',
+ change_id: '1',
+ };
sinon.stub(element.$.metadata, '_computeLabelNames');
navigateToChangeStub.restore();
sinon.stub(GerritNav, 'navigateToChange').callsFake((...args) => {
diff --git a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.ts b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.ts
index f5e3588..1957f5c 100644
--- a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.ts
@@ -65,7 +65,7 @@
loadData() {
if (!this.changeNum) {
- return;
+ return Promise.reject(new Error('missing required property changeNum'));
}
this._filterText = '';
return this.$.restAPI.getChangeIncludedIn(this.changeNum).then(configs => {
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
index b3478b1..85f5b71 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
@@ -113,7 +113,7 @@
const STORAGE_DEBOUNCE_INTERVAL_MS = 400;
-enum FocusTarget {
+export enum FocusTarget {
ANY = 'any',
BODY = 'body',
CCS = 'cc',
diff --git a/polygerrit-ui/app/elements/gr-app-types.ts b/polygerrit-ui/app/elements/gr-app-types.ts
index 448b281..29eef48 100644
--- a/polygerrit-ui/app/elements/gr-app-types.ts
+++ b/polygerrit-ui/app/elements/gr-app-types.ts
@@ -105,6 +105,15 @@
leftSide?: boolean;
commentLink?: boolean;
}
+export interface AppElementChangeViewParams {
+ view: GerritView.CHANGE;
+ changeNum: NumericChangeId;
+ project: RepoName;
+ edit?: boolean;
+ patchNum?: PatchSetNum;
+ basePatchNum?: PatchSetNum;
+ queryMap?: Map<string, string> | URLSearchParams;
+}
export interface AppElementJustRegisteredParams {
// We use params.view === ... as a type guard.
@@ -120,6 +129,7 @@
| AppElementDashboardParams
| AppElementGroupParams
| AppElementAdminParams
+ | AppElementChangeViewParams
| AppElementRepoParams
| AppElementDocSearchParams
| AppElementPluginScreenParams
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.ts b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.ts
index 8c26d4a..626c2dc 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.ts
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.ts
@@ -35,6 +35,7 @@
} from './gr-js-api-types';
import {EventType, TargetElement} from '../../plugins/gr-plugin-types';
import {DiffLayer, HighlightJS} from '../../../types/types';
+import {ParsedChangeInfo} from '../gr-rest-api-interface/gr-reviewer-updates-parser';
const elements: {[key: string]: HTMLElement} = {};
const eventCallbacks: {[key: string]: EventCallback[]} = {};
@@ -177,7 +178,7 @@
}
}
- handleCommitMessage(change: ChangeInfo, msg: string) {
+ handleCommitMessage(change: ChangeInfo | ParsedChangeInfo, msg: string) {
for (const cb of this._getEventCallbacks(EventType.COMMIT_MSG_EDIT)) {
try {
cb(change, msg);
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts
index 15cdac4..75af8a4 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts
@@ -139,6 +139,7 @@
GroupName,
Hashtag,
TopMenuEntryInfo,
+ MergeableInfo,
} from '../../../types/common';
import {
CancelConditionCallback,
@@ -1551,7 +1552,7 @@
endpoint: '/commit?links',
patchNum,
reportEndpointAsIs: true,
- });
+ }) as Promise<CommitInfo | undefined>;
}
getChangeFiles(
@@ -3582,7 +3583,7 @@
changeNum,
endpoint: '/revisions/current/mergeable',
reportEndpointAsIs: true,
- });
+ }) as Promise<MergeableInfo | undefined>;
}
deleteDraftComments(query: string): Promise<Response> {
diff --git a/polygerrit-ui/app/elements/shared/revision-info/revision-info.ts b/polygerrit-ui/app/elements/shared/revision-info/revision-info.ts
index 180fb2e..fadbfa7 100644
--- a/polygerrit-ui/app/elements/shared/revision-info/revision-info.ts
+++ b/polygerrit-ui/app/elements/shared/revision-info/revision-info.ts
@@ -17,6 +17,7 @@
import {patchNumEquals} from '../../../utils/patch-set-util';
import {ChangeInfo, PatchSetNum} from '../../../types/common';
+import {ParsedChangeInfo} from '../gr-rest-api-interface/gr-reviewer-updates-parser';
type RevNumberToParentCountMap = {[revNumber: number]: number};
@@ -26,7 +27,7 @@
* @param change A change object resulting from a change detail
* call that includes revision information.
*/
- constructor(private change: ChangeInfo) {}
+ constructor(private change: ChangeInfo | ParsedChangeInfo) {}
/**
* Get the largest number of parents of the commit in any revision. For
diff --git a/polygerrit-ui/app/services/services/gr-rest-api/gr-rest-api.ts b/polygerrit-ui/app/services/services/gr-rest-api/gr-rest-api.ts
index 7ff65e2..950619b 100644
--- a/polygerrit-ui/app/services/services/gr-rest-api/gr-rest-api.ts
+++ b/polygerrit-ui/app/services/services/gr-rest-api/gr-rest-api.ts
@@ -98,6 +98,8 @@
Hashtag,
FileNameToFileInfoMap,
TopMenuEntryInfo,
+ MergeableInfo,
+ CommitInfo,
} from '../../../types/common';
import {ParsedChangeInfo} from '../../../elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser';
import {HttpMethod, IgnoreWhitespaceType} from '../../../constants/constants';
@@ -216,7 +218,7 @@
getChangeDetail(
changeNum: number | string,
- opt_errFn?: Function,
+ opt_errFn?: ErrorCallback,
opt_cancelCondition?: Function
): Promise<ParsedChangeInfo | null | undefined>;
@@ -851,4 +853,15 @@
getTopMenus(errFn?: ErrorCallback): Promise<TopMenuEntryInfo[] | undefined>;
setInProjectLookup(changeNum: NumericChangeId, project: RepoName): void;
+ getMergeable(changeNum: NumericChangeId): Promise<MergeableInfo | undefined>;
+
+ putChangeCommitMessage(
+ changeNum: NumericChangeId,
+ message: string
+ ): Promise<Response>;
+
+ getChangeCommitInfo(
+ changeNum: NumericChangeId,
+ patchNum: PatchSetNum
+ ): Promise<CommitInfo | undefined>;
}
diff --git a/polygerrit-ui/app/types/common.ts b/polygerrit-ui/app/types/common.ts
index bd71f2c..81eae16 100644
--- a/polygerrit-ui/app/types/common.ts
+++ b/polygerrit-ui/app/types/common.ts
@@ -44,6 +44,7 @@
NotifyType,
EmailFormat,
AuthType,
+ MergeStrategy,
} from '../constants/constants';
import {PolymerDeepPropertyChange} from '@polymer/polymer/interfaces';
@@ -804,7 +805,7 @@
large_change: string;
reply_label: string;
reply_tooltip: string;
- update_delay: string;
+ update_delay: number;
submit_whole_topic: boolean;
disable_private_changes: boolean;
mergeability_computation_behavior: string;
@@ -2235,3 +2236,16 @@
topic?: TopicName;
allow_empty?: boolean;
}
+
+/**
+ * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#mergeable-info
+ */
+export interface MergeableInfo {
+ submit_type: SubmitType;
+ strategy?: MergeStrategy;
+ mergeable: boolean;
+ commit_merged?: boolean;
+ content_merged?: boolean;
+ conflicts?: string[];
+ mergeable_into?: string[];
+}
diff --git a/polygerrit-ui/app/types/types.ts b/polygerrit-ui/app/types/types.ts
index 708b5d8..3bb8e37 100644
--- a/polygerrit-ui/app/types/types.ts
+++ b/polygerrit-ui/app/types/types.ts
@@ -20,6 +20,7 @@
import {FlattenedNodesObserver} from '@polymer/polymer/lib/utils/flattened-nodes-observer';
import {PaperInputElement} from '@polymer/paper-input/paper-input';
import {CommitId, NumericChangeId, PatchRange, PatchSetNum} from './common';
+import {PolymerSpliceChange} from '@polymer/polymer/interfaces';
export function notUndefined<T>(x: T | undefined): x is T {
return x !== undefined;
@@ -213,3 +214,10 @@
basePath?: string;
path: string;
}
+
+export function isPolymerSpliceChange<
+ T,
+ U extends Array<{} | null | undefined>
+>(x: T | PolymerSpliceChange<U>): x is PolymerSpliceChange<U> {
+ return (x as PolymerSpliceChange<U>).indexSplices !== undefined;
+}
diff --git a/polygerrit-ui/app/utils/patch-set-util.ts b/polygerrit-ui/app/utils/patch-set-util.ts
index 962278d..48ef367 100644
--- a/polygerrit-ui/app/utils/patch-set-util.ts
+++ b/polygerrit-ui/app/utils/patch-set-util.ts
@@ -57,10 +57,6 @@
basePatchNum?: PatchSetNum;
}
-interface PatchRangeRecord {
- base: PatchRange;
-}
-
/**
* As patchNum can be either a string (e.g. 'edit', 'PARENT') OR a number,
* this function checks for patchNum equality.
@@ -246,7 +242,9 @@
export const _testOnly_computeWipForPatchSets = _computeWipForPatchSets;
-export function computeLatestPatchNum(allPatchSets?: PatchSet[]) {
+export function computeLatestPatchNum(
+ allPatchSets?: PatchSet[]
+): PatchSetNum | undefined {
if (!allPatchSets || !allPatchSets.length) {
return undefined;
}
@@ -263,11 +261,7 @@
return allPatchSets[0].num === EditPatchSetNum;
}
-export function hasEditPatchsetLoaded(patchRangeRecord: PatchRangeRecord) {
- const patchRange = patchRangeRecord.base;
- if (!patchRange) {
- return false;
- }
+export function hasEditPatchsetLoaded(patchRange: PatchRange) {
return (
patchRange.patchNum === EditPatchSetNum ||
patchRange.basePatchNum === EditPatchSetNum
@@ -283,7 +277,7 @@
* meantime. The promise is rejected on network error.
*/
export function fetchChangeUpdates(
- change: ChangeInfo,
+ change: ChangeInfo | ParsedChangeInfo,
restAPI: RestApiService
) {
const knownLatest = computeLatestPatchNum(computeAllPatchSets(change));