Filter robotComments without Human Reply

* in comment summary
* in resolved comments message

On change with 1 robot comment
Before: https://imgur.com/a/MVrOYyt
After: https://imgur.com/a/yJ8J6yo

Change-Id: I4aa3e46508455a6c685e36dce06c9633bdfc4118
diff --git a/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary.ts b/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary.ts
index 88481a4..991a6cc 100644
--- a/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary.ts
@@ -47,6 +47,8 @@
   isResolved,
   isUnresolved,
   getFirstComment,
+  isRobotThread,
+  hasHumanReply,
 } from '../../../utils/comment-util';
 import {pluralize} from '../../../utils/string-util';
 import {AccountInfo} from '../../../types/common';
@@ -372,9 +374,11 @@
 
   render() {
     this.detailsQuota = DETAILS_QUOTA;
-    const countResolvedComments =
-      this.commentThreads?.filter(isResolved).length ?? 0;
-    const unresolvedThreads = this.commentThreads?.filter(isUnresolved) ?? [];
+    const commentThreads =
+      this.commentThreads?.filter(t => !isRobotThread(t) || hasHumanReply(t)) ??
+      [];
+    const countResolvedComments = commentThreads.filter(isResolved).length;
+    const unresolvedThreads = commentThreads.filter(isUnresolved);
     const countUnresolvedComments = unresolvedThreads.length;
     const unresolvedAuthors = this.getAccounts(unresolvedThreads);
     const draftCount = this.changeComments?.computeDraftCount() ?? 0;
diff --git a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.ts b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.ts
index 7cea964..22b2e43 100644
--- a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.ts
@@ -34,9 +34,10 @@
 import {
   CommentThread,
   isDraft,
-  UIRobot,
   isUnresolved,
   isDraftThread,
+  isRobotThread,
+  hasHumanReply,
 } from '../../../utils/comment-util';
 import {pluralize} from '../../../utils/string-util';
 import {fireThreadListModifiedEvent} from '../../../utils/event-util';
@@ -136,8 +137,12 @@
   _computeResolvedCommentsMessage(
     threads: CommentThread[],
     displayedThreads: CommentThread[],
-    unresolvedOnly: boolean
+    unresolvedOnly: boolean,
+    onlyShowRobotCommentsWithHumanReply: boolean
   ) {
+    if (onlyShowRobotCommentsWithHumanReply) {
+      threads = this.filterRobotThreadsWithoutHumanReply(threads) ?? [];
+    }
     if (unresolvedOnly && threads.length && !displayedThreads.length) {
       return `Show ${pluralize(threads.length, 'resolved comment')}`;
     }
@@ -401,15 +406,9 @@
     const lastComment = comments.length
       ? comments[comments.length - 1]
       : undefined;
-    let hasRobotComment = false;
-    let hasHumanReplyToRobotComment = false;
-    comments.forEach(comment => {
-      if ((comment as UIRobot).robot_id) {
-        hasRobotComment = true;
-      } else if (hasRobotComment) {
-        hasHumanReplyToRobotComment = true;
-      }
-    });
+    const hasRobotComment = isRobotThread(thread);
+    const hasHumanReplyToRobotComment =
+      hasRobotComment && hasHumanReply(thread);
     let updated = undefined;
     if (lastComment) {
       if (isDraft(lastComment)) updated = lastComment.__date;
@@ -472,15 +471,21 @@
   }
 
   _countUnresolved(threads?: CommentThread[]) {
-    return threads?.filter(isUnresolved).length ?? 0;
+    return (
+      this.filterRobotThreadsWithoutHumanReply(threads)?.filter(isUnresolved)
+        .length ?? 0
+    );
   }
 
   _countAllThreads(threads?: CommentThread[]) {
-    return threads?.length ?? 0;
+    return this.filterRobotThreadsWithoutHumanReply(threads)?.length ?? 0;
   }
 
   _countDrafts(threads?: CommentThread[]) {
-    return threads?.filter(isDraftThread).length ?? 0;
+    return (
+      this.filterRobotThreadsWithoutHumanReply(threads)?.filter(isDraftThread)
+        .length ?? 0
+    );
   }
 
   /**
@@ -489,6 +494,10 @@
   _onTapUnresolvedToggle(e: Event) {
     e.preventDefault();
   }
+
+  filterRobotThreadsWithoutHumanReply(threads?: CommentThread[]) {
+    return threads?.filter(t => !isRobotThread(t) || hasHumanReply(t));
+  }
 }
 
 declare global {
diff --git a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.ts b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.ts
index 28e8da8..8a52555 100644
--- a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.ts
@@ -150,7 +150,7 @@
               link
               on-click="_handleResolvedCommentsMessageClick">
                 [[_computeResolvedCommentsMessage(threads, _displayedThreads,
-                unresolvedOnly)]]
+                unresolvedOnly, onlyShowRobotCommentsWithHumanReply)]]
             </gr-button>
           </template>
         </span>
diff --git a/polygerrit-ui/app/utils/comment-util.ts b/polygerrit-ui/app/utils/comment-util.ts
index d7e7595..0c4a3c6 100644
--- a/polygerrit-ui/app/utils/comment-util.ts
+++ b/polygerrit-ui/app/utils/comment-util.ts
@@ -177,6 +177,10 @@
   return thread?.comments?.[0];
 }
 
+export function countComments(thread?: CommentThread) {
+  return thread?.comments?.length ?? 0;
+}
+
 export function isUnresolved(thread?: CommentThread): boolean {
   return !isResolved(thread);
 }
@@ -189,6 +193,14 @@
   return isDraft(getLastComment(thread));
 }
 
+export function isRobotThread(thread?: CommentThread): boolean {
+  return isRobot(getFirstComment(thread));
+}
+
+export function hasHumanReply(thread?: CommentThread): boolean {
+  return countComments(thread) > 1 && !isRobot(getLastComment(thread));
+}
+
 /**
  * Whether the given comment should be included in the base side of the
  * given patch range.