blob: 58e56a44507c84ed81701b82982707ca6b78eae8 [file] [log] [blame]
// 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);
},
});
})();