| <!-- |
| Copyright (C) 2015 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. |
| --> |
| |
| <link rel="import" href="../bower_components/polymer/polymer.html"> |
| <link rel="import" href="gr-diff-comment.html"> |
| |
| <dom-module id="gr-diff-comment-thread"> |
| <template> |
| <style> |
| :host { |
| display: block; |
| white-space: normal; |
| } |
| gr-diff-comment { |
| border-left: 1px solid #ddd; |
| } |
| gr-diff-comment:first-of-type { |
| border-top: 1px solid #ddd; |
| } |
| gr-diff-comment:last-of-type { |
| border-bottom: 1px solid #ddd; |
| } |
| </style> |
| <div id="container"> |
| <template id="commentList" is="dom-repeat" items="{{_orderedComments}}" as="comment"> |
| <gr-diff-comment |
| comment="{{comment}}" |
| change-num="[[changeNum]]" |
| patch-num="[[patchNum]]" |
| draft="[[comment.__draft]]" |
| show-actions="[[showActions]]" |
| project-config="[[projectConfig]]" |
| on-height-change="_handleCommentHeightChange" |
| on-reply="_handleCommentReply" |
| on-discard="_handleCommentDiscard" |
| on-done="_handleCommentDone"></gr-diff-comment> |
| </template> |
| </div> |
| </template> |
| <script> |
| (function() { |
| 'use strict'; |
| |
| |
| Polymer({ |
| is: 'gr-diff-comment-thread', |
| |
| /** |
| * Fired when the height of the thread changes. |
| * |
| * @event height-change |
| */ |
| |
| /** |
| * Fired when the thread should be discarded. |
| * |
| * @event discard |
| */ |
| |
| properties: { |
| changeNum: String, |
| comments: { |
| type: Array, |
| value: function() { return []; }, |
| }, |
| patchNum: String, |
| path: String, |
| showActions: Boolean, |
| projectConfig: Object, |
| |
| _boundWindowResizeHandler: { |
| type: Function, |
| value: function() { return this._handleWindowResize.bind(this); } |
| }, |
| _lastHeight: Number, |
| _orderedComments: Array, |
| }, |
| |
| get naturalHeight() { |
| return this.$.container.offsetHeight; |
| }, |
| |
| observers: [ |
| '_commentsChanged(comments.splices)', |
| ], |
| |
| attached: function() { |
| window.addEventListener('resize', this._boundWindowResizeHandler); |
| }, |
| |
| detached: function() { |
| window.removeEventListener('resize', this._boundWindowResizeHandler); |
| }, |
| |
| _handleWindowResize: function(e) { |
| this._heightChanged(); |
| }, |
| |
| _commentsChanged: function(changeRecord) { |
| this._orderedComments = this._sortedComments(this.comments); |
| }, |
| |
| _sortedComments: function(comments) { |
| comments.sort(function(c1, c2) { |
| var c1Date = c1.__date || util.parseDate(c1.updated); |
| var c2Date = c2.__date || util.parseDate(c2.updated); |
| return c1Date - c2Date; |
| }); |
| |
| var commentIDToReplies = {}; |
| var topLevelComments = []; |
| for (var i = 0; i < comments.length; i++) { |
| var c = comments[i]; |
| if (c.in_reply_to) { |
| if (commentIDToReplies[c.in_reply_to] == null) { |
| commentIDToReplies[c.in_reply_to] = []; |
| } |
| commentIDToReplies[c.in_reply_to].push(c); |
| } else { |
| topLevelComments.push(c); |
| } |
| } |
| var results = []; |
| for (var i = 0; i < topLevelComments.length; i++) { |
| this._visitComment(topLevelComments[i], commentIDToReplies, results); |
| } |
| return results; |
| }, |
| |
| _visitComment: function(parent, commentIDToReplies, results) { |
| results.push(parent); |
| |
| var replies = commentIDToReplies[parent.id]; |
| if (!replies) { return; } |
| for (var i = 0; i < replies.length; i++) { |
| this._visitComment(replies[i], commentIDToReplies, results); |
| } |
| }, |
| |
| _handleCommentHeightChange: function(e) { |
| e.stopPropagation(); |
| this._heightChanged(); |
| }, |
| |
| _handleCommentReply: function(e) { |
| var comment = e.detail.comment; |
| var quoteStr; |
| if (e.detail.quote) { |
| var msg = comment.message; |
| var quoteStr = msg.split('\n').map( |
| function(line) { return ' > ' + line; }).join('\n') + '\n\n'; |
| } |
| var reply = |
| this._newReply(comment.id, comment.line, this.path, quoteStr); |
| this.push('comments', reply); |
| |
| // Allow the reply to render in the dom-repeat. |
| this.async(function() { |
| var commentEl = this._commentElWithDraftID(reply.__draftID); |
| commentEl.editing = true; |
| this.async(this._heightChanged.bind(this), 1); |
| }.bind(this), 1); |
| }, |
| |
| _handleCommentDone: function(e) { |
| var comment = e.detail.comment; |
| var reply = this._newReply(comment.id, comment.line, this.path, 'Done'); |
| this.push('comments', reply); |
| |
| // Allow the reply to render in the dom-repeat. |
| this.async(function() { |
| var commentEl = this._commentElWithDraftID(reply.__draftID); |
| commentEl.save(); |
| this.async(this._heightChanged.bind(this), 1); |
| }.bind(this), 1); |
| }, |
| |
| _commentElWithDraftID: function(draftID) { |
| var commentEls = |
| Polymer.dom(this.root).querySelectorAll('gr-diff-comment'); |
| for (var i = 0; i < commentEls.length; i++) { |
| if (commentEls[i].comment.__draftID == draftID) { |
| return commentEls[i]; |
| } |
| } |
| return null; |
| }, |
| |
| _newReply: function(inReplyTo, line, path, opt_message) { |
| var c = { |
| __draft: true, |
| __draftID: Math.random().toString(36), |
| __date: new Date(), |
| line: line, |
| path: path, |
| in_reply_to: inReplyTo, |
| }; |
| if (opt_message != null) { |
| c.message = opt_message; |
| } |
| return c; |
| }, |
| |
| _handleCommentDiscard: function(e) { |
| // TODO(andybons): In Shadow DOM, the event bubbles up, while in Shady |
| // DOM, it respects the bubbles property. |
| // https://github.com/Polymer/polymer/issues/3226 |
| e.stopPropagation(); |
| var diffCommentEl = Polymer.dom(e).rootTarget; |
| var idx = this._indexOf(diffCommentEl.comment, this.comments); |
| if (idx == -1) { |
| throw Error('Cannot find comment ' + |
| JSON.stringify(diffCommentEl.comment)); |
| } |
| this.splice('comments', idx, 1); |
| if (this.comments.length == 0) { |
| this.fire('discard', null, {bubbles: false}); |
| return; |
| } |
| this.async(this._heightChanged.bind(this), 1); |
| }, |
| |
| _heightChanged: function() { |
| var height = this.$.container.offsetHeight; |
| if (height == this._lastHeight) { return; } |
| |
| this.fire('height-change', {height: height}, {bubbles: false}); |
| this._lastHeight = height; |
| }, |
| |
| _indexOf: function(comment, arr) { |
| for (var i = 0; i < arr.length; i++) { |
| var c = arr[i]; |
| if ((c.__draftID != null && c.__draftID == comment.__draftID) || |
| (c.id != null && c.id == comment.id)) { |
| return i; |
| } |
| } |
| return -1; |
| }, |
| |
| }); |
| })(); |
| </script> |
| </dom-module> |