Files
gerrit/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
Wyatt Allen 9468e2c7db Update URL generation in gr-comment-list
The gr-message-list component and its children need to the project
config as well as the project name for linkification and URL
construction respectively. With this change, instead of taking the
project config, the message list takes the project name and uses it for
both purposes.

Bug: Issue 6446
Change-Id: I551b0f0cbb74b71e10c35a613b525aa281b3f740
2017-08-08 11:16:38 -07:00

343 lines
11 KiB
JavaScript

// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
(function() {
'use strict';
const MAX_INITIAL_SHOWN_MESSAGES = 20;
const MESSAGES_INCREMENT = 5;
const ReportingEvent = {
SHOW_ALL: 'show-all-messages',
SHOW_MORE: 'show-more-messages',
};
Polymer({
is: 'gr-messages-list',
properties: {
changeNum: Number,
messages: {
type: Array,
value() { return []; },
},
reviewerUpdates: {
type: Array,
value() { return []; },
},
comments: Object,
projectName: String,
showReplyButtons: {
type: Boolean,
value: false,
},
_expanded: {
type: Boolean,
value: false,
observer: '_expandedChanged',
},
_hideAutomated: {
type: Boolean,
value: false,
},
/**
* The messages after processing and including merged reviewer updates.
*/
_processedMessages: {
type: Array,
computed: '_computeItems(messages, reviewerUpdates)',
observer: '_processedMessagesChanged',
},
/**
* The subset of _processedMessages that is visible to the user.
*/
_visibleMessages: {
type: Array,
value() { return []; },
},
},
scrollToMessage(messageID) {
let el = this.$$('[data-message-id="' + messageID + '"]');
// If the message is hidden, expand the hidden messages back to that
// point.
if (!el) {
let index;
for (index = 0; index < this._processedMessages.length; index++) {
if (this._processedMessages[index].id === messageID) {
break;
}
}
if (index === this._processedMessages.length) { return; }
const newMessages = this._processedMessages.slice(index,
-this._visibleMessages.length);
// Add newMessages to the beginning of _visibleMessages.
this.splice(...['_visibleMessages', 0, 0].concat(newMessages));
// Allow the dom-repeat to stamp.
Polymer.dom.flush();
el = this.$$('[data-message-id="' + messageID + '"]');
}
el.set('message.expanded', true);
let top = el.offsetTop;
for (let offsetParent = el.offsetParent;
offsetParent;
offsetParent = offsetParent.offsetParent) {
top += offsetParent.offsetTop;
}
window.scrollTo(0, top);
this._highlightEl(el);
},
_isAutomated(message) {
return !!(message.reviewer ||
(message.tag && message.tag.startsWith('autogenerated')));
},
_computeItems(messages, reviewerUpdates) {
messages = messages || [];
reviewerUpdates = reviewerUpdates || [];
let mi = 0;
let ri = 0;
let result = [];
let mDate;
let rDate;
for (let i = 0; i < messages.length; i++) {
messages[i]._index = i;
}
while (mi < messages.length || ri < reviewerUpdates.length) {
if (mi >= messages.length) {
result = result.concat(reviewerUpdates.slice(ri));
break;
}
if (ri >= reviewerUpdates.length) {
result = result.concat(messages.slice(mi));
break;
}
mDate = mDate || util.parseDate(messages[mi].date);
rDate = rDate || util.parseDate(reviewerUpdates[ri].date);
if (rDate < mDate) {
result.push(reviewerUpdates[ri++]);
rDate = null;
} else {
result.push(messages[mi++]);
mDate = null;
}
}
return result;
},
_expandedChanged(exp) {
for (let i = 0; i < this._processedMessages.length; i++) {
this._processedMessages[i].expanded = exp;
if (i < this._visibleMessages.length) {
this.set(['_visibleMessages', i, 'expanded'], exp);
}
}
},
_highlightEl(el) {
const highlightedEls =
Polymer.dom(this.root).querySelectorAll('.highlighted');
for (const highlighedEl of highlightedEls) {
highlighedEl.classList.remove('highlighted');
}
function handleAnimationEnd() {
el.removeEventListener('animationend', handleAnimationEnd);
el.classList.remove('highlighted');
}
el.addEventListener('animationend', handleAnimationEnd);
el.classList.add('highlighted');
},
/**
* @param {boolean} expand
*/
handleExpandCollapse(expand) {
this._expanded = expand;
},
_handleExpandCollapseTap(e) {
e.preventDefault();
this.handleExpandCollapse(!this._expanded);
},
_handleAutomatedMessageToggleTap(e) {
e.preventDefault();
this._hideAutomated = !this._hideAutomated;
},
_handleScrollTo(e) {
this.scrollToMessage(e.detail.message.id);
},
_hasAutomatedMessages(messages) {
if (!messages) { return false; }
for (const message of messages) {
if (this._isAutomated(message)) {
return true;
}
}
return false;
},
_computeExpandCollapseMessage(expanded) {
return expanded ? 'Collapse all' : 'Expand all';
},
_computeAutomatedToggleText(hideAutomated) {
return hideAutomated ? 'Show all messages' : 'Show comments only';
},
/**
* Computes message author's file comments for change's message.
* Method uses this.messages to find next message and relies on messages
* to be sorted by date field descending.
* @param {!Object} comments Hash of arrays of comments, filename as key.
* @param {!Object} message
* @return {!Object} Hash of arrays of comments, filename as key.
*/
_computeCommentsForMessage(comments, message) {
if (message._index === undefined || !comments || !this.messages) {
return [];
}
const messages = this.messages || [];
const index = message._index;
const authorId = message.author && message.author._account_id;
const mDate = util.parseDate(message.date).getTime();
// NB: Messages array has oldest messages first.
let nextMDate;
if (index > 0) {
for (let i = index - 1; i >= 0; i--) {
if (messages[i] && messages[i].author &&
messages[i].author._account_id === authorId) {
nextMDate = util.parseDate(messages[i].date).getTime();
break;
}
}
}
const msgComments = {};
for (const file in comments) {
if (!comments.hasOwnProperty(file)) { continue; }
const fileComments = comments[file];
for (let i = 0; i < fileComments.length; i++) {
if (fileComments[i].author &&
fileComments[i].author._account_id !== authorId) {
continue;
}
const cDate = util.parseDate(fileComments[i].updated).getTime();
if (cDate <= mDate) {
if (nextMDate && cDate <= nextMDate) {
continue;
}
msgComments[file] = msgComments[file] || [];
msgComments[file].push(fileComments[i]);
}
}
}
return msgComments;
},
/**
* Returns the number of messages to splice to the beginning of
* _visibleMessages. This is the minimum of the total number of messages
* remaining in the list and the number of messages needed to display five
* more visible messages in the list.
*/
_getDelta(visibleMessages, messages, hideAutomated) {
let delta = MESSAGES_INCREMENT;
const msgsRemaining = messages.length - visibleMessages.length;
if (hideAutomated) {
let counter = 0;
let i;
for (i = msgsRemaining; i > 0 && counter < MESSAGES_INCREMENT; i--) {
if (!this._isAutomated(messages[i - 1])) { counter++; }
}
delta = msgsRemaining - i;
}
return Math.min(msgsRemaining, delta);
},
/**
* Gets the number of messages that would be visible, but do not currently
* exist in _visibleMessages.
*/
_numRemaining(visibleMessages, messages, hideAutomated) {
if (hideAutomated) {
return this._getHumanMessages(messages).length -
this._getHumanMessages(visibleMessages).length;
}
return messages.length - visibleMessages.length;
},
_computeIncrementText(visibleMessages, messages, hideAutomated) {
let delta = this._getDelta(visibleMessages, messages, hideAutomated);
delta = Math.min(
this._numRemaining(visibleMessages, messages, hideAutomated), delta);
return 'Show ' + Math.min(MESSAGES_INCREMENT, delta) + ' more';
},
_getHumanMessages(messages) {
return messages.filter(msg => {
return !this._isAutomated(msg);
});
},
_computeShowHideTextHidden(visibleMessages, messages,
hideAutomated) {
if (hideAutomated) {
messages = this._getHumanMessages(messages);
visibleMessages = this._getHumanMessages(visibleMessages);
}
return visibleMessages.length >= messages.length;
},
_handleShowAllTap() {
this._visibleMessages = this._processedMessages;
this.$.reporting.reportInteraction(ReportingEvent.SHOW_ALL);
},
_handleIncrementShownMessages() {
const delta = this._getDelta(this._visibleMessages,
this._processedMessages, this._hideAutomated);
const len = this._visibleMessages.length;
const newMessages = this._processedMessages.slice(-(len + delta), -len);
// Add newMessages to the beginning of _visibleMessages
this.splice(...['_visibleMessages', 0, 0].concat(newMessages));
this.$.reporting.reportInteraction(ReportingEvent.SHOW_MORE);
},
_processedMessagesChanged(messages) {
this._visibleMessages = messages.slice(-MAX_INITIAL_SHOWN_MESSAGES);
},
_computeNumMessagesText(visibleMessages, messages,
hideAutomated) {
const total =
this._numRemaining(visibleMessages, messages, hideAutomated);
return total === 1 ? 'Show 1 message' : 'Show all ' + total + ' messages';
},
_computeIncrementHidden(visibleMessages, messages,
hideAutomated) {
const total =
this._numRemaining(visibleMessages, messages, hideAutomated);
return total <= this._getDelta(visibleMessages, messages, hideAutomated);
},
});
})();