| <!-- |
| 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-account-link.html"> |
| <link rel="import" href="gr-comment-list.html"> |
| <link rel="import" href="gr-date-formatter.html"> |
| <link rel="import" href="gr-linked-text.html"> |
| |
| <dom-module id="gr-message"> |
| <template> |
| <style> |
| :host { |
| border-top: 1px solid #ddd; |
| display: block; |
| position: relative; |
| } |
| :host(:not([expanded])) { |
| cursor: pointer; |
| } |
| gr-avatar { |
| position: absolute; |
| left: var(--default-horizontal-margin); |
| } |
| .collapsed .contentContainer { |
| color: #777; |
| white-space: nowrap; |
| overflow-x: hidden; |
| text-overflow: ellipsis; |
| } |
| .showAvatar.expanded .contentContainer { |
| margin-left: calc(var(--default-horizontal-margin) + 2.5em); |
| padding: 10px 0; |
| } |
| .showAvatar.collapsed .contentContainer { |
| margin-left: calc(var(--default-horizontal-margin) + 1.75em); |
| padding: 10px 75px 10px 0; |
| } |
| .hideAvatar.collapsed .contentContainer, |
| .hideAvatar.expanded .contentContainer { |
| margin-left: 0; |
| padding: 10px 75px 10px 0; |
| } |
| .collapsed gr-avatar { |
| top: 8px; |
| height: 1.75em; |
| width: 1.75em; |
| } |
| .expanded gr-avatar { |
| top: 12px; |
| height: 2.5em; |
| width: 2.5em; |
| } |
| .name { |
| font-weight: bold; |
| } |
| .collapsed .name, |
| .collapsed .content, |
| .collapsed .message { |
| display: inline; |
| } |
| .collapsed gr-comment-list, |
| .collapsed .replyContainer { |
| display: none; |
| } |
| .collapsed .name { |
| color: var(--default-text-color); |
| } |
| .expanded .name { |
| cursor: pointer; |
| } |
| .date { |
| color: #666; |
| position: absolute; |
| right: var(--default-horizontal-margin); |
| top: 10px; |
| } |
| .replyContainer { |
| padding: .5em 0 1em; |
| } |
| .reply { |
| padding: .2em .6em .3em; |
| } |
| </style> |
| <div class$="[[_computeClass(expanded, showAvatar)]]"> |
| <gr-avatar account="[[message.author]]" image-size="100"></gr-avatar> |
| <div class="contentContainer"> |
| <div class="name" id="name">[[message.author.name]]</div> |
| <div class="content"> |
| <gr-linked-text class="message" |
| pre="[[expanded]]" |
| content="[[message.message]]" |
| disabled="[[!expanded]]" |
| config="[[projectConfig.commentlinks]]"></gr-linked-text> |
| <gr-comment-list |
| comments="[[comments]]" |
| change-num="[[changeNum]]" |
| patch-num="[[message._revision_number]]"></gr-comment-list> |
| </div> |
| <a class="date" href$="[[_computeMessageHash(message)]]" on-tap="_handleLinkTap"> |
| <gr-date-formatter date-str="[[message.date]]"></gr-date-formatter> |
| </a> |
| </div> |
| <div class="replyContainer" hidden$="[[!showReplyButton]]" hidden> |
| <button class="reply" on-tap="_handleReplyTap">Reply</button> |
| </div> |
| </div> |
| </template> |
| <script> |
| (function() { |
| 'use strict'; |
| |
| Polymer({ |
| is: 'gr-message', |
| |
| /** |
| * Fired when this message's permalink is tapped. |
| * |
| * @event scroll-to |
| */ |
| |
| /** |
| * Fired when this message's reply link is tapped. |
| * |
| * @event reply |
| */ |
| |
| listeners: { |
| 'tap': '_tapHandler', |
| 'name.tap': '_collapseHandler', |
| }, |
| |
| properties: { |
| changeNum: Number, |
| message: Object, |
| comments: { |
| type: Object, |
| observer: '_commentsChanged', |
| }, |
| expanded: { |
| type: Boolean, |
| value: true, |
| reflectToAttribute: true, |
| }, |
| showAvatar: { |
| type: Boolean, |
| value: false, |
| }, |
| showReplyButton: { |
| type: Boolean, |
| value: false, |
| }, |
| projectConfig: Object, |
| }, |
| |
| ready: function() { |
| app.configReady.then(function(cfg) { |
| this.showAvatar = !!(cfg && cfg.plugin && cfg.plugin.has_avatars) && |
| this.message && this.message.author; |
| }.bind(this)); |
| }, |
| |
| _commentsChanged: function(value) { |
| this.expanded = Object.keys(value || {}).length > 0; |
| }, |
| |
| _tapHandler: function(e) { |
| if (this.expanded) { return; } |
| this.expanded = true; |
| }, |
| |
| _collapseHandler: function(e) { |
| if (!this.expanded) { return; } |
| e.stopPropagation(); |
| this.expanded = false; |
| }, |
| |
| _computeClass: function(expanded, showAvatar) { |
| var classes = []; |
| classes.push(expanded ? 'expanded' : 'collapsed'); |
| classes.push(showAvatar ? 'showAvatar' : 'hideAvatar'); |
| return classes.join(' '); |
| }, |
| |
| _computeMessageHash: function(message) { |
| return '#message-' + message.id; |
| }, |
| |
| _handleLinkTap: function(e) { |
| e.preventDefault(); |
| |
| this.fire('scroll-to', {message: this.message}, {bubbles: false}); |
| |
| var hash = this._computeMessageHash(this.message); |
| // Don't add the hash to the window history if it's already there. |
| // Otherwise you mess up expected back button behavior. |
| if (window.location.hash == hash) { return; } |
| // Change the URL but don’t trigger a nav event. Otherwise it will |
| // reload the page. |
| page.show(window.location.pathname + hash, null, false); |
| }, |
| |
| _handleReplyTap: function(e) { |
| e.preventDefault(); |
| this.fire('reply', {message: this.message}); |
| }, |
| }); |
| })(); |
| </script> |
| </dom-module> |