| <!-- |
| 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="../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html"> |
| <link rel="import" href="../bower_components/iron-dropdown/iron-dropdown.html"> |
| <link rel="import" href="../bower_components/iron-selector/iron-selector.html"> |
| <link rel="import" href="../behaviors/rest-client-behavior.html"> |
| <link rel="import" href="gr-ajax.html"> |
| <link rel="import" href="gr-request.html"> |
| |
| <dom-module id="gr-reply-dropdown"> |
| <style> |
| :host { |
| display: inline-block; |
| } |
| :host([disabled]) { |
| pointer-events: none; |
| } |
| :host([disabled]) .dropdown-content { |
| opacity: .5; |
| } |
| iron-dropdown { |
| background-color: #fff; |
| box-shadow: 0 1px 5px rgba(0, 0, 0, .3); |
| width: 40em; |
| } |
| button { |
| background-color: #f1f2f3; |
| border: 1px solid #aaa; |
| border-radius: 2px; |
| cursor: pointer; |
| font: inherit; |
| padding: .2em .5em; |
| } |
| section { |
| border-top: 1px solid #ddd; |
| padding: .5em .75em; |
| } |
| .textareaContainer { |
| position: relative; |
| } |
| iron-autogrow-textarea { |
| padding: 0; |
| } |
| .message { |
| border: none; |
| width: 100%; |
| } |
| .labelContainer:not(:first-of-type) { |
| margin-top: .5em; |
| } |
| .labelName { |
| display: inline-block; |
| text-align: right; |
| width: 7em; |
| margin-right: .5em; |
| white-space: nowrap; |
| } |
| iron-selector { |
| display: inline-flex; |
| } |
| iron-selector > button { |
| background-color: #fff; |
| border: 1px solid #ddd; |
| border-left: none; |
| padding: .25em 0; |
| cursor: pointer; |
| width: 3em; |
| text-align: center; |
| } |
| iron-selector > button:first-of-type { |
| border-top-left-radius: 2px; |
| border-bottom-left-radius: 2px; |
| border-left: 1px solid #ddd; |
| } |
| iron-selector > button:last-of-type { |
| border-top-right-radius: 2px; |
| border-bottom-right-radius: 2px; |
| } |
| iron-selector > button.iron-selected { |
| background-color: #ddd; |
| } |
| .draftsContainer h3 { |
| margin-top: .25em; |
| } |
| .actionsContainer { |
| display: flex; |
| } |
| .action:link, |
| .action:visited { |
| color: #00e; |
| } |
| .danger { |
| display: flex; |
| flex: 1; |
| justify-content: flex-end; |
| } |
| </style> |
| <template> |
| <gr-ajax id="draftsXHR" |
| url="[[_computeDraftsURL(changeNum)]]" |
| last-response="{{_drafts}}"></gr-ajax> |
| <button id="trigger" on-tap="_showPopupTapHandler">Reply</button> |
| <iron-dropdown id="dropdown" |
| vertical-align="top" |
| vertical-offset="25" |
| horizontal-align="right"> |
| <div class="dropdown-content"> |
| <section class="textareaContainer"> |
| <iron-autogrow-textarea |
| id="textarea" |
| class="message" |
| placeholder="Say something..." |
| disabled="{{disabled}}" |
| rows="1" |
| bind-value="{{draft}}"></iron-autogrow-textarea> |
| </section> |
| <section> |
| <template is="dom-repeat" |
| items="[[_computeLabelArray(permittedLabels)]]" as="label"> |
| <div class="labelContainer"> |
| <span class="labelName">[[label]]</span> |
| <iron-selector data-label$="[[label]]" |
| selected="[[_computeIndexOfLabelValue(labels, permittedLabels, label, _account)]]"> |
| <template is="dom-repeat" |
| items="[[_computePermittedLabelValues(permittedLabels, label)]]" |
| as="value"> |
| <button data-value$="[[value]]">[[value]]</button> |
| </template> |
| </iron-selector> |
| </div> |
| </template> |
| </section> |
| <section class="draftsContainer" hidden$="[[_computeHideDraftList(_drafts)]]"> |
| <h3>[[_computeDraftsTitle(_drafts)]]</h3> |
| <gr-comment-list |
| comments="[[_drafts]]" |
| change-num="[[changeNum]]" |
| patch-num="[[patchNum]]"></gr-comment-list> |
| </section> |
| <section class="actionsContainer"> |
| <button class="action send" on-tap="_sendTapHandler">Send</button> |
| <div class="danger"> |
| <button class="action cancel" on-tap="_cancelTapHandler">Cancel</button> |
| </div> |
| </section> |
| </div> |
| </iron-dropdown> |
| </template> |
| <script> |
| (function() { |
| 'use strict'; |
| |
| Polymer({ |
| is: 'gr-reply-dropdown', |
| |
| /** |
| * Fired when a reply is successfully sent. |
| * |
| * @event send |
| */ |
| |
| properties: { |
| changeNum: String, |
| patchNum: String, |
| disabled: { |
| type: Boolean, |
| value: false, |
| reflectToAttribute: true, |
| }, |
| draft: { |
| type: String, |
| value: '', |
| }, |
| labels: Object, |
| permittedLabels: Object, |
| |
| _account: Object, |
| _drafts: Object, |
| _xhrPromise: Object, // Used for testing. |
| }, |
| |
| behaviors: [ |
| Gerrit.RESTClientBehavior, |
| ], |
| |
| get opened() { |
| return this.$.dropdown.opened; |
| }, |
| |
| ready: function() { |
| app.accountReady.then(function() { |
| this._account = app.account; |
| }.bind(this)); |
| }, |
| |
| open: function() { |
| this.$.draftsXHR.generateRequest(); |
| this.$.dropdown.open(); |
| this.async(function() { |
| this.$.textarea.textarea.focus(); |
| }.bind(this)); |
| }, |
| |
| close: function() { |
| this._drafts = null; |
| this.$.dropdown.close(); |
| }, |
| |
| _computeDraftsURL: function(changeNum) { |
| return '/changes/' + changeNum + '/drafts'; |
| }, |
| |
| _computeHideDraftList: function(drafts) { |
| return Object.keys(drafts || {}).length == 0; |
| }, |
| |
| _computeDraftsTitle: function(drafts) { |
| var total = 0; |
| for (var file in drafts) { |
| total += drafts[file].length; |
| } |
| if (total == 0) { return ''; } |
| if (total == 1) { return '1 Draft'; } |
| if (total > 1) { return total + ' Drafts'; } |
| }, |
| |
| _computeLabelArray: function(labelsObj) { |
| return Object.keys(labelsObj).sort(); |
| }, |
| |
| _computeIndexOfLabelValue: function( |
| labels, permittedLabels, labelName, account) { |
| var t = labels[labelName]; |
| if (!t) { return null; } |
| var labelValue = t.default_value; |
| |
| // Is there an existing vote for the current user? If so, use that. |
| var votes = labels[labelName]; |
| if (votes.all && votes.all.length > 0) { |
| for (var i = 0; i < votes.all.length; i++) { |
| if (votes.all[i]._account_id == account._account_id) { |
| labelValue = votes.all[i].value; |
| break; |
| } |
| } |
| } |
| |
| var len = permittedLabels[labelName] != null ? |
| permittedLabels[labelName].length : 0; |
| for (var i = 0; i < len; i++) { |
| var val = parseInt(permittedLabels[labelName][i], 10); |
| if (val == labelValue) { |
| return i; |
| } |
| } |
| return null; |
| }, |
| |
| _computePermittedLabelValues: function(permittedLabels, label) { |
| return permittedLabels[label]; |
| }, |
| |
| _showPopupTapHandler: function(e) { |
| e.preventDefault(); |
| this.open(); |
| }, |
| |
| _cancelTapHandler: function(e) { |
| e.preventDefault(); |
| this.$.dropdown.close(); |
| }, |
| |
| _sendTapHandler: function(e) { |
| e.preventDefault(); |
| var obj = { |
| drafts: 'PUBLISH_ALL_REVISIONS', |
| labels: {}, |
| }; |
| for (var label in this.permittedLabels) { |
| var selectorEl = this.$$('iron-selector[data-label="' + label + '"]'); |
| var selectedVal = selectorEl.selectedItem.getAttribute('data-value'); |
| selectedVal = parseInt(selectedVal, 10); |
| obj.labels[label] = selectedVal; |
| } |
| if (this.draft != null) { |
| obj.message = this.draft; |
| } |
| this.disabled = true; |
| this._send(obj).then(function(req) { |
| this.fire('send', null, {bubbles: false}); |
| this.draft = ''; |
| this.disabled = false; |
| this.$.dropdown.close(); |
| }.bind(this)).catch(function(err) { |
| alert('Oops. Something went wrong. Check the console and bug the ' + |
| 'PolyGerrit team for assistance.'); |
| throw err; |
| }.bind(this)); |
| }, |
| |
| _send: function(payload) { |
| var xhr = document.createElement('gr-request'); |
| this._xhrPromise = xhr.send({ |
| method: 'POST', |
| url: this.changeBaseURL(this.changeNum, this.patchNum) + '/review', |
| body: payload, |
| }); |
| |
| return this._xhrPromise; |
| }, |
| }); |
| })(); |
| </script> |
| </dom-module> |