258 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			258 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<!--
 | 
						|
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>
 |