Limit shown changes in user dashboard and show their count

For heavy users loading dashboard is slow (several seconds) since
they are set on many (> 100) changes. We set 25 limit to sections
and 10 for CCed so dashboard query load under 1s.

This also adds information about number of changes in section
title and there is still option to click on section title to see
all changes with paging.

Users can override this with their own custom dashboards.

Bug: Issue 11705
Change-Id: Ief9afa4b5a107520b3337017dddcb462a2ab9626
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 b79e5f3..44a1140 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
@@ -210,7 +210,7 @@
               this._showNewUserHelp = lastResultSet.length == 0;
             }
             this._results = changes.map((results, i) => ({
-              name: res.sections[i].name,
+              name: this._computeSectionName(res.sections[i].name, results),
               query: res.sections[i].query,
               results,
               isOutgoing: res.sections[i].isOutgoing,
@@ -220,6 +220,17 @@
           });
     },
 
+    _computeSectionName(name, changes) {
+      if (!changes || !changes.length || changes.length == 0) {
+        return name;
+      }
+      const more = changes[changes.length - 1]._more_changes;
+      const numChanges = changes.length;
+      const andMore = more ? ' and more' : '';
+      const changeLabel = `change${numChanges > 1 ? 's' : ''}`;
+      return `${name} (${numChanges} ${changeLabel}${andMore})`;
+    },
+
     _computeUserHeaderClass(params) {
       if (!params || !!params.project || !params.user
           || params.user === 'self') {
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
index 9f3e675..7918366 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
@@ -149,6 +149,31 @@
       assert.equal(element._computeTitle('not self'), 'Dashboard for not self');
     });
 
+    suite('_computeSectionName', () => {
+      test('empty changes dont change name', () => {
+        const name = 'Work in progress';
+        assert.equal(name, element._computeSectionName(name, []));
+      });
+
+      test('1 change', () => {
+        const name = 'Work in progress';
+        assert.equal(name + ' (1 change)',
+            element._computeSectionName(name, ['1']));
+      });
+
+      test('2 changes', () => {
+        const name = 'Work in progress';
+        assert.equal(name + ' (2 changes)',
+            element._computeSectionName(name, ['1', '2']));
+      });
+
+      test('1 change and more', () => {
+        const name = 'Work in progress';
+        assert.equal(name + ' (1 change and more)',
+            element._computeSectionName(name, [{_more_changes: true}]));
+      });
+    });
+
     suite('_isViewActive', () => {
       test('nothing happens when user param is falsy', () => {
         element.params = {};
@@ -280,7 +305,7 @@
 
       return element._fetchDashboardChanges({sections}, false).then(() => {
         assert.equal(element._results.length, 1);
-        assert.equal(element._results[0].name, 'test2');
+        assert.equal(element._results[0].name, 'test2 (1 change)');
       });
     });
 
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
index c523b5130..9eaf603 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
@@ -115,6 +115,7 @@
         query: 'assignee:${user} (-is:wip OR owner:self OR assignee:self) ' +
             'is:open -is:ignored',
         hideIfEmpty: true,
+        suffixForDashboard: 'limit:25',
       },
       {
         // WIP open changes owned by viewing user. This section is omitted when
@@ -123,6 +124,7 @@
         query: 'is:open owner:${user} is:wip',
         selfOnly: true,
         hideIfEmpty: true,
+        suffixForDashboard: 'limit:25',
       },
       {
         // Non-WIP open changes owned by viewed user. Filter out changes ignored
@@ -130,6 +132,7 @@
         name: 'Outgoing reviews',
         query: 'is:open owner:${user} -is:wip -is:ignored',
         isOutgoing: true,
+        suffixForDashboard: 'limit:25',
       },
       {
         // Non-WIP open changes not owned by the viewed user, that the viewed user
@@ -138,12 +141,14 @@
         name: 'Incoming reviews',
         query: 'is:open -owner:${user} -is:wip -is:ignored ' +
             '(reviewer:${user} OR assignee:${user})',
+        suffixForDashboard: 'limit:25',
       },
       {
         // Open changes the viewed user is CCed on. Changes ignored by the viewing
         // user are filtered out.
         name: 'CCed on',
         query: 'is:open -is:ignored cc:${user}',
+        suffixForDashboard: 'limit:10',
       },
       {
         name: 'Recently closed',