
+ Re-center once drafts have been loaded. + Clamp the drafts section so that it scrolls and doesn't push the action buttons off screen. + Allow the dialog a bit more width if it needs it. + Set a max-height of 90vh. Change-Id: Ifae6d1ccfadcae38a3907c55bf3bab6e906a0472
308 lines
8.6 KiB
HTML
308 lines
8.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="../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.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-button.html">
|
|
<link rel="import" href="gr-request.html">
|
|
|
|
<dom-module id="gr-reply-dialog">
|
|
<style>
|
|
:host {
|
|
display: block;
|
|
max-height: 90vh;
|
|
}
|
|
:host([disabled]) {
|
|
pointer-events: none;
|
|
}
|
|
:host([disabled]) .container {
|
|
opacity: .5;
|
|
}
|
|
.container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
max-height: 90vh;
|
|
}
|
|
section {
|
|
border-top: 1px solid #ddd;
|
|
padding: .5em .75em;
|
|
}
|
|
.textareaContainer,
|
|
.labelsContainer,
|
|
.actionsContainer {
|
|
flex-shrink: 0;
|
|
}
|
|
.textareaContainer {
|
|
position: relative;
|
|
}
|
|
iron-autogrow-textarea {
|
|
padding: 0;
|
|
font-family: var(--monospace-font-family);
|
|
}
|
|
.message {
|
|
border: none;
|
|
width: 100%;
|
|
}
|
|
.labelContainer:not(:first-of-type) {
|
|
margin-top: .5em;
|
|
}
|
|
.labelName {
|
|
display: inline-block;
|
|
width: 7em;
|
|
margin-right: .5em;
|
|
white-space: nowrap;
|
|
}
|
|
iron-selector {
|
|
display: inline-flex;
|
|
}
|
|
iron-selector > gr-button {
|
|
margin-right: .25em;
|
|
}
|
|
iron-selector > gr-button:first-of-type {
|
|
border-top-left-radius: 2px;
|
|
border-bottom-left-radius: 2px;
|
|
}
|
|
iron-selector > gr-button:last-of-type {
|
|
border-top-right-radius: 2px;
|
|
border-bottom-right-radius: 2px;
|
|
}
|
|
iron-selector > gr-button.iron-selected {
|
|
background-color: #ddd;
|
|
}
|
|
.draftsContainer {
|
|
overflow-y: auto;
|
|
}
|
|
.draftsContainer h3 {
|
|
margin-top: .25em;
|
|
}
|
|
.actionsContainer {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
.action:link,
|
|
.action:visited {
|
|
color: #00e;
|
|
}
|
|
</style>
|
|
<template>
|
|
<gr-ajax id="draftsXHR"
|
|
url="[[_computeDraftsURL(changeNum)]]"
|
|
last-response="{{_drafts}}"></gr-ajax>
|
|
<div class="container">
|
|
<section class="textareaContainer">
|
|
<iron-autogrow-textarea
|
|
id="textarea"
|
|
class="message"
|
|
placeholder="Say something..."
|
|
disabled="{{disabled}}"
|
|
rows="4"
|
|
max-rows="15"
|
|
bind-value="{{draft}}"></iron-autogrow-textarea>
|
|
</section>
|
|
<section class="labelsContainer">
|
|
<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">
|
|
<gr-button data-value$="[[value]]">[[value]]</gr-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">
|
|
<gr-button primary class="action send" on-tap="_sendTapHandler">Send</gr-button>
|
|
<gr-button class="action cancel" on-tap="_cancelTapHandler">Cancel</gr-button>
|
|
</section>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
(function() {
|
|
'use strict';
|
|
|
|
Polymer({
|
|
is: 'gr-reply-dialog',
|
|
|
|
/**
|
|
* Fired when a reply is successfully sent.
|
|
*
|
|
* @event send
|
|
*/
|
|
|
|
/**
|
|
* Fired when the user presses the cancel button.
|
|
*
|
|
* @event cancel
|
|
*/
|
|
|
|
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,
|
|
],
|
|
|
|
ready: function() {
|
|
app.accountReady.then(function() {
|
|
this._account = app.account;
|
|
}.bind(this));
|
|
},
|
|
|
|
reload: function() {
|
|
return this.$.draftsXHR.generateRequest().completes;
|
|
},
|
|
|
|
focus: function() {
|
|
this.async(function() {
|
|
this.$.textarea.textarea.focus();
|
|
}.bind(this));
|
|
},
|
|
|
|
_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];
|
|
},
|
|
|
|
_cancelTapHandler: function(e) {
|
|
e.preventDefault();
|
|
this._drafts = null;
|
|
this.fire('cancel', null, {bubbles: false});
|
|
},
|
|
|
|
_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._drafts = null;
|
|
}.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>
|