Merge "Fix redundant chat requests on change property updates"
diff --git a/polygerrit-ui/app/models/chat/chat-model.ts b/polygerrit-ui/app/models/chat/chat-model.ts
index ffd12fe..d7fdf8d 100644
--- a/polygerrit-ui/app/models/chat/chat-model.ts
+++ b/polygerrit-ui/app/models/chat/chat-model.ts
@@ -370,6 +370,8 @@
     this.pluginsModel.aiCodeReviewPlugins$.subscribe(plugins => {
       const provider = plugins[0]?.provider;
 
+      if (this.plugin === provider) return;
+
       this.plugin = provider;
       this.updateState({
         provider,
@@ -388,10 +390,13 @@
 
     this.filesModel.files$.subscribe(files => (this.files = files ?? []));
     this.changeModel.change$.subscribe(change => {
+      const isNewChange = change?._number !== this.change?._number;
       this.change = change as ChangeInfo;
-      // When navigating to a different change, we want to reset the chat state.
-      // Otherwise, we might show a chat from a previous change, or have some
-      // lingering state like selected models or actions.
+      // We only want to reset the chat state and fetch models when navigating
+      // to a different change. Otherwise, property updates on the change
+      // object (e.g. submittability loaded) will trigger duplicate requests.
+      if (!isNewChange) return;
+
       this.updateState({
         ...initialConversationState,
         // We need to explicitly clear these, because updateState does a shallow
@@ -406,6 +411,9 @@
         contextItemTypesLoadingError: undefined,
         conversations: undefined,
       });
+
+      if (!this.change) return;
+
       this.getModels();
       this.getActions();
       this.getContextItemTypes();
diff --git a/polygerrit-ui/app/models/chat/chat-model_test.ts b/polygerrit-ui/app/models/chat/chat-model_test.ts
index dc6b03a..8657dde 100644
--- a/polygerrit-ui/app/models/chat/chat-model_test.ts
+++ b/polygerrit-ui/app/models/chat/chat-model_test.ts
@@ -267,4 +267,25 @@
     assert.isUndefined(state.selectedModelId);
     assert.isEmpty(state.turns);
   });
+
+  test('change property update does not trigger API calls', () => {
+    const change = {
+      ...createParsedChange(),
+      _number: 123,
+    } as unknown as ParsedChangeInfo;
+    changeModel.updateStateChange(change);
+    assert.isTrue((provider.getModels as sinon.SinonStub).calledOnce);
+
+    // Update some property but keep _number the same
+    const updatedChange = {
+      ...change,
+      subject: 'updated subject',
+    } as unknown as ParsedChangeInfo;
+    changeModel.updateStateChange(updatedChange);
+
+    // API calls should not be triggered again
+    assert.isTrue((provider.getModels as sinon.SinonStub).calledOnce);
+    assert.isTrue((provider.getActions as sinon.SinonStub).calledOnce);
+    assert.isTrue((provider.getContextItemTypes as sinon.SinonStub).calledOnce);
+  });
 });