| /** | |
| * @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; | |
| }, | |
| }); | |
| })(); |