| /** |
| * @license |
| * Copyright (C) 2018 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. |
| */ |
| (function() { |
| 'use strict'; |
| |
| /** |
| * Fired when a comment is saved or deleted |
| * |
| * @event thread-list-modified |
| */ |
| class GrThreadList extends Polymer.GestureEventListeners( |
| Polymer.LegacyElementMixin( |
| Polymer.Element)) { |
| static get is() { return 'gr-thread-list'; } |
| |
| static get properties() { |
| return { |
| /** @type {?} */ |
| change: Object, |
| threads: Array, |
| changeNum: String, |
| loggedIn: Boolean, |
| _sortedThreads: { |
| type: Array, |
| }, |
| _filteredThreads: { |
| type: Array, |
| computed: '_computeFilteredThreads(_sortedThreads, ' + |
| '_unresolvedOnly, _draftsOnly)', |
| }, |
| _unresolvedOnly: { |
| type: Boolean, |
| value: false, |
| }, |
| _draftsOnly: { |
| type: Boolean, |
| value: false, |
| }, |
| }; |
| } |
| |
| static get observers() { return ['_computeSortedThreads(threads.*)']; } |
| |
| _computeShowDraftToggle(loggedIn) { |
| return loggedIn ? 'show' : ''; |
| } |
| |
| /** |
| * Order as follows: |
| * - Unresolved threads with drafts (reverse chronological) |
| * - Unresolved threads without drafts (reverse chronological) |
| * - Resolved threads with drafts (reverse chronological) |
| * - Resolved threads without drafts (reverse chronological) |
| * @param {!Object} changeRecord |
| */ |
| _computeSortedThreads(changeRecord) { |
| const threads = changeRecord.base; |
| if (!threads) { return []; } |
| this._updateSortedThreads(threads); |
| } |
| |
| _updateSortedThreads(threads) { |
| this._sortedThreads = |
| threads.map(this._getThreadWithSortInfo).sort((c1, c2) => { |
| const c1Date = c1.__date || util.parseDate(c1.updated); |
| const c2Date = c2.__date || util.parseDate(c2.updated); |
| const dateCompare = c2Date - c1Date; |
| if (c2.unresolved || c1.unresolved) { |
| if (!c1.unresolved) { return 1; } |
| if (!c2.unresolved) { return -1; } |
| } |
| if (c2.hasDraft || c1.hasDraft) { |
| if (!c1.hasDraft) { return 1; } |
| if (!c2.hasDraft) { return -1; } |
| } |
| |
| if (dateCompare === 0 && (!c1.id || !c1.id.localeCompare)) { |
| return 0; |
| } |
| return dateCompare ? dateCompare : c1.id.localeCompare(c2.id); |
| }); |
| } |
| |
| _computeFilteredThreads(sortedThreads, unresolvedOnly, draftsOnly) { |
| // Polymer 2: check for undefined |
| if ([ |
| sortedThreads, |
| unresolvedOnly, |
| draftsOnly, |
| ].some(arg => arg === undefined)) { |
| return undefined; |
| } |
| |
| return sortedThreads.filter(c => { |
| if (draftsOnly) { |
| return c.hasDraft; |
| } else if (unresolvedOnly) { |
| return c.unresolved; |
| } else { |
| const comments = c && c.thread && c.thread.comments; |
| let robotComment = false; |
| let humanReplyToRobotComment = false; |
| comments.forEach(comment => { |
| if (comment.robot_id) { |
| robotComment = true; |
| } else if (robotComment) { |
| // Robot comment exists and human comment exists after it |
| humanReplyToRobotComment = true; |
| } |
| }); |
| if (robotComment) { |
| return humanReplyToRobotComment ? c : false; |
| } |
| return c; |
| } |
| }).map(threadInfo => threadInfo.thread); |
| } |
| |
| _getThreadWithSortInfo(thread) { |
| const lastComment = thread.comments[thread.comments.length - 1] || {}; |
| |
| const lastNonDraftComment = |
| (lastComment.__draft && thread.comments.length > 1) ? |
| thread.comments[thread.comments.length - 2] : |
| lastComment; |
| |
| return { |
| thread, |
| // Use the unresolved bit for the last non draft comment. This is what |
| // anybody other than the current user would see. |
| unresolved: !!lastNonDraftComment.unresolved, |
| hasDraft: !!lastComment.__draft, |
| updated: lastComment.updated, |
| }; |
| } |
| |
| removeThread(rootId) { |
| for (let i = 0; i < this.threads.length; i++) { |
| if (this.threads[i].rootId === rootId) { |
| this.splice('threads', i, 1); |
| // Needed to ensure threads get re-rendered in the correct order. |
| Polymer.dom.flush(); |
| return; |
| } |
| } |
| } |
| |
| _handleThreadDiscard(e) { |
| this.removeThread(e.detail.rootId); |
| } |
| |
| _handleCommentsChanged(e) { |
| // Reset threads so thread computations occur on deep array changes to |
| // threads comments that are not observed naturally. |
| this._updateSortedThreads(this.threads); |
| |
| this.dispatchEvent(new CustomEvent('thread-list-modified', |
| {detail: {rootId: e.detail.rootId, path: e.detail.path}})); |
| } |
| |
| _isOnParent(side) { |
| return !!side; |
| } |
| } |
| |
| customElements.define(GrThreadList.is, GrThreadList); |
| })(); |