text: /**
   * @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',
  
      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) {
        // 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;
      },
    });
  })();
  