blob: 2abd0a8de8fb21e619702622d7ac10e77c1bbd9c [file] [log] [blame]
Andrew Bonventre78792e82016-03-04 17:48:22 -05001// Copyright (C) 2016 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14(function() {
15 'use strict';
16
Wyatt Allen7a4aa8c2016-05-18 12:37:53 -070017 var STORAGE_DEBOUNCE_INTERVAL = 400;
18
Andrew Bonventre78792e82016-03-04 17:48:22 -050019 Polymer({
20 is: 'gr-diff-comment',
21
22 /**
Andrew Bonventre78792e82016-03-04 17:48:22 -050023 * Fired when the Reply action is triggered.
24 *
25 * @event reply
26 */
27
28 /**
29 * Fired when the Done action is triggered.
30 *
31 * @event done
32 */
33
34 /**
35 * Fired when this comment is discarded.
36 *
Andrew Bonventre6a9312f2016-03-25 11:29:36 -040037 * @event comment-discard
Andrew Bonventre78792e82016-03-04 17:48:22 -050038 */
39
Andrew Bonventre28165262016-05-19 17:24:45 -070040 /**
41 * Fired when this comment is saved.
42 *
43 * @event comment-save
44 */
45
Viktar Donich7ad28922016-05-23 15:24:05 -070046 /**
47 * Fired when this comment is updated.
48 *
49 * @event comment-update
50 */
51
Viktar Donichb2198e82016-06-09 16:08:04 -070052 /**
53 * @event comment-mouse-over
54 */
55
56 /**
57 * @event comment-mouse-out
58 */
59
Andrew Bonventre78792e82016-03-04 17:48:22 -050060 properties: {
61 changeNum: String,
62 comment: {
63 type: Object,
64 notify: true,
Viktar Donich7ad28922016-05-23 15:24:05 -070065 observer: '_commentChanged',
Andrew Bonventre78792e82016-03-04 17:48:22 -050066 },
67 disabled: {
68 type: Boolean,
69 value: false,
70 reflectToAttribute: true,
71 },
72 draft: {
73 type: Boolean,
74 value: false,
75 observer: '_draftChanged',
76 },
77 editing: {
78 type: Boolean,
79 value: false,
80 observer: '_editingChanged',
81 },
82 patchNum: String,
83 showActions: Boolean,
Becky Siegeleb4ea182016-10-10 17:31:56 -070084 collapsed: {
Becky Siegel6bf4e4f2016-10-06 10:18:32 -070085 type: Boolean,
86 value: true,
87 observer: '_toggleCollapseClass',
88 },
Andrew Bonventre78792e82016-03-04 17:48:22 -050089 projectConfig: Object,
90
91 _xhrPromise: Object, // Used for testing.
Wyatt Allen035c74f2016-05-23 13:53:10 -070092 _messageText: {
Wyatt Allen7a4aa8c2016-05-18 12:37:53 -070093 type: String,
Andrew Bonventrea12d3cd2016-05-23 19:03:11 -040094 value: '',
Wyatt Allen035c74f2016-05-23 13:53:10 -070095 observer: '_messageTextChanged',
Wyatt Allen7a4aa8c2016-05-18 12:37:53 -070096 },
Andrew Bonventre78792e82016-03-04 17:48:22 -050097 },
98
Andrew Bonventrea12d3cd2016-05-23 19:03:11 -040099 observers: [
100 '_commentMessageChanged(comment.message)',
101 '_loadLocalDraft(changeNum, patchNum, comment)',
102 ],
Andrew Bonventre78792e82016-03-04 17:48:22 -0500103
Becky Siegel6bf4e4f2016-10-06 10:18:32 -0700104 attached: function() {
105 if (this.editing) {
Becky Siegeleb4ea182016-10-10 17:31:56 -0700106 this.collapsed = false;
Becky Siegel6bf4e4f2016-10-06 10:18:32 -0700107 }
108 },
109
Viktar Donich7ad28922016-05-23 15:24:05 -0700110 detached: function() {
Viktar Donichb2198e82016-06-09 16:08:04 -0700111 this.cancelDebouncer('fire-update');
Viktar Donich7ad28922016-05-23 15:24:05 -0700112 },
113
Becky Siegel6bf4e4f2016-10-06 10:18:32 -0700114 _computeShowHideText: function(collapsed) {
115 return collapsed ? '◀' : '▼';
116 },
117
Andrew Bonventre78792e82016-03-04 17:48:22 -0500118 save: function() {
Wyatt Allen035c74f2016-05-23 13:53:10 -0700119 this.comment.message = this._messageText;
Andrew Bonventre78792e82016-03-04 17:48:22 -0500120 this.disabled = true;
Wyatt Allen7a4aa8c2016-05-18 12:37:53 -0700121
Wyatt Allen035c74f2016-05-23 13:53:10 -0700122 this.$.storage.eraseDraftComment({
123 changeNum: this.changeNum,
124 patchNum: this.patchNum,
125 path: this.comment.path,
126 line: this.comment.line,
127 });
Wyatt Allen7a4aa8c2016-05-18 12:37:53 -0700128
Andrew Bonventre180693c2016-05-03 15:14:57 -0400129 this._xhrPromise = this._saveDraft(this.comment).then(function(response) {
Andrew Bonventre78792e82016-03-04 17:48:22 -0500130 this.disabled = false;
Andrew Bonventre8f231072016-05-02 15:59:59 -0400131 if (!response.ok) { return response; }
132
Andrew Bonventre180693c2016-05-03 15:14:57 -0400133 return this.$.restAPI.getResponseObject(response).then(function(obj) {
134 var comment = obj;
135 comment.__draft = true;
136 // Maintain the ephemeral draft ID for identification by other
137 // elements.
138 if (this.comment.__draftID) {
139 comment.__draftID = this.comment.__draftID;
140 }
141 this.comment = comment;
142 this.editing = false;
Viktar Donichb2198e82016-06-09 16:08:04 -0700143 this._fireSave();
Andrew Bonventre180693c2016-05-03 15:14:57 -0400144 return obj;
145 }.bind(this));
Andrew Bonventre78792e82016-03-04 17:48:22 -0500146 }.bind(this)).catch(function(err) {
Andrew Bonventre78792e82016-03-04 17:48:22 -0500147 this.disabled = false;
Andrew Bonventre8f231072016-05-02 15:59:59 -0400148 throw err;
Andrew Bonventre78792e82016-03-04 17:48:22 -0500149 }.bind(this));
150 },
151
Viktar Donich7ad28922016-05-23 15:24:05 -0700152 _commentChanged: function(comment) {
153 this.editing = !!comment.__editing;
Viktar Donichb2198e82016-06-09 16:08:04 -0700154 if (this.editing) { // It's a new draft/reply, notify.
155 this._fireUpdate();
156 }
157 },
158
159 _getEventPayload: function(opt_mixin) {
160 var payload = {
161 comment: this.comment,
162 patchNum: this.patchNum,
163 };
164 for (var k in opt_mixin) {
165 payload[k] = opt_mixin[k];
166 }
167 return payload;
168 },
169
170 _fireSave: function() {
171 this.fire('comment-save', this._getEventPayload());
Viktar Donich7ad28922016-05-23 15:24:05 -0700172 },
173
174 _fireUpdate: function() {
175 this.debounce('fire-update', function() {
Viktar Donichb2198e82016-06-09 16:08:04 -0700176 this.fire('comment-update', this._getEventPayload());
Wyatt Allend0dd3922016-07-14 12:31:09 -0700177 });
Viktar Donich7ad28922016-05-23 15:24:05 -0700178 },
179
Andrew Bonventre78792e82016-03-04 17:48:22 -0500180 _draftChanged: function(draft) {
181 this.$.container.classList.toggle('draft', draft);
182 },
183
Viktar Donichb2198e82016-06-09 16:08:04 -0700184 _editingChanged: function(editing, previousValue) {
Andrew Bonventre78792e82016-03-04 17:48:22 -0500185 this.$.container.classList.toggle('editing', editing);
186 if (editing) {
187 var textarea = this.$.editTextarea.textarea;
188 // Put the cursor at the end always.
189 textarea.selectionStart = textarea.value.length;
190 textarea.selectionEnd = textarea.selectionStart;
191 this.async(function() {
192 textarea.focus();
193 }.bind(this));
194 }
195 if (this.comment && this.comment.id) {
196 this.$$('.cancel').hidden = !editing;
197 }
Viktar Donich7ad28922016-05-23 15:24:05 -0700198 if (this.comment) {
199 this.comment.__editing = this.editing;
200 }
Viktar Donichb2198e82016-06-09 16:08:04 -0700201 if (editing != !!previousValue) {
202 // To prevent event firing on comment creation.
203 this._fireUpdate();
204 }
Andrew Bonventre78792e82016-03-04 17:48:22 -0500205 },
206
207 _computeLinkToComment: function(comment) {
208 return '#' + comment.line;
209 },
210
211 _computeSaveDisabled: function(draft) {
212 return draft == null || draft.trim() == '';
213 },
214
Andrew Bonventre78792e82016-03-04 17:48:22 -0500215 _handleTextareaKeydown: function(e) {
Kasper Nilssonb8523262016-08-23 12:07:04 -0700216 switch (e.keyCode) {
217 case 27: // 'esc'
218 this._handleCancel(e);
219 break;
220 case 83: // 's'
221 if (e.ctrlKey) {
222 this._handleSave(e);
223 }
224 break;
Andrew Bonventre78792e82016-03-04 17:48:22 -0500225 }
226 },
227
Becky Siegel6bf4e4f2016-10-06 10:18:32 -0700228 _handleToggleCollapsed: function() {
Becky Siegeleb4ea182016-10-10 17:31:56 -0700229 this.collapsed = !this.collapsed;
Becky Siegel6bf4e4f2016-10-06 10:18:32 -0700230 },
231
Becky Siegeleb4ea182016-10-10 17:31:56 -0700232 _toggleCollapseClass: function(collapsed) {
233 if (collapsed) {
Becky Siegel6bf4e4f2016-10-06 10:18:32 -0700234 this.$.container.classList.add('collapsed');
235 } else {
236 this.$.container.classList.remove('collapsed');
237 }
238 },
239
Andrew Bonventrea12d3cd2016-05-23 19:03:11 -0400240 _commentMessageChanged: function(message) {
241 this._messageText = message || '';
242 },
243
Wyatt Allen035c74f2016-05-23 13:53:10 -0700244 _messageTextChanged: function(newValue, oldValue) {
Andrew Bonventrea12d3cd2016-05-23 19:03:11 -0400245 if (!this.comment || (this.comment && this.comment.id)) { return; }
Wyatt Allen7a4aa8c2016-05-18 12:37:53 -0700246
247 this.debounce('store', function() {
Wyatt Allen035c74f2016-05-23 13:53:10 -0700248 var message = this._messageText;
Wyatt Allen7a4aa8c2016-05-18 12:37:53 -0700249
Wyatt Allen035c74f2016-05-23 13:53:10 -0700250 var commentLocation = {
251 changeNum: this.changeNum,
252 patchNum: this.patchNum,
253 path: this.comment.path,
254 line: this.comment.line,
255 };
256
257 if ((!this._messageText || !this._messageText.length) && oldValue) {
258 // If the draft has been modified to be empty, then erase the storage
259 // entry.
260 this.$.storage.eraseDraftComment(commentLocation);
261 } else {
262 this.$.storage.setDraftComment(commentLocation, message);
Wyatt Allen7a4aa8c2016-05-18 12:37:53 -0700263 }
Viktar Donich7ad28922016-05-23 15:24:05 -0700264 this._fireUpdate();
Wyatt Allen035c74f2016-05-23 13:53:10 -0700265 }, STORAGE_DEBOUNCE_INTERVAL);
Wyatt Allen7a4aa8c2016-05-18 12:37:53 -0700266 },
267
Andrew Bonventre78792e82016-03-04 17:48:22 -0500268 _handleLinkTap: function(e) {
269 e.preventDefault();
270 var hash = this._computeLinkToComment(this.comment);
271 // Don't add the hash to the window history if it's already there.
272 // Otherwise you mess up expected back button behavior.
273 if (window.location.hash == hash) { return; }
274 // Change the URL but don’t trigger a nav event. Otherwise it will
275 // reload the page.
276 page.show(window.location.pathname + hash, null, false);
277 },
278
279 _handleReply: function(e) {
280 this._preventDefaultAndBlur(e);
Viktar Donichb2198e82016-06-09 16:08:04 -0700281 this.fire('reply', this._getEventPayload(), {bubbles: false});
Andrew Bonventre78792e82016-03-04 17:48:22 -0500282 },
283
284 _handleQuote: function(e) {
285 this._preventDefaultAndBlur(e);
Viktar Donichb2198e82016-06-09 16:08:04 -0700286 this.fire(
287 'reply', this._getEventPayload({quote: true}), {bubbles: false});
Andrew Bonventre78792e82016-03-04 17:48:22 -0500288 },
289
290 _handleDone: function(e) {
291 this._preventDefaultAndBlur(e);
Viktar Donichb2198e82016-06-09 16:08:04 -0700292 this.fire('done', this._getEventPayload(), {bubbles: false});
Andrew Bonventre78792e82016-03-04 17:48:22 -0500293 },
294
295 _handleEdit: function(e) {
296 this._preventDefaultAndBlur(e);
Wyatt Allen035c74f2016-05-23 13:53:10 -0700297 this._messageText = this.comment.message;
Andrew Bonventre78792e82016-03-04 17:48:22 -0500298 this.editing = true;
299 },
300
301 _handleSave: function(e) {
302 this._preventDefaultAndBlur(e);
303 this.save();
304 },
305
306 _handleCancel: function(e) {
307 this._preventDefaultAndBlur(e);
308 if (this.comment.message == null || this.comment.message.length == 0) {
Viktar Donichb2198e82016-06-09 16:08:04 -0700309 this._fireDiscard();
Andrew Bonventre78792e82016-03-04 17:48:22 -0500310 return;
311 }
Wyatt Allen035c74f2016-05-23 13:53:10 -0700312 this._messageText = this.comment.message;
Andrew Bonventre78792e82016-03-04 17:48:22 -0500313 this.editing = false;
314 },
315
Viktar Donichb2198e82016-06-09 16:08:04 -0700316 _fireDiscard: function() {
Viktar Donichbed05852016-08-05 15:05:50 -0700317 this.cancelDebouncer('fire-update');
Viktar Donichb2198e82016-06-09 16:08:04 -0700318 this.fire('comment-discard', this._getEventPayload());
319 },
320
Andrew Bonventre78792e82016-03-04 17:48:22 -0500321 _handleDiscard: function(e) {
322 this._preventDefaultAndBlur(e);
323 if (!this.comment.__draft) {
324 throw Error('Cannot discard a non-draft comment.');
325 }
Andrew Bonventrea12d3cd2016-05-23 19:03:11 -0400326 this.editing = false;
Andrew Bonventre78792e82016-03-04 17:48:22 -0500327 this.disabled = true;
Andrew Bonventre180693c2016-05-03 15:14:57 -0400328 if (!this.comment.id) {
Andrew Bonventrea12d3cd2016-05-23 19:03:11 -0400329 this.disabled = false;
Viktar Donichb2198e82016-06-09 16:08:04 -0700330 this._fireDiscard();
Andrew Bonventre78792e82016-03-04 17:48:22 -0500331 return;
332 }
Andrew Bonventre180693c2016-05-03 15:14:57 -0400333
Viktar Donich7ad28922016-05-23 15:24:05 -0700334 this._xhrPromise = this._deleteDraft(this.comment).then(
335 function(response) {
336 this.disabled = false;
337 if (!response.ok) { return response; }
Andrew Bonventre8f231072016-05-02 15:59:59 -0400338
Viktar Donichb2198e82016-06-09 16:08:04 -0700339 this._fireDiscard();
Viktar Donich7ad28922016-05-23 15:24:05 -0700340 }.bind(this)).catch(function(err) {
341 this.disabled = false;
342 throw err;
343 }.bind(this));
Andrew Bonventre78792e82016-03-04 17:48:22 -0500344 },
345
346 _preventDefaultAndBlur: function(e) {
347 e.preventDefault();
348 Polymer.dom(e).rootTarget.blur();
349 },
350
Andrew Bonventre180693c2016-05-03 15:14:57 -0400351 _saveDraft: function(draft) {
352 return this.$.restAPI.saveDiffDraft(this.changeNum, this.patchNum, draft);
Andrew Bonventre78792e82016-03-04 17:48:22 -0500353 },
354
Andrew Bonventre180693c2016-05-03 15:14:57 -0400355 _deleteDraft: function(draft) {
356 return this.$.restAPI.deleteDiffDraft(this.changeNum, this.patchNum,
357 draft);
Andrew Bonventre78792e82016-03-04 17:48:22 -0500358 },
Wyatt Allen7a4aa8c2016-05-18 12:37:53 -0700359
Andrew Bonventrea12d3cd2016-05-23 19:03:11 -0400360 _loadLocalDraft: function(changeNum, patchNum, comment) {
361 // Only apply local drafts to comments that haven't been saved
362 // remotely, and haven't been given a default message already.
363 if (!comment || comment.id || comment.message) {
364 return;
365 }
Wyatt Allen7a4aa8c2016-05-18 12:37:53 -0700366
Andrew Bonventrea12d3cd2016-05-23 19:03:11 -0400367 var draft = this.$.storage.getDraftComment({
368 changeNum: changeNum,
369 patchNum: patchNum,
370 path: comment.path,
371 line: comment.line,
372 });
Wyatt Allen7a4aa8c2016-05-18 12:37:53 -0700373
Andrew Bonventrea12d3cd2016-05-23 19:03:11 -0400374 if (draft) {
375 this.set('comment.message', draft.message);
376 }
Wyatt Allen7a4aa8c2016-05-18 12:37:53 -0700377 },
Viktar Donichb2198e82016-06-09 16:08:04 -0700378
379 _handleMouseEnter: function(e) {
380 this.fire('comment-mouse-over', this._getEventPayload());
381 },
382
383 _handleMouseLeave: function(e) {
384 this.fire('comment-mouse-out', this._getEventPayload());
385 },
Andrew Bonventre78792e82016-03-04 17:48:22 -0500386 });
387})();