| // 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.*)', |
| ], |
| |
| /** |
| * 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. |
| * |
| * @param {!Object=} opt_range |
| * @return {!Object|undefined} |
| */ |
| getThread(opt_range) { |
| const threadEls = |
| Polymer.dom(this.root).querySelectorAll('gr-diff-comment-thread'); |
| const threads = [].filter.call(threadEls, |
| thread => this._rangesEqual(thread.range, opt_range)); |
| if (threads.length === 1) { |
| return threads[0]; |
| } |
| }, |
| |
| /** |
| * 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.patchNum || this.patchForNewThreads; |
| }, |
| |
| _getThreads(comments) { |
| const sortedComments = comments.slice(0).sort((a, b) => |
| 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, |
| }; |
| if (comment.range) { |
| newThread.range = Object.assign({}, comment.range); |
| } |
| threads.push(newThread); |
| } |
| return threads; |
| }, |
| }); |
| })(); |