From 58acaa9ae666d4e40c5b68316513a0fcb9200daf Mon Sep 17 00:00:00 2001
From: Renaud Chaput <renchap@gmail.com>
Date: Wed, 23 Aug 2023 08:18:07 +0200
Subject: [PATCH 01/13] Better hashtag normalization when processing a post
 (#26614)

---
 .../components/__tests__/hashtag_bar.tsx      | 15 ++++++++
 .../mastodon/components/hashtag_bar.tsx       | 34 +++++++++++++------
 2 files changed, 38 insertions(+), 11 deletions(-)

diff --git a/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx b/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx
index c7db485d084..1856b7109e6 100644
--- a/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx
+++ b/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx
@@ -105,6 +105,21 @@ describe('computeHashtagBarForStatus', () => {
     );
   });
 
+  it('handles server-side normalized tags with accentuated characters', () => {
+    const status = createStatus(
+      '<p>Text</p><p><a href="test">#éaa</a> <a href="test">#Éaa</a></p>',
+      ['eaa'], // The server may normalize the hashtags in the `tags` attribute
+    );
+
+    const { hashtagsInBar, statusContentProps } =
+      computeHashtagBarForStatus(status);
+
+    expect(hashtagsInBar).toEqual(['Éaa']);
+    expect(statusContentProps.statusContent).toMatchInlineSnapshot(
+      `"<p>Text</p>"`,
+    );
+  });
+
   it('does not display in bar a hashtag in content with a case difference', () => {
     const status = createStatus(
       '<p>Text <a href="test">#Éaa</a></p><p><a href="test">#éaa</a></p>',
diff --git a/app/javascript/mastodon/components/hashtag_bar.tsx b/app/javascript/mastodon/components/hashtag_bar.tsx
index 8781c266303..75bd74da01c 100644
--- a/app/javascript/mastodon/components/hashtag_bar.tsx
+++ b/app/javascript/mastodon/components/hashtag_bar.tsx
@@ -23,8 +23,9 @@ export type StatusLike = Record<{
 }>;
 
 function normalizeHashtag(hashtag: string) {
-  if (hashtag && hashtag.startsWith('#')) return hashtag.slice(1);
-  else return hashtag;
+  return (
+    hashtag && hashtag.startsWith('#') ? hashtag.slice(1) : hashtag
+  ).normalize('NFKC');
 }
 
 function isNodeLinkHashtag(element: Node): element is HTMLLinkElement {
@@ -70,9 +71,16 @@ function uniqueHashtagsWithCaseHandling(hashtags: string[]) {
 }
 
 // Create the collator once, this is much more efficient
-const collator = new Intl.Collator(undefined, { sensitivity: 'accent' });
+const collator = new Intl.Collator(undefined, {
+  sensitivity: 'base', // we use this to emulate the ASCII folding done on the server-side, hopefuly more efficiently
+});
+
 function localeAwareInclude(collection: string[], value: string) {
-  return collection.find((item) => collator.compare(item, value) === 0);
+  const normalizedValue = value.normalize('NFKC');
+
+  return !!collection.find(
+    (item) => collator.compare(item.normalize('NFKC'), normalizedValue) === 0,
+  );
 }
 
 // We use an intermediate function here to make it easier to test
@@ -121,11 +129,13 @@ export function computeHashtagBarForStatus(status: StatusLike): {
   // try to see if the last line is only hashtags
   let onlyHashtags = true;
 
+  const normalizedTagNames = tagNames.map((tag) => tag.normalize('NFKC'));
+
   Array.from(lastChild.childNodes).forEach((node) => {
     if (isNodeLinkHashtag(node) && node.textContent) {
       const normalized = normalizeHashtag(node.textContent);
 
-      if (!localeAwareInclude(tagNames, normalized)) {
+      if (!localeAwareInclude(normalizedTagNames, normalized)) {
         // stop here, this is not a real hashtag, so consider it as text
         onlyHashtags = false;
         return;
@@ -140,12 +150,14 @@ export function computeHashtagBarForStatus(status: StatusLike): {
     }
   });
 
-  const hashtagsInBar = tagNames.filter(
-    (tag) =>
-      // the tag does not appear at all in the status content, it is an out-of-band tag
-      !localeAwareInclude(contentHashtags, tag) &&
-      !localeAwareInclude(lastLineHashtags, tag),
-  );
+  const hashtagsInBar = tagNames.filter((tag) => {
+    const normalizedTag = tag.normalize('NFKC');
+    // the tag does not appear at all in the status content, it is an out-of-band tag
+    return (
+      !localeAwareInclude(contentHashtags, normalizedTag) &&
+      !localeAwareInclude(lastLineHashtags, normalizedTag)
+    );
+  });
 
   const isOnlyOneLine = contentWithoutLastLine.content.childElementCount === 0;
   const hasMedia = status.get('media_attachments').size > 0;

From 74b8b8ea144e5515684c76d96e62c778a3bf6971 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 23 Aug 2023 08:22:48 +0200
Subject: [PATCH 02/13] Update dependency rails to v7.0.7.2 (#26612)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 Gemfile.lock | 106 +++++++++++++++++++++++++--------------------------
 1 file changed, 53 insertions(+), 53 deletions(-)

diff --git a/Gemfile.lock b/Gemfile.lock
index 706df8ffd93..64ad456f134 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -39,47 +39,47 @@ GIT
 GEM
   remote: https://rubygems.org/
   specs:
-    actioncable (7.0.7)
-      actionpack (= 7.0.7)
-      activesupport (= 7.0.7)
+    actioncable (7.0.7.2)
+      actionpack (= 7.0.7.2)
+      activesupport (= 7.0.7.2)
       nio4r (~> 2.0)
       websocket-driver (>= 0.6.1)
-    actionmailbox (7.0.7)
-      actionpack (= 7.0.7)
-      activejob (= 7.0.7)
-      activerecord (= 7.0.7)
-      activestorage (= 7.0.7)
-      activesupport (= 7.0.7)
+    actionmailbox (7.0.7.2)
+      actionpack (= 7.0.7.2)
+      activejob (= 7.0.7.2)
+      activerecord (= 7.0.7.2)
+      activestorage (= 7.0.7.2)
+      activesupport (= 7.0.7.2)
       mail (>= 2.7.1)
       net-imap
       net-pop
       net-smtp
-    actionmailer (7.0.7)
-      actionpack (= 7.0.7)
-      actionview (= 7.0.7)
-      activejob (= 7.0.7)
-      activesupport (= 7.0.7)
+    actionmailer (7.0.7.2)
+      actionpack (= 7.0.7.2)
+      actionview (= 7.0.7.2)
+      activejob (= 7.0.7.2)
+      activesupport (= 7.0.7.2)
       mail (~> 2.5, >= 2.5.4)
       net-imap
       net-pop
       net-smtp
       rails-dom-testing (~> 2.0)
-    actionpack (7.0.7)
-      actionview (= 7.0.7)
-      activesupport (= 7.0.7)
+    actionpack (7.0.7.2)
+      actionview (= 7.0.7.2)
+      activesupport (= 7.0.7.2)
       rack (~> 2.0, >= 2.2.4)
       rack-test (>= 0.6.3)
       rails-dom-testing (~> 2.0)
       rails-html-sanitizer (~> 1.0, >= 1.2.0)
-    actiontext (7.0.7)
-      actionpack (= 7.0.7)
-      activerecord (= 7.0.7)
-      activestorage (= 7.0.7)
-      activesupport (= 7.0.7)
+    actiontext (7.0.7.2)
+      actionpack (= 7.0.7.2)
+      activerecord (= 7.0.7.2)
+      activestorage (= 7.0.7.2)
+      activesupport (= 7.0.7.2)
       globalid (>= 0.6.0)
       nokogiri (>= 1.8.5)
-    actionview (7.0.7)
-      activesupport (= 7.0.7)
+    actionview (7.0.7.2)
+      activesupport (= 7.0.7.2)
       builder (~> 3.1)
       erubi (~> 1.4)
       rails-dom-testing (~> 2.0)
@@ -89,22 +89,22 @@ GEM
       activemodel (>= 4.1, < 7.1)
       case_transform (>= 0.2)
       jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
-    activejob (7.0.7)
-      activesupport (= 7.0.7)
+    activejob (7.0.7.2)
+      activesupport (= 7.0.7.2)
       globalid (>= 0.3.6)
-    activemodel (7.0.7)
-      activesupport (= 7.0.7)
-    activerecord (7.0.7)
-      activemodel (= 7.0.7)
-      activesupport (= 7.0.7)
-    activestorage (7.0.7)
-      actionpack (= 7.0.7)
-      activejob (= 7.0.7)
-      activerecord (= 7.0.7)
-      activesupport (= 7.0.7)
+    activemodel (7.0.7.2)
+      activesupport (= 7.0.7.2)
+    activerecord (7.0.7.2)
+      activemodel (= 7.0.7.2)
+      activesupport (= 7.0.7.2)
+    activestorage (7.0.7.2)
+      actionpack (= 7.0.7.2)
+      activejob (= 7.0.7.2)
+      activerecord (= 7.0.7.2)
+      activesupport (= 7.0.7.2)
       marcel (~> 1.0)
       mini_mime (>= 1.1.0)
-    activesupport (7.0.7)
+    activesupport (7.0.7.2)
       concurrent-ruby (~> 1.0, >= 1.0.2)
       i18n (>= 1.6, < 2)
       minitest (>= 5.1)
@@ -556,20 +556,20 @@ GEM
       rack
     rack-test (2.1.0)
       rack (>= 1.3)
-    rails (7.0.7)
-      actioncable (= 7.0.7)
-      actionmailbox (= 7.0.7)
-      actionmailer (= 7.0.7)
-      actionpack (= 7.0.7)
-      actiontext (= 7.0.7)
-      actionview (= 7.0.7)
-      activejob (= 7.0.7)
-      activemodel (= 7.0.7)
-      activerecord (= 7.0.7)
-      activestorage (= 7.0.7)
-      activesupport (= 7.0.7)
+    rails (7.0.7.2)
+      actioncable (= 7.0.7.2)
+      actionmailbox (= 7.0.7.2)
+      actionmailer (= 7.0.7.2)
+      actionpack (= 7.0.7.2)
+      actiontext (= 7.0.7.2)
+      actionview (= 7.0.7.2)
+      activejob (= 7.0.7.2)
+      activemodel (= 7.0.7.2)
+      activerecord (= 7.0.7.2)
+      activestorage (= 7.0.7.2)
+      activesupport (= 7.0.7.2)
       bundler (>= 1.15.0)
-      railties (= 7.0.7)
+      railties (= 7.0.7.2)
     rails-controller-testing (1.0.5)
       actionpack (>= 5.0.1.rc1)
       actionview (>= 5.0.1.rc1)
@@ -584,9 +584,9 @@ GEM
     rails-i18n (7.0.7)
       i18n (>= 0.7, < 2)
       railties (>= 6.0.0, < 8)
-    railties (7.0.7)
-      actionpack (= 7.0.7)
-      activesupport (= 7.0.7)
+    railties (7.0.7.2)
+      actionpack (= 7.0.7.2)
+      activesupport (= 7.0.7.2)
       method_source
       rake (>= 12.2)
       thor (~> 1.0)

From ea1a221e2de95bf40aefaa34cab8b08716f941a5 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 23 Aug 2023 08:26:30 +0200
Subject: [PATCH 03/13] Update dependency react-textarea-autosize to v8.5.3
 (#26607)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 yarn.lock | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/yarn.lock b/yarn.lock
index 779b3a21ba2..a57b203de5a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10086,9 +10086,9 @@ react-test-renderer@^18.2.0:
     scheduler "^0.23.0"
 
 react-textarea-autosize@*, react-textarea-autosize@^8.4.1:
-  version "8.5.2"
-  resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.5.2.tgz#6421df2b5b50b9ca8c5e96fd31be688ea7fa2f9d"
-  integrity sha512-uOkyjkEl0ByEK21eCJMHDGBAAd/BoFQBawYK5XItjAmCTeSbjxghd8qnt7nzsLYzidjnoObu6M26xts0YGKsGg==
+  version "8.5.3"
+  resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz#d1e9fe760178413891484847d3378706052dd409"
+  integrity sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ==
   dependencies:
     "@babel/runtime" "^7.20.13"
     use-composed-ref "^1.3.0"

From cf6f70799b128d385d830d1f55eae8046bea3c60 Mon Sep 17 00:00:00 2001
From: Robert R George <rgeorge@midnightweb.net>
Date: Tue, 22 Aug 2023 23:27:24 -0700
Subject: [PATCH 04/13] Add support for federating `memorial` attribute 
 (#26583)

---
 app/helpers/context_helper.rb                       | 1 +
 app/serializers/activitypub/actor_serializer.rb     | 5 +++--
 app/services/activitypub/process_account_service.rb | 1 +
 3 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/app/helpers/context_helper.rb b/app/helpers/context_helper.rb
index d8fa9b92e19..945ef9b91a4 100644
--- a/app/helpers/context_helper.rb
+++ b/app/helpers/context_helper.rb
@@ -21,6 +21,7 @@ module ContextHelper
     blurhash: { 'toot' => 'http://joinmastodon.org/ns#', 'blurhash' => 'toot:blurhash' },
     discoverable: { 'toot' => 'http://joinmastodon.org/ns#', 'discoverable' => 'toot:discoverable' },
     indexable: { 'toot' => 'http://joinmastodon.org/ns#', 'indexable' => 'toot:indexable' },
+    memorial: { 'toot' => 'http://joinmastodon.org/ns#', 'memorial' => 'toot:memorial' },
     voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' },
     olm: {
       'toot' => 'http://joinmastodon.org/ns#', 'Device' => 'toot:Device', 'Ed25519Signature' => 'toot:Ed25519Signature', 'Ed25519Key' => 'toot:Ed25519Key', 'Curve25519Key' => 'toot:Curve25519Key', 'EncryptedMessage' => 'toot:EncryptedMessage', 'publicKeyBase64' => 'toot:publicKeyBase64', 'deviceId' => 'toot:deviceId',
diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb
index 36397857f01..4998d003998 100644
--- a/app/serializers/activitypub/actor_serializer.rb
+++ b/app/serializers/activitypub/actor_serializer.rb
@@ -7,13 +7,14 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
   context :security
 
   context_extensions :manually_approves_followers, :featured, :also_known_as,
-                     :moved_to, :property_value, :discoverable, :olm, :suspended
+                     :moved_to, :property_value, :discoverable, :olm, :suspended,
+                     :memorial
 
   attributes :id, :type, :following, :followers,
              :inbox, :outbox, :featured, :featured_tags,
              :preferred_username, :name, :summary,
              :url, :manually_approves_followers,
-             :discoverable, :published
+             :discoverable, :published, :memorial
 
   has_one :public_key, serializer: ActivityPub::PublicKeySerializer
 
diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb
index ae30e88a298..1304ca8242b 100644
--- a/app/services/activitypub/process_account_service.rb
+++ b/app/services/activitypub/process_account_service.rb
@@ -116,6 +116,7 @@ class ActivityPub::ProcessAccountService < BaseService
     @account.also_known_as           = as_array(@json['alsoKnownAs'] || []).map { |item| value_or_id(item) }
     @account.discoverable            = @json['discoverable'] || false
     @account.indexable               = @json['indexable'] || false
+    @account.memorial                = @json['memorial'] || false
   end
 
   def set_fetchable_key!

From 3aac12981c6db519e6fcbee5509b2a146c3f4d1e Mon Sep 17 00:00:00 2001
From: yufushiro <62991447+yufushiro@users.noreply.github.com>
Date: Wed, 23 Aug 2023 15:44:56 +0900
Subject: [PATCH 05/13] Fix unexpected audio stream transcoding when uploaded
 video is eligible to passthrough (#26608)

Co-authored-by: Claire <claire.github-309c@sitedethib.com>
---
 lib/paperclip/transcoder.rb | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/lib/paperclip/transcoder.rb b/lib/paperclip/transcoder.rb
index be40b492410..0f2e30f7d5e 100644
--- a/lib/paperclip/transcoder.rb
+++ b/lib/paperclip/transcoder.rb
@@ -37,12 +37,14 @@ module Paperclip
         @output_options['f']       = 'image2'
         @output_options['vframes'] = 1
       when 'mp4'
-        @output_options['acodec'] = 'aac'
-        @output_options['strict'] = 'experimental'
+        unless eligible_to_passthrough?(metadata)
+          @output_options['acodec'] = 'aac'
+          @output_options['strict'] = 'experimental'
 
-        if high_vfr?(metadata) && !eligible_to_passthrough?(metadata)
-          @output_options['vsync'] = 'vfr'
-          @output_options['r'] = @vfr_threshold
+          if high_vfr?(metadata)
+            @output_options['vsync'] = 'vfr'
+            @output_options['r'] = @vfr_threshold
+          end
         end
       end
 

From 85057865b4e6ce8ec09feee84ceb6f931300548e Mon Sep 17 00:00:00 2001
From: jsgoldstein <jakegoldstein95@gmail.com>
Date: Wed, 23 Aug 2023 09:40:09 -0400
Subject: [PATCH 06/13] Update Account Search to prioritize username over
 display name (#26623)

---
 app/services/account_search_service.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/services/account_search_service.rb b/app/services/account_search_service.rb
index a15b6912110..b437ff47593 100644
--- a/app/services/account_search_service.rb
+++ b/app/services/account_search_service.rb
@@ -124,7 +124,7 @@ class AccountSearchService < BaseService
         multi_match: {
           query: @query,
           type: 'bool_prefix',
-          fields: %w(username username.* display_name display_name.*),
+          fields: %w(username^2 username.*^2 display_name display_name.*),
         },
       }
     end

From 44ba785242c14bdfcd95854683a69a8451288dba Mon Sep 17 00:00:00 2001
From: Renaud Chaput <renchap@gmail.com>
Date: Wed, 23 Aug 2023 15:40:31 +0200
Subject: [PATCH 07/13] Change the hashtag bar to be hidden when there is a CW
 and the post is not expanded (#26615)

---
 app/javascript/mastodon/components/status.jsx                | 5 +++--
 .../mastodon/features/status/components/detailed_status.jsx  | 3 ++-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status.jsx
index 45a2106dba6..30692d1cd97 100644
--- a/app/javascript/mastodon/components/status.jsx
+++ b/app/javascript/mastodon/components/status.jsx
@@ -546,6 +546,7 @@ class Status extends ImmutablePureComponent {
     const visibilityIcon = visibilityIconInfo[status.get('visibility')];
 
     const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status);
+    const expanded = !status.get('hidden')
 
     return (
       <HotKeys handlers={handlers}>
@@ -574,7 +575,7 @@ class Status extends ImmutablePureComponent {
             <StatusContent
               status={status}
               onClick={this.handleClick}
-              expanded={!status.get('hidden')}
+              expanded={expanded}
               onExpandedToggle={this.handleExpandedToggle}
               onTranslate={this.handleTranslate}
               collapsible
@@ -584,7 +585,7 @@ class Status extends ImmutablePureComponent {
 
             {media}
 
-            {hashtagBar}
+            {expanded && hashtagBar}
 
             <StatusActionBar scrollKey={scrollKey} status={status} account={account} onFilter={matchedFilters ? this.handleFilterClick : null} {...other} />
           </div>
diff --git a/app/javascript/mastodon/features/status/components/detailed_status.jsx b/app/javascript/mastodon/features/status/components/detailed_status.jsx
index 401550e49f2..e41a042c7c0 100644
--- a/app/javascript/mastodon/features/status/components/detailed_status.jsx
+++ b/app/javascript/mastodon/features/status/components/detailed_status.jsx
@@ -293,6 +293,7 @@ class DetailedStatus extends ImmutablePureComponent {
     }
 
     const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status);
+    const expanded = !status.get('hidden')
 
     return (
       <div style={outerStyle}>
@@ -318,7 +319,7 @@ class DetailedStatus extends ImmutablePureComponent {
 
           {media}
 
-          {hashtagBar}
+          {expanded && hashtagBar}
 
           <div className='detailed-status__meta'>
             <a className='detailed-status__datetime' href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} target='_blank' rel='noopener noreferrer'>

From 152b10b6246987bfb2cc73ecd2a20578d05b62dc Mon Sep 17 00:00:00 2001
From: Christian Schmidt <github@chsc.dk>
Date: Wed, 23 Aug 2023 15:43:41 +0200
Subject: [PATCH 08/13] Fix some React warnings (#26609)

---
 app/javascript/mastodon/features/explore/results.jsx      | 8 ++++----
 app/javascript/mastodon/features/status/index.jsx         | 2 +-
 .../mastodon/features/ui/components/modal_root.jsx        | 5 ++++-
 3 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/app/javascript/mastodon/features/explore/results.jsx b/app/javascript/mastodon/features/explore/results.jsx
index bf2f0b95adb..7d1ce69ad00 100644
--- a/app/javascript/mastodon/features/explore/results.jsx
+++ b/app/javascript/mastodon/features/explore/results.jsx
@@ -108,10 +108,10 @@ class Results extends PureComponent {
     return (
       <>
         <div className='account__section-headline'>
-          <button onClick={this.handleSelectAll} className={type === 'all' && 'active'}><FormattedMessage id='search_results.all' defaultMessage='All' /></button>
-          <button onClick={this.handleSelectAccounts} className={type === 'accounts' && 'active'}><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></button>
-          <button onClick={this.handleSelectHashtags} className={type === 'hashtags' && 'active'}><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></button>
-          <button onClick={this.handleSelectStatuses} className={type === 'statuses' && 'active'}><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></button>
+          <button onClick={this.handleSelectAll} className={type === 'all' ? 'active' : undefined}><FormattedMessage id='search_results.all' defaultMessage='All' /></button>
+          <button onClick={this.handleSelectAccounts} className={type === 'accounts' ? 'active' : undefined}><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></button>
+          <button onClick={this.handleSelectHashtags} className={type === 'hashtags' ? 'active' : undefined}><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></button>
+          <button onClick={this.handleSelectStatuses} className={type === 'statuses' ? 'active' : undefined}><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></button>
         </div>
 
         <div className='explore__search-results'>
diff --git a/app/javascript/mastodon/features/status/index.jsx b/app/javascript/mastodon/features/status/index.jsx
index f1d591c73f5..799354d183c 100644
--- a/app/javascript/mastodon/features/status/index.jsx
+++ b/app/javascript/mastodon/features/status/index.jsx
@@ -568,7 +568,7 @@ class Status extends ImmutablePureComponent {
         onMoveUp={this.handleMoveUp}
         onMoveDown={this.handleMoveDown}
         contextType='thread'
-        previousId={i > 0 && list.get(i - 1)}
+        previousId={i > 0 ? list.get(i - 1) : undefined}
         nextId={list.get(i + 1) || (ancestors && statusId)}
         rootId={statusId}
       />
diff --git a/app/javascript/mastodon/features/ui/components/modal_root.jsx b/app/javascript/mastodon/features/ui/components/modal_root.jsx
index fb6acfaeaa9..f6de5dad356 100644
--- a/app/javascript/mastodon/features/ui/components/modal_root.jsx
+++ b/app/javascript/mastodon/features/ui/components/modal_root.jsx
@@ -115,7 +115,10 @@ export default class ModalRoot extends PureComponent {
         {visible && (
           <>
             <BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}>
-              {(SpecificComponent) => <SpecificComponent {...props} onChangeBackgroundColor={this.setBackgroundColor} onClose={this.handleClose} ref={this.setModalRef} />}
+              {(SpecificComponent) => {
+                const ref = typeof SpecificComponent !== 'function' ? this.setModalRef : undefined;
+                return <SpecificComponent {...props} onChangeBackgroundColor={this.setBackgroundColor} onClose={this.handleClose} ref={ref} />
+              }}
             </BundleContainer>
 
             <Helmet>

From 613cfd625c8be11b4fb91d769ddbeee7a535a57a Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Wed, 23 Aug 2023 15:44:52 +0200
Subject: [PATCH 09/13] Change hashtag bar tags to be de-emphasized (#26606)

---
 .../mastodon/components/hashtag_bar.tsx           |  2 +-
 app/javascript/styles/mastodon/components.scss    | 15 +++++++--------
 2 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/app/javascript/mastodon/components/hashtag_bar.tsx b/app/javascript/mastodon/components/hashtag_bar.tsx
index 75bd74da01c..674c481b817 100644
--- a/app/javascript/mastodon/components/hashtag_bar.tsx
+++ b/app/javascript/mastodon/components/hashtag_bar.tsx
@@ -216,7 +216,7 @@ const HashtagBar: React.FC<{
     <div className='hashtag-bar'>
       {revealedHashtags.map((hashtag) => (
         <Link key={hashtag} to={`/tags/${hashtag}`}>
-          #{hashtag}
+          #<span>{hashtag}</span>
         </Link>
       ))}
 
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 7662f3d9b7d..664f6ca02b3 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -9305,16 +9305,15 @@ noscript {
 
   a {
     display: inline-flex;
-    border-radius: 4px;
-    background: rgba($highlight-text-color, 0.2);
-    color: $highlight-text-color;
-    padding: 0.4em 0.6em;
+    color: $dark-text-color;
     text-decoration: none;
 
-    &:hover,
-    &:focus,
-    &:active {
-      background: rgba($highlight-text-color, 0.3);
+    &:hover {
+      text-decoration: none;
+
+      span {
+        text-decoration: underline;
+      }
     }
   }
 }

From de8c2427a5d93e2e1d03681d0c9bae3a367dfa2f Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 23 Aug 2023 15:45:21 +0200
Subject: [PATCH 10/13] Update dependency immutable to v4.3.3 (#26622)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 yarn.lock | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/yarn.lock b/yarn.lock
index a57b203de5a..7ef2a0d5b4a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6643,9 +6643,9 @@ immutable@^3.8.2:
   integrity sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==
 
 immutable@^4.0.0, immutable@^4.0.0-rc.1, immutable@^4.3.0:
-  version "4.3.2"
-  resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.2.tgz#f89d910f8dfb6e15c03b2cae2faaf8c1f66455fe"
-  integrity sha512-oGXzbEDem9OOpDWZu88jGiYCvIsLHMvGw+8OXlpsvTFvIQplQbjg1B1cvKg8f7Hoch6+NGjpPsH1Fr+Mc2D1aA==
+  version "4.3.3"
+  resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.3.tgz#8934ff6826d996a7642c8dc4b46e694dd19561e3"
+  integrity sha512-808ZFYMsIRAjLAu5xkKo0TsbY9LBy9H5MazTKIEHerNkg0ymgilGfBPMR/3G7d/ihGmuK2Hw8S1izY2d3kd3wA==
 
 import-fresh@^3.2.1:
   version "3.3.0"

From 060b554a9d13465366c45da0a5c4c841472e872c Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 23 Aug 2023 15:45:38 +0200
Subject: [PATCH 11/13] Update dependency oj to v3.16.0 (#26520)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 Gemfile.lock | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Gemfile.lock b/Gemfile.lock
index 64ad456f134..e3d9eac9631 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -482,7 +482,7 @@ GEM
     nokogiri (1.15.4)
       mini_portile2 (~> 2.8.2)
       racc (~> 1.4)
-    oj (3.15.0)
+    oj (3.16.0)
     omniauth (2.1.1)
       hashie (>= 3.4.6)
       rack (>= 2.2.3)

From 34f5b90dc77956d33750695124f23c37a6ea7fd5 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 23 Aug 2023 15:45:56 +0200
Subject: [PATCH 12/13] Update dependency sass to v1.66.1 (#26534)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 yarn.lock | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/yarn.lock b/yarn.lock
index 7ef2a0d5b4a..403748ea9dd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10599,9 +10599,9 @@ sass-loader@^10.2.0:
     semver "^7.3.2"
 
 sass@^1.62.1:
-  version "1.65.1"
-  resolved "https://registry.yarnpkg.com/sass/-/sass-1.65.1.tgz#8f283b0c26335a88246a448d22e1342ba2ea1432"
-  integrity sha512-9DINwtHmA41SEd36eVPQ9BJKpn7eKDQmUHmpI0y5Zv2Rcorrh0zS+cFrt050hdNbmmCNKTW3hV5mWfuegNRsEA==
+  version "1.66.1"
+  resolved "https://registry.yarnpkg.com/sass/-/sass-1.66.1.tgz#04b51c4671e4650aa393740e66a4e58b44d055b1"
+  integrity sha512-50c+zTsZOJVgFfTgwwEzkjA3/QACgdNsKueWPyAR0mRINIvLAStVQBbPg14iuqEQ74NPDbXzJARJ/O4SI1zftA==
   dependencies:
     chokidar ">=3.0.0 <4.0.0"
     immutable "^4.0.0"

From b91724fb9d0839365391310e20c2589ff6062d4f Mon Sep 17 00:00:00 2001
From: jsgoldstein <jakegoldstein95@gmail.com>
Date: Wed, 23 Aug 2023 09:46:14 -0400
Subject: [PATCH 13/13] Add elastic search installation into Vagrantfile
 (#26512)

---
 .env.vagrant |  4 ++++
 Vagrantfile  | 43 +++++++++++++++++++++++++++++++++++++++----
 2 files changed, 43 insertions(+), 4 deletions(-)

diff --git a/.env.vagrant b/.env.vagrant
index 32ed9b92294..69c1bf1fb3d 100644
--- a/.env.vagrant
+++ b/.env.vagrant
@@ -2,3 +2,7 @@ VAGRANT=true
 LOCAL_DOMAIN=mastodon.local
 BIND=0.0.0.0
 DB_HOST=/var/run/postgresql/
+
+ES_ENABLED=true
+ES_HOST=localhost
+ES_PORT=9200
\ No newline at end of file
diff --git a/Vagrantfile b/Vagrantfile
index 880cc18495d..1117d62fff2 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -60,6 +60,37 @@ sudo usermod -a -G rvm $USER
 
 SCRIPT
 
+$provisionElasticsearch = <<SCRIPT
+# Install Elastic Search
+sudo apt install openjdk-17-jre-headless -y
+sudo wget -O /usr/share/keyrings/elasticsearch.asc https://artifacts.elastic.co/GPG-KEY-elasticsearch
+sudo sh -c 'echo "deb [signed-by=/usr/share/keyrings/elasticsearch.asc] https://artifacts.elastic.co/packages/7.x/apt stable main" > /etc/apt/sources.list.d/elastic-7.x.list'
+sudo apt update
+sudo apt install elasticsearch -y
+
+sudo systemctl daemon-reload
+sudo systemctl enable --now elasticsearch
+
+echo 'path.data: /var/lib/elasticsearch
+path.logs: /var/log/elasticsearch
+network.host: 0.0.0.0
+http.port: 9200
+discovery.seed_hosts: ["localhost"]
+cluster.initial_master_nodes: ["node-1"]' > /etc/elasticsearch/elasticsearch.yml
+
+sudo systemctl restart elasticsearch
+
+# Install Kibana
+sudo apt install kibana -y
+sudo systemctl enable --now kibana
+
+echo 'server.host: "0.0.0.0"
+elasticsearch.hosts: ["http://localhost:9200"]' > /etc/kibana/kibana.yml
+
+sudo systemctl restart kibana
+
+SCRIPT
+
 $provisionB = <<SCRIPT
 
 source "/etc/profile.d/rvm.sh"
@@ -102,10 +133,8 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
 
   config.vm.provider :virtualbox do |vb|
     vb.name = "mastodon"
-    vb.customize ["modifyvm", :id, "--memory", "2048"]
-    # Increase the number of CPUs. Uncomment and adjust to
-    # increase performance
-    # vb.customize ["modifyvm", :id, "--cpus", "3"]
+    vb.customize ["modifyvm", :id, "--memory", "8192"]
+    vb.customize ["modifyvm", :id, "--cpus", "3"]
 
     # Disable VirtualBox DNS proxy to skip long-delay IPv6 resolutions.
     # https://github.com/mitchellh/vagrant/issues/1172
@@ -141,9 +170,15 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
   config.vm.network :forwarded_port, guest: 3000, host: 3000
   config.vm.network :forwarded_port, guest: 4000, host: 4000
   config.vm.network :forwarded_port, guest: 8080, host: 8080
+  config.vm.network :forwarded_port, guest: 9200, host: 9200
+  config.vm.network :forwarded_port, guest: 9300, host: 9300
+  config.vm.network :forwarded_port, guest: 9243, host: 9243
+  config.vm.network :forwarded_port, guest: 5601, host: 5601
 
   # Full provisioning script, only runs on first 'vagrant up' or with 'vagrant provision'
   config.vm.provision :shell, inline: $provisionA, privileged: false, reset: true
+  # Run with elevated privileges for Elasticsearch installation
+  config.vm.provision :shell, inline: $provisionElasticsearch, privileged: true
   config.vm.provision :shell, inline: $provisionB, privileged: false
 
   config.vm.post_up_message = <<MESSAGE