summaryrefslogtreecommitdiffstats
path: root/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
diff options
context:
space:
mode:
Diffstat (limited to 'polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js')
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js1075
1 files changed, 618 insertions, 457 deletions
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index baaf002943..1f050baada 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -14,18 +14,30 @@
(function() {
'use strict';
- var CHANGE_ID_ERROR = {
+ const CHANGE_ID_ERROR = {
MISMATCH: 'mismatch',
MISSING: 'missing',
};
- var CHANGE_ID_REGEX_PATTERN = /^Change-Id\:\s(I[0-9a-f]{8,40})/gm;
- var COMMENT_SAVE = 'Saving... Try again after all comments are saved.';
+ const CHANGE_ID_REGEX_PATTERN = /^Change-Id\:\s(I[0-9a-f]{8,40})/gm;
- var MIN_LINES_FOR_COMMIT_COLLAPSE = 30;
+ const MIN_LINES_FOR_COMMIT_COLLAPSE = 30;
+ const DEFAULT_NUM_FILES_SHOWN = 200;
- // Maximum length for patch set descriptions.
- var PATCH_DESC_MAX_LENGTH = 500;
- var REVIEWERS_REGEX = /^R=/gm;
+ const REVIEWERS_REGEX = /^(R|CC)=/gm;
+ const MIN_CHECK_INTERVAL_SECS = 0;
+
+ // These are the same as the breakpoint set in CSS. Make sure both are changed
+ // together.
+ const BREAKPOINT_RELATED_SMALL = '50em';
+ const BREAKPOINT_RELATED_MED = '60em';
+
+ // In the event that the related changes medium width calculation is too close
+ // to zero, provide some height.
+ const MINIMUM_RELATED_MAX_HEIGHT = 100;
+
+ const SMALL_RELATED_HEIGHT = 400;
+
+ const REPLY_REFIT_DEBOUNCE_INTERVAL_MS = 500;
Polymer({
is: 'gr-change-view',
@@ -42,6 +54,12 @@
* @event page-error
*/
+ /**
+ * Fired if being logged in is required.
+ *
+ * @event show-auth-required
+ */
+
properties: {
/**
* URL params passed from the router.
@@ -50,34 +68,51 @@
type: Object,
observer: '_paramsChanged',
},
+ /** @type {?} */
viewState: {
type: Object,
notify: true,
- value: function() { return {}; },
+ value() { return {}; },
+ observer: '_viewStateChanged',
},
backPage: String,
hasParent: Boolean,
- serverConfig: Object,
keyEventTarget: {
type: Object,
- value: function() { return document.body; },
+ value() { return document.body; },
+ },
+ /** @type {?} */
+ _serverConfig: {
+ type: Object,
+ observer: '_startUpdateCheckTimer',
+ },
+ _diffPrefs: Object,
+ _numFilesShown: {
+ type: Number,
+ value: DEFAULT_NUM_FILES_SHOWN,
+ observer: '_numFilesShownChanged',
},
-
_account: {
type: Object,
value: {},
},
+ _canStartReview: {
+ type: Boolean,
+ computed: '_computeCanStartReview(_change)',
+ },
_comments: Object,
+ /** @type {?} */
_change: {
type: Object,
observer: '_changeChanged',
},
+ /** @type {?} */
_commitInfo: Object,
_files: Object,
_changeNum: String,
_diffDrafts: {
type: Object,
- value: function() { return {}; },
+ value() { return {}; },
},
_editingCommitMessage: {
type: Boolean,
@@ -88,6 +123,8 @@
computed: '_computeHideEditCommitMessage(_loggedIn, ' +
'_editingCommitMessage, _change)',
},
+ _diffAgainst: String,
+ /** @type {?string} */
_latestCommitMessage: {
type: String,
value: '',
@@ -98,10 +135,20 @@
computed:
'_computeChangeIdCommitMessageError(_latestCommitMessage, _change)',
},
+ /** @type {?} */
_patchRange: {
type: Object,
- observer: '_updateSelected',
},
+ // These are kept as separate properties from the patchRange so that the
+ // observer can be aware of the previous value. In order to view sub
+ // property changes for _patchRange, a complex observer must be used, and
+ // that only displays the new value.
+ //
+ // If a previous value did not exist, the change is not reloaded with the
+ // new patches. This is just the initial setting from the change view vs.
+ // an update coming from the two way data binding.
+ _patchNum: String,
+ _basePatchNum: String,
_relatedChangesLoading: {
type: Boolean,
value: true,
@@ -109,37 +156,35 @@
_currentRevisionActions: Object,
_allPatchSets: {
type: Array,
- computed: '_computeAllPatchSets(_change, _change.revisions.*)',
+ computed: 'computeAllPatchSets(_change, _change.revisions.*)',
},
_loggedIn: {
type: Boolean,
value: false,
},
_loading: Boolean,
+ /** @type {?} */
_projectConfig: Object,
_rebaseOnCurrent: Boolean,
_replyButtonLabel: {
type: String,
value: 'Reply',
- computed: '_computeReplyButtonLabel(_diffDrafts.*)',
+ computed: '_computeReplyButtonLabel(_diffDrafts.*, _canStartReview)',
},
_selectedPatchSet: String,
+ _shownFileCount: Number,
_initialLoadComplete: {
type: Boolean,
value: false,
},
- _descriptionReadOnly: {
- type: Boolean,
- computed: '_computeDescriptionReadOnly(_loggedIn, _change, _account)',
- },
_replyDisabled: {
type: Boolean,
value: true,
- computed: '_computeReplyDisabled(serverConfig)',
+ computed: '_computeReplyDisabled(_serverConfig)',
},
_changeStatus: {
type: String,
- computed: '_computeChangeStatus(_change, _patchRange.patchNum)',
+ computed: 'changeStatusString(_change)',
},
_commitCollapsed: {
type: Boolean,
@@ -149,18 +194,35 @@
type: Boolean,
value: true,
},
+ /** @type {?number} */
+ _updateCheckTimerHandle: Number,
+ _sortedRevisions: Array,
+ _editLoaded: {
+ type: Boolean,
+ computed: '_computeEditLoaded(_patchRange.*)',
+ },
},
behaviors: [
- Gerrit.BaseUrlBehavior,
Gerrit.KeyboardShortcutBehavior,
Gerrit.PatchSetBehavior,
Gerrit.RESTClientBehavior,
],
+ listeners: {
+ 'topic-changed': '_handleTopicChanged',
+ // 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': '_handleHideBackgroundContent',
+ 'fullscreen-overlay-closed': '_handleShowBackgroundContent',
+ },
observers: [
'_labelsChanged(_change.labels.*)',
'_paramsAndChangeChanged(params, _change)',
+ '_updateSortedRevisions(_change.revisions.*)',
],
keyBindings: {
@@ -171,19 +233,26 @@
'u': '_handleUKey',
'x': '_handleXKey',
'z': '_handleZKey',
+ ',': '_handleCommaKey',
},
- attached: function() {
- this._getLoggedIn().then(function(loggedIn) {
+ attached() {
+ this._getServerConfig().then(config => {
+ this._serverConfig = config;
+ });
+
+ this._getLoggedIn().then(loggedIn => {
this._loggedIn = loggedIn;
if (loggedIn) {
- this.$.restAPI.getAccount().then(function(acct) {
+ this.$.restAPI.getAccount().then(acct => {
this._account = acct;
- }.bind(this));
+ });
}
- }.bind(this));
+ this._setDiffViewMode();
+ });
this.addEventListener('comment-save', this._handleCommentSave.bind(this));
+ this.addEventListener('comment-refresh', this._getDiffDrafts.bind(this));
this.addEventListener('comment-discard',
this._handleCommentDiscard.bind(this));
this.addEventListener('editable-content-save',
@@ -191,53 +260,74 @@
this.addEventListener('editable-content-cancel',
this._handleCommitMessageCancel.bind(this));
this.listen(window, 'scroll', '_handleScroll');
+ this.listen(document, 'visibilitychange', '_handleVisibilityChange');
},
- detached: function() {
+ detached() {
this.unlisten(window, 'scroll', '_handleScroll');
+ this.unlisten(document, 'visibilitychange', '_handleVisibilityChange');
+
+ if (this._updateCheckTimerHandle) {
+ this._cancelUpdateCheckTimer();
+ }
},
- _handleEditCommitMessage: function(e) {
+ /**
+ * @param {boolean=} opt_reset
+ */
+ _setDiffViewMode(opt_reset) {
+ if (!opt_reset && this.viewState.diffViewMode) { return; }
+
+ return this.$.restAPI.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');
+ }
+ });
+ },
+
+ _updateSortedRevisions(revisionsRecord) {
+ const revisions = revisionsRecord.base;
+ this._sortedRevisions = this.sortRevisions(Object.values(revisions));
+ },
+
+ _handleEditCommitMessage(e) {
this._editingCommitMessage = true;
this.$.commitMessageEditor.focusTextarea();
},
- _handleCommitMessageSave: function(e) {
- var message = e.detail.content;
+ _handleCommitMessageSave(e) {
+ const message = e.detail.content;
this.$.jsAPI.handleCommitMessage(this._change, message);
this.$.commitMessageEditor.disabled = true;
- this._saveCommitMessage(message).then(function(resp) {
- this.$.commitMessageEditor.disabled = false;
- if (!resp.ok) { return; }
-
- this._latestCommitMessage = this._prepareCommitMsgForLinkify(message);
- this._editingCommitMessage = false;
- this._reloadWindow();
- }.bind(this)).catch(function(err) {
- this.$.commitMessageEditor.disabled = false;
- }.bind(this));
+ 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;
+ });
},
- _reloadWindow: function() {
+ _reloadWindow() {
window.location.reload();
},
- _handleCommitMessageCancel: function(e) {
+ _handleCommitMessageCancel(e) {
this._editingCommitMessage = false;
},
- _saveCommitMessage: function(message) {
- return this.$.restAPI.saveChangeCommitMessageEdit(
- this._changeNum, message).then(function(resp) {
- if (!resp.ok) { return resp; }
-
- return this.$.restAPI.publishChangeEdit(this._changeNum);
- }.bind(this));
- },
-
- _computeHideEditCommitMessage: function(loggedIn, editing, change) {
+ _computeHideEditCommitMessage(loggedIn, editing, change) {
if (!loggedIn || editing || change.status === this.ChangeStatus.MERGED) {
return true;
}
@@ -245,24 +335,23 @@
return false;
},
- _handleCommentSave: function(e) {
+ _handleCommentSave(e) {
if (!e.target.comment.__draft) { return; }
- var draft = e.target.comment;
+ const draft = e.target.comment;
draft.patch_set = draft.patch_set || this._patchRange.patchNum;
// The use of path-based notification helpers (set, push) can’t be used
// because the paths could contain dots in them. A new object must be
// created to satisfy Polymer’s dirty checking.
// https://github.com/Polymer/polymer/issues/3127
- // TODO(andybons): Polyfill for Object.assign in IE.
- var diffDrafts = Object.assign({}, this._diffDrafts);
+ const diffDrafts = Object.assign({}, this._diffDrafts);
if (!diffDrafts[draft.path]) {
diffDrafts[draft.path] = [draft];
this._diffDrafts = diffDrafts;
return;
}
- for (var i = 0; i < this._diffDrafts[draft.path].length; i++) {
+ for (let i = 0; i < this._diffDrafts[draft.path].length; i++) {
if (this._diffDrafts[draft.path][i].id === draft.id) {
diffDrafts[draft.path][i] = draft;
this._diffDrafts = diffDrafts;
@@ -270,7 +359,7 @@
}
}
diffDrafts[draft.path].push(draft);
- diffDrafts[draft.path].sort(function(c1, c2) {
+ diffDrafts[draft.path].sort((c1, c2) => {
// No line number means that it’s a file comment. Sort it above the
// others.
return (c1.line || -1) - (c2.line || -1);
@@ -278,15 +367,15 @@
this._diffDrafts = diffDrafts;
},
- _handleCommentDiscard: function(e) {
+ _handleCommentDiscard(e) {
if (!e.target.comment.__draft) { return; }
- var draft = e.target.comment;
+ const draft = e.target.comment;
if (!this._diffDrafts[draft.path]) {
return;
}
- var index = -1;
- for (var i = 0; i < this._diffDrafts[draft.path].length; i++) {
+ let index = -1;
+ for (let i = 0; i < this._diffDrafts[draft.path].length; i++) {
if (this._diffDrafts[draft.path][i].id === draft.id) {
index = i;
break;
@@ -304,8 +393,7 @@
// because the paths could contain dots in them. A new object must be
// created to satisfy Polymer’s dirty checking.
// https://github.com/Polymer/polymer/issues/3127
- // TODO(andybons): Polyfill for Object.assign in IE.
- var diffDrafts = Object.assign({}, this._diffDrafts);
+ const diffDrafts = Object.assign({}, this._diffDrafts);
diffDrafts[draft.path].splice(index, 1);
if (diffDrafts[draft.path].length === 0) {
delete diffDrafts[draft.path];
@@ -313,32 +401,44 @@
this._diffDrafts = diffDrafts;
},
- _handlePatchChange: function(e) {
- this._changePatchNum(parseInt(e.target.value, 10), true);
- },
-
- _handleReplyTap: function(e) {
+ _handleReplyTap(e) {
e.preventDefault();
this._openReplyDialog();
},
- _handleDownloadTap: function(e) {
- e.preventDefault();
- this.$.downloadOverlay.open().then(function() {
+ _handleOpenDiffPrefs() {
+ this.$.fileList.openDiffPrefs();
+ },
+
+
+ _handleOpenIncludedInDialog() {
+ this.$.includedInDialog.loadData().then(() => {
+ Polymer.dom.flush();
+ this.$.includedInOverlay.refit();
+ });
+ this.$.includedInOverlay.open();
+ },
+
+ _handleIncludedInDialogClose(e) {
+ this.$.includedInOverlay.close();
+ },
+
+ _handleOpenDownloadDialog() {
+ this.$.downloadOverlay.open().then(() => {
this.$.downloadOverlay
.setFocusStops(this.$.downloadDialog.getFocusStops());
this.$.downloadDialog.focus();
- }.bind(this));
+ });
},
- _handleDownloadDialogClose: function(e) {
+ _handleDownloadDialogClose(e) {
this.$.downloadOverlay.close();
},
- _handleMessageReply: function(e) {
- var msg = e.detail.message.message;
- var quoteStr = msg.split('\n').map(
- function(line) { return '> ' + line; }).join('\n') + '\n\n';
+ _handleMessageReply(e) {
+ const msg = e.detail.message.message;
+ const quoteStr = msg.split('\n').map(
+ line => { return '> ' + line; }).join('\n') + '\n\n';
if (quoteStr !== this.$.replyDialog.quote) {
this.$.replyDialog.draft = quoteStr;
@@ -347,49 +447,71 @@
this._openReplyDialog();
},
- _handleReplyOverlayOpen: function(e) {
- this.$.replyDialog.focus();
+ _handleReplyOverlayOpen(e) {
+ // This is needed so that focus is not set on the reply overlay
+ // when the suggestion overaly from gr-autogrow-textarea opens.
+ if (e.target === this.$.replyOverlay) {
+ this.$.replyDialog.focus();
+ }
+ },
+
+ _handleHideBackgroundContent() {
+ this.$.mainContent.classList.add('overlayOpen');
},
- _handleReplySent: function(e) {
+ _handleShowBackgroundContent() {
+ this.$.mainContent.classList.remove('overlayOpen');
+ },
+
+ _handleReplySent(e) {
this.$.replyOverlay.close();
this._reload();
},
- _handleReplyCancel: function(e) {
+ _handleReplyCancel(e) {
this.$.replyOverlay.close();
},
- _handleReplyAutogrow: function(e) {
- this.$.replyOverlay.refit();
+ _handleReplyAutogrow(e) {
+ // If the textarea resizes, we need to re-fit the overlay.
+ this.debounce('reply-overlay-refit', () => {
+ this.$.replyOverlay.refit();
+ }, REPLY_REFIT_DEBOUNCE_INTERVAL_MS);
},
- _handleShowReplyDialog: function(e) {
- var target = this.$.replyDialog.FocusTarget.REVIEWERS;
+ _handleShowReplyDialog(e) {
+ let target = this.$.replyDialog.FocusTarget.REVIEWERS;
if (e.detail.value && e.detail.value.ccsOnly) {
target = this.$.replyDialog.FocusTarget.CCS;
}
this._openReplyDialog(target);
},
- _handleScroll: function() {
- this.debounce('scroll', function() {
- history.replaceState(
- {
- scrollTop: document.body.scrollTop,
- path: location.pathname,
- },
- location.pathname);
+ _handleScroll() {
+ this.debounce('scroll', () => {
+ this.viewState.scrollTop = document.body.scrollTop;
}, 150);
},
- _paramsChanged: function(value) {
- if (value.view !== this.tagName.toLowerCase()) {
+ _setShownFiles(e) {
+ this._shownFileCount = e.detail.length;
+ },
+
+ _expandAllDiffs() {
+ this.$.fileList.expandAllDiffs();
+ },
+
+ _collapseAllDiffs() {
+ this.$.fileList.collapseAllDiffs();
+ },
+
+ _paramsChanged(value) {
+ if (value.view !== Gerrit.Nav.View.CHANGE) {
this._initialLoadComplete = false;
return;
}
- var patchChanged = this._patchRange &&
+ const patchChanged = this._patchRange &&
(value.patchNum !== undefined && value.basePatchNum !== undefined) &&
(this._patchRange.patchNum !== value.patchNum ||
this._patchRange.basePatchNum !== value.basePatchNum);
@@ -398,53 +520,38 @@
this._initialLoadComplete = false;
}
- var patchRange = {
+ const patchRange = {
patchNum: value.patchNum,
basePatchNum: value.basePatchNum || 'PARENT',
};
+ this.$.fileList.collapseAllDiffs();
+ this._patchRange = patchRange;
+
if (this._initialLoadComplete && patchChanged) {
if (patchRange.patchNum == null) {
- patchRange.patchNum = this._computeLatestPatchNum(this._allPatchSets);
+ patchRange.patchNum = this.computeLatestPatchNum(this._allPatchSets);
}
- this._patchRange = patchRange;
- this._reloadPatchNumDependentResources().then(function() {
+ this._reloadPatchNumDependentResources().then(() => {
this.$.jsAPI.handleEvent(this.$.jsAPI.EventType.SHOW_CHANGE, {
change: this._change,
patchNum: patchRange.patchNum,
});
- }.bind(this));
+ });
return;
}
this._changeNum = value.changeNum;
- this._patchRange = patchRange;
this.$.relatedChanges.clear();
- this._reload().then(function() {
+ this._reload().then(() => {
this._performPostLoadTasks();
- }.bind(this));
- },
-
- _performPostLoadTasks: function() {
- // Allow the message list and related changes to render before scrolling.
- // Related changes are loaded here (after everything else) because they
- // take the longest and are secondary information. Because the element may
- // alter the total height of the page, the call to potentially scroll to
- // a linked message is performed after related changes is fully loaded.
- this.$.relatedChanges.reload().then(function() {
- this.async(function() {
- if (history.state && history.state.scrollTop) {
- document.documentElement.scrollTop =
- document.body.scrollTop = history.state.scrollTop;
- } else {
- this._maybeScrollToMessage();
- }
- }, 1);
- }.bind(this));
+ });
+ },
+ _performPostLoadTasks() {
+ this.$.relatedChanges.reload();
this._maybeShowReplyDialog();
-
this._maybeShowRevertDialog();
this.$.jsAPI.handleEvent(this.$.jsAPI.EventType.SHOW_CHANGE, {
@@ -452,13 +559,21 @@
patchNum: this._patchRange.patchNum,
});
- this._initialLoadComplete = true;
+ this.async(() => {
+ if (this.viewState.scrollTop) {
+ document.documentElement.scrollTop =
+ document.body.scrollTop = this.viewState.scrollTop;
+ } else {
+ this._maybeScrollToMessage(window.location.hash);
+ }
+ this._initialLoadComplete = true;
+ });
},
- _paramsAndChangeChanged: function(value) {
+ _paramsAndChangeChanged(value) {
// If the change number or patch range is different, then reset the
// selected file index.
- var patchRangeState = this.viewState.patchRange;
+ const patchRangeState = this.viewState.patchRange;
if (this.viewState.changeNum !== this._changeNum ||
patchRangeState.basePatchNum !== this._patchRange.basePatchNum ||
patchRangeState.patchNum !== this._patchRange.patchNum) {
@@ -466,24 +581,32 @@
}
},
- _maybeScrollToMessage: function() {
- var msgPrefix = '#message-';
- var hash = window.location.hash;
- if (hash.indexOf(msgPrefix) === 0) {
+ _viewStateChanged(viewState) {
+ this._numFilesShown = viewState.numFilesShown ?
+ viewState.numFilesShown : DEFAULT_NUM_FILES_SHOWN;
+ },
+
+ _numFilesShownChanged(numFilesShown) {
+ this.viewState.numFilesShown = numFilesShown;
+ },
+
+ _maybeScrollToMessage(hash) {
+ const msgPrefix = '#message-';
+ if (hash.startsWith(msgPrefix)) {
this.$.messageList.scrollToMessage(hash.substr(msgPrefix.length));
}
},
- _getLocationSearch: function() {
+ _getLocationSearch() {
// Not inlining to make it easier to test.
return window.location.search;
},
- _getUrlParameter: function(param) {
- var pageURL = this._getLocationSearch().substring(1);
- var vars = pageURL.split('&');
- for (var i = 0; i < vars.length; i++) {
- var name = vars[i].split('=');
+ _getUrlParameter(param) {
+ 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) {
return name[0];
}
@@ -491,107 +614,71 @@
return null;
},
- _maybeShowRevertDialog: function() {
+ _maybeShowRevertDialog() {
Gerrit.awaitPluginsLoaded()
- .then(this._getLoggedIn.bind(this))
- .then(function(loggedIn) {
- if (!loggedIn || this._change.status !== this.ChangeStatus.MERGED) {
+ .then(this._getLoggedIn.bind(this))
+ .then(loggedIn => {
+ if (!loggedIn || this._change.status !== this.ChangeStatus.MERGED) {
// Do not display dialog if not logged-in or the change is not
// merged.
- return;
- }
- if (!!this._getUrlParameter('revert')) {
- this.$.actions.showRevertDialog();
- }
- }.bind(this));
+ return;
+ }
+ if (this._getUrlParameter('revert')) {
+ this.$.actions.showRevertDialog();
+ }
+ });
},
- _maybeShowReplyDialog: function() {
- this._getLoggedIn().then(function(loggedIn) {
+ _maybeShowReplyDialog() {
+ this._getLoggedIn().then(loggedIn => {
if (!loggedIn) { return; }
if (this.viewState.showReplyDialog) {
this._openReplyDialog();
- this.async(function() { this.$.replyOverlay.center(); }, 1);
+ // TODO(kaspern@): Find a better signal for when to call center.
+ this.async(() => { this.$.replyOverlay.center(); }, 100);
+ this.async(() => { this.$.replyOverlay.center(); }, 1000);
this.set('viewState.showReplyDialog', false);
}
- }.bind(this));
+ });
},
- _resetFileListViewState: function() {
+ _resetFileListViewState() {
this.set('viewState.selectedFileIndex', 0);
+ this.set('viewState.scrollTop', 0);
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.set('viewState.diffMode', null);
+ this._setDiffViewMode(true);
+ this.set('_numFilesShown', DEFAULT_NUM_FILES_SHOWN);
}
this.set('viewState.changeNum', this._changeNum);
this.set('viewState.patchRange', this._patchRange);
},
- _changeChanged: function(change) {
- if (!change) { return; }
+ _changeChanged(change) {
+ if (!change || !this._patchRange || !this._allPatchSets) { return; }
this.set('_patchRange.basePatchNum',
this._patchRange.basePatchNum || 'PARENT');
this.set('_patchRange.patchNum',
this._patchRange.patchNum ||
- this._computeLatestPatchNum(this._allPatchSets));
-
- this._updateSelected();
+ this.computeLatestPatchNum(this._allPatchSets));
- var title = change.subject + ' (' + change.change_id.substr(0, 9) + ')';
- this.fire('title-change', {title: title});
+ const title = change.subject + ' (' + change.change_id.substr(0, 9) + ')';
+ this.fire('title-change', {title});
},
- /**
- * Change active patch to the provided patch num.
- * @param {number} patchNum the patchn number to be viewed.
- * @param {boolean} opt_forceParams When set to true, the resulting URL will
- * always include the patch range, even if the requested patchNum is
- * known to be the latest.
- */
- _changePatchNum: function(patchNum, opt_forceParams) {
- if (!opt_forceParams) {
- var currentPatchNum;
- if (this._change.current_revision) {
- currentPatchNum =
- this._change.revisions[this._change.current_revision]._number;
- } else {
- currentPatchNum = this._computeLatestPatchNum(this._allPatchSets);
- }
- if (patchNum === currentPatchNum &&
- this._patchRange.basePatchNum === 'PARENT') {
- page.show(this.changePath(this._changeNum));
- return;
- }
- }
- var patchExpr = this._patchRange.basePatchNum === 'PARENT' ? patchNum :
- this._patchRange.basePatchNum + '..' + patchNum;
- page.show(this.changePath(this._changeNum) + '/' + patchExpr);
- },
-
- _computeChangePermalink: function(changeNum) {
- return this.getBaseUrl() + '/' + changeNum;
- },
-
- _computeChangeStatus: function(change, patchNum) {
- var statusString = this.changeStatusString(change);
- if (change.status === this.ChangeStatus.NEW) {
- var rev = this.getRevisionByPatchNum(change.revisions, patchNum);
- if (rev && rev.draft === true) {
- statusString = 'Draft';
- }
- }
- return statusString;
+ _computeChangeUrl(change) {
+ return Gerrit.Nav.getUrlForChange(change);
},
- _computeShowCommitInfo: function(changeStatus, current_revision) {
+ _computeShowCommitInfo(changeStatus, current_revision) {
return changeStatus === 'Merged' && current_revision;
},
- _computeMergedCommitInfo: function(current_revision, revisions) {
- var rev = revisions[current_revision];
+ _computeMergedCommitInfo(current_revision, revisions) {
+ const rev = revisions[current_revision];
if (!rev || !rev.commit) { return {}; }
// CommitInfo.commit is optional. Set commit in all cases to avoid error
// in <gr-commit-info>. @see Issue 5337
@@ -599,11 +686,11 @@
return rev.commit;
},
- _computeChangeIdClass: function(displayChangeId) {
+ _computeChangeIdClass(displayChangeId) {
return displayChangeId === CHANGE_ID_ERROR.MISMATCH ? 'warning' : '';
},
- _computeTitleAttributeWarning: function(displayChangeId) {
+ _computeTitleAttributeWarning(displayChangeId) {
if (displayChangeId === CHANGE_ID_ERROR.MISMATCH) {
return 'Change-Id mismatch';
} else if (displayChangeId === CHANGE_ID_ERROR.MISSING) {
@@ -611,12 +698,12 @@
}
},
- _computeChangeIdCommitMessageError: function(commitMessage, change) {
+ _computeChangeIdCommitMessageError(commitMessage, change) {
if (!commitMessage) { return CHANGE_ID_ERROR.MISSING; }
// Find the last match in the commit message:
- var changeId;
- var changeIdArr;
+ let changeId;
+ let changeIdArr;
while (changeIdArr = CHANGE_ID_REGEX_PATTERN.exec(commitMessage)) {
changeId = changeIdArr[1];
@@ -636,56 +723,19 @@
return CHANGE_ID_ERROR.MISSING;
},
- _computeLatestPatchNum: function(allPatchSets) {
- return allPatchSets[allPatchSets.length - 1].num;
- },
-
- _computePatchInfoClass: function(patchNum, allPatchSets) {
- if (parseInt(patchNum, 10) ===
- this._computeLatestPatchNum(allPatchSets)) {
- return '';
- }
- return 'patchInfo--oldPatchSet';
- },
-
- /**
- * Determines if a patch number should be disabled based on value of the
- * basePatchNum from gr-file-list.
- * @param {Number} patchNum Patch number available in dropdown
- * @param {Number|String} basePatchNum Base patch number from file list
- * @return {Boolean}
- */
- _computePatchSetDisabled: function(patchNum, basePatchNum) {
- basePatchNum = basePatchNum === 'PARENT' ? 0 : basePatchNum;
- return parseInt(patchNum, 10) <= parseInt(basePatchNum, 10);
- },
-
- _computeAllPatchSets: function(change) {
- var patchNums = [];
- for (var commit in change.revisions) {
- if (change.revisions.hasOwnProperty(commit)) {
- patchNums.push({
- num: change.revisions[commit]._number,
- desc: change.revisions[commit].description,
- });
- }
- }
- return patchNums.sort(function(a, b) { return a.num - b.num; });
- },
-
- _computeLabelNames: function(labels) {
+ _computeLabelNames(labels) {
return Object.keys(labels).sort();
},
- _computeLabelValues: function(labelName, labels) {
- var result = [];
- var t = labels[labelName];
+ _computeLabelValues(labelName, labels) {
+ const result = [];
+ const t = labels[labelName];
if (!t) { return result; }
- var approvals = t.all || [];
- approvals.forEach(function(label) {
+ const approvals = t.all || [];
+ for (const label of approvals) {
if (label.value && label.value != labels[labelName].default_value) {
- var labelClassName;
- var labelValPrefix = '';
+ let labelClassName;
+ let labelValPrefix = '';
if (label.value > 0) {
labelValPrefix = '+';
labelClassName = 'approved';
@@ -698,33 +748,44 @@
account: label,
});
}
- });
+ }
return result;
},
- _computeReplyButtonLabel: function(changeRecord) {
- var drafts = (changeRecord && changeRecord.base) || {};
- var draftCount = Object.keys(drafts).reduce(function(count, file) {
+ _computeReplyButtonLabel(changeRecord, canStartReview) {
+ if (canStartReview) {
+ return 'Start review';
+ }
+
+ const drafts = (changeRecord && changeRecord.base) || {};
+ const draftCount = Object.keys(drafts).reduce((count, file) => {
return count + drafts[file].length;
}, 0);
- var label = 'Reply';
+ let label = 'Reply';
if (draftCount > 0) {
label += ' (' + draftCount + ')';
}
return label;
},
- _handleAKey: function(e) {
+ _handleAKey(e) {
if (this.shouldSuppressKeyboardShortcut(e) ||
- this.modifierPressed(e) ||
- !this._loggedIn) { return; }
+ this.modifierPressed(e)) {
+ return;
+ }
+ this._getLoggedIn().then(isLoggedIn => {
+ if (!isLoggedIn) {
+ this.fire('show-auth-required');
+ return;
+ }
- e.preventDefault();
- this._openReplyDialog();
+ e.preventDefault();
+ this._openReplyDialog();
+ });
},
- _handleDKey: function(e) {
+ _handleDKey(e) {
if (this.shouldSuppressKeyboardShortcut(e) ||
this.modifierPressed(e)) { return; }
@@ -732,13 +793,13 @@
this.$.downloadOverlay.open();
},
- _handleCapitalRKey: function(e) {
+ _handleCapitalRKey(e) {
if (this.shouldSuppressKeyboardShortcut(e)) { return; }
e.preventDefault();
- page.show('/c/' + this._change._number);
+ Gerrit.Nav.navigateToChange(this._change);
},
- _handleSKey: function(e) {
+ _handleSKey(e) {
if (this.shouldSuppressKeyboardShortcut(e) ||
this.modifierPressed(e)) { return; }
@@ -746,14 +807,15 @@
this.$.changeStar.toggleStar();
},
- _handleUKey: function(e) {
- if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+ _handleUKey(e) {
+ if (this.shouldSuppressKeyboardShortcut(e) ||
+ this.modifierPressed(e)) { return; }
e.preventDefault();
this._determinePageBack();
},
- _handleXKey: function(e) {
+ _handleXKey(e) {
if (this.shouldSuppressKeyboardShortcut(e) ||
this.modifierPressed(e)) { return; }
@@ -761,7 +823,7 @@
this.$.messageList.handleExpandCollapse(true);
},
- _handleZKey: function(e) {
+ _handleZKey(e) {
if (this.shouldSuppressKeyboardShortcut(e) ||
this.modifierPressed(e)) { return; }
@@ -769,20 +831,26 @@
this.$.messageList.handleExpandCollapse(false);
},
- _determinePageBack: function() {
+ _handleCommaKey(e) {
+ if (this.shouldSuppressKeyboardShortcut(e) ||
+ this.modifierPressed(e)) { return; }
+
+ e.preventDefault();
+ this.$.fileList.openDiffPrefs();
+ },
+
+ _determinePageBack() {
// Default backPage to '/' if user came to change view page
// via an email link, etc.
- page.show(this.backPage || '/');
+ Gerrit.Nav.navigateToRelativeUrl(this.backPage || '/');
},
- _handleLabelRemoved: function(splices, path) {
- for (var i = 0; i < splices.length; i++) {
- var splice = splices[i];
- for (var j = 0; j < splice.removed.length; j++) {
- var removed = splice.removed[j];
- var changePath = path.split('.');
- var labelPath = changePath.splice(0, changePath.length - 2);
- var labelDict = this.get(labelPath);
+ _handleLabelRemoved(splices, path) {
+ 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) {
this._reload();
@@ -792,9 +860,9 @@
}
},
- _labelsChanged: function(changeRecord) {
+ _labelsChanged(changeRecord) {
if (!changeRecord) { return; }
- if (changeRecord.value.indexSplices) {
+ if (changeRecord.value && changeRecord.value.indexSplices) {
this._handleLabelRemoved(changeRecord.value.indexSplices,
changeRecord.path);
}
@@ -803,51 +871,55 @@
});
},
- _openReplyDialog: function(opt_section) {
- if (this.$.restAPI.hasPendingDiffDrafts()) {
- this.dispatchEvent(new CustomEvent('show-alert',
- {detail: {message: COMMENT_SAVE}, bubbles: true}));
- return;
- }
- this.$.replyOverlay.open().then(function() {
+ /**
+ * @param {string=} opt_section
+ */
+ _openReplyDialog(opt_section) {
+ this.$.replyOverlay.open().then(() => {
this.$.replyOverlay.setFocusStops(this.$.replyDialog.getFocusStops());
this.$.replyDialog.open(opt_section);
- }.bind(this));
+ Polymer.dom.flush();
+ this.$.replyOverlay.center();
+ });
},
- _handleReloadChange: function(e) {
- return this._reload().then(function() {
- // If the change was rebased, we need to reload the page with the
- // latest patch.
- if (e.detail.action === 'rebase') {
- page.show(this.changePath(this._changeNum));
+ _handleReloadChange(e) {
+ return this._reload().then(() => {
+ // If the change was rebased or submitted, we need to reload the page
+ // with the latest patch.
+ const action = e.detail.action;
+ if (action === 'rebase' || action === 'submit') {
+ Gerrit.Nav.navigateToChange(this._change);
}
- }.bind(this));
+ });
},
- _handleGetChangeDetailError: function(response) {
- this.fire('page-error', {response: response});
+ _handleGetChangeDetailError(response) {
+ this.fire('page-error', {response});
},
- _getDiffDrafts: function() {
- return this.$.restAPI.getDiffDrafts(this._changeNum).then(
- function(drafts) {
- return this._diffDrafts = drafts;
- }.bind(this));
+ _getDiffDrafts() {
+ return this.$.restAPI.getDiffDrafts(this._changeNum).then(drafts => {
+ this._diffDrafts = drafts;
+ });
},
- _getLoggedIn: function() {
+ _getLoggedIn() {
return this.$.restAPI.getLoggedIn();
},
- _getProjectConfig: function() {
+ _getServerConfig() {
+ return this.$.restAPI.getConfig();
+ },
+
+ _getProjectConfig() {
return this.$.restAPI.getProjectConfig(this._change.project).then(
- function(config) {
+ config => {
this._projectConfig = config;
- }.bind(this));
+ });
},
- _updateRebaseAction: function(revisionActions) {
+ _updateRebaseAction(revisionActions) {
if (revisionActions && revisionActions.rebase) {
revisionActions.rebase.rebaseOnCurrent =
!!revisionActions.rebase.enabled;
@@ -856,73 +928,116 @@
return revisionActions;
},
- _prepareCommitMsgForLinkify: function(msg) {
+ _prepareCommitMsgForLinkify(msg) {
// 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= as part of the email address.
- return msg.replace(REVIEWERS_REGEX, 'R=\u200B');
- },
-
- _getChangeDetail: function() {
- return this.$.restAPI.getChangeDetail(this._changeNum,
- this._handleGetChangeDetailError.bind(this)).then(
- function(change) {
- // Issue 4190: Coalesce missing topics to null.
- if (!change.topic) { change.topic = null; }
- if (!change.reviewer_updates) {
- change.reviewer_updates = null;
- }
- var latestRevisionSha = this._getLatestRevisionSHA(change);
- var currentRevision = change.revisions[latestRevisionSha];
- if (currentRevision.commit && currentRevision.commit.message) {
- this._latestCommitMessage = this._prepareCommitMsgForLinkify(
- currentRevision.commit.message);
- } else {
- this._latestCommitMessage = null;
- }
- var lineHeight = getComputedStyle(this).lineHeight;
- this._lineHeight = lineHeight.slice(0, lineHeight.length - 2);
-
- this._change = change;
- if (!this._patchRange || !this._patchRange.patchNum ||
- 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._currentRevisionActions =
+ // from including R= or CC= as part of the email address.
+ return msg.replace(REVIEWERS_REGEX, '$1=\u200B');
+ },
+
+ /**
+ * 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: this.EDIT_NAME,
+ basePatchNum: edit.base_patch_set_number,
+ commit: edit.commit,
+ fetch: edit.fetch,
+ };
+ // 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._patchRange.patchNum = this.EDIT_NAME;
+ // 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;
+ }
+ },
+
+ _getChangeDetail() {
+ const detailCompletes = this.$.restAPI.getChangeDetail(
+ this._changeNum, this._handleGetChangeDetailError.bind(this));
+ const editCompletes = this._getEdit();
+
+ return Promise.all([detailCompletes, editCompletes])
+ .then(([change, edit]) => {
+ if (!change) {
+ return '';
+ }
+ 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 ||
+ this.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._currentRevisionActions =
this._updateRebaseAction(currentRevision.actions);
// TODO: Fetch and process files.
- }
- }.bind(this));
+ }
+ });
+ },
+
+ _getComments() {
+ return this.$.restAPI.getDiffComments(this._changeNum).then(comments => {
+ this._comments = comments;
+ });
},
- _getComments: function() {
- return this.$.restAPI.getDiffComments(this._changeNum).then(
- function(comments) {
- this._comments = comments;
- }.bind(this));
+ _getEdit() {
+ return this.$.restAPI.getChangeEdit(this._changeNum, true);
},
- _getLatestCommitMessage: function() {
+ _getLatestCommitMessage() {
return this.$.restAPI.getChangeCommitInfo(this._changeNum,
- this._computeLatestPatchNum(this._allPatchSets)).then(
- function(commitInfo) {
- this._latestCommitMessage =
+ this.computeLatestPatchNum(this._allPatchSets)).then(commitInfo => {
+ this._latestCommitMessage =
this._prepareCommitMsgForLinkify(commitInfo.message);
- }.bind(this));
+ });
},
- _getLatestRevisionSHA: function(change) {
+ _getLatestRevisionSHA(change) {
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.
- var latestRev = null;
- var latestPatchNum = -1;
- for (var rev in change.revisions) {
+ let latestRev = null;
+ let latestPatchNum = -1;
+ for (const rev in change.revisions) {
if (!change.revisions.hasOwnProperty(rev)) { continue; }
if (change.revisions[rev]._number > latestPatchNum) {
@@ -933,54 +1048,54 @@
return latestRev;
},
- _getCommitInfo: function() {
+ _getCommitInfo() {
return this.$.restAPI.getChangeCommitInfo(
this._changeNum, this._patchRange.patchNum).then(
- function(commitInfo) {
- this._commitInfo = commitInfo;
- }.bind(this));
+ commitInfo => {
+ this._commitInfo = commitInfo;
+ });
},
- _reloadDiffDrafts: function() {
+ _reloadDiffDrafts() {
this._diffDrafts = {};
- this._getDiffDrafts().then(function() {
+ this._getDiffDrafts().then(() => {
if (this.$.replyOverlay.opened) {
- this.async(function() { this.$.replyOverlay.center(); }, 1);
+ this.async(() => { this.$.replyOverlay.center(); }, 1);
}
- }.bind(this));
+ });
},
- _reload: function() {
+ _reload() {
this._loading = true;
this._relatedChangesCollapsed = true;
- this._getLoggedIn().then(function(loggedIn) {
+ this._getLoggedIn().then(loggedIn => {
if (!loggedIn) { return; }
this._reloadDiffDrafts();
- }.bind(this));
+ });
- var detailCompletes = this._getChangeDetail().then(function() {
+ const detailCompletes = this._getChangeDetail().then(() => {
this._loading = false;
this._getProjectConfig();
- }.bind(this));
+ });
this._getComments();
if (this._patchRange.patchNum) {
return Promise.all([
this._reloadPatchNumDependentResources(),
detailCompletes,
- ]).then(function() {
+ ]).then(() => {
return this.$.actions.reload();
- }.bind(this));
+ });
} else {
// The patch number is reliant on the change detail request.
- return detailCompletes.then(function() {
+ return detailCompletes.then(() => {
this.$.fileList.reload();
if (!this._latestCommitMessage) {
this._getLatestCommitMessage();
}
- }.bind(this));
+ });
}
},
@@ -988,115 +1103,75 @@
* Kicks off requests for resources that rely on the patch range
* (`this._patchRange`) being defined.
*/
- _reloadPatchNumDependentResources: function() {
+ _reloadPatchNumDependentResources() {
return Promise.all([
this._getCommitInfo(),
this.$.fileList.reload(),
]);
},
- _updateSelected: function() {
- this._selectedPatchSet = this._patchRange.patchNum;
- },
-
- _computePatchSetDescription: function(change, patchNum) {
- var rev = this.getRevisionByPatchNum(change.revisions, patchNum);
- return (rev && rev.description) ?
- rev.description.substring(0, PATCH_DESC_MAX_LENGTH) : '';
- },
-
- _computeDescriptionPlaceholder: function(readOnly) {
- return (readOnly ? 'No' : 'Add a') + ' patch set description';
- },
-
- _handleDescriptionChanged: function(e) {
- var desc = e.detail.trim();
- var rev = this.getRevisionByPatchNum(this._change.revisions,
- this._selectedPatchSet);
- var sha = this._getPatchsetHash(this._change.revisions, rev);
- this.$.restAPI.setDescription(this._changeNum,
- this._selectedPatchSet, desc)
- .then(function(res) {
- if (res.ok) {
- this.set(['_change', 'revisions', sha, 'description'], desc);
- }
- }.bind(this));
- },
-
-
- /**
- * @param {Object} revisions The revisions object keyed by revision hashes
- * @param {Object} patchSet A revision already fetched from {revisions}
- * @return {string} the SHA hash corresponding to the revision.
- */
- _getPatchsetHash: function(revisions, patchSet) {
- for (var rev in revisions) {
- if (revisions.hasOwnProperty(rev) &&
- revisions[rev] === patchSet) {
- return rev;
- }
- }
- },
-
- _computeDescriptionReadOnly: function(loggedIn, change, account) {
- return !(loggedIn && (account._account_id === change.owner._account_id));
+ _computeCanStartReview(change) {
+ return !!(change.actions && change.actions.ready &&
+ change.actions.ready.enabled);
},
- _computeReplyDisabled: function() { return false; },
+ _computeReplyDisabled() { return false; },
- _computeChangePermalinkAriaLabel: function(changeNum) {
+ _computeChangePermalinkAriaLabel(changeNum) {
return 'Change ' + changeNum;
},
- _computeCommitClass: function(collapsed, commitMessage) {
+ _computeCommitClass(collapsed, commitMessage) {
if (this._computeCommitToggleHidden(commitMessage)) { return ''; }
return collapsed ? 'collapsed' : '';
},
- _computeRelatedChangesClass: function(collapsed, loading) {
+ _computeRelatedChangesClass(collapsed, loading) {
+ // TODO(beckysiegel) figure out how to check for customstyle in Polymer2,
+ // since customStyle was removed.
if (!loading && !this.customStyle['--relation-chain-max-height']) {
this._updateRelatedChangeMaxHeight();
}
return collapsed ? 'collapsed' : '';
},
- _computeCollapseText: function(collapsed) {
+ _computeCollapseText(collapsed) {
// Symbols are up and down triangles.
return collapsed ? '\u25bc Show more' : '\u25b2 Show less';
},
- _toggleCommitCollapsed: function() {
+ _toggleCommitCollapsed() {
this._commitCollapsed = !this._commitCollapsed;
if (this._commitCollapsed) {
window.scrollTo(0, 0);
}
},
- _toggleRelatedChangesCollapsed: function() {
+ _toggleRelatedChangesCollapsed() {
this._relatedChangesCollapsed = !this._relatedChangesCollapsed;
if (this._relatedChangesCollapsed) {
window.scrollTo(0, 0);
}
},
- _computeCommitToggleHidden: function(commitMessage) {
+ _computeCommitToggleHidden(commitMessage) {
if (!commitMessage) { return true; }
return commitMessage.split('\n').length < MIN_LINES_FOR_COMMIT_COLLAPSE;
},
- _getOffsetHeight: function(element) {
+ _getOffsetHeight(element) {
return element.offsetHeight;
},
- _getScrollHeight: function(element) {
+ _getScrollHeight(element) {
return element.scrollHeight;
},
/**
* Get the line height of an element to the nearest integer.
*/
- _getLineHeight: function(element) {
- var lineHeightStr = getComputedStyle(element).lineHeight;
+ _getLineHeight(element) {
+ const lineHeightStr = getComputedStyle(element).lineHeight;
return Math.round(lineHeightStr.slice(0, lineHeightStr.length - 2));
},
@@ -1104,41 +1179,127 @@
* New max height for the related changes section, shorter than the existing
* change info height.
*/
- _updateRelatedChangeMaxHeight: function() {
+ _updateRelatedChangeMaxHeight() {
// Takes into account approximate height for the expand button and
- // bottom margin
- var extraHeight = 24;
- var maxExistingHeight;
- var hasCommitToggle =
+ // bottom margin.
+ const EXTRA_HEIGHT = 30;
+ let newHeight;
+ const hasCommitToggle =
!this._computeCommitToggleHidden(this._latestCommitMessage);
- if (hasCommitToggle) {
- // Make sure the content is lined up if both areas have buttons. If the
- // commit message is not collapsed, instead use the change info hight.
- maxExistingHeight = this._getOffsetHeight(this.$.commitMessage);
+
+ 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) {
+ // 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);
+ newHeight = medRelatedHeight;
} else {
- maxExistingHeight = this._getOffsetHeight(this.$.mainChangeInfo) -
- extraHeight;
+ if (hasCommitToggle) {
+ // Make sure the content is lined up if both areas have buttons. If
+ // the commit message is not collapsed, instead use the change info
+ // height.
+ newHeight = this._getOffsetHeight(this.$.commitMessage);
+ } else {
+ newHeight = this._getOffsetHeight(this.$.commitAndRelated) -
+ EXTRA_HEIGHT;
+ }
}
+ const stylesToUpdate = {};
// Get the line height of related changes, and convert it to the nearest
// integer.
- var lineHeight = this._getLineHeight(this.$.relatedChanges);
+ const lineHeight = this._getLineHeight(this.$.relatedChanges);
// Figure out a new height that is divisible by the rounded line height.
- var remainder = maxExistingHeight % lineHeight;
- var newHeight = maxExistingHeight - remainder;
+ const remainder = newHeight % lineHeight;
+ newHeight = newHeight - remainder;
- // Update the max-height of the relation chain to this new height;
- this.customStyle['--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 (hasCommitToggle) {
- this.customStyle['--related-change-btn-top-padding'] = remainder + 'px';
+ stylesToUpdate['--related-change-btn-top-padding'] = remainder + 'px';
}
- this.updateStyles();
+
+ this.updateStyles(stylesToUpdate);
+ },
+
+ _computeRelatedChangesToggleClass() {
+ // 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 ''; }
+
+ return this._getScrollHeight(this.$.relatedChanges) >
+ (this._getOffsetHeight(this.$.relatedChanges) +
+ this._getLineHeight(this.$.relatedChanges)) ? 'showToggle' : '';
+ },
+
+ _startUpdateCheckTimer() {
+ 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(() => {
+ this.fetchIsLatestKnown(this._change, this.$.restAPI)
+ .then(latest => {
+ if (latest) {
+ this._startUpdateCheckTimer();
+ } else {
+ this._cancelUpdateCheckTimer();
+ this.fire('show-alert', {
+ message: 'A newer patch set has been uploaded.',
+ // Persist this alert.
+ dismissOnNavigation: true,
+ action: 'Reload',
+ callback: function() {
+ // Load the current change without any patch range.
+ Gerrit.Nav.navigateToChange(this._change);
+ }.bind(this),
+ });
+ }
+ });
+ }, this._serverConfig.change.update_delay * 1000);
+ },
+
+ _cancelUpdateCheckTimer() {
+ if (this._updateCheckTimerHandle) {
+ this.cancelAsync(this._updateCheckTimerHandle);
+ }
+ this._updateCheckTimerHandle = null;
+ },
+
+ _handleVisibilityChange() {
+ if (document.hidden && this._updateCheckTimerHandle) {
+ this._cancelUpdateCheckTimer();
+ } else if (!this._updateCheckTimerHandle) {
+ this._startUpdateCheckTimer();
+ }
+ },
+
+ _handleTopicChanged() {
+ this.$.relatedChanges.reload();
+ },
+
+ _computeHeaderClass(change) {
+ return change.work_in_progress ? 'header wip' : 'header';
},
- _computeRelatedChangesToggleHidden: function() {
- return this._getScrollHeight(this.$.relatedChanges) <=
- this._getOffsetHeight(this.$.relatedChanges);
+ _computeEditLoaded(patchRangeRecord) {
+ const patchRange = patchRangeRecord.base || {};
+ return this.patchNumEquals(patchRange.patchNum, this.EDIT_NAME);
},
});
})();