Merge "Use gr-date-formatter in chat history"
diff --git a/java/com/google/gerrit/server/change/ModifyReviewersEmail.java b/java/com/google/gerrit/server/change/ModifyReviewersEmail.java
index 8f1fc1e..cac40d1 100644
--- a/java/com/google/gerrit/server/change/ModifyReviewersEmail.java
+++ b/java/com/google/gerrit/server/change/ModifyReviewersEmail.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.Change;
-import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.config.SendEmailExecutor;
 import com.google.gerrit.server.mail.EmailFactories;
@@ -83,8 +82,6 @@
 
     // Make immutable copies of collections and hand over only immutable data types to the other
     // thread.
-    Change.Id cId = change.getId();
-    Project.NameKey projectNameKey = change.getProject();
     ImmutableList<Address> immutableAddedByEmail = ImmutableList.copyOf(addedByEmail);
     ImmutableList<Address> immutableCopiedByEmail = ImmutableList.copyOf(copiedByEmail);
     ImmutableList<Address> immutableRemovedByEmail = ImmutableList.copyOf(removedByEmail);
@@ -103,7 +100,7 @@
                 startReviewEmail.addRemovedReviewers(immutableToRemove);
                 startReviewEmail.addRemovedByEmailReviewers(immutableRemovedByEmail);
                 ChangeEmail changeEmail =
-                    emailFactories.createChangeEmail(projectNameKey, cId, startReviewEmail);
+                    emailFactories.createChangeEmail(change, startReviewEmail);
                 OutgoingEmail outgoingEmail =
                     emailFactories.createOutgoingEmail(REVIEW_REQUESTED, changeEmail);
                 outgoingEmail.setNotify(notify);
diff --git a/java/com/google/gerrit/server/mail/EmailFactories.java b/java/com/google/gerrit/server/mail/EmailFactories.java
index 0209f4f..47b6beb 100644
--- a/java/com/google/gerrit/server/mail/EmailFactories.java
+++ b/java/com/google/gerrit/server/mail/EmailFactories.java
@@ -144,6 +144,9 @@
   ChangeEmail createChangeEmail(
       Project.NameKey project, Change.Id changeId, ChangeEmailDecorator changeEmailDecorator);
 
+  /** Base email decorator for change-related emails. */
+  ChangeEmail createChangeEmail(Change change, ChangeEmailDecorator changeEmailDecorator);
+
   /** Email decorator for adding a key to the account. */
   EmailDecorator createAddKeyEmail(IdentifiedUser user, AccountSshKey sshKey);
 
diff --git a/java/com/google/gerrit/server/mail/send/ChangeEmailImpl.java b/java/com/google/gerrit/server/mail/send/ChangeEmailImpl.java
index 54c4061..26c71be 100644
--- a/java/com/google/gerrit/server/mail/send/ChangeEmailImpl.java
+++ b/java/com/google/gerrit/server/mail/send/ChangeEmailImpl.java
@@ -120,6 +120,18 @@
     this.changeEmailDecorator = changeEmailDecorator;
   }
 
+  public ChangeEmailImpl(
+      @Provided EmailArguments args, Change change, ChangeEmailDecorator changeEmailDecorator) {
+    this.args = args;
+    this.changeData = args.changeDataFactory.create(change);
+    this.change = changeData.change();
+    emailOnlyAuthors = false;
+    emailOnlyAttentionSetIfEnabled = true;
+    currentAttentionSet = getAttentionSet();
+    branch = changeData.change().getDest();
+    this.changeEmailDecorator = changeEmailDecorator;
+  }
+
   @Override
   public void markAsReply() {
     isThreadReply = true;
diff --git a/java/com/google/gerrit/server/mail/send/DefaultEmailFactories.java b/java/com/google/gerrit/server/mail/send/DefaultEmailFactories.java
index 0657ec2..079f660 100644
--- a/java/com/google/gerrit/server/mail/send/DefaultEmailFactories.java
+++ b/java/com/google/gerrit/server/mail/send/DefaultEmailFactories.java
@@ -151,6 +151,11 @@
   }
 
   @Override
+  public ChangeEmail createChangeEmail(Change change, ChangeEmailDecorator changeEmailDecorator) {
+    return changeEmailFactory.create(change, changeEmailDecorator);
+  }
+
+  @Override
   public EmailDecorator createAddKeyEmail(IdentifiedUser user, AccountSshKey sshKey) {
     return addKeyEmailFactory.create(user, sshKey);
   }
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
index dada354..adcea96 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
@@ -799,6 +799,29 @@
   }
 
   @Test
+  public void addReviewerWithUpdatedSubject() throws Exception {
+    PushOneCommit.Result r = createChange();
+    String changeId = r.getChangeId();
+
+    // Update the subject
+    String newSubject = "Updated Subject";
+    amendChange(changeId, newSubject, "file.txt", "content");
+
+    // Add reviewer
+    ReviewerInput in = new ReviewerInput();
+    in.reviewer = user.email();
+    gApi.changes().id(changeId).addReviewer(in);
+
+    // Verify email subject
+    ImmutableList<Message> messages = sender.getMessages();
+    assertThat(messages).hasSize(1);
+    Message m = messages.get(0);
+    assertThat(m.headers()).containsKey("Subject");
+    assertThat(m.headers().get("Subject").toString()).contains(newSubject);
+    assertThat(m.body()).contains(newSubject);
+  }
+
+  @Test
   public void notifyDetailsWorkOnPostReviewers() throws Exception {
     PushOneCommit.Result r = createChange();
     TestAccount userToNotify = createAccounts(1, "notify-details-post-reviewers").get(0);
diff --git a/polygerrit-ui/app/elements/chat-panel/context-chip.ts b/polygerrit-ui/app/elements/chat-panel/context-chip.ts
index 33e12d8..c451bbb 100644
--- a/polygerrit-ui/app/elements/chat-panel/context-chip.ts
+++ b/polygerrit-ui/app/elements/chat-panel/context-chip.ts
@@ -13,7 +13,6 @@
 
 import {ContextItem} from '../../api/ai-code-review';
 import {chatModelToken} from '../../models/chat/chat-model';
-import {navigationToken} from '../core/gr-navigation/gr-navigation';
 import {resolve} from '../../models/dependency';
 import {fire} from '../../utils/event-util';
 
@@ -21,8 +20,6 @@
 export class ContextChip extends LitElement {
   private readonly getChatModel = resolve(this, chatModelToken);
 
-  private readonly getNavigation = resolve(this, navigationToken);
-
   @property({type: String}) text = '';
 
   @property({type: Object}) contextItem?: ContextItem;
@@ -133,10 +130,10 @@
   }
 
   private navigateToUrl() {
-    const link = this.contextItem?.link;
+    const link = this.contextItem?.link?.trim();
     if (!link) return;
     const url = link.startsWith('http') ? link : `http://${link}`;
-    this.getNavigation().setUrl(url);
+    window.open(url, '_blank');
   }
 }
 
diff --git a/polygerrit-ui/app/elements/chat-panel/context-chip_test.ts b/polygerrit-ui/app/elements/chat-panel/context-chip_test.ts
index 8b6a1f4..fde442a 100644
--- a/polygerrit-ui/app/elements/chat-panel/context-chip_test.ts
+++ b/polygerrit-ui/app/elements/chat-panel/context-chip_test.ts
@@ -14,11 +14,9 @@
 import {chatProvider, createChange} from '../../test/test-data-generators';
 import {changeModelToken} from '../../models/change/change-model';
 import {ParsedChangeInfo} from '../../types/types';
-import {navigationToken} from '../core/gr-navigation/gr-navigation';
 
 suite('context-chip tests', () => {
   let element: ContextChip;
-  let setUrlStub: sinon.SinonStub;
 
   setup(async () => {
     const pluginLoader = testResolver(pluginLoaderToken);
@@ -32,9 +30,6 @@
       change: createChange() as ParsedChangeInfo,
     });
 
-    setUrlStub = sinon.stub();
-    testResolver(navigationToken).setUrl = setUrlStub;
-
     element = await fixture(html`<context-chip></context-chip>`);
   });
 
@@ -121,15 +116,16 @@
   });
 
   test('navigates to url', async () => {
+    const openSpy = sinon.spy(window, 'open');
     const contextItem: ContextItem = {
       type_id: 'file',
       title: 'test.ts',
-      link: 'gerrit.test/test.ts',
+      link: ' gerrit.test/test.ts ',
     };
     element.contextItem = contextItem;
     await element.updateComplete;
     const chip = element.shadowRoot?.querySelector('md-filter-chip');
     chip?.click();
-    assert.isTrue(setUrlStub.calledWith('http://gerrit.test/test.ts'));
+    assert.isTrue(openSpy.calledWith('http://gerrit.test/test.ts', '_blank'));
   });
 });