|  | /** | 
|  | * @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 | 
|  | */ | 
|  |  | 
|  | Polymer({ | 
|  | is: 'gr-thread-list', | 
|  | _legacyUndefinedCheck: true, | 
|  |  | 
|  | properties: { | 
|  | /** @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, | 
|  | }, | 
|  | }, | 
|  |  | 
|  | observers: ['_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) { | 
|  | return sortedThreads.filter(c => { | 
|  | if (draftsOnly) { | 
|  | return c.hasDraft; | 
|  | } else if (unresolvedOnly) { | 
|  | return c.unresolved; | 
|  | } else { | 
|  | 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; | 
|  | }, | 
|  | }); | 
|  | })(); |