Add a dashboard section for attention set

Also add the attention icon to the `owner` and `reviewers` columns.

Also tweak the `reviewers` column to only show two reviewers, but
preferring the reviewers that are in the attention set.

Screenshot: https://imgur.com/a/Esi6W6q

Change-Id: I3e6fe939c0e19dca721638e5bfce0d8cab6f94b6
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
index 172e444..fe0298a 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
@@ -37,6 +37,7 @@
 import {URLEncodingBehavior} from '../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.js';
 import {RESTClientBehavior} from '../../../behaviors/rest-client-behavior/rest-client-behavior.js';
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
+import {GrDisplayNameUtils} from '../../../scripts/gr-display-name-utils/gr-display-name-utils.js';
 import {pluginEndpoints} from '../../shared/gr-js-api-interface/gr-plugin-endpoints.js';
 import {pluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader.js';
 
@@ -47,6 +48,9 @@
   LARGE: 1000,
 };
 
+// How many reviewers should be shown with an account-label?
+const PRIMARY_REVIEWERS_COUNT = 2;
+
 /**
  * @appliesMixin RESTClientMixin
  * @extends PolymerElement
@@ -73,6 +77,7 @@
 
       /** @type {?} */
       change: Object,
+      config: Object,
       changeURL: {
         type: String,
         computed: '_computeChangeURL(change)',
@@ -213,6 +218,45 @@
     }
   }
 
+  _hasAttention(account) {
+    if (!this.change || !this.change.attention_set) return false;
+    return this.change.attention_set.hasOwnProperty(account._account_id);
+  }
+
+  /**
+   * Computes the array of all reviewers with sorting the reviewers first
+   * that are in the attention set.
+   */
+  _computeReviewers(change) {
+    if (!change || !change.reviewers || !change.reviewers.REVIEWER) return [];
+    const reviewers = [...change.reviewers.REVIEWER];
+    reviewers.sort((r1, r2) => {
+      if (this._hasAttention(r1)) return -1;
+      if (this._hasAttention(r2)) return 1;
+      return 0;
+    });
+    return reviewers;
+  }
+
+  _computePrimaryReviewers(change) {
+    return this._computeReviewers(change).slice(0, PRIMARY_REVIEWERS_COUNT);
+  }
+
+  _computeAdditionalReviewers(change) {
+    return this._computeReviewers(change).slice(PRIMARY_REVIEWERS_COUNT);
+  }
+
+  _computeAdditionalReviewersCount(change) {
+    return this._computeAdditionalReviewers(change).length;
+  }
+
+  _computeAdditionalReviewersTitle(change, config) {
+    if (!change || !config) return '';
+    return this._computeAdditionalReviewers(change)
+        .map(user => GrDisplayNameUtils.getDisplayName(config, user))
+        .join(', ');
+  }
+
   _computeComments(unresolved_comment_count) {
     if (!unresolved_comment_count || unresolved_comment_count < 1) return '';
     return `${unresolved_comment_count} unresolved`;
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.js b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.js
index 36f84f7..d2ab2c6 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.js
@@ -50,6 +50,9 @@
     .reviewers {
       white-space: nowrap;
     }
+    .reviewers {
+      --account-max-length: 75px;
+    }
     .spacer {
       height: 0;
       overflow: hidden;
@@ -150,7 +153,11 @@
     class="cell owner"
     hidden$="[[isColumnHidden('Owner', visibleChangeTableColumns)]]"
   >
-    <gr-account-link account="[[change.owner]]"></gr-account-link>
+    <gr-account-link
+      show-attention
+      change="[[change]]"
+      account="[[change.owner]]"
+    ></gr-account-link>
   </td>
   <td
     class="cell assignee"
@@ -173,16 +180,22 @@
     <div>
       <template
         is="dom-repeat"
-        items="[[change.reviewers.REVIEWER]]"
+        items="[[_computePrimaryReviewers(change)]]"
         as="reviewer"
       >
         <gr-account-link
           hide-avatar=""
           hide-status=""
+          show-attention
+          change="[[change]]"
           account="[[reviewer]]"
         ></gr-account-link
-        ><!--
-       --><span class="lastChildHidden">, </span>
+        ><span class="lastChildHidden">, </span>
+      </template>
+      <template is="dom-if" if="[[_computeAdditionalReviewersCount(change)]]">
+        <span title="[[_computeAdditionalReviewersTitle(change, config)]]">
+          +[[_computeAdditionalReviewersCount(change, config)]]
+        </span>
       </template>
     </div>
   </td>
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_html.js b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_html.js
index 2e50cd8..4802773 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_html.js
@@ -135,6 +135,7 @@
             highlight$="[[_computeItemHighlight(account, change)]]"
             needs-review$="[[_computeItemNeedsReview(account, change, showReviewedState)]]"
             change="[[change]]"
+            config="[[_config]]"
             visible-change-table-columns="[[visibleChangeTableColumns]]"
             show-number="[[showNumber]]"
             show-star="[[showStar]]"
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
index 1cc2316..c5d50c1 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
@@ -182,10 +182,14 @@
     const {project, dashboard, title, user, sections} = this.params;
     const dashboardPromise = project ?
       this._getProjectDashboard(project, dashboard) :
-      Promise.resolve(GerritNav.getUserDashboard(
-          user,
-          sections,
-          title || this._computeTitle(user)));
+      this.$.restAPI.getConfig().then(
+          config => Promise.resolve(GerritNav.getUserDashboard(
+              user,
+              sections,
+              title || this._computeTitle(user),
+              config
+          ))
+      );
 
     const checkForNewUser = !project && user === 'self';
     return dashboardPromise
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.js b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.js
index 69e2989..24438eb 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.js
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.js
@@ -102,12 +102,21 @@
     suffixForDashboard: 'limit:10',
   },
   {
+    // Changes where the user is in the attention set.
+    name: 'Your Turn',
+    query: 'attention:${user}',
+    hideIfEmpty: false,
+    suffixForDashboard: 'limit:25',
+    attentionSetOnly: true,
+  },
+  {
     // Changes that are assigned to the viewed user.
     name: 'Assigned reviews',
     query: 'assignee:${user} (-is:wip OR owner:self OR assignee:self) ' +
         'is:open -is:ignored',
     hideIfEmpty: true,
     suffixForDashboard: 'limit:25',
+    assigneeOnly: true,
   },
   {
     // WIP open changes owned by viewing user. This section is omitted when
@@ -730,8 +739,14 @@
   },
 
   getUserDashboard(user = 'self', sections = DEFAULT_SECTIONS,
-      title = '') {
+      title = '', config = {}) {
+    const attentionEnabled =
+        config.change && !!config.change.enable_attention_set;
+    const assigneeEnabled =
+        config.change && !!config.change.enable_assignee;
     sections = sections
+        .filter(section => (attentionEnabled || !section.attentionSetOnly))
+        .filter(section => (assigneeEnabled || !section.assigneeOnly))
         .filter(section => (user === 'self' || !section.selfOnly))
         .map(section => Object.assign({}, section, {
           name: section.name,