Refactor <gr-confirm-submit-dialog>

Use `change` and `commentThreads` from the state instead of relying on
parent components to hand it down.

Change-Id: Idd5107ff4644f797970a349c83c785445ed072ae
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts
index b10c17e..536ef92 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts
@@ -99,7 +99,6 @@
   getVotingRange,
   StandardLabels,
 } from '../../../utils/label-util';
-import {CommentThread} from '../../../utils/comment-util';
 import {ShowAlertEventDetail} from '../../../types/events';
 import {
   ActionPriority,
@@ -448,9 +447,6 @@
   @property({type: String})
   _actionLoadingMessage = '';
 
-  @property({type: Array})
-  commentThreads: CommentThread[] = [];
-
   @property({
     type: Array,
     computed:
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.ts b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.ts
index d21c29f..17ca7cf 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.ts
@@ -213,11 +213,9 @@
     <gr-confirm-submit-dialog
       id="confirmSubmitDialog"
       class="confirmDialog"
-      change="[[change]]"
       action="[[_revisionSubmitAction]]"
       on-cancel="_handleConfirmDialogCancel"
       on-confirm="_handleSubmitConfirm"
-      comment-threads="[[commentThreads]]"
       hidden=""
     ></gr-confirm-submit-dialog>
     <gr-dialog
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
index 633fb5c..9181ca0 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
@@ -381,7 +381,6 @@
             on-stop-edit-tap="_handleStopEditTap"
             on-download-tap="_handleOpenDownloadDialog"
             on-included-tap="_handleOpenIncludedInDialog"
-            comment-threads="[[_commentThreads]]"
           ></gr-change-actions>
         </div>
         <!-- end commit actions -->
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.ts b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.ts
index 9d371d3..a9b7b81 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.ts
@@ -20,14 +20,18 @@
 import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator';
 import '../../plugins/gr-endpoint-param/gr-endpoint-param';
 import '../gr-thread-list/gr-thread-list';
-import {ChangeInfo, ActionInfo} from '../../../types/common';
+import {ActionInfo} from '../../../types/common';
 import {GrDialog} from '../../shared/gr-dialog/gr-dialog';
 import {pluralize} from '../../../utils/string-util';
 import {CommentThread, isUnresolved} from '../../../utils/comment-util';
 import {sharedStyles} from '../../../styles/shared-styles';
 import {LitElement, css, html} from 'lit';
-import {customElement, property, query} from 'lit/decorators';
+import {customElement, property, query, state} from 'lit/decorators';
 import {fontStyles} from '../../../styles/gr-font-styles';
+import {subscribe} from '../../lit/subscription-controller';
+import {change$} from '../../../services/change/change-model';
+import {threads$} from '../../../services/comments/comments-model';
+import {ParsedChangeInfo} from '../../../types/types';
 
 @customElement('gr-confirm-submit-dialog')
 export class GrConfirmSubmitDialog extends LitElement {
@@ -47,16 +51,16 @@
    */
 
   @property({type: Object})
-  change?: ChangeInfo;
-
-  @property({type: Object})
   action?: ActionInfo;
 
-  @property({type: Array})
-  commentThreads?: CommentThread[] = [];
+  @state()
+  change?: ParsedChangeInfo;
 
-  @property({type: Boolean})
-  _initialised = false;
+  @state()
+  unresolvedThreads: CommentThread[] = [];
+
+  @state()
+  initialised = false;
 
   static override get styles() {
     return [
@@ -84,6 +88,16 @@
     ];
   }
 
+  constructor() {
+    super();
+    subscribe(this, change$, x => (this.change = x));
+    subscribe(
+      this,
+      threads$,
+      x => (this.unresolvedThreads = x.filter(isUnresolved))
+    );
+  }
+
   private renderPrivate() {
     if (!this.change?.is_private) return '';
     return html`
@@ -106,11 +120,11 @@
           icon="gr-icons:warning"
           class="warningBeforeSubmit"
         ></iron-icon>
-        ${this._computeUnresolvedCommentsWarning(this.change)}
+        ${this.computeUnresolvedCommentsWarning()}
       </p>
       <gr-thread-list
         id="commentList"
-        .threads="${this._computeUnresolvedThreads(this.commentThreads)}"
+        .threads="${this.unresolvedThreads}"
         .change="${this.change}"
         .changeNum="${this.change?._number}"
         logged-in
@@ -121,7 +135,7 @@
   }
 
   private renderChangeEdit() {
-    if (!this._computeHasChangeEdit(this.change)) return '';
+    if (!this.computeHasChangeEdit()) return '';
     return html`
       <iron-icon
         icon="gr-icons:warning"
@@ -133,7 +147,7 @@
   }
 
   private renderInitialised() {
-    if (!this._initialised) return '';
+    if (!this.initialised) return '';
     return html`
       <div class="header" slot="header">${this.action?.label}</div>
       <div class="main" slot="main">
@@ -159,48 +173,43 @@
       id="dialog"
       confirm-label="Continue"
       confirm-on-enter=""
-      @cancel=${this._handleCancelTap}
-      @confirm=${this._handleConfirmTap}
+      @cancel=${this.handleCancelTap}
+      @confirm=${this.handleConfirmTap}
     >
       ${this.renderInitialised()}
     </gr-dialog>`;
   }
 
   init() {
-    this._initialised = true;
+    this.initialised = true;
   }
 
   resetFocus() {
     this.dialog?.resetFocus();
   }
 
-  _computeHasChangeEdit(change?: ChangeInfo) {
-    return (
-      !!change &&
-      !!change.revisions &&
-      Object.values(change.revisions).some(rev => rev._number === 'edit')
+  // Private method, but visible for testing.
+  computeHasChangeEdit() {
+    return Object.values(this.change?.revisions ?? {}).some(
+      rev => rev._number === 'edit'
     );
   }
 
-  _computeUnresolvedThreads(commentThreads?: CommentThread[]) {
-    if (!commentThreads) return [];
-    return commentThreads.filter(thread => isUnresolved(thread));
-  }
-
-  _computeUnresolvedCommentsWarning(change?: ChangeInfo) {
-    if (!change) return '';
-    const unresolvedCount = change.unresolved_comment_count;
+  // Private method, but visible for testing.
+  computeUnresolvedCommentsWarning() {
+    if (!this.change) return '';
+    const unresolvedCount = this.change.unresolved_comment_count;
     if (!unresolvedCount) throw new Error('unresolved comments undefined or 0');
     return `Heads Up! ${pluralize(unresolvedCount, 'unresolved comment')}.`;
   }
 
-  _handleConfirmTap(e: Event) {
+  private handleConfirmTap(e: Event) {
     e.preventDefault();
     e.stopPropagation();
     this.dispatchEvent(new CustomEvent('confirm', {bubbles: false}));
   }
 
-  _handleCancelTap(e: Event) {
+  private handleCancelTap(e: Event) {
     e.preventDefault();
     e.stopPropagation();
     this.dispatchEvent(new CustomEvent('cancel', {bubbles: false}));
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.ts b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.ts
index e1823b1..0426cb6 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.ts
@@ -16,7 +16,10 @@
  */
 
 import '../../../test/common-test-setup-karma';
-import {createChange, createRevision} from '../../../test/test-data-generators';
+import {
+  createParsedChange,
+  createRevision,
+} from '../../../test/test-data-generators';
 import {queryAndAssert} from '../../../test/test-utils';
 import {PatchSetNum} from '../../../types/common';
 import {GrConfirmSubmitDialog} from './gr-confirm-submit-dialog';
@@ -28,13 +31,13 @@
 
   setup(() => {
     element = basicFixture.instantiate();
-    element._initialised = true;
+    element.initialised = true;
   });
 
   test('display', async () => {
     element.action = {label: 'my-label'};
     element.change = {
-      ...createChange(),
+      ...createParsedChange(),
       subject: 'my-subject',
       revisions: {},
     };
@@ -47,23 +50,23 @@
     assert.notEqual(message.textContent!.indexOf('my-subject'), -1);
   });
 
-  test('_computeUnresolvedCommentsWarning', () => {
-    const change = {...createChange(), unresolved_comment_count: 1};
+  test('computeUnresolvedCommentsWarning', () => {
+    element.change = {...createParsedChange(), unresolved_comment_count: 1};
     assert.equal(
-      element._computeUnresolvedCommentsWarning(change),
+      element.computeUnresolvedCommentsWarning(),
       'Heads Up! 1 unresolved comment.'
     );
 
-    const change2 = {...createChange(), unresolved_comment_count: 2};
+    element.change = {...createParsedChange(), unresolved_comment_count: 2};
     assert.equal(
-      element._computeUnresolvedCommentsWarning(change2),
+      element.computeUnresolvedCommentsWarning(),
       'Heads Up! 2 unresolved comments.'
     );
   });
 
-  test('_computeHasChangeEdit', () => {
-    const change = {
-      ...createChange(),
+  test('computeHasChangeEdit', () => {
+    element.change = {
+      ...createParsedChange(),
       revisions: {
         d442ff05d6c4f2a3af0eeca1f67374b39f9dc3d8: {
           ...createRevision(),
@@ -73,10 +76,10 @@
       unresolved_comment_count: 0,
     };
 
-    assert.isTrue(element._computeHasChangeEdit(change));
+    assert.isTrue(element.computeHasChangeEdit());
 
-    const change2 = {
-      ...createChange(),
+    element.change = {
+      ...createParsedChange(),
       revisions: {
         d442ff05d6c4f2a3af0eeca1f67374b39f9dc3d8: {
           ...createRevision(),
@@ -84,6 +87,6 @@
         },
       },
     };
-    assert.isFalse(element._computeHasChangeEdit(change2));
+    assert.isFalse(element.computeHasChangeEdit());
   });
 });