
These tags are preserved by the Closure compiler and vulcanize in order to serve the license notices embedded in the outputs. In a standalone Gerrit server, these license are also covered in the LICENSES.txt served with the documentation. When serving PG assets from a CDN, it's less obvious what the corresponding LICENSES.txt file is, since the CDN is not directly linked to a running Gerrit server. Safer to embed the licenses in the assets themselves. Change-Id: Id1add1451fad1baa7916882a6bda02c326ccc988
192 lines
5.4 KiB
JavaScript
192 lines
5.4 KiB
JavaScript
/**
|
|
* @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;
|
|
},
|
|
});
|
|
})();
|