blob: b053330a32ed9931c14f8356e657e31b27a079bd [file] [log] [blame]
/**
* @license
* Copyright (C) 2017 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';
Polymer({
is: 'gr-diff-comment-thread-group',
properties: {
changeNum: String,
comments: {
type: Array,
value() { return []; },
},
projectName: String,
patchForNewThreads: String,
range: Object,
isOnParent: {
type: Boolean,
value: false,
},
parentIndex: {
type: Number,
value: null,
},
_threads: {
type: Array,
value() { return []; },
},
},
observers: [
'_commentsChanged(comments.*)',
],
get threadEls() {
return Polymer.dom(this.root).querySelectorAll('gr-diff-comment-thread');
},
/**
* Adds a new thread. Range is optional because a comment can be
* added to a line without a range selected.
*
* @param {!Object} opt_range
*/
addNewThread(commentSide, opt_range) {
this.push('_threads', {
comments: [],
commentSide,
patchNum: this.patchForNewThreads,
range: opt_range,
});
},
removeThread(rootId) {
for (let i = 0; i < this._threads.length; i++) {
if (this._threads[i].rootId === rootId) {
this.splice('_threads', i, 1);
return;
}
}
},
/**
* Fetch the thread group at the given range, or the range-less thread
* on the line if no range is provided, lineNum, and side.
*
* @param {string} side
* @param {!Object=} opt_range
* @return {!Object|undefined}
*/
getThread(side, opt_range) {
const threads = [].filter.call(this.threadEls,
thread => this._rangesEqual(thread.range, opt_range))
.filter(thread => thread.commentSide === side);
if (threads.length === 1) {
return threads[0];
}
},
_handleThreadDiscard(e) {
this.removeThread(e.detail.rootId);
},
/**
* Compare two ranges. Either argument may be falsy, but will only return
* true if both are falsy or if neither are falsy and have the same position
* values.
*
* @param {Object=} a range 1
* @param {Object=} b range 2
* @return {boolean}
*/
_rangesEqual(a, b) {
if (!a && !b) { return true; }
if (!a || !b) { return false; }
return a.startLine === b.startLine &&
a.startChar === b.startChar &&
a.endLine === b.endLine &&
a.endChar === b.endChar;
},
_commentsChanged() {
this._threads = this._getThreads(this.comments);
},
_sortByDate(threadGroups) {
if (!threadGroups.length) { return; }
return threadGroups.sort((a, b) => {
// If a comment is a draft, it doesn't have a start_datetime yet.
// Assume it is newer than the comment it is being compared to.
if (!a.start_datetime) {
return 1;
}
if (!b.start_datetime) {
return -1;
}
return util.parseDate(a.start_datetime) -
util.parseDate(b.start_datetime);
});
},
_calculateLocationRange(range, comment) {
return 'range-' + range.start_line + '-' +
range.start_character + '-' +
range.end_line + '-' +
range.end_character + '-' +
comment.__commentSide;
},
/**
* Determines what the patchNum of a thread should be. Use patchNum from
* comment if it exists, otherwise the property of the thread group.
* This is needed for switching between side-by-side and unified views when
* there are unsaved drafts.
*/
_getPatchNum(comment) {
return comment.patch_set || this.patchForNewThreads;
},
_getThreads(comments) {
const sortedComments = comments.slice(0).sort((a, b) => {
if (b.__draft && !a.__draft ) { return 0; }
if (a.__draft && !b.__draft ) { return 1; }
return util.parseDate(a.updated) - util.parseDate(b.updated);
});
const threads = [];
for (const comment of sortedComments) {
// If the comment is in reply to another comment, find that comment's
// thread and append to it.
if (comment.in_reply_to) {
const thread = threads.find(thread =>
thread.comments.some(c => c.id === comment.in_reply_to));
if (thread) {
thread.comments.push(comment);
continue;
}
}
// Otherwise, this comment starts its own thread.
const newThread = {
start_datetime: comment.updated,
comments: [comment],
commentSide: comment.__commentSide,
patchNum: this._getPatchNum(comment),
rootId: comment.id || comment.__draftID,
};
if (comment.range) {
newThread.range = Object.assign({}, comment.range);
}
threads.push(newThread);
}
return threads;
},
});
})();