[results feed] Only load results that the UI filter is showing

This speeds up the UI loading results, and reduces the number of Firestore
documents read when loading the results feed with a filter.

Also switched the default filter, used with no URL fragment, to show
only unapproved failures, to gain the benefit of this

Change-Id: I11069d43a62226e171ae05518cb21f9bd4290c49
Reviewed-on: https://dart-review.googlesource.com/c/dart_ci/+/136141
Reviewed-by: Alexander Thomas <athom@google.com>
diff --git a/results_feed/lib/src/components/app_component.dart b/results_feed/lib/src/components/app_component.dart
index a787fca..57db52e 100644
--- a/results_feed/lib/src/components/app_component.dart
+++ b/results_feed/lib/src/components/app_component.dart
@@ -111,17 +111,16 @@
     }
   }
 
-  List<Change> getInMap(Map map, String name, IntRange range) => map
-      .putIfAbsent(name, () => SplayTreeMap<IntRange, List<Change>>(reverse))
-      .putIfAbsent(range, () => <Change>[]);
-
   Future fetchData() => fetching ??= () async {
         try {
+          final loadedResultsStatus = LoadedResultsStatus()
+            ..failuresOnly = filterService.filter.showLatestFailures
+            ..unapprovedOnly = filterService.filter.showUnapprovedOnly;
           final before = commits.isEmpty ? null : commits.keys.last;
           final range = await fetchEarlierCommits(before);
-          await fetchResults(range);
+          await fetchResults(range, loadedResultsStatus);
           await fetchComments(range);
-          updateRanges();
+          updateRanges(loadedResultsStatus);
           updateFetchDate();
         } finally {
           // Force a reevaluation of changed views
@@ -140,8 +139,8 @@
     for (Commit commit in newCommits) {
       commits[commit.index] = commit;
       final range = IntRange(commit.index, commit.index);
-      changeGroups.putIfAbsent(
-          range, () => ChangeGroup(range, commits, [], []));
+      changeGroups.putIfAbsent(range,
+          () => ChangeGroup(range, commits, [], [], LoadedResultsStatus()));
     }
     final range = IntRange(
         commits.keys.last, before == null ? commits.keys.first : before - 1);
@@ -157,9 +156,13 @@
     return range;
   }
 
-  Future fetchResults(IntRange commitRange) async {
+  Future fetchResults(
+      IntRange commitRange, LoadedResultsStatus loadedResultsStatus) async {
     final resultsData = await _firestoreService.fetchChanges(
-        commitRange.start, commitRange.end);
+        commitRange.start,
+        commitRange.end,
+        loadedResultsStatus.failuresOnly,
+        loadedResultsStatus.unapprovedOnly);
     for (var resultData in resultsData) {
       final change = Change.fromDocument(resultData);
       final range = IntRange(change.pinnedIndex ?? change.blamelistStartIndex,
@@ -191,13 +194,13 @@
     }
   }
 
-  void updateRanges() {
+  void updateRanges(LoadedResultsStatus loadedResultsStatus) {
     for (final range in modifiedRanges) {
       if (changes[range] == null || changes[range].isEmpty) {
         changeGroups.remove(range);
       } else {
-        changeGroups[range] =
-            ChangeGroup(range, commits, comments[range] ?? [], changes[range]);
+        changeGroups[range] = ChangeGroup(range, commits, comments[range] ?? [],
+            changes[range], loadedResultsStatus);
       }
     }
     modifiedRanges.clear();
diff --git a/results_feed/lib/src/components/commit_component.dart b/results_feed/lib/src/components/commit_component.dart
index 633fbee..6463bba 100644
--- a/results_feed/lib/src/components/commit_component.dart
+++ b/results_feed/lib/src/components/commit_component.dart
@@ -37,7 +37,7 @@
       'commit_component.css',
       'package:angular_components/css/mdc_web/card/mdc-card.scss.css'
     ]))
-class CommitComponent {
+class CommitComponent implements AfterChanges {
   CommitComponent(this.firestoreService);
 
   @Input()
@@ -49,6 +49,19 @@
   @Input()
   ChangeGroup changeGroup;
 
+  void ngAfterChanges() {
+    if (filter != null && changeGroup != null) {
+      if (changeGroup.loadedResultsStatus.unapprovedOnly &&
+          !filter.showUnapprovedOnly) {
+        window.location.reload();
+      }
+      if (changeGroup.loadedResultsStatus.failuresOnly &&
+          !filter.showLatestFailures) {
+        window.location.reload();
+      }
+    }
+  }
+
   bool collapsedBlamelist = true;
   int resultLimit = 10;
 
diff --git a/results_feed/lib/src/components/try_results_component.dart b/results_feed/lib/src/components/try_results_component.dart
index 85660ff..d184f4b 100644
--- a/results_feed/lib/src/components/try_results_component.dart
+++ b/results_feed/lib/src/components/try_results_component.dart
@@ -46,7 +46,8 @@
   int review;
   int patchset;
   ReviewInfo reviewInfo;
-  ChangeGroup changeGroup = ChangeGroup(null, {}, [], []);
+  ChangeGroup changeGroup =
+      ChangeGroup(null, {}, [], [], LoadedResultsStatus());
   int cachedReview;
   int cachedPatchset;
   List<Change> changes;
@@ -133,7 +134,8 @@
       comments..sort();
       cachedReview = review;
       cachedPatchset = patchset;
-      changeGroup = ChangeGroup(null, {}, comments, changes);
+      changeGroup =
+          ChangeGroup(null, {}, comments, changes, LoadedResultsStatus());
     }
   }
 
diff --git a/results_feed/lib/src/model/commit.dart b/results_feed/lib/src/model/commit.dart
index 54a7f33..fa20e26 100644
--- a/results_feed/lib/src/model/commit.dart
+++ b/results_feed/lib/src/model/commit.dart
@@ -71,6 +71,12 @@
   String toString() => "$index $hash $author $created $title";
 }
 
+class LoadedResultsStatus {
+  bool loaded = true;
+  bool failuresOnly = false;
+  bool unapprovedOnly = false;
+}
+
 class ChangeGroup implements Comparable {
   final IntRange range;
   List<Commit> commits;
@@ -79,9 +85,10 @@
   final Changes latestChanges;
   Changes _filteredChanges;
   Filter _filter = Filter.defaultFilter;
+  LoadedResultsStatus loadedResultsStatus;
 
   ChangeGroup(this.range, Map<int, Commit> allCommits, List<Comment> comments,
-      Iterable<Change> changeList)
+      Iterable<Change> changeList, this.loadedResultsStatus)
       : changes = Changes(changeList),
         latestChanges = Changes.active(changeList) {
     commits = [
diff --git a/results_feed/lib/src/services/firestore_service.dart b/results_feed/lib/src/services/firestore_service.dart
index 4914fee..58a3e03 100644
--- a/results_feed/lib/src/services/firestore_service.dart
+++ b/results_feed/lib/src/services/firestore_service.dart
@@ -43,10 +43,17 @@
     return (await query.get()).docs;
   }
 
-  Future<List<firestore.DocumentSnapshot>> fetchChanges(
-      int startIndex, int endIndex) async {
-    final results = app.firestore().collection('results');
-    final firestore.QuerySnapshot snapshot = await results
+  Future<List<firestore.DocumentSnapshot>> fetchChanges(int startIndex,
+      int endIndex, bool failuresOnly, bool unapprovedOnly) async {
+    firestore.Query query = app.firestore().collection('results');
+    if (failuresOnly) {
+      // Because the index contains 'active' and 'approved', we
+      // always need to give values for both of them (or neither).
+      query = query
+          .where('active', '==', true)
+          .where('approved', 'in', [false, if (!unapprovedOnly) true]);
+    }
+    final snapshot = await query
         .where('blamelist_end_index', '>=', startIndex)
         .where('blamelist_end_index', '<=', endIndex)
         .get();
diff --git a/results_feed/pubspec.lock b/results_feed/pubspec.lock
index 9338777..c5b977c 100644
--- a/results_feed/pubspec.lock
+++ b/results_feed/pubspec.lock
@@ -8,13 +8,6 @@
       url: "https://pub.dartlang.org"
     source: hosted
     version: "0.37.1+1"
-  analyzer_plugin:
-    dependency: transitive
-    description:
-      name: analyzer_plugin
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.2.1"
   angular:
     dependency: "direct main"
     description:
@@ -70,7 +63,7 @@
       name: archive
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.0.10"
+    version: "2.0.13"
   args:
     dependency: transitive
     description:
@@ -98,35 +91,35 @@
       name: boolean_selector
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.0.5"
+    version: "2.0.0"
   build:
     dependency: transitive
     description:
       name: build
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.2.1"
+    version: "1.2.2"
   build_config:
     dependency: transitive
     description:
       name: build_config
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.4.1+1"
+    version: "0.4.2"
   build_daemon:
     dependency: transitive
     description:
       name: build_daemon
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.1.0"
+    version: "2.1.3"
   build_modules:
     dependency: transitive
     description:
       name: build_modules
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.6.3"
+    version: "2.8.0"
   build_resolvers:
     dependency: transitive
     description:
@@ -140,56 +133,49 @@
       name: build_runner
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.7.2"
+    version: "1.7.4"
   build_runner_core:
     dependency: transitive
     description:
       name: build_runner_core
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "4.1.0"
+    version: "4.4.0"
   build_test:
     dependency: "direct dev"
     description:
       name: build_test
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.10.9+1"
+    version: "0.10.12+1"
   build_web_compilers:
     dependency: "direct dev"
     description:
       name: build_web_compilers
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.7.1"
+    version: "2.9.0"
   built_collection:
     dependency: transitive
     description:
       name: built_collection
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "4.2.2"
+    version: "4.3.2"
   built_value:
     dependency: transitive
     description:
       name: built_value
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "6.8.2"
-  built_value_generator:
-    dependency: transitive
-    description:
-      name: built_value_generator
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "6.8.2"
+    version: "7.0.9"
   charcode:
     dependency: transitive
     description:
       name: charcode
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.1.2"
+    version: "1.1.3"
   checked_yaml:
     dependency: transitive
     description:
@@ -210,7 +196,7 @@
       name: code_builder
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "3.2.0"
+    version: "3.2.1"
   collection:
     dependency: transitive
     description:
@@ -231,14 +217,14 @@
       name: coverage
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.13.3+1"
+    version: "0.13.6"
   crypto:
     dependency: transitive
     description:
       name: crypto
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.1.3"
+    version: "2.1.4"
   csslib:
     dependency: transitive
     description:
@@ -266,7 +252,7 @@
       name: firebase
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "5.0.4"
+    version: "7.2.1"
   fixnum:
     dependency: transitive
     description:
@@ -308,14 +294,14 @@
       name: http
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.12.0+2"
+    version: "0.12.0+4"
   http_multi_server:
     dependency: transitive
     description:
       name: http_multi_server
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.1.0"
+    version: "2.2.0"
   http_parser:
     dependency: transitive
     description:
@@ -350,7 +336,7 @@
       name: json_annotation
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "3.0.0"
+    version: "3.0.1"
   kernel:
     dependency: transitive
     description:
@@ -364,7 +350,7 @@
       name: logging
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.11.3+2"
+    version: "0.11.4"
   matcher:
     dependency: transitive
     description:
@@ -441,7 +427,7 @@
       name: pageloader
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "3.2.2"
+    version: "3.3.0"
   path:
     dependency: transitive
     description:
@@ -455,7 +441,7 @@
       name: pedantic
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.8.0+1"
+    version: "1.9.0"
   pool:
     dependency: transitive
     description:
@@ -476,7 +462,7 @@
       name: pub_semver
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.4.2"
+    version: "1.4.3"
   pubspec_parse:
     dependency: transitive
     description:
@@ -497,7 +483,7 @@
       name: sass
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.23.3"
+    version: "1.25.0"
   sass_builder:
     dependency: "direct dev"
     description:
@@ -511,7 +497,7 @@
       name: scratch_space
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.0.4+1"
+    version: "0.0.4+2"
   service_worker:
     dependency: "direct main"
     description:
@@ -567,14 +553,14 @@
       name: source_maps
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.10.8"
+    version: "0.10.9"
   source_span:
     dependency: transitive
     description:
       name: source_span
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.5.5"
+    version: "1.6.0"
   stack_trace:
     dependency: transitive
     description:
@@ -595,7 +581,7 @@
       name: stream_transform
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.0.20"
+    version: "1.1.0"
   string_scanner:
     dependency: transitive
     description:
@@ -609,7 +595,7 @@
       name: sync_http
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.1.4"
+    version: "0.2.0"
   term_glyph:
     dependency: transitive
     description:
@@ -623,21 +609,21 @@
       name: test
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.9.4"
+    version: "1.12.0"
   test_api:
     dependency: transitive
     description:
       name: test_api
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.2.11"
+    version: "0.2.14"
   test_core:
     dependency: transitive
     description:
       name: test_core
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.2.15"
+    version: "0.3.0"
   timing:
     dependency: transitive
     description:
@@ -665,14 +651,14 @@
       name: vm_service
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.1.1"
+    version: "2.3.1"
   watcher:
     dependency: transitive
     description:
       name: watcher
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.9.7+12"
+    version: "0.9.7+13"
   web_socket_channel:
     dependency: transitive
     description:
@@ -686,7 +672,14 @@
       name: webdriver
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.1.1"
+    version: "2.1.2"
+  webkit_inspection_protocol:
+    dependency: transitive
+    description:
+      name: webkit_inspection_protocol
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.5.0"
   yaml:
     dependency: transitive
     description:
@@ -695,4 +688,4 @@
     source: hosted
     version: "2.2.0"
 sdks:
-  dart: ">=2.6.0 <=2.8.0-dev.0.0"
+  dart: ">=2.7.0-dev <=2.8.0-dev.9.0"
diff --git a/results_feed/pubspec.yaml b/results_feed/pubspec.yaml
index b684f6d..622032c 100644
--- a/results_feed/pubspec.yaml
+++ b/results_feed/pubspec.yaml
@@ -10,7 +10,7 @@
   angular_components: '>=0.13.0'
   angular_forms: ^2.0.0-alpha
   angular_router: ^2.0.0-alpha+22
-  firebase: ^5.0.0
+  firebase: ^7.0.0
   http: ^0.12.0
   intl: ^0.15.0
   quiver: ^2.1.0
diff --git a/results_feed/web/index.html b/results_feed/web/index.html
index c0c6a1b..86952d2 100644
--- a/results_feed/web/index.html
+++ b/results_feed/web/index.html
@@ -3,9 +3,9 @@
 
 <head>
   <base href="/">
-  <script src="https://www.gstatic.com/firebasejs/5.9.2/firebase-app.js"></script>
-  <script src="https://www.gstatic.com/firebasejs/5.9.2/firebase-auth.js"></script>
-  <script src="https://www.gstatic.com/firebasejs/5.9.2/firebase-firestore.js"></script>
+  <script src="https://www.gstatic.com/firebasejs/7.6.1/firebase-app.js"></script>
+  <script src="https://www.gstatic.com/firebasejs/7.6.1/firebase-auth.js"></script>
+  <script src="https://www.gstatic.com/firebasejs/7.6.1/firebase-firestore.js"></script>
 
   <title>Results Feed</title>
   <meta charset="utf-8">