From 44a7d87cb1f5df953b6c14c16c59e2e4ead1bcb9 Mon Sep 17 00:00:00 2001
From: Renaud Chaput <renchap@gmail.com>
Date: Mon, 20 Feb 2023 03:20:59 +0100
Subject: Rename JSX files with proper `.jsx` extension (#23733)

---
 .../features/status/components/detailed_status.jsx | 288 +++++++++++++++++++++
 1 file changed, 288 insertions(+)
 create mode 100644 app/javascript/mastodon/features/status/components/detailed_status.jsx

(limited to 'app/javascript/mastodon/features/status/components/detailed_status.jsx')

diff --git a/app/javascript/mastodon/features/status/components/detailed_status.jsx b/app/javascript/mastodon/features/status/components/detailed_status.jsx
new file mode 100644
index 000000000..064231ffe
--- /dev/null
+++ b/app/javascript/mastodon/features/status/components/detailed_status.jsx
@@ -0,0 +1,288 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import Avatar from '../../../components/avatar';
+import DisplayName from '../../../components/display_name';
+import StatusContent from '../../../components/status_content';
+import MediaGallery from '../../../components/media_gallery';
+import { Link } from 'react-router-dom';
+import { injectIntl, defineMessages, FormattedDate } from 'react-intl';
+import Card from './card';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import Video from '../../video';
+import Audio from '../../audio';
+import scheduleIdleTask from '../../ui/util/schedule_idle_task';
+import classNames from 'classnames';
+import Icon from 'mastodon/components/icon';
+import AnimatedNumber from 'mastodon/components/animated_number';
+import PictureInPicturePlaceholder from 'mastodon/components/picture_in_picture_placeholder';
+import EditedTimestamp from 'mastodon/components/edited_timestamp';
+
+const messages = defineMessages({
+  public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
+  unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
+  private_short: { id: 'privacy.private.short', defaultMessage: 'Followers-only' },
+  direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
+});
+
+export default  @injectIntl
+class DetailedStatus extends ImmutablePureComponent {
+
+  static contextTypes = {
+    router: PropTypes.object,
+  };
+
+  static propTypes = {
+    status: ImmutablePropTypes.map,
+    onOpenMedia: PropTypes.func.isRequired,
+    onOpenVideo: PropTypes.func.isRequired,
+    onToggleHidden: PropTypes.func.isRequired,
+    onTranslate: PropTypes.func.isRequired,
+    measureHeight: PropTypes.bool,
+    onHeightChange: PropTypes.func,
+    domain: PropTypes.string.isRequired,
+    compact: PropTypes.bool,
+    showMedia: PropTypes.bool,
+    pictureInPicture: ImmutablePropTypes.contains({
+      inUse: PropTypes.bool,
+      available: PropTypes.bool,
+    }),
+    onToggleMediaVisibility: PropTypes.func,
+  };
+
+  state = {
+    height: null,
+  };
+
+  handleAccountClick = (e) => {
+    if (e.button === 0 && !(e.ctrlKey || e.metaKey) && this.context.router) {
+      e.preventDefault();
+      this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}`);
+    }
+
+    e.stopPropagation();
+  };
+
+  handleOpenVideo = (options) => {
+    this.props.onOpenVideo(this.props.status.getIn(['media_attachments', 0]), options);
+  };
+
+  handleExpandedToggle = () => {
+    this.props.onToggleHidden(this.props.status);
+  };
+
+  _measureHeight (heightJustChanged) {
+    if (this.props.measureHeight && this.node) {
+      scheduleIdleTask(() => this.node && this.setState({ height: Math.ceil(this.node.scrollHeight) + 1 }));
+
+      if (this.props.onHeightChange && heightJustChanged) {
+        this.props.onHeightChange();
+      }
+    }
+  }
+
+  setRef = c => {
+    this.node = c;
+    this._measureHeight();
+  };
+
+  componentDidUpdate (prevProps, prevState) {
+    this._measureHeight(prevState.height !== this.state.height);
+  }
+
+  handleModalLink = e => {
+    e.preventDefault();
+
+    let href;
+
+    if (e.target.nodeName !== 'A') {
+      href = e.target.parentNode.href;
+    } else {
+      href = e.target.href;
+    }
+
+    window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
+  };
+
+  handleTranslate = () => {
+    const { onTranslate, status } = this.props;
+    onTranslate(status);
+  };
+
+  render () {
+    const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
+    const outerStyle = { boxSizing: 'border-box' };
+    const { intl, compact, pictureInPicture } = this.props;
+
+    if (!status) {
+      return null;
+    }
+
+    let media           = '';
+    let applicationLink = '';
+    let reblogLink = '';
+    let reblogIcon = 'retweet';
+    let favouriteLink = '';
+    let edited = '';
+
+    if (this.props.measureHeight) {
+      outerStyle.height = `${this.state.height}px`;
+    }
+
+    if (pictureInPicture.get('inUse')) {
+      media = <PictureInPicturePlaceholder />;
+    } else if (status.get('media_attachments').size > 0) {
+      if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
+        const attachment = status.getIn(['media_attachments', 0]);
+
+        media = (
+          <Audio
+            src={attachment.get('url')}
+            alt={attachment.get('description')}
+            duration={attachment.getIn(['meta', 'original', 'duration'], 0)}
+            poster={attachment.get('preview_url') || status.getIn(['account', 'avatar_static'])}
+            backgroundColor={attachment.getIn(['meta', 'colors', 'background'])}
+            foregroundColor={attachment.getIn(['meta', 'colors', 'foreground'])}
+            accentColor={attachment.getIn(['meta', 'colors', 'accent'])}
+            sensitive={status.get('sensitive')}
+            visible={this.props.showMedia}
+            blurhash={attachment.get('blurhash')}
+            height={150}
+            onToggleVisibility={this.props.onToggleMediaVisibility}
+          />
+        );
+      } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
+        const attachment = status.getIn(['media_attachments', 0]);
+
+        media = (
+          <Video
+            preview={attachment.get('preview_url')}
+            frameRate={attachment.getIn(['meta', 'original', 'frame_rate'])}
+            blurhash={attachment.get('blurhash')}
+            src={attachment.get('url')}
+            alt={attachment.get('description')}
+            width={300}
+            height={150}
+            inline
+            onOpenVideo={this.handleOpenVideo}
+            sensitive={status.get('sensitive')}
+            visible={this.props.showMedia}
+            onToggleVisibility={this.props.onToggleMediaVisibility}
+          />
+        );
+      } else {
+        media = (
+          <MediaGallery
+            standalone
+            sensitive={status.get('sensitive')}
+            media={status.get('media_attachments')}
+            height={300}
+            onOpenMedia={this.props.onOpenMedia}
+            visible={this.props.showMedia}
+            onToggleVisibility={this.props.onToggleMediaVisibility}
+          />
+        );
+      }
+    } else if (status.get('spoiler_text').length === 0) {
+      media = <Card sensitive={status.get('sensitive')} onOpenMedia={this.props.onOpenMedia} card={status.get('card', null)} />;
+    }
+
+    if (status.get('application')) {
+      applicationLink = <React.Fragment> · <a className='detailed-status__application' href={status.getIn(['application', 'website'])} target='_blank' rel='noopener noreferrer'>{status.getIn(['application', 'name'])}</a></React.Fragment>;
+    }
+
+    const visibilityIconInfo = {
+      'public': { icon: 'globe', text: intl.formatMessage(messages.public_short) },
+      'unlisted': { icon: 'unlock', text: intl.formatMessage(messages.unlisted_short) },
+      'private': { icon: 'lock', text: intl.formatMessage(messages.private_short) },
+      'direct': { icon: 'at', text: intl.formatMessage(messages.direct_short) },
+    };
+
+    const visibilityIcon = visibilityIconInfo[status.get('visibility')];
+    const visibilityLink = <React.Fragment> · <Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></React.Fragment>;
+
+    if (['private', 'direct'].includes(status.get('visibility'))) {
+      reblogLink = '';
+    } else if (this.context.router) {
+      reblogLink = (
+        <React.Fragment>
+          <React.Fragment> · </React.Fragment>
+          <Link to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}/reblogs`} className='detailed-status__link'>
+            <Icon id={reblogIcon} />
+            <span className='detailed-status__reblogs'>
+              <AnimatedNumber value={status.get('reblogs_count')} />
+            </span>
+          </Link>
+        </React.Fragment>
+      );
+    } else {
+      reblogLink = (
+        <React.Fragment>
+          <React.Fragment> · </React.Fragment>
+          <a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}>
+            <Icon id={reblogIcon} />
+            <span className='detailed-status__reblogs'>
+              <AnimatedNumber value={status.get('reblogs_count')} />
+            </span>
+          </a>
+        </React.Fragment>
+      );
+    }
+
+    if (this.context.router) {
+      favouriteLink = (
+        <Link to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}/favourites`} className='detailed-status__link'>
+          <Icon id='star' />
+          <span className='detailed-status__favorites'>
+            <AnimatedNumber value={status.get('favourites_count')} />
+          </span>
+        </Link>
+      );
+    } else {
+      favouriteLink = (
+        <a href={`/interact/${status.get('id')}?type=favourite`} className='detailed-status__link' onClick={this.handleModalLink}>
+          <Icon id='star' />
+          <span className='detailed-status__favorites'>
+            <AnimatedNumber value={status.get('favourites_count')} />
+          </span>
+        </a>
+      );
+    }
+
+    if (status.get('edited_at')) {
+      edited = (
+        <React.Fragment>
+          <React.Fragment> · </React.Fragment>
+          <EditedTimestamp statusId={status.get('id')} timestamp={status.get('edited_at')} />
+        </React.Fragment>
+      );
+    }
+
+    return (
+      <div style={outerStyle}>
+        <div ref={this.setRef} className={classNames('detailed-status', `detailed-status-${status.get('visibility')}`, { compact })}>
+          <a href={`/@${status.getIn(['account', 'acct'])}`} onClick={this.handleAccountClick} className='detailed-status__display-name'>
+            <div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={46} /></div>
+            <DisplayName account={status.get('account')} localDomain={this.props.domain} />
+          </a>
+
+          <StatusContent
+            status={status}
+            expanded={!status.get('hidden')}
+            onExpandedToggle={this.handleExpandedToggle}
+            onTranslate={this.handleTranslate}
+          />
+
+          {media}
+
+          <div className='detailed-status__meta'>
+            <a className='detailed-status__datetime' href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} target='_blank' rel='noopener noreferrer'>
+              <FormattedDate value={new Date(status.get('created_at'))} hour12={false} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' />
+            </a>{edited}{visibilityLink}{applicationLink}{reblogLink} · {favouriteLink}
+          </div>
+        </div>
+      </div>
+    );
+  }
+
+}
-- 
cgit 


From d3eefead3014175b264cb56f6f4cb552cbaaeac6 Mon Sep 17 00:00:00 2001
From: Christian Schmidt <github@chsc.dk>
Date: Sun, 26 Feb 2023 20:13:27 +0100
Subject: Add `lang` attribute to media and poll options (#23891)

---
 app/javascript/mastodon/components/gifv.jsx               |  5 ++++-
 app/javascript/mastodon/components/media_attachments.jsx  |  6 +++++-
 app/javascript/mastodon/components/media_gallery.jsx      | 15 +++++++++------
 app/javascript/mastodon/components/poll.jsx               |  5 ++++-
 app/javascript/mastodon/components/status.jsx             |  3 +++
 app/javascript/mastodon/components/status_content.jsx     |  2 +-
 .../features/account_gallery/components/media_item.jsx    |  3 +++
 app/javascript/mastodon/features/audio/index.jsx          |  4 +++-
 .../features/status/components/detailed_status.jsx        |  3 +++
 .../mastodon/features/ui/components/audio_modal.jsx       |  7 +++++--
 .../features/ui/components/compare_history_modal.jsx      | 11 +++++++----
 .../mastodon/features/ui/components/image_loader.jsx      |  5 ++++-
 .../mastodon/features/ui/components/media_modal.jsx       | 13 +++++++++++--
 .../mastodon/features/ui/components/video_modal.jsx       | 12 ++++++++++--
 .../mastodon/features/ui/components/zoomable_image.jsx    |  5 ++++-
 app/javascript/mastodon/features/video/index.jsx          |  4 +++-
 16 files changed, 79 insertions(+), 24 deletions(-)

(limited to 'app/javascript/mastodon/features/status/components/detailed_status.jsx')

diff --git a/app/javascript/mastodon/components/gifv.jsx b/app/javascript/mastodon/components/gifv.jsx
index 1f0f99b46..9ec201c6c 100644
--- a/app/javascript/mastodon/components/gifv.jsx
+++ b/app/javascript/mastodon/components/gifv.jsx
@@ -6,6 +6,7 @@ export default class GIFV extends React.PureComponent {
   static propTypes = {
     src: PropTypes.string.isRequired,
     alt: PropTypes.string,
+    lang: PropTypes.string,
     width: PropTypes.number,
     height: PropTypes.number,
     onClick: PropTypes.func,
@@ -35,7 +36,7 @@ export default class GIFV extends React.PureComponent {
   };
 
   render () {
-    const { src, width, height, alt } = this.props;
+    const { src, width, height, alt, lang } = this.props;
     const { loading } = this.state;
 
     return (
@@ -48,6 +49,7 @@ export default class GIFV extends React.PureComponent {
             tabIndex='0'
             aria-label={alt}
             title={alt}
+            lang={lang}
             onClick={this.handleClick}
           />
         )}
@@ -58,6 +60,7 @@ export default class GIFV extends React.PureComponent {
           tabIndex='0'
           aria-label={alt}
           title={alt}
+          lang={lang}
           muted
           loop
           autoPlay
diff --git a/app/javascript/mastodon/components/media_attachments.jsx b/app/javascript/mastodon/components/media_attachments.jsx
index 565a30330..0e25e5973 100644
--- a/app/javascript/mastodon/components/media_attachments.jsx
+++ b/app/javascript/mastodon/components/media_attachments.jsx
@@ -10,6 +10,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
 
   static propTypes = {
     status: ImmutablePropTypes.map.isRequired,
+    lang: PropTypes.string,
     height: PropTypes.number,
     width: PropTypes.number,
   };
@@ -48,7 +49,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
   };
 
   render () {
-    const { status, width, height } = this.props;
+    const { status, lang, width, height } = this.props;
     const mediaAttachments = status.get('media_attachments');
 
     if (mediaAttachments.size === 0) {
@@ -64,6 +65,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
             <Component
               src={audio.get('url')}
               alt={audio.get('description')}
+              lang={lang || status.get('language')}
               width={width}
               height={height}
               poster={audio.get('preview_url') || status.getIn(['account', 'avatar_static'])}
@@ -87,6 +89,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
               blurhash={video.get('blurhash')}
               src={video.get('url')}
               alt={video.get('description')}
+              lang={lang || status.get('language')}
               width={width}
               height={height}
               inline
@@ -102,6 +105,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
           {Component => (
             <Component
               media={mediaAttachments}
+              lang={lang || status.get('language')}
               sensitive={status.get('sensitive')}
               defaultWidth={width}
               height={height}
diff --git a/app/javascript/mastodon/components/media_gallery.jsx b/app/javascript/mastodon/components/media_gallery.jsx
index 659a83375..f869bd62d 100644
--- a/app/javascript/mastodon/components/media_gallery.jsx
+++ b/app/javascript/mastodon/components/media_gallery.jsx
@@ -17,6 +17,7 @@ class Item extends React.PureComponent {
 
   static propTypes = {
     attachment: ImmutablePropTypes.map.isRequired,
+    lang: PropTypes.string,
     standalone: PropTypes.bool,
     index: PropTypes.number.isRequired,
     size: PropTypes.number.isRequired,
@@ -78,7 +79,7 @@ class Item extends React.PureComponent {
   };
 
   render () {
-    const { attachment, index, size, standalone, displayWidth, visible } = this.props;
+    const { attachment, lang, index, size, standalone, displayWidth, visible } = this.props;
 
     let width  = 50;
     let height = 100;
@@ -134,7 +135,7 @@ class Item extends React.PureComponent {
     if (attachment.get('type') === 'unknown') {
       return (
         <div className={classNames('media-gallery__item', { standalone })} key={attachment.get('id')} style={{ left: left, top: top, right: right, bottom: bottom, width: `${width}%`, height: `${height}%` }}>
-          <a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} style={{ cursor: 'pointer' }} title={attachment.get('description')} target='_blank' rel='noopener noreferrer'>
+          <a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} style={{ cursor: 'pointer' }} title={attachment.get('description')} lang={lang} target='_blank' rel='noopener noreferrer'>
             <Blurhash
               hash={attachment.get('blurhash')}
               className='media-gallery__preview'
@@ -174,6 +175,7 @@ class Item extends React.PureComponent {
             sizes={sizes}
             alt={attachment.get('description')}
             title={attachment.get('description')}
+            lang={lang}
             style={{ objectPosition: `${x}% ${y}%` }}
             onLoad={this.handleImageLoad}
           />
@@ -188,6 +190,7 @@ class Item extends React.PureComponent {
             className='media-gallery__item-gifv-thumbnail'
             aria-label={attachment.get('description')}
             title={attachment.get('description')}
+            lang={lang}
             role='application'
             src={attachment.get('url')}
             onClick={this.handleClick}
@@ -227,6 +230,7 @@ class MediaGallery extends React.PureComponent {
     sensitive: PropTypes.bool,
     standalone: PropTypes.bool,
     media: ImmutablePropTypes.list.isRequired,
+    lang: PropTypes.string,
     size: PropTypes.object,
     height: PropTypes.number.isRequired,
     onOpenMedia: PropTypes.func.isRequired,
@@ -310,9 +314,8 @@ class MediaGallery extends React.PureComponent {
   }
 
   render () {
-    const { media, intl, sensitive, height, defaultWidth, standalone, autoplay } = this.props;
+    const { media, lang, intl, sensitive, height, defaultWidth, standalone, autoplay } = this.props;
     const { visible } = this.state;
-
     const width = this.state.width || defaultWidth;
 
     let children, spoilerButton;
@@ -333,9 +336,9 @@ class MediaGallery extends React.PureComponent {
     const uncached = media.every(attachment => attachment.get('type') === 'unknown');
 
     if (standalone && this.isFullSizeEligible()) {
-      children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} displayWidth={width} visible={visible} />;
+      children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} lang={lang} displayWidth={width} visible={visible} />;
     } else {
-      children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} autoplay={autoplay} onClick={this.handleClick} attachment={attachment} index={i} size={size} displayWidth={width} visible={visible || uncached} />);
+      children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} autoplay={autoplay} onClick={this.handleClick} attachment={attachment} index={i} lang={lang} size={size} displayWidth={width} visible={visible || uncached} />);
     }
 
     if (uncached) {
diff --git a/app/javascript/mastodon/components/poll.jsx b/app/javascript/mastodon/components/poll.jsx
index 95a900c49..7efedfe34 100644
--- a/app/javascript/mastodon/components/poll.jsx
+++ b/app/javascript/mastodon/components/poll.jsx
@@ -40,6 +40,7 @@ class Poll extends ImmutablePureComponent {
 
   static propTypes = {
     poll: ImmutablePropTypes.map,
+    lang: PropTypes.string,
     intl: PropTypes.object.isRequired,
     disabled: PropTypes.bool,
     refresh: PropTypes.func,
@@ -126,7 +127,7 @@ class Poll extends ImmutablePureComponent {
   };
 
   renderOption (option, optionIndex, showResults) {
-    const { poll, disabled, intl } = this.props;
+    const { poll, lang, disabled, intl } = this.props;
     const pollVotesCount  = poll.get('voters_count') || poll.get('votes_count');
     const percent         = pollVotesCount === 0 ? 0 : (option.get('votes_count') / pollVotesCount) * 100;
     const leading         = poll.get('options').filterNot(other => other.get('title') === option.get('title')).every(other => option.get('votes_count') >= other.get('votes_count'));
@@ -159,6 +160,7 @@ class Poll extends ImmutablePureComponent {
               onKeyPress={this.handleOptionKeyPress}
               aria-checked={active}
               aria-label={option.get('title')}
+              lang={lang}
               data-index={optionIndex}
             />
           )}
@@ -175,6 +177,7 @@ class Poll extends ImmutablePureComponent {
 
           <span
             className='poll__option__text translate'
+            lang={lang}
             dangerouslySetInnerHTML={{ __html: titleEmojified }}
           />
 
diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status.jsx
index 2e2d96634..a48230baf 100644
--- a/app/javascript/mastodon/components/status.jsx
+++ b/app/javascript/mastodon/components/status.jsx
@@ -417,6 +417,7 @@ class Status extends ImmutablePureComponent {
               <Component
                 src={attachment.get('url')}
                 alt={attachment.get('description')}
+                lang={status.get('language')}
                 poster={attachment.get('preview_url') || status.getIn(['account', 'avatar_static'])}
                 backgroundColor={attachment.getIn(['meta', 'colors', 'background'])}
                 foregroundColor={attachment.getIn(['meta', 'colors', 'foreground'])}
@@ -446,6 +447,7 @@ class Status extends ImmutablePureComponent {
                 blurhash={attachment.get('blurhash')}
                 src={attachment.get('url')}
                 alt={attachment.get('description')}
+                lang={status.get('language')}
                 width={this.props.cachedMediaWidth}
                 height={110}
                 inline
@@ -465,6 +467,7 @@ class Status extends ImmutablePureComponent {
             {Component => (
               <Component
                 media={status.get('media_attachments')}
+                lang={status.get('language')}
                 sensitive={status.get('sensitive')}
                 height={110}
                 onOpenMedia={this.handleOpenMedia}
diff --git a/app/javascript/mastodon/components/status_content.jsx b/app/javascript/mastodon/components/status_content.jsx
index ece54621f..a1c38171f 100644
--- a/app/javascript/mastodon/components/status_content.jsx
+++ b/app/javascript/mastodon/components/status_content.jsx
@@ -242,7 +242,7 @@ class StatusContent extends React.PureComponent {
     );
 
     const poll = !!status.get('poll') && (
-      <PollContainer pollId={status.get('poll')} />
+      <PollContainer pollId={status.get('poll')} lang={status.get('language')} />
     );
 
     if (status.get('spoiler_text').length > 0) {
diff --git a/app/javascript/mastodon/features/account_gallery/components/media_item.jsx b/app/javascript/mastodon/features/account_gallery/components/media_item.jsx
index d6d60ebda..53b04acfd 100644
--- a/app/javascript/mastodon/features/account_gallery/components/media_item.jsx
+++ b/app/javascript/mastodon/features/account_gallery/components/media_item.jsx
@@ -76,6 +76,7 @@ export default class MediaItem extends ImmutablePureComponent {
           <img
             src={attachment.get('preview_url') || attachment.getIn(['account', 'avatar_static'])}
             alt={attachment.get('description')}
+            lang={status.get('language')}
             onLoad={this.handleImageLoad}
           />
         );
@@ -95,6 +96,7 @@ export default class MediaItem extends ImmutablePureComponent {
           <img
             src={attachment.get('preview_url')}
             alt={attachment.get('description')}
+            lang={status.get('language')}
             style={{ objectPosition: `${x}% ${y}%` }}
             onLoad={this.handleImageLoad}
           />
@@ -105,6 +107,7 @@ export default class MediaItem extends ImmutablePureComponent {
             className='media-gallery__item-gifv-thumbnail'
             aria-label={attachment.get('description')}
             title={attachment.get('description')}
+            lang={status.get('language')}
             role='application'
             src={attachment.get('url')}
             onMouseEnter={this.handleMouseEnter}
diff --git a/app/javascript/mastodon/features/audio/index.jsx b/app/javascript/mastodon/features/audio/index.jsx
index bf954c06d..9a9de02ae 100644
--- a/app/javascript/mastodon/features/audio/index.jsx
+++ b/app/javascript/mastodon/features/audio/index.jsx
@@ -28,6 +28,7 @@ class Audio extends React.PureComponent {
   static propTypes = {
     src: PropTypes.string.isRequired,
     alt: PropTypes.string,
+    lang: PropTypes.string,
     poster: PropTypes.string,
     duration: PropTypes.number,
     width: PropTypes.number,
@@ -458,7 +459,7 @@ class Audio extends React.PureComponent {
   };
 
   render () {
-    const { src, intl, alt, editable, autoPlay, sensitive, blurhash } = this.props;
+    const { src, intl, alt, lang, editable, autoPlay, sensitive, blurhash } = this.props;
     const { paused, muted, volume, currentTime, duration, buffer, dragging, revealed } = this.state;
     const progress = Math.min((currentTime / duration) * 100, 100);
 
@@ -503,6 +504,7 @@ class Audio extends React.PureComponent {
           onKeyDown={this.handleAudioKeyDown}
           title={alt}
           aria-label={alt}
+          lang={lang}
         />
 
         <div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed || editable })}>
diff --git a/app/javascript/mastodon/features/status/components/detailed_status.jsx b/app/javascript/mastodon/features/status/components/detailed_status.jsx
index 064231ffe..f9ff57261 100644
--- a/app/javascript/mastodon/features/status/components/detailed_status.jsx
+++ b/app/javascript/mastodon/features/status/components/detailed_status.jsx
@@ -139,6 +139,7 @@ class DetailedStatus extends ImmutablePureComponent {
           <Audio
             src={attachment.get('url')}
             alt={attachment.get('description')}
+            lang={status.get('language')}
             duration={attachment.getIn(['meta', 'original', 'duration'], 0)}
             poster={attachment.get('preview_url') || status.getIn(['account', 'avatar_static'])}
             backgroundColor={attachment.getIn(['meta', 'colors', 'background'])}
@@ -161,6 +162,7 @@ class DetailedStatus extends ImmutablePureComponent {
             blurhash={attachment.get('blurhash')}
             src={attachment.get('url')}
             alt={attachment.get('description')}
+            lang={status.get('language')}
             width={300}
             height={150}
             inline
@@ -176,6 +178,7 @@ class DetailedStatus extends ImmutablePureComponent {
             standalone
             sensitive={status.get('sensitive')}
             media={status.get('media_attachments')}
+            lang={status.get('language')}
             height={300}
             onOpenMedia={this.props.onOpenMedia}
             visible={this.props.showMedia}
diff --git a/app/javascript/mastodon/features/ui/components/audio_modal.jsx b/app/javascript/mastodon/features/ui/components/audio_modal.jsx
index c46fefce8..a2fa8c49c 100644
--- a/app/javascript/mastodon/features/ui/components/audio_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/audio_modal.jsx
@@ -7,15 +7,17 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
 import Footer from 'mastodon/features/picture_in_picture/components/footer';
 
 const mapStateToProps = (state, { statusId }) => ({
+  language: state.getIn(['statuses', statusId, 'language']),
   accountStaticAvatar: state.getIn(['accounts', state.getIn(['statuses', statusId, 'account']), 'avatar_static']),
 });
 
-export default @connect(mapStateToProps)
+export default @connect(mapStateToProps, null, null, { forwardRef: true })
 class AudioModal extends ImmutablePureComponent {
 
   static propTypes = {
     media: ImmutablePropTypes.map.isRequired,
     statusId: PropTypes.string.isRequired,
+    language: PropTypes.string,
     accountStaticAvatar: PropTypes.string.isRequired,
     options: PropTypes.shape({
       autoPlay: PropTypes.bool,
@@ -25,7 +27,7 @@ class AudioModal extends ImmutablePureComponent {
   };
 
   render () {
-    const { media, accountStaticAvatar, statusId, onClose } = this.props;
+    const { media, language, accountStaticAvatar, statusId, onClose } = this.props;
     const options = this.props.options || {};
 
     return (
@@ -34,6 +36,7 @@ class AudioModal extends ImmutablePureComponent {
           <Audio
             src={media.get('url')}
             alt={media.get('description')}
+            lang={language}
             duration={media.getIn(['meta', 'original', 'duration'], 0)}
             height={150}
             poster={media.get('preview_url') || accountStaticAvatar}
diff --git a/app/javascript/mastodon/features/ui/components/compare_history_modal.jsx b/app/javascript/mastodon/features/ui/components/compare_history_modal.jsx
index ecccc8f7d..cbb1567b7 100644
--- a/app/javascript/mastodon/features/ui/components/compare_history_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/compare_history_modal.jsx
@@ -12,6 +12,7 @@ import RelativeTimestamp from 'mastodon/components/relative_timestamp';
 import MediaAttachments from 'mastodon/components/media_attachments';
 
 const mapStateToProps = (state, { statusId }) => ({
+  language: state.getIn(['statuses', statusId, 'language']),
   versions: state.getIn(['history', statusId, 'items']),
 });
 
@@ -30,11 +31,12 @@ class CompareHistoryModal extends React.PureComponent {
     onClose: PropTypes.func.isRequired,
     index: PropTypes.number.isRequired,
     statusId: PropTypes.string.isRequired,
+    language: PropTypes.string.isRequired,
     versions: ImmutablePropTypes.list.isRequired,
   };
 
   render () {
-    const { index, versions, onClose } = this.props;
+    const { index, versions, language, onClose } = this.props;
     const currentVersion = versions.get(index);
 
     const emojiMap = currentVersion.get('emojis').reduce((obj, emoji) => {
@@ -65,12 +67,12 @@ class CompareHistoryModal extends React.PureComponent {
           <div className='status__content'>
             {currentVersion.get('spoiler_text').length > 0 && (
               <React.Fragment>
-                <div className='translate' dangerouslySetInnerHTML={spoilerContent} />
+                <div className='translate' dangerouslySetInnerHTML={spoilerContent} lang={language} />
                 <hr />
               </React.Fragment>
             )}
 
-            <div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} />
+            <div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} lang={language} />
 
             {!!currentVersion.get('poll') && (
               <div className='poll'>
@@ -82,6 +84,7 @@ class CompareHistoryModal extends React.PureComponent {
                       <span
                         className='poll__option__text translate'
                         dangerouslySetInnerHTML={{ __html: emojify(escapeTextContentForBrowser(option.get('title')), emojiMap) }}
+                        lang={language}
                       />
                     </li>
                   ))}
@@ -89,7 +92,7 @@ class CompareHistoryModal extends React.PureComponent {
               </div>
             )}
 
-            <MediaAttachments status={currentVersion} />
+            <MediaAttachments status={currentVersion} lang={language} />
           </div>
         </div>
       </div>
diff --git a/app/javascript/mastodon/features/ui/components/image_loader.jsx b/app/javascript/mastodon/features/ui/components/image_loader.jsx
index 92aeef5c4..9093eab28 100644
--- a/app/javascript/mastodon/features/ui/components/image_loader.jsx
+++ b/app/javascript/mastodon/features/ui/components/image_loader.jsx
@@ -8,6 +8,7 @@ export default class ImageLoader extends PureComponent {
 
   static propTypes = {
     alt: PropTypes.string,
+    lang: PropTypes.string,
     src: PropTypes.string.isRequired,
     previewSrc: PropTypes.string,
     width: PropTypes.number,
@@ -18,6 +19,7 @@ export default class ImageLoader extends PureComponent {
 
   static defaultProps = {
     alt: '',
+    lang: '',
     width: null,
     height: null,
   };
@@ -129,7 +131,7 @@ export default class ImageLoader extends PureComponent {
   };
 
   render () {
-    const { alt, src, width, height, onClick } = this.props;
+    const { alt, lang, src, width, height, onClick } = this.props;
     const { loading } = this.state;
 
     const className = classNames('image-loader', {
@@ -154,6 +156,7 @@ export default class ImageLoader extends PureComponent {
         ) : (
           <ZoomableImage
             alt={alt}
+            lang={lang}
             src={src}
             onClick={onClick}
             width={width}
diff --git a/app/javascript/mastodon/features/ui/components/media_modal.jsx b/app/javascript/mastodon/features/ui/components/media_modal.jsx
index 1cda8de04..086da8cf5 100644
--- a/app/javascript/mastodon/features/ui/components/media_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/media_modal.jsx
@@ -3,6 +3,7 @@ import ReactSwipeableViews from 'react-swipeable-views';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Video from 'mastodon/features/video';
+import { connect } from 'react-redux';
 import classNames from 'classnames';
 import { defineMessages, injectIntl } from 'react-intl';
 import IconButton from 'mastodon/components/icon_button';
@@ -20,7 +21,12 @@ const messages = defineMessages({
   next: { id: 'lightbox.next', defaultMessage: 'Next' },
 });
 
-export default @injectIntl
+const mapStateToProps = (state, { statusId }) => ({
+  language: state.getIn(['statuses', statusId, 'language']),
+});
+
+export default @connect(mapStateToProps, null, null, { forwardRef: true })
+@injectIntl
 class MediaModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -129,7 +135,7 @@ class MediaModal extends ImmutablePureComponent {
   };
 
   render () {
-    const { media, statusId, intl, onClose } = this.props;
+    const { media, language, statusId, intl, onClose } = this.props;
     const { navigationHidden } = this.state;
 
     const index = this.getIndex();
@@ -149,6 +155,7 @@ class MediaModal extends ImmutablePureComponent {
             width={width}
             height={height}
             alt={image.get('description')}
+            lang={language}
             key={image.get('url')}
             onClick={this.toggleNavigation}
             zoomButtonHidden={this.state.zoomButtonHidden}
@@ -171,6 +178,7 @@ class MediaModal extends ImmutablePureComponent {
             onCloseVideo={onClose}
             detailed
             alt={image.get('description')}
+            lang={language}
             key={image.get('url')}
           />
         );
@@ -182,6 +190,7 @@ class MediaModal extends ImmutablePureComponent {
             height={height}
             key={image.get('preview_url')}
             alt={image.get('description')}
+            lang={language}
             onClick={this.toggleNavigation}
           />
         );
diff --git a/app/javascript/mastodon/features/ui/components/video_modal.jsx b/app/javascript/mastodon/features/ui/components/video_modal.jsx
index abaccbe98..1737d52db 100644
--- a/app/javascript/mastodon/features/ui/components/video_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/video_modal.jsx
@@ -2,15 +2,22 @@ import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Video from 'mastodon/features/video';
+import { connect } from 'react-redux';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import Footer from 'mastodon/features/picture_in_picture/components/footer';
 import { getAverageFromBlurhash } from 'mastodon/blurhash';
 
-export default class VideoModal extends ImmutablePureComponent {
+const mapStateToProps = (state, { statusId }) => ({
+  language: state.getIn(['statuses', statusId, 'language']),
+});
+
+export default @connect(mapStateToProps, null, null, { forwardRef: true })
+class VideoModal extends ImmutablePureComponent {
 
   static propTypes = {
     media: ImmutablePropTypes.map.isRequired,
     statusId: PropTypes.string,
+    language: PropTypes.string,
     options: PropTypes.shape({
       startTime: PropTypes.number,
       autoPlay: PropTypes.bool,
@@ -31,7 +38,7 @@ export default class VideoModal extends ImmutablePureComponent {
   }
 
   render () {
-    const { media, statusId, onClose } = this.props;
+    const { media, statusId, language, onClose } = this.props;
     const options = this.props.options || {};
 
     return (
@@ -49,6 +56,7 @@ export default class VideoModal extends ImmutablePureComponent {
             autoFocus
             detailed
             alt={media.get('description')}
+            lang={language}
           />
         </div>
 
diff --git a/app/javascript/mastodon/features/ui/components/zoomable_image.jsx b/app/javascript/mastodon/features/ui/components/zoomable_image.jsx
index 3b2bb0286..7faba4c10 100644
--- a/app/javascript/mastodon/features/ui/components/zoomable_image.jsx
+++ b/app/javascript/mastodon/features/ui/components/zoomable_image.jsx
@@ -96,6 +96,7 @@ class ZoomableImage extends React.PureComponent {
 
   static propTypes = {
     alt: PropTypes.string,
+    lang: PropTypes.string,
     src: PropTypes.string.isRequired,
     width: PropTypes.number,
     height: PropTypes.number,
@@ -106,6 +107,7 @@ class ZoomableImage extends React.PureComponent {
 
   static defaultProps = {
     alt: '',
+    lang: '',
     width: null,
     height: null,
   };
@@ -403,7 +405,7 @@ class ZoomableImage extends React.PureComponent {
   };
 
   render () {
-    const { alt, src, width, height, intl } = this.props;
+    const { alt, lang, src, width, height, intl } = this.props;
     const { scale, lockTranslate } = this.state;
     const overflow = scale === MIN_SCALE ? 'hidden' : 'scroll';
     const zoomButtonShouldHide = this.state.navigationHidden || this.props.zoomButtonHidden || this.state.zoomMatrix.rate <= MIN_SCALE ? 'media-modal__zoom-button--hidden' : '';
@@ -431,6 +433,7 @@ class ZoomableImage extends React.PureComponent {
             ref={this.setImageRef}
             alt={alt}
             title={alt}
+            lang={lang}
             src={src}
             width={width}
             height={height}
diff --git a/app/javascript/mastodon/features/video/index.jsx b/app/javascript/mastodon/features/video/index.jsx
index 8d63394aa..dc722a7a7 100644
--- a/app/javascript/mastodon/features/video/index.jsx
+++ b/app/javascript/mastodon/features/video/index.jsx
@@ -102,6 +102,7 @@ class Video extends React.PureComponent {
     frameRate: PropTypes.string,
     src: PropTypes.string.isRequired,
     alt: PropTypes.string,
+    lang: PropTypes.string,
     width: PropTypes.number,
     height: PropTypes.number,
     sensitive: PropTypes.bool,
@@ -524,7 +525,7 @@ class Video extends React.PureComponent {
   }
 
   render () {
-    const { preview, src, inline, onOpenVideo, onCloseVideo, intl, alt, detailed, sensitive, editable, blurhash, autoFocus } = this.props;
+    const { preview, src, inline, onOpenVideo, onCloseVideo, intl, alt, lang, detailed, sensitive, editable, blurhash, autoFocus } = this.props;
     const { containerWidth, currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
     const progress = Math.min((currentTime / duration) * 100, 100);
     const playerStyle = {};
@@ -585,6 +586,7 @@ class Video extends React.PureComponent {
           tabIndex='0'
           aria-label={alt}
           title={alt}
+          lang={lang}
           width={width}
           height={height}
           volume={volume}
-- 
cgit 


From d1b057a0ac41a5756b6a416d2f290b086cb59220 Mon Sep 17 00:00:00 2001
From: Yamagishi Kazutoshi <ykzts@desire.sh>
Date: Fri, 24 Mar 2023 11:17:53 +0900
Subject: Remove legacy decorators syntax (#18357)

---
 .eslintrc.js                                       |  24 +++-
 app/javascript/mastodon/components/account.jsx     |   3 +-
 .../components/admin/ReportReasonSelector.jsx      |   3 +-
 .../mastodon/components/column_header.jsx          |   3 +-
 .../mastodon/components/dismissable_banner.jsx     |   3 +-
 app/javascript/mastodon/components/domain.jsx      |   3 +-
 .../mastodon/components/edited_timestamp/index.jsx |   4 +-
 .../mastodon/components/inline_account.jsx         |   3 +-
 app/javascript/mastodon/components/load_gap.jsx    |   3 +-
 .../mastodon/components/media_gallery.jsx          |   3 +-
 .../mastodon/components/navigation_portal.jsx      |   2 +-
 .../components/picture_in_picture_placeholder.jsx  |   3 +-
 app/javascript/mastodon/components/poll.jsx        |   3 +-
 .../mastodon/components/relative_timestamp.jsx     |   3 +-
 .../mastodon/components/scrollable_list.jsx        |   3 +-
 .../mastodon/components/server_banner.jsx          |   4 +-
 app/javascript/mastodon/components/status.jsx      |   3 +-
 .../mastodon/components/status_action_bar.jsx      |   4 +-
 .../mastodon/components/status_content.jsx         |   4 +-
 app/javascript/mastodon/features/about/index.jsx   |   4 +-
 .../features/account/components/account_note.jsx   |   3 +-
 .../features/account/components/featured_tags.jsx  |   3 +-
 .../features/account/components/header.jsx         |   3 +-
 .../mastodon/features/account/navigation.jsx       |   3 +-
 .../mastodon/features/account_gallery/index.jsx    |   3 +-
 .../components/limited_account_hint.jsx            |   3 +-
 .../mastodon/features/account_timeline/index.jsx   |   3 +-
 app/javascript/mastodon/features/audio/index.jsx   |   3 +-
 app/javascript/mastodon/features/blocks/index.jsx  |   4 +-
 .../features/bookmarked_statuses/index.jsx         |   4 +-
 .../features/closed_registrations_modal/index.jsx  |   3 +-
 .../components/column_settings.jsx                 |   3 +-
 .../mastodon/features/community_timeline/index.jsx |   4 +-
 .../features/compose/components/action_bar.jsx     |   3 +-
 .../features/compose/components/compose_form.jsx   |   3 +-
 .../compose/components/emoji_picker_dropdown.jsx   |   8 +-
 .../compose/components/language_dropdown.jsx       |   3 +-
 .../features/compose/components/poll_button.jsx    |   4 +-
 .../features/compose/components/poll_form.jsx      |   9 +-
 .../compose/components/privacy_dropdown.jsx        |   3 +-
 .../compose/components/reply_indicator.jsx         |   3 +-
 .../features/compose/components/search.jsx         |   3 +-
 .../features/compose/components/search_results.jsx |   3 +-
 .../features/compose/components/upload_button.jsx  |   4 +-
 app/javascript/mastodon/features/compose/index.jsx |   4 +-
 .../direct_timeline/components/conversation.jsx    |   3 +-
 .../mastodon/features/direct_timeline/index.jsx    |   4 +-
 .../features/directory/components/account_card.jsx |   5 +-
 .../mastodon/features/directory/index.jsx          |   4 +-
 .../mastodon/features/domain_blocks/index.jsx      |   4 +-
 app/javascript/mastodon/features/explore/index.jsx |   4 +-
 app/javascript/mastodon/features/explore/links.jsx |   3 +-
 .../mastodon/features/explore/results.jsx          |   4 +-
 .../mastodon/features/explore/statuses.jsx         |   3 +-
 .../mastodon/features/explore/suggestions.jsx      |   3 +-
 app/javascript/mastodon/features/explore/tags.jsx  |   3 +-
 .../features/favourited_statuses/index.jsx         |   4 +-
 .../mastodon/features/favourites/index.jsx         |   4 +-
 .../mastodon/features/filters/added_to_filter.jsx  |   3 +-
 .../mastodon/features/filters/select_filter.jsx    |   4 +-
 .../follow_recommendations/components/account.jsx  |   4 +-
 .../features/follow_recommendations/index.jsx      |   3 +-
 .../components/account_authorize.jsx               |   3 +-
 .../mastodon/features/follow_requests/index.jsx    |   4 +-
 .../mastodon/features/followed_tags/index.jsx      |   4 +-
 .../mastodon/features/followers/index.jsx          |   3 +-
 .../mastodon/features/following/index.jsx          |   3 +-
 .../getting_started/components/announcements.jsx   |   3 +-
 .../mastodon/features/getting_started/index.jsx    |   4 +-
 .../components/column_settings.jsx                 |   3 +-
 .../mastodon/features/hashtag_timeline/index.jsx   |   4 +-
 .../home_timeline/components/column_settings.jsx   |   3 +-
 .../mastodon/features/home_timeline/index.jsx      |   4 +-
 .../mastodon/features/interaction_modal/index.jsx  |   3 +-
 .../mastodon/features/keyboard_shortcuts/index.jsx |   3 +-
 .../features/list_adder/components/account.jsx     |   5 +-
 .../features/list_adder/components/list.jsx        |   4 +-
 .../mastodon/features/list_adder/index.jsx         |   4 +-
 .../features/list_editor/components/account.jsx    |   4 +-
 .../list_editor/components/edit_list_form.jsx      |   4 +-
 .../features/list_editor/components/search.jsx     |   4 +-
 .../mastodon/features/list_editor/index.jsx        |   4 +-
 .../mastodon/features/list_timeline/index.jsx      |   4 +-
 .../features/lists/components/new_list_form.jsx    |   4 +-
 app/javascript/mastodon/features/lists/index.jsx   |   4 +-
 app/javascript/mastodon/features/mutes/index.jsx   |   4 +-
 .../notifications/components/filter_bar.jsx        |   3 +-
 .../notifications/components/follow_request.jsx    |   9 +-
 .../notifications/components/notification.jsx      |   3 +-
 .../components/notifications_permission_banner.jsx |   4 +-
 .../features/notifications/components/report.jsx   |   3 +-
 .../mastodon/features/notifications/index.jsx      |   4 +-
 .../picture_in_picture/components/footer.jsx       |   4 +-
 .../picture_in_picture/components/header.jsx       |   4 +-
 .../mastodon/features/picture_in_picture/index.jsx |   3 +-
 .../mastodon/features/pinned_statuses/index.jsx    |   4 +-
 .../mastodon/features/privacy_policy/index.jsx     |   3 +-
 .../public_timeline/components/column_settings.jsx |   3 +-
 .../mastodon/features/public_timeline/index.jsx    |   4 +-
 app/javascript/mastodon/features/reblogs/index.jsx |   4 +-
 .../mastodon/features/report/category.jsx          |   4 +-
 .../mastodon/features/report/comment.jsx           |   3 +-
 .../report/components/status_check_box.jsx         |   3 +-
 app/javascript/mastodon/features/report/rules.jsx  |   3 +-
 .../mastodon/features/report/statuses.jsx          |   3 +-
 app/javascript/mastodon/features/report/thanks.jsx |   3 +-
 .../features/status/components/action_bar.jsx      |   4 +-
 .../features/status/components/detailed_status.jsx |   3 +-
 app/javascript/mastodon/features/status/index.jsx  |   4 +-
 .../features/subscribed_languages_modal/index.jsx  |   4 +-
 .../features/ui/components/audio_modal.jsx         |   3 +-
 .../features/ui/components/block_modal.jsx         |   4 +-
 .../features/ui/components/boost_modal.jsx         |   4 +-
 .../features/ui/components/bundle_column_error.jsx |   3 +-
 .../ui/components/compare_history_modal.jsx        |   3 +-
 .../features/ui/components/compose_panel.jsx       |   3 +-
 .../features/ui/components/confirmation_modal.jsx  |   3 +-
 .../ui/components/disabled_account_banner.jsx      |   4 +-
 .../features/ui/components/embed_modal.jsx         |   3 +-
 .../features/ui/components/filter_modal.jsx        |   4 +-
 .../features/ui/components/focal_point_modal.jsx   |   6 +-
 .../ui/components/follow_requests_column_link.jsx  |   4 +-
 .../mastodon/features/ui/components/header.jsx     |   4 +-
 .../features/ui/components/image_modal.jsx         |   3 +-
 .../features/ui/components/link_footer.jsx         |   4 +-
 .../mastodon/features/ui/components/list_panel.jsx |   4 +-
 .../features/ui/components/media_modal.jsx         |   4 +-
 .../mastodon/features/ui/components/mute_modal.jsx |   4 +-
 .../features/ui/components/navigation_panel.jsx    |   3 +-
 .../features/ui/components/report_modal.jsx        |   4 +-
 .../features/ui/components/video_modal.jsx         |   3 +-
 .../features/ui/components/zoomable_image.jsx      |   3 +-
 app/javascript/mastodon/features/ui/index.jsx      |   5 +-
 app/javascript/mastodon/features/video/index.jsx   |   3 +-
 babel.config.js                                    |   1 -
 package.json                                       |   1 -
 yarn.lock                                          | 125 ++++++++++++++++-----
 137 files changed, 396 insertions(+), 236 deletions(-)

(limited to 'app/javascript/mastodon/features/status/components/detailed_status.jsx')

diff --git a/.eslintrc.js b/.eslintrc.js
index 606a87e41..faed3c54a 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -32,10 +32,14 @@ module.exports = {
   parserOptions: {
     sourceType: 'module',
     ecmaFeatures: {
-      experimentalObjectRestSpread: true,
       jsx: true,
     },
     ecmaVersion: 2021,
+    requireConfigFile: false,
+    babelOptions: {
+      configFile: false,
+      presets: ['@babel/react', '@babel/env'],
+    },
   },
 
   settings: {
@@ -214,4 +218,22 @@ module.exports = {
     'promise/no-nesting': 'off',
     'promise/no-promise-in-callback': 'off',
   },
+
+  overrides: [
+    {
+      files: [
+        '*.config.js',
+        '.*rc.js',
+        'ide-helper.js',
+      ],
+
+      env: {
+        commonjs: true,
+      },
+
+      parserOptions: {
+        sourceType: 'script',
+      },
+    },
+  ],
 };
diff --git a/app/javascript/mastodon/components/account.jsx b/app/javascript/mastodon/components/account.jsx
index 7706c3f88..7aaa668fe 100644
--- a/app/javascript/mastodon/components/account.jsx
+++ b/app/javascript/mastodon/components/account.jsx
@@ -23,7 +23,6 @@ const messages = defineMessages({
   block: { id: 'account.block', defaultMessage: 'Block @{name}' },
 });
 
-export default @injectIntl
 class Account extends ImmutablePureComponent {
 
   static propTypes = {
@@ -155,3 +154,5 @@ class Account extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Account);
diff --git a/app/javascript/mastodon/components/admin/ReportReasonSelector.jsx b/app/javascript/mastodon/components/admin/ReportReasonSelector.jsx
index 1f91d2517..58a861fde 100644
--- a/app/javascript/mastodon/components/admin/ReportReasonSelector.jsx
+++ b/app/javascript/mastodon/components/admin/ReportReasonSelector.jsx
@@ -84,7 +84,6 @@ class Rule extends React.PureComponent {
 
 }
 
-export default @injectIntl
 class ReportReasonSelector extends React.PureComponent {
 
   static propTypes = {
@@ -157,3 +156,5 @@ class ReportReasonSelector extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ReportReasonSelector);
diff --git a/app/javascript/mastodon/components/column_header.jsx b/app/javascript/mastodon/components/column_header.jsx
index 9ba783d90..afc526f27 100644
--- a/app/javascript/mastodon/components/column_header.jsx
+++ b/app/javascript/mastodon/components/column_header.jsx
@@ -12,7 +12,6 @@ const messages = defineMessages({
   moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' },
 });
 
-export default @injectIntl
 class ColumnHeader extends React.PureComponent {
 
   static contextTypes = {
@@ -209,3 +208,5 @@ class ColumnHeader extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ColumnHeader);
diff --git a/app/javascript/mastodon/components/dismissable_banner.jsx b/app/javascript/mastodon/components/dismissable_banner.jsx
index 47ca7e4bc..242021e76 100644
--- a/app/javascript/mastodon/components/dismissable_banner.jsx
+++ b/app/javascript/mastodon/components/dismissable_banner.jsx
@@ -8,7 +8,6 @@ const messages = defineMessages({
   dismiss: { id: 'dismissable_banner.dismiss', defaultMessage: 'Dismiss' },
 });
 
-export default @injectIntl
 class DismissableBanner extends React.PureComponent {
 
   static propTypes = {
@@ -49,3 +48,5 @@ class DismissableBanner extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(DismissableBanner);
diff --git a/app/javascript/mastodon/components/domain.jsx b/app/javascript/mastodon/components/domain.jsx
index e09fa4591..85ebdbde9 100644
--- a/app/javascript/mastodon/components/domain.jsx
+++ b/app/javascript/mastodon/components/domain.jsx
@@ -8,7 +8,6 @@ const messages = defineMessages({
   unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
 });
 
-export default @injectIntl
 class Account extends ImmutablePureComponent {
 
   static propTypes = {
@@ -40,3 +39,5 @@ class Account extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Account);
diff --git a/app/javascript/mastodon/components/edited_timestamp/index.jsx b/app/javascript/mastodon/components/edited_timestamp/index.jsx
index b30d88572..1513f9361 100644
--- a/app/javascript/mastodon/components/edited_timestamp/index.jsx
+++ b/app/javascript/mastodon/components/edited_timestamp/index.jsx
@@ -16,8 +16,6 @@ const mapDispatchToProps = (dispatch, { statusId }) => ({
 
 });
 
-export default @connect(null, mapDispatchToProps)
-@injectIntl
 class EditedTimestamp extends React.PureComponent {
 
   static propTypes = {
@@ -68,3 +66,5 @@ class EditedTimestamp extends React.PureComponent {
   }
 
 }
+
+export default connect(null, mapDispatchToProps)(injectIntl(EditedTimestamp));
diff --git a/app/javascript/mastodon/components/inline_account.jsx b/app/javascript/mastodon/components/inline_account.jsx
index a1b495590..31dc63f93 100644
--- a/app/javascript/mastodon/components/inline_account.jsx
+++ b/app/javascript/mastodon/components/inline_account.jsx
@@ -14,7 +14,6 @@ const makeMapStateToProps = () => {
   return mapStateToProps;
 };
 
-export default @connect(makeMapStateToProps)
 class InlineAccount extends React.PureComponent {
 
   static propTypes = {
@@ -32,3 +31,5 @@ class InlineAccount extends React.PureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps)(InlineAccount);
diff --git a/app/javascript/mastodon/components/load_gap.jsx b/app/javascript/mastodon/components/load_gap.jsx
index c50b245fc..2c91d37be 100644
--- a/app/javascript/mastodon/components/load_gap.jsx
+++ b/app/javascript/mastodon/components/load_gap.jsx
@@ -7,7 +7,6 @@ const messages = defineMessages({
   load_more: { id: 'status.load_more', defaultMessage: 'Load more' },
 });
 
-export default @injectIntl
 class LoadGap extends React.PureComponent {
 
   static propTypes = {
@@ -32,3 +31,5 @@ class LoadGap extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(LoadGap);
diff --git a/app/javascript/mastodon/components/media_gallery.jsx b/app/javascript/mastodon/components/media_gallery.jsx
index f869bd62d..5be0070a3 100644
--- a/app/javascript/mastodon/components/media_gallery.jsx
+++ b/app/javascript/mastodon/components/media_gallery.jsx
@@ -223,7 +223,6 @@ class Item extends React.PureComponent {
 
 }
 
-export default @injectIntl
 class MediaGallery extends React.PureComponent {
 
   static propTypes = {
@@ -369,3 +368,5 @@ class MediaGallery extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(MediaGallery);
diff --git a/app/javascript/mastodon/components/navigation_portal.jsx b/app/javascript/mastodon/components/navigation_portal.jsx
index 45407be43..a100dc04a 100644
--- a/app/javascript/mastodon/components/navigation_portal.jsx
+++ b/app/javascript/mastodon/components/navigation_portal.jsx
@@ -15,7 +15,6 @@ const DefaultNavigation = () => (
   </>
 );
 
-export default @withRouter
 class NavigationPortal extends React.PureComponent {
 
   render () {
@@ -33,3 +32,4 @@ class NavigationPortal extends React.PureComponent {
   }
 
 }
+export default withRouter(NavigationPortal);
diff --git a/app/javascript/mastodon/components/picture_in_picture_placeholder.jsx b/app/javascript/mastodon/components/picture_in_picture_placeholder.jsx
index 0effddef9..c8aa8f757 100644
--- a/app/javascript/mastodon/components/picture_in_picture_placeholder.jsx
+++ b/app/javascript/mastodon/components/picture_in_picture_placeholder.jsx
@@ -6,7 +6,6 @@ import { connect } from 'react-redux';
 import { debounce } from 'lodash';
 import { FormattedMessage } from 'react-intl';
 
-export default @connect()
 class PictureInPicturePlaceholder extends React.PureComponent {
 
   static propTypes = {
@@ -67,3 +66,5 @@ class PictureInPicturePlaceholder extends React.PureComponent {
   }
 
 }
+
+export default connect()(PictureInPicturePlaceholder);
diff --git a/app/javascript/mastodon/components/poll.jsx b/app/javascript/mastodon/components/poll.jsx
index 7efedfe34..360557d00 100644
--- a/app/javascript/mastodon/components/poll.jsx
+++ b/app/javascript/mastodon/components/poll.jsx
@@ -31,7 +31,6 @@ const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => {
   return obj;
 }, {});
 
-export default @injectIntl
 class Poll extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -234,3 +233,5 @@ class Poll extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Poll);
diff --git a/app/javascript/mastodon/components/relative_timestamp.jsx b/app/javascript/mastodon/components/relative_timestamp.jsx
index 512480339..e6c3e0880 100644
--- a/app/javascript/mastodon/components/relative_timestamp.jsx
+++ b/app/javascript/mastodon/components/relative_timestamp.jsx
@@ -121,7 +121,6 @@ const timeRemainingString = (intl, date, now, timeGiven = true) => {
   return relativeTime;
 };
 
-export default @injectIntl
 class RelativeTimestamp extends React.Component {
 
   static propTypes = {
@@ -197,3 +196,5 @@ class RelativeTimestamp extends React.Component {
   }
 
 }
+
+export default injectIntl(RelativeTimestamp);
diff --git a/app/javascript/mastodon/components/scrollable_list.jsx b/app/javascript/mastodon/components/scrollable_list.jsx
index 4a6ffb149..57bc88121 100644
--- a/app/javascript/mastodon/components/scrollable_list.jsx
+++ b/app/javascript/mastodon/components/scrollable_list.jsx
@@ -20,7 +20,6 @@ const mapStateToProps = (state, { scrollKey }) => {
   };
 };
 
-export default @connect(mapStateToProps, null, null, { forwardRef: true })
 class ScrollableList extends PureComponent {
 
   static contextTypes = {
@@ -365,3 +364,5 @@ class ScrollableList extends PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, null, null, { forwardRef: true })(ScrollableList);
diff --git a/app/javascript/mastodon/components/server_banner.jsx b/app/javascript/mastodon/components/server_banner.jsx
index 617fdecdf..c21e414b4 100644
--- a/app/javascript/mastodon/components/server_banner.jsx
+++ b/app/javascript/mastodon/components/server_banner.jsx
@@ -18,8 +18,6 @@ const mapStateToProps = state => ({
   server: state.getIn(['server', 'server']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class ServerBanner extends React.PureComponent {
 
   static propTypes = {
@@ -91,3 +89,5 @@ class ServerBanner extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(ServerBanner));
diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status.jsx
index a48230baf..2d200a55b 100644
--- a/app/javascript/mastodon/components/status.jsx
+++ b/app/javascript/mastodon/components/status.jsx
@@ -59,7 +59,6 @@ const messages = defineMessages({
   edited: { id: 'status.edited', defaultMessage: 'Edited {date}' },
 });
 
-export default @injectIntl
 class Status extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -549,3 +548,5 @@ class Status extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Status);
diff --git a/app/javascript/mastodon/components/status_action_bar.jsx b/app/javascript/mastodon/components/status_action_bar.jsx
index eeb376561..08e37e089 100644
--- a/app/javascript/mastodon/components/status_action_bar.jsx
+++ b/app/javascript/mastodon/components/status_action_bar.jsx
@@ -53,8 +53,6 @@ const mapStateToProps = (state, { status }) => ({
   relationship: state.getIn(['relationships', status.getIn(['account', 'id'])]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class StatusActionBar extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -385,3 +383,5 @@ class StatusActionBar extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(StatusActionBar));
diff --git a/app/javascript/mastodon/components/status_content.jsx b/app/javascript/mastodon/components/status_content.jsx
index 67a487b00..8d3a80248 100644
--- a/app/javascript/mastodon/components/status_content.jsx
+++ b/app/javascript/mastodon/components/status_content.jsx
@@ -52,8 +52,6 @@ const mapStateToProps = state => ({
   languages: state.getIn(['server', 'translationLanguages', 'items']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class StatusContent extends React.PureComponent {
 
   static contextTypes = {
@@ -311,3 +309,5 @@ class StatusContent extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(StatusContent));
diff --git a/app/javascript/mastodon/features/about/index.jsx b/app/javascript/mastodon/features/about/index.jsx
index dc1942c63..aa3d5b7f8 100644
--- a/app/javascript/mastodon/features/about/index.jsx
+++ b/app/javascript/mastodon/features/about/index.jsx
@@ -80,8 +80,6 @@ class Section extends React.PureComponent {
 
 }
 
-export default @connect(mapStateToProps)
-@injectIntl
 class About extends React.PureComponent {
 
   static propTypes = {
@@ -217,3 +215,5 @@ class About extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(About));
diff --git a/app/javascript/mastodon/features/account/components/account_note.jsx b/app/javascript/mastodon/features/account/components/account_note.jsx
index fdacc7583..5201ebd4d 100644
--- a/app/javascript/mastodon/features/account/components/account_note.jsx
+++ b/app/javascript/mastodon/features/account/components/account_note.jsx
@@ -43,7 +43,6 @@ class InlineAlert extends React.PureComponent {
 
 }
 
-export default @injectIntl
 class AccountNote extends ImmutablePureComponent {
 
   static propTypes = {
@@ -168,3 +167,5 @@ class AccountNote extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(AccountNote);
diff --git a/app/javascript/mastodon/features/account/components/featured_tags.jsx b/app/javascript/mastodon/features/account/components/featured_tags.jsx
index 24a3f2171..52aa232ca 100644
--- a/app/javascript/mastodon/features/account/components/featured_tags.jsx
+++ b/app/javascript/mastodon/features/account/components/featured_tags.jsx
@@ -10,7 +10,6 @@ const messages = defineMessages({
   empty: { id: 'account.featured_tags.last_status_never', defaultMessage: 'No posts' },
 });
 
-export default @injectIntl
 class FeaturedTags extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -50,3 +49,5 @@ class FeaturedTags extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(FeaturedTags);
diff --git a/app/javascript/mastodon/features/account/components/header.jsx b/app/javascript/mastodon/features/account/components/header.jsx
index 539d72574..be6b17896 100644
--- a/app/javascript/mastodon/features/account/components/header.jsx
+++ b/app/javascript/mastodon/features/account/components/header.jsx
@@ -76,7 +76,6 @@ const dateFormatOptions = {
   minute: '2-digit',
 };
 
-export default @injectIntl
 class Header extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -419,3 +418,5 @@ class Header extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Header);
diff --git a/app/javascript/mastodon/features/account/navigation.jsx b/app/javascript/mastodon/features/account/navigation.jsx
index eb9ff9a95..07dc3757c 100644
--- a/app/javascript/mastodon/features/account/navigation.jsx
+++ b/app/javascript/mastodon/features/account/navigation.jsx
@@ -19,7 +19,6 @@ const mapStateToProps = (state, { match: { params: { acct } } }) => {
   };
 };
 
-export default @connect(mapStateToProps)
 class AccountNavigation extends React.PureComponent {
 
   static propTypes = {
@@ -50,3 +49,5 @@ class AccountNavigation extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(AccountNavigation);
diff --git a/app/javascript/mastodon/features/account_gallery/index.jsx b/app/javascript/mastodon/features/account_gallery/index.jsx
index 3942b57cb..db76507db 100644
--- a/app/javascript/mastodon/features/account_gallery/index.jsx
+++ b/app/javascript/mastodon/features/account_gallery/index.jsx
@@ -60,7 +60,6 @@ class LoadMoreMedia extends ImmutablePureComponent {
 
 }
 
-export default @connect(mapStateToProps)
 class AccountGallery extends ImmutablePureComponent {
 
   static propTypes = {
@@ -226,3 +225,5 @@ class AccountGallery extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(AccountGallery);
diff --git a/app/javascript/mastodon/features/account_timeline/components/limited_account_hint.jsx b/app/javascript/mastodon/features/account_timeline/components/limited_account_hint.jsx
index 9ee347bb5..e6a111470 100644
--- a/app/javascript/mastodon/features/account_timeline/components/limited_account_hint.jsx
+++ b/app/javascript/mastodon/features/account_timeline/components/limited_account_hint.jsx
@@ -14,7 +14,6 @@ const mapDispatchToProps = (dispatch, { accountId }) => ({
 
 });
 
-export default @connect(() => {}, mapDispatchToProps)
 class LimitedAccountHint extends React.PureComponent {
 
   static propTypes = {
@@ -34,3 +33,5 @@ class LimitedAccountHint extends React.PureComponent {
   }
 
 }
+
+export default connect(() => {}, mapDispatchToProps)(LimitedAccountHint);
diff --git a/app/javascript/mastodon/features/account_timeline/index.jsx b/app/javascript/mastodon/features/account_timeline/index.jsx
index 337977fde..d77563684 100644
--- a/app/javascript/mastodon/features/account_timeline/index.jsx
+++ b/app/javascript/mastodon/features/account_timeline/index.jsx
@@ -64,7 +64,6 @@ RemoteHint.propTypes = {
   url: PropTypes.string.isRequired,
 };
 
-export default @connect(mapStateToProps)
 class AccountTimeline extends ImmutablePureComponent {
 
   static propTypes = {
@@ -206,3 +205,5 @@ class AccountTimeline extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(AccountTimeline);
diff --git a/app/javascript/mastodon/features/audio/index.jsx b/app/javascript/mastodon/features/audio/index.jsx
index 9a9de02ae..b0c6eeee6 100644
--- a/app/javascript/mastodon/features/audio/index.jsx
+++ b/app/javascript/mastodon/features/audio/index.jsx
@@ -22,7 +22,6 @@ const messages = defineMessages({
 const TICK_SIZE = 10;
 const PADDING   = 180;
 
-export default @injectIntl
 class Audio extends React.PureComponent {
 
   static propTypes = {
@@ -569,3 +568,5 @@ class Audio extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(Audio);
diff --git a/app/javascript/mastodon/features/blocks/index.jsx b/app/javascript/mastodon/features/blocks/index.jsx
index e00f2b60e..38616ba84 100644
--- a/app/javascript/mastodon/features/blocks/index.jsx
+++ b/app/javascript/mastodon/features/blocks/index.jsx
@@ -22,8 +22,6 @@ const mapStateToProps = state => ({
   isLoading: state.getIn(['user_lists', 'blocks', 'isLoading'], true),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Blocks extends ImmutablePureComponent {
 
   static propTypes = {
@@ -77,3 +75,5 @@ class Blocks extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Blocks));
diff --git a/app/javascript/mastodon/features/bookmarked_statuses/index.jsx b/app/javascript/mastodon/features/bookmarked_statuses/index.jsx
index 8ef7855c1..46fff856e 100644
--- a/app/javascript/mastodon/features/bookmarked_statuses/index.jsx
+++ b/app/javascript/mastodon/features/bookmarked_statuses/index.jsx
@@ -22,8 +22,6 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['status_lists', 'bookmarks', 'next']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Bookmarks extends ImmutablePureComponent {
 
   static propTypes = {
@@ -106,3 +104,5 @@ class Bookmarks extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Bookmarks));
diff --git a/app/javascript/mastodon/features/closed_registrations_modal/index.jsx b/app/javascript/mastodon/features/closed_registrations_modal/index.jsx
index e6540e825..40df804f2 100644
--- a/app/javascript/mastodon/features/closed_registrations_modal/index.jsx
+++ b/app/javascript/mastodon/features/closed_registrations_modal/index.jsx
@@ -9,7 +9,6 @@ const mapStateToProps = state => ({
   message: state.getIn(['server', 'server', 'registrations', 'message']),
 });
 
-export default @connect(mapStateToProps)
 class ClosedRegistrationsModal extends ImmutablePureComponent {
 
   componentDidMount () {
@@ -73,3 +72,5 @@ class ClosedRegistrationsModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(ClosedRegistrationsModal);
diff --git a/app/javascript/mastodon/features/community_timeline/components/column_settings.jsx b/app/javascript/mastodon/features/community_timeline/components/column_settings.jsx
index 0cb6db883..d6181919a 100644
--- a/app/javascript/mastodon/features/community_timeline/components/column_settings.jsx
+++ b/app/javascript/mastodon/features/community_timeline/components/column_settings.jsx
@@ -4,7 +4,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import { injectIntl, FormattedMessage } from 'react-intl';
 import SettingToggle from '../../notifications/components/setting_toggle';
 
-export default @injectIntl
 class ColumnSettings extends React.PureComponent {
 
   static propTypes = {
@@ -27,3 +26,5 @@ class ColumnSettings extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ColumnSettings);
diff --git a/app/javascript/mastodon/features/community_timeline/index.jsx b/app/javascript/mastodon/features/community_timeline/index.jsx
index 4dbd55cf2..e41d11bb5 100644
--- a/app/javascript/mastodon/features/community_timeline/index.jsx
+++ b/app/javascript/mastodon/features/community_timeline/index.jsx
@@ -30,8 +30,6 @@ const mapStateToProps = (state, { columnId }) => {
   };
 };
 
-export default @connect(mapStateToProps)
-@injectIntl
 class CommunityTimeline extends React.PureComponent {
 
   static contextTypes = {
@@ -158,3 +156,5 @@ class CommunityTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(CommunityTimeline));
diff --git a/app/javascript/mastodon/features/compose/components/action_bar.jsx b/app/javascript/mastodon/features/compose/components/action_bar.jsx
index ee584cb1b..13760582e 100644
--- a/app/javascript/mastodon/features/compose/components/action_bar.jsx
+++ b/app/javascript/mastodon/features/compose/components/action_bar.jsx
@@ -20,7 +20,6 @@ const messages = defineMessages({
   bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
 });
 
-export default @injectIntl
 class ActionBar extends React.PureComponent {
 
   static propTypes = {
@@ -65,3 +64,5 @@ class ActionBar extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ActionBar);
diff --git a/app/javascript/mastodon/features/compose/components/compose_form.jsx b/app/javascript/mastodon/features/compose/components/compose_form.jsx
index e641d59f4..4a8f40301 100644
--- a/app/javascript/mastodon/features/compose/components/compose_form.jsx
+++ b/app/javascript/mastodon/features/compose/components/compose_form.jsx
@@ -31,7 +31,6 @@ const messages = defineMessages({
   saveChanges: { id: 'compose_form.save_changes', defaultMessage: 'Save changes' },
 });
 
-export default @injectIntl
 class ComposeForm extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -299,3 +298,5 @@ class ComposeForm extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(ComposeForm);
diff --git a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.jsx b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.jsx
index 79378454d..4fb131b47 100644
--- a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.jsx
+++ b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.jsx
@@ -144,8 +144,7 @@ class ModifierPicker extends React.PureComponent {
 
 }
 
-@injectIntl
-class EmojiPickerMenu extends React.PureComponent {
+class EmojiPickerMenuImpl extends React.PureComponent {
 
   static propTypes = {
     custom_emojis: ImmutablePropTypes.list,
@@ -305,7 +304,8 @@ class EmojiPickerMenu extends React.PureComponent {
 
 }
 
-export default @injectIntl
+const EmojiPickerMenu = injectIntl(EmojiPickerMenuImpl);
+
 class EmojiPickerDropdown extends React.PureComponent {
 
   static propTypes = {
@@ -409,3 +409,5 @@ class EmojiPickerDropdown extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(EmojiPickerDropdown);
diff --git a/app/javascript/mastodon/features/compose/components/language_dropdown.jsx b/app/javascript/mastodon/features/compose/components/language_dropdown.jsx
index 82547e079..908cb61a7 100644
--- a/app/javascript/mastodon/features/compose/components/language_dropdown.jsx
+++ b/app/javascript/mastodon/features/compose/components/language_dropdown.jsx
@@ -237,7 +237,6 @@ class LanguageDropdownMenu extends React.PureComponent {
 
 }
 
-export default @injectIntl
 class LanguageDropdown extends React.PureComponent {
 
   static propTypes = {
@@ -325,3 +324,5 @@ class LanguageDropdown extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(LanguageDropdown);
diff --git a/app/javascript/mastodon/features/compose/components/poll_button.jsx b/app/javascript/mastodon/features/compose/components/poll_button.jsx
index ff7a104aa..6ad689ccc 100644
--- a/app/javascript/mastodon/features/compose/components/poll_button.jsx
+++ b/app/javascript/mastodon/features/compose/components/poll_button.jsx
@@ -13,8 +13,6 @@ const iconStyle = {
   lineHeight: '27px',
 };
 
-export default
-@injectIntl
 class PollButton extends React.PureComponent {
 
   static propTypes = {
@@ -53,3 +51,5 @@ class PollButton extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(PollButton);
diff --git a/app/javascript/mastodon/features/compose/components/poll_form.jsx b/app/javascript/mastodon/features/compose/components/poll_form.jsx
index bb03f6f66..c5a8f8ca7 100644
--- a/app/javascript/mastodon/features/compose/components/poll_form.jsx
+++ b/app/javascript/mastodon/features/compose/components/poll_form.jsx
@@ -20,8 +20,7 @@ const messages = defineMessages({
   days: { id: 'intervals.full.days', defaultMessage: '{number, plural, one {# day} other {# days}}' },
 });
 
-@injectIntl
-class Option extends React.PureComponent {
+class OptionIntl extends React.PureComponent {
 
   static propTypes = {
     title: PropTypes.string.isRequired,
@@ -113,8 +112,8 @@ class Option extends React.PureComponent {
 
 }
 
-export default
-@injectIntl
+const Option = injectIntl(OptionIntl);
+
 class PollForm extends ImmutablePureComponent {
 
   static propTypes = {
@@ -180,3 +179,5 @@ class PollForm extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(PollForm);
diff --git a/app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx b/app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx
index ffd1094cd..6c4b1064e 100644
--- a/app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx
+++ b/app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx
@@ -132,7 +132,6 @@ class PrivacyDropdownMenu extends React.PureComponent {
 
 }
 
-export default @injectIntl
 class PrivacyDropdown extends React.PureComponent {
 
   static propTypes = {
@@ -285,3 +284,5 @@ class PrivacyDropdown extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(PrivacyDropdown);
diff --git a/app/javascript/mastodon/features/compose/components/reply_indicator.jsx b/app/javascript/mastodon/features/compose/components/reply_indicator.jsx
index 98b142ab8..81de4ea76 100644
--- a/app/javascript/mastodon/features/compose/components/reply_indicator.jsx
+++ b/app/javascript/mastodon/features/compose/components/reply_indicator.jsx
@@ -12,7 +12,6 @@ const messages = defineMessages({
   cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },
 });
 
-export default @injectIntl
 class ReplyIndicator extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -69,3 +68,5 @@ class ReplyIndicator extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(ReplyIndicator);
diff --git a/app/javascript/mastodon/features/compose/components/search.jsx b/app/javascript/mastodon/features/compose/components/search.jsx
index 0539c6b80..5d2d8d194 100644
--- a/app/javascript/mastodon/features/compose/components/search.jsx
+++ b/app/javascript/mastodon/features/compose/components/search.jsx
@@ -32,7 +32,6 @@ class SearchPopout extends React.PureComponent {
 
 }
 
-export default @injectIntl
 class Search extends React.PureComponent {
 
   static contextTypes = {
@@ -145,3 +144,5 @@ class Search extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(Search);
diff --git a/app/javascript/mastodon/features/compose/components/search_results.jsx b/app/javascript/mastodon/features/compose/components/search_results.jsx
index 44ab43638..78da3ca27 100644
--- a/app/javascript/mastodon/features/compose/components/search_results.jsx
+++ b/app/javascript/mastodon/features/compose/components/search_results.jsx
@@ -14,7 +14,6 @@ const messages = defineMessages({
   dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
 });
 
-export default @injectIntl
 class SearchResults extends ImmutablePureComponent {
 
   static propTypes = {
@@ -138,3 +137,5 @@ class SearchResults extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(SearchResults);
diff --git a/app/javascript/mastodon/features/compose/components/upload_button.jsx b/app/javascript/mastodon/features/compose/components/upload_button.jsx
index 964340d82..f2e6ff85c 100644
--- a/app/javascript/mastodon/features/compose/components/upload_button.jsx
+++ b/app/javascript/mastodon/features/compose/components/upload_button.jsx
@@ -23,8 +23,6 @@ const iconStyle = {
   lineHeight: '27px',
 };
 
-export default @connect(makeMapStateToProps)
-@injectIntl
 class UploadButton extends ImmutablePureComponent {
 
   static propTypes = {
@@ -81,3 +79,5 @@ class UploadButton extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps)(injectIntl(UploadButton));
diff --git a/app/javascript/mastodon/features/compose/index.jsx b/app/javascript/mastodon/features/compose/index.jsx
index 4b30d09ae..6cba1e9ad 100644
--- a/app/javascript/mastodon/features/compose/index.jsx
+++ b/app/javascript/mastodon/features/compose/index.jsx
@@ -38,8 +38,6 @@ const mapStateToProps = (state, ownProps) => ({
   showSearch: ownProps.multiColumn ? state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']) : false,
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Compose extends React.PureComponent {
 
   static propTypes = {
@@ -148,3 +146,5 @@ class Compose extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Compose));
diff --git a/app/javascript/mastodon/features/direct_timeline/components/conversation.jsx b/app/javascript/mastodon/features/direct_timeline/components/conversation.jsx
index fbdff1bdd..5996dd666 100644
--- a/app/javascript/mastodon/features/direct_timeline/components/conversation.jsx
+++ b/app/javascript/mastodon/features/direct_timeline/components/conversation.jsx
@@ -24,7 +24,6 @@ const messages = defineMessages({
   unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
 });
 
-export default @injectIntl
 class Conversation extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -198,3 +197,5 @@ class Conversation extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Conversation);
diff --git a/app/javascript/mastodon/features/direct_timeline/index.jsx b/app/javascript/mastodon/features/direct_timeline/index.jsx
index a45965bb2..e2667d8e9 100644
--- a/app/javascript/mastodon/features/direct_timeline/index.jsx
+++ b/app/javascript/mastodon/features/direct_timeline/index.jsx
@@ -14,8 +14,6 @@ const messages = defineMessages({
   title: { id: 'column.direct', defaultMessage: 'Direct messages' },
 });
 
-export default @connect()
-@injectIntl
 class DirectTimeline extends React.PureComponent {
 
   static propTypes = {
@@ -105,3 +103,5 @@ class DirectTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect()(injectIntl(DirectTimeline));
diff --git a/app/javascript/mastodon/features/directory/components/account_card.jsx b/app/javascript/mastodon/features/directory/components/account_card.jsx
index 15c8ad303..1cee3a4e4 100644
--- a/app/javascript/mastodon/features/directory/components/account_card.jsx
+++ b/app/javascript/mastodon/features/directory/components/account_card.jsx
@@ -91,9 +91,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
 
 });
 
-export default
-@injectIntl
-@connect(makeMapStateToProps, mapDispatchToProps)
 class AccountCard extends ImmutablePureComponent {
 
   static propTypes = {
@@ -233,3 +230,5 @@ class AccountCard extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(AccountCard));
diff --git a/app/javascript/mastodon/features/directory/index.jsx b/app/javascript/mastodon/features/directory/index.jsx
index bb5e021cc..afaf39b77 100644
--- a/app/javascript/mastodon/features/directory/index.jsx
+++ b/app/javascript/mastodon/features/directory/index.jsx
@@ -29,8 +29,6 @@ const mapStateToProps = state => ({
   domain: state.getIn(['meta', 'domain']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Directory extends React.PureComponent {
 
   static contextTypes = {
@@ -176,3 +174,5 @@ class Directory extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Directory));
diff --git a/app/javascript/mastodon/features/domain_blocks/index.jsx b/app/javascript/mastodon/features/domain_blocks/index.jsx
index 43b275c2d..0c22aa239 100644
--- a/app/javascript/mastodon/features/domain_blocks/index.jsx
+++ b/app/javascript/mastodon/features/domain_blocks/index.jsx
@@ -23,8 +23,6 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['domain_lists', 'blocks', 'next']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Blocks extends ImmutablePureComponent {
 
   static propTypes = {
@@ -81,3 +79,5 @@ class Blocks extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Blocks));
diff --git a/app/javascript/mastodon/features/explore/index.jsx b/app/javascript/mastodon/features/explore/index.jsx
index d91755ff6..569b8612c 100644
--- a/app/javascript/mastodon/features/explore/index.jsx
+++ b/app/javascript/mastodon/features/explore/index.jsx
@@ -24,8 +24,6 @@ const mapStateToProps = state => ({
   isSearching: state.getIn(['search', 'submitted']) || !showTrends,
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Explore extends React.PureComponent {
 
   static contextTypes = {
@@ -105,3 +103,5 @@ class Explore extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Explore));
diff --git a/app/javascript/mastodon/features/explore/links.jsx b/app/javascript/mastodon/features/explore/links.jsx
index b47fc8fcf..193739916 100644
--- a/app/javascript/mastodon/features/explore/links.jsx
+++ b/app/javascript/mastodon/features/explore/links.jsx
@@ -13,7 +13,6 @@ const mapStateToProps = state => ({
   isLoading: state.getIn(['trends', 'links', 'isLoading']),
 });
 
-export default @connect(mapStateToProps)
 class Links extends React.PureComponent {
 
   static propTypes = {
@@ -68,3 +67,5 @@ class Links extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Links);
diff --git a/app/javascript/mastodon/features/explore/results.jsx b/app/javascript/mastodon/features/explore/results.jsx
index b2f6c72b7..27132f132 100644
--- a/app/javascript/mastodon/features/explore/results.jsx
+++ b/app/javascript/mastodon/features/explore/results.jsx
@@ -42,8 +42,6 @@ const renderStatuses = (results, onLoadMore) => appendLoadMore('statuses', resul
   <Status key={`status-${item}`} id={item} />
 )), onLoadMore);
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Results extends React.PureComponent {
 
   static propTypes = {
@@ -124,3 +122,5 @@ class Results extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Results));
diff --git a/app/javascript/mastodon/features/explore/statuses.jsx b/app/javascript/mastodon/features/explore/statuses.jsx
index b027487d5..a98a6d046 100644
--- a/app/javascript/mastodon/features/explore/statuses.jsx
+++ b/app/javascript/mastodon/features/explore/statuses.jsx
@@ -14,7 +14,6 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['status_lists', 'trending', 'next']),
 });
 
-export default @connect(mapStateToProps)
 class Statuses extends React.PureComponent {
 
   static propTypes = {
@@ -62,3 +61,5 @@ class Statuses extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Statuses);
diff --git a/app/javascript/mastodon/features/explore/suggestions.jsx b/app/javascript/mastodon/features/explore/suggestions.jsx
index e6ad09974..53eb7ba4b 100644
--- a/app/javascript/mastodon/features/explore/suggestions.jsx
+++ b/app/javascript/mastodon/features/explore/suggestions.jsx
@@ -12,7 +12,6 @@ const mapStateToProps = state => ({
   isLoading: state.getIn(['suggestions', 'isLoading']),
 });
 
-export default @connect(mapStateToProps)
 class Suggestions extends React.PureComponent {
 
   static propTypes = {
@@ -49,3 +48,5 @@ class Suggestions extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Suggestions);
diff --git a/app/javascript/mastodon/features/explore/tags.jsx b/app/javascript/mastodon/features/explore/tags.jsx
index 258dc392f..3ba813c3f 100644
--- a/app/javascript/mastodon/features/explore/tags.jsx
+++ b/app/javascript/mastodon/features/explore/tags.jsx
@@ -13,7 +13,6 @@ const mapStateToProps = state => ({
   isLoadingHashtags: state.getIn(['trends', 'tags', 'isLoading']),
 });
 
-export default @connect(mapStateToProps)
 class Tags extends React.PureComponent {
 
   static propTypes = {
@@ -60,3 +59,5 @@ class Tags extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Tags);
diff --git a/app/javascript/mastodon/features/favourited_statuses/index.jsx b/app/javascript/mastodon/features/favourited_statuses/index.jsx
index 89093f682..2cbf00291 100644
--- a/app/javascript/mastodon/features/favourited_statuses/index.jsx
+++ b/app/javascript/mastodon/features/favourited_statuses/index.jsx
@@ -22,8 +22,6 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['status_lists', 'favourites', 'next']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Favourites extends ImmutablePureComponent {
 
   static propTypes = {
@@ -106,3 +104,5 @@ class Favourites extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Favourites));
diff --git a/app/javascript/mastodon/features/favourites/index.jsx b/app/javascript/mastodon/features/favourites/index.jsx
index 7179e6470..b1e81debd 100644
--- a/app/javascript/mastodon/features/favourites/index.jsx
+++ b/app/javascript/mastodon/features/favourites/index.jsx
@@ -21,8 +21,6 @@ const mapStateToProps = (state, props) => ({
   accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Favourites extends ImmutablePureComponent {
 
   static propTypes = {
@@ -90,3 +88,5 @@ class Favourites extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Favourites));
diff --git a/app/javascript/mastodon/features/filters/added_to_filter.jsx b/app/javascript/mastodon/features/filters/added_to_filter.jsx
index 3785eb3c5..d935d96e2 100644
--- a/app/javascript/mastodon/features/filters/added_to_filter.jsx
+++ b/app/javascript/mastodon/features/filters/added_to_filter.jsx
@@ -10,7 +10,6 @@ const mapStateToProps = (state, { filterId }) => ({
   filter: state.getIn(['filters', filterId]),
 });
 
-export default @connect(mapStateToProps)
 class AddedToFilter extends React.PureComponent {
 
   static propTypes = {
@@ -100,3 +99,5 @@ class AddedToFilter extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(AddedToFilter);
diff --git a/app/javascript/mastodon/features/filters/select_filter.jsx b/app/javascript/mastodon/features/filters/select_filter.jsx
index 8a21905d7..b0aede187 100644
--- a/app/javascript/mastodon/features/filters/select_filter.jsx
+++ b/app/javascript/mastodon/features/filters/select_filter.jsx
@@ -22,8 +22,6 @@ const mapStateToProps = (state, { contextType }) => ({
   ]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class SelectFilter extends React.PureComponent {
 
   static propTypes = {
@@ -190,3 +188,5 @@ class SelectFilter extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(SelectFilter));
diff --git a/app/javascript/mastodon/features/follow_recommendations/components/account.jsx b/app/javascript/mastodon/features/follow_recommendations/components/account.jsx
index ddd0c8baa..9cb26fe64 100644
--- a/app/javascript/mastodon/features/follow_recommendations/components/account.jsx
+++ b/app/javascript/mastodon/features/follow_recommendations/components/account.jsx
@@ -32,8 +32,6 @@ const getFirstSentence = str => {
   return arr[0];
 };
 
-export default @connect(makeMapStateToProps)
-@injectIntl
 class Account extends ImmutablePureComponent {
 
   static propTypes = {
@@ -83,3 +81,5 @@ class Account extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps)(injectIntl(Account));
diff --git a/app/javascript/mastodon/features/follow_recommendations/index.jsx b/app/javascript/mastodon/features/follow_recommendations/index.jsx
index 436cc582b..7ba34b51f 100644
--- a/app/javascript/mastodon/features/follow_recommendations/index.jsx
+++ b/app/javascript/mastodon/features/follow_recommendations/index.jsx
@@ -19,7 +19,6 @@ const mapStateToProps = state => ({
   isLoading: state.getIn(['suggestions', 'isLoading']),
 });
 
-export default @connect(mapStateToProps)
 class FollowRecommendations extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -114,3 +113,5 @@ class FollowRecommendations extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(FollowRecommendations);
diff --git a/app/javascript/mastodon/features/follow_requests/components/account_authorize.jsx b/app/javascript/mastodon/features/follow_requests/components/account_authorize.jsx
index d41f331e5..0bd45592c 100644
--- a/app/javascript/mastodon/features/follow_requests/components/account_authorize.jsx
+++ b/app/javascript/mastodon/features/follow_requests/components/account_authorize.jsx
@@ -13,7 +13,6 @@ const messages = defineMessages({
   reject: { id: 'follow_request.reject', defaultMessage: 'Reject' },
 });
 
-export default @injectIntl
 class AccountAuthorize extends ImmutablePureComponent {
 
   static propTypes = {
@@ -47,3 +46,5 @@ class AccountAuthorize extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(AccountAuthorize);
diff --git a/app/javascript/mastodon/features/follow_requests/index.jsx b/app/javascript/mastodon/features/follow_requests/index.jsx
index 526ae4cde..a8875bbd3 100644
--- a/app/javascript/mastodon/features/follow_requests/index.jsx
+++ b/app/javascript/mastodon/features/follow_requests/index.jsx
@@ -25,8 +25,6 @@ const mapStateToProps = state => ({
   domain: state.getIn(['meta', 'domain']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class FollowRequests extends ImmutablePureComponent {
 
   static propTypes = {
@@ -89,3 +87,5 @@ class FollowRequests extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(FollowRequests));
diff --git a/app/javascript/mastodon/features/followed_tags/index.jsx b/app/javascript/mastodon/features/followed_tags/index.jsx
index c2d0e4731..7c53542c2 100644
--- a/app/javascript/mastodon/features/followed_tags/index.jsx
+++ b/app/javascript/mastodon/features/followed_tags/index.jsx
@@ -22,8 +22,6 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['followed_tags', 'next']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class FollowedTags extends ImmutablePureComponent {
 
   static propTypes = {
@@ -87,3 +85,5 @@ class FollowedTags extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(FollowedTags));
diff --git a/app/javascript/mastodon/features/followers/index.jsx b/app/javascript/mastodon/features/followers/index.jsx
index 277eb702f..fe86ebb3b 100644
--- a/app/javascript/mastodon/features/followers/index.jsx
+++ b/app/javascript/mastodon/features/followers/index.jsx
@@ -54,7 +54,6 @@ RemoteHint.propTypes = {
   url: PropTypes.string.isRequired,
 };
 
-export default @connect(mapStateToProps)
 class Followers extends ImmutablePureComponent {
 
   static propTypes = {
@@ -168,3 +167,5 @@ class Followers extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Followers);
diff --git a/app/javascript/mastodon/features/following/index.jsx b/app/javascript/mastodon/features/following/index.jsx
index e23d9b35c..8095d73a3 100644
--- a/app/javascript/mastodon/features/following/index.jsx
+++ b/app/javascript/mastodon/features/following/index.jsx
@@ -54,7 +54,6 @@ RemoteHint.propTypes = {
   url: PropTypes.string.isRequired,
 };
 
-export default @connect(mapStateToProps)
 class Following extends ImmutablePureComponent {
 
   static propTypes = {
@@ -168,3 +167,5 @@ class Following extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Following);
diff --git a/app/javascript/mastodon/features/getting_started/components/announcements.jsx b/app/javascript/mastodon/features/getting_started/components/announcements.jsx
index 0cae0bd1f..954f407f5 100644
--- a/app/javascript/mastodon/features/getting_started/components/announcements.jsx
+++ b/app/javascript/mastodon/features/getting_started/components/announcements.jsx
@@ -355,7 +355,6 @@ class Announcement extends ImmutablePureComponent {
 
 }
 
-export default @injectIntl
 class Announcements extends ImmutablePureComponent {
 
   static propTypes = {
@@ -447,3 +446,5 @@ class Announcements extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Announcements);
diff --git a/app/javascript/mastodon/features/getting_started/index.jsx b/app/javascript/mastodon/features/getting_started/index.jsx
index fc91070d1..ee8a009ee 100644
--- a/app/javascript/mastodon/features/getting_started/index.jsx
+++ b/app/javascript/mastodon/features/getting_started/index.jsx
@@ -58,8 +58,6 @@ const badgeDisplay = (number, limit) => {
   }
 };
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class GettingStarted extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -153,3 +151,5 @@ class GettingStarted extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(GettingStarted));
diff --git a/app/javascript/mastodon/features/hashtag_timeline/components/column_settings.jsx b/app/javascript/mastodon/features/hashtag_timeline/components/column_settings.jsx
index ac7863ed3..f140f2d01 100644
--- a/app/javascript/mastodon/features/hashtag_timeline/components/column_settings.jsx
+++ b/app/javascript/mastodon/features/hashtag_timeline/components/column_settings.jsx
@@ -12,7 +12,6 @@ const messages = defineMessages({
   noOptions: { id: 'hashtag.column_settings.select.no_options_message', defaultMessage: 'No suggestions found' },
 });
 
-export default @injectIntl
 class ColumnSettings extends React.PureComponent {
 
   static propTypes = {
@@ -131,3 +130,5 @@ class ColumnSettings extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ColumnSettings);
diff --git a/app/javascript/mastodon/features/hashtag_timeline/index.jsx b/app/javascript/mastodon/features/hashtag_timeline/index.jsx
index e5262d70d..58423f429 100644
--- a/app/javascript/mastodon/features/hashtag_timeline/index.jsx
+++ b/app/javascript/mastodon/features/hashtag_timeline/index.jsx
@@ -26,8 +26,6 @@ const mapStateToProps = (state, props) => ({
   tag: state.getIn(['tags', props.params.id]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class HashtagTimeline extends React.PureComponent {
 
   disconnects = [];
@@ -235,3 +233,5 @@ class HashtagTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(HashtagTimeline));
diff --git a/app/javascript/mastodon/features/home_timeline/components/column_settings.jsx b/app/javascript/mastodon/features/home_timeline/components/column_settings.jsx
index 455e21881..bd15390c0 100644
--- a/app/javascript/mastodon/features/home_timeline/components/column_settings.jsx
+++ b/app/javascript/mastodon/features/home_timeline/components/column_settings.jsx
@@ -4,7 +4,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import { injectIntl, FormattedMessage } from 'react-intl';
 import SettingToggle from '../../notifications/components/setting_toggle';
 
-export default @injectIntl
 class ColumnSettings extends React.PureComponent {
 
   static propTypes = {
@@ -32,3 +31,5 @@ class ColumnSettings extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ColumnSettings);
diff --git a/app/javascript/mastodon/features/home_timeline/index.jsx b/app/javascript/mastodon/features/home_timeline/index.jsx
index 001de15d1..9aa4c6d82 100644
--- a/app/javascript/mastodon/features/home_timeline/index.jsx
+++ b/app/javascript/mastodon/features/home_timeline/index.jsx
@@ -30,8 +30,6 @@ const mapStateToProps = state => ({
   showAnnouncements: state.getIn(['announcements', 'show']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class HomeTimeline extends React.PureComponent {
 
   static contextTypes = {
@@ -174,3 +172,5 @@ class HomeTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(HomeTimeline));
diff --git a/app/javascript/mastodon/features/interaction_modal/index.jsx b/app/javascript/mastodon/features/interaction_modal/index.jsx
index c1d346fed..5742f1104 100644
--- a/app/javascript/mastodon/features/interaction_modal/index.jsx
+++ b/app/javascript/mastodon/features/interaction_modal/index.jsx
@@ -74,7 +74,6 @@ class Copypaste extends React.PureComponent {
 
 }
 
-export default @connect(mapStateToProps, mapDispatchToProps)
 class InteractionModal extends React.PureComponent {
 
   static propTypes = {
@@ -159,3 +158,5 @@ class InteractionModal extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(InteractionModal);
diff --git a/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx b/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx
index 9a870478d..70f019712 100644
--- a/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx
+++ b/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx
@@ -10,7 +10,6 @@ const messages = defineMessages({
   heading: { id: 'keyboard_shortcuts.heading', defaultMessage: 'Keyboard Shortcuts' },
 });
 
-export default @injectIntl
 class KeyboardShortcuts extends ImmutablePureComponent {
 
   static propTypes = {
@@ -174,3 +173,5 @@ class KeyboardShortcuts extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(KeyboardShortcuts);
diff --git a/app/javascript/mastodon/features/list_adder/components/account.jsx b/app/javascript/mastodon/features/list_adder/components/account.jsx
index 1369aac07..786af3bb1 100644
--- a/app/javascript/mastodon/features/list_adder/components/account.jsx
+++ b/app/javascript/mastodon/features/list_adder/components/account.jsx
@@ -17,9 +17,6 @@ const makeMapStateToProps = () => {
   return mapStateToProps;
 };
 
-
-export default @connect(makeMapStateToProps)
-@injectIntl
 class Account extends ImmutablePureComponent {
 
   static propTypes = {
@@ -41,3 +38,5 @@ class Account extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps)(injectIntl(Account));
diff --git a/app/javascript/mastodon/features/list_adder/components/list.jsx b/app/javascript/mastodon/features/list_adder/components/list.jsx
index 60c8958a7..34ccf8451 100644
--- a/app/javascript/mastodon/features/list_adder/components/list.jsx
+++ b/app/javascript/mastodon/features/list_adder/components/list.jsx
@@ -23,8 +23,6 @@ const mapDispatchToProps = (dispatch, { listId }) => ({
   onAdd: () => dispatch(addToListAdder(listId)),
 });
 
-export default @connect(MapStateToProps, mapDispatchToProps)
-@injectIntl
 class List extends ImmutablePureComponent {
 
   static propTypes = {
@@ -67,3 +65,5 @@ class List extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(MapStateToProps, mapDispatchToProps)(injectIntl(List));
diff --git a/app/javascript/mastodon/features/list_adder/index.jsx b/app/javascript/mastodon/features/list_adder/index.jsx
index cb8a15e8c..45d5589f9 100644
--- a/app/javascript/mastodon/features/list_adder/index.jsx
+++ b/app/javascript/mastodon/features/list_adder/index.jsx
@@ -28,8 +28,6 @@ const mapDispatchToProps = dispatch => ({
   onReset: () => dispatch(resetListAdder()),
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class ListAdder extends ImmutablePureComponent {
 
   static propTypes = {
@@ -71,3 +69,5 @@ class ListAdder extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(ListAdder));
diff --git a/app/javascript/mastodon/features/list_editor/components/account.jsx b/app/javascript/mastodon/features/list_editor/components/account.jsx
index 48085af43..86209bb3c 100644
--- a/app/javascript/mastodon/features/list_editor/components/account.jsx
+++ b/app/javascript/mastodon/features/list_editor/components/account.jsx
@@ -31,8 +31,6 @@ const mapDispatchToProps = (dispatch, { accountId }) => ({
   onAdd: () => dispatch(addToListEditor(accountId)),
 });
 
-export default @connect(makeMapStateToProps, mapDispatchToProps)
-@injectIntl
 class Account extends ImmutablePureComponent {
 
   static propTypes = {
@@ -75,3 +73,5 @@ class Account extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps, mapDispatchToProps)(injectIntl(Account));
diff --git a/app/javascript/mastodon/features/list_editor/components/edit_list_form.jsx b/app/javascript/mastodon/features/list_editor/components/edit_list_form.jsx
index 4d7e49ec0..9c1c244cb 100644
--- a/app/javascript/mastodon/features/list_editor/components/edit_list_form.jsx
+++ b/app/javascript/mastodon/features/list_editor/components/edit_list_form.jsx
@@ -19,8 +19,6 @@ const mapDispatchToProps = dispatch => ({
   onSubmit: () => dispatch(submitListEditor(false)),
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class ListForm extends React.PureComponent {
 
   static propTypes = {
@@ -68,3 +66,5 @@ class ListForm extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(ListForm));
diff --git a/app/javascript/mastodon/features/list_editor/components/search.jsx b/app/javascript/mastodon/features/list_editor/components/search.jsx
index 3ee26c8eb..b9745e0a8 100644
--- a/app/javascript/mastodon/features/list_editor/components/search.jsx
+++ b/app/javascript/mastodon/features/list_editor/components/search.jsx
@@ -20,8 +20,6 @@ const mapDispatchToProps = dispatch => ({
   onChange: value => dispatch(changeListSuggestions(value)),
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class Search extends React.PureComponent {
 
   static propTypes = {
@@ -74,3 +72,5 @@ class Search extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(Search));
diff --git a/app/javascript/mastodon/features/list_editor/index.jsx b/app/javascript/mastodon/features/list_editor/index.jsx
index 48466604a..65cebbdc8 100644
--- a/app/javascript/mastodon/features/list_editor/index.jsx
+++ b/app/javascript/mastodon/features/list_editor/index.jsx
@@ -22,8 +22,6 @@ const mapDispatchToProps = dispatch => ({
   onReset: () => dispatch(resetListEditor()),
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class ListEditor extends ImmutablePureComponent {
 
   static propTypes = {
@@ -77,3 +75,5 @@ class ListEditor extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(ListEditor));
diff --git a/app/javascript/mastodon/features/list_timeline/index.jsx b/app/javascript/mastodon/features/list_timeline/index.jsx
index 25dbe311a..d0ba2a186 100644
--- a/app/javascript/mastodon/features/list_timeline/index.jsx
+++ b/app/javascript/mastodon/features/list_timeline/index.jsx
@@ -31,8 +31,6 @@ const mapStateToProps = (state, props) => ({
   hasUnread: state.getIn(['timelines', `list:${props.params.id}`, 'unread']) > 0,
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class ListTimeline extends React.PureComponent {
 
   static contextTypes = {
@@ -219,3 +217,5 @@ class ListTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(ListTimeline));
diff --git a/app/javascript/mastodon/features/lists/components/new_list_form.jsx b/app/javascript/mastodon/features/lists/components/new_list_form.jsx
index 4e00e5200..50b52518e 100644
--- a/app/javascript/mastodon/features/lists/components/new_list_form.jsx
+++ b/app/javascript/mastodon/features/lists/components/new_list_form.jsx
@@ -20,8 +20,6 @@ const mapDispatchToProps = dispatch => ({
   onSubmit: () => dispatch(submitListEditor(true)),
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class NewListForm extends React.PureComponent {
 
   static propTypes = {
@@ -75,3 +73,5 @@ class NewListForm extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(NewListForm));
diff --git a/app/javascript/mastodon/features/lists/index.jsx b/app/javascript/mastodon/features/lists/index.jsx
index 3a0b1373a..afd645a30 100644
--- a/app/javascript/mastodon/features/lists/index.jsx
+++ b/app/javascript/mastodon/features/lists/index.jsx
@@ -32,8 +32,6 @@ const mapStateToProps = state => ({
   lists: getOrderedLists(state),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Lists extends ImmutablePureComponent {
 
   static propTypes = {
@@ -87,3 +85,5 @@ class Lists extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Lists));
diff --git a/app/javascript/mastodon/features/mutes/index.jsx b/app/javascript/mastodon/features/mutes/index.jsx
index 65df6149f..5c05d2f70 100644
--- a/app/javascript/mastodon/features/mutes/index.jsx
+++ b/app/javascript/mastodon/features/mutes/index.jsx
@@ -23,8 +23,6 @@ const mapStateToProps = state => ({
   isLoading: state.getIn(['user_lists', 'mutes', 'isLoading'], true),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Mutes extends ImmutablePureComponent {
 
   static propTypes = {
@@ -82,3 +80,5 @@ class Mutes extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Mutes));
diff --git a/app/javascript/mastodon/features/notifications/components/filter_bar.jsx b/app/javascript/mastodon/features/notifications/components/filter_bar.jsx
index 368eb0b7e..88e95cf48 100644
--- a/app/javascript/mastodon/features/notifications/components/filter_bar.jsx
+++ b/app/javascript/mastodon/features/notifications/components/filter_bar.jsx
@@ -12,7 +12,6 @@ const tooltips = defineMessages({
   statuses: { id: 'notifications.filter.statuses', defaultMessage: 'Updates from people you follow' },
 });
 
-export default @injectIntl
 class FilterBar extends React.PureComponent {
 
   static propTypes = {
@@ -108,3 +107,5 @@ class FilterBar extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(FilterBar);
diff --git a/app/javascript/mastodon/features/notifications/components/follow_request.jsx b/app/javascript/mastodon/features/notifications/components/follow_request.jsx
index 08de875e3..01bf67065 100644
--- a/app/javascript/mastodon/features/notifications/components/follow_request.jsx
+++ b/app/javascript/mastodon/features/notifications/components/follow_request.jsx
@@ -1,4 +1,4 @@
-import React, { Fragment } from 'react';
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Avatar from 'mastodon/components/avatar';
@@ -13,7 +13,6 @@ const messages = defineMessages({
   reject: { id: 'follow_request.reject', defaultMessage: 'Reject' },
 });
 
-export default @injectIntl
 class FollowRequest extends ImmutablePureComponent {
 
   static propTypes = {
@@ -32,10 +31,10 @@ class FollowRequest extends ImmutablePureComponent {
 
     if (hidden) {
       return (
-        <Fragment>
+        <React.Fragment>
           {account.get('display_name')}
           {account.get('username')}
-        </Fragment>
+        </React.Fragment>
       );
     }
 
@@ -57,3 +56,5 @@ class FollowRequest extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(FollowRequest);
diff --git a/app/javascript/mastodon/features/notifications/components/notification.jsx b/app/javascript/mastodon/features/notifications/components/notification.jsx
index 9e2517f08..cf9d9f72c 100644
--- a/app/javascript/mastodon/features/notifications/components/notification.jsx
+++ b/app/javascript/mastodon/features/notifications/components/notification.jsx
@@ -33,7 +33,6 @@ const notificationForScreenReader = (intl, message, timestamp) => {
   return output.join(', ');
 };
 
-export default @injectIntl
 class Notification extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -447,3 +446,5 @@ class Notification extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Notification);
diff --git a/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.jsx b/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.jsx
index 3a7556c1d..c54137e60 100644
--- a/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.jsx
+++ b/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.jsx
@@ -12,8 +12,6 @@ const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
 });
 
-export default @connect()
-@injectIntl
 class NotificationsPermissionBanner extends React.PureComponent {
 
   static propTypes = {
@@ -46,3 +44,5 @@ class NotificationsPermissionBanner extends React.PureComponent {
   }
 
 }
+
+export default connect()(injectIntl(NotificationsPermissionBanner));
diff --git a/app/javascript/mastodon/features/notifications/components/report.jsx b/app/javascript/mastodon/features/notifications/components/report.jsx
index 3ce3eb9d3..4663b2359 100644
--- a/app/javascript/mastodon/features/notifications/components/report.jsx
+++ b/app/javascript/mastodon/features/notifications/components/report.jsx
@@ -13,7 +13,6 @@ const messages = defineMessages({
   violation: { id: 'report_notification.categories.violation', defaultMessage: 'Rule violation' },
 });
 
-export default @injectIntl
 class Report extends ImmutablePureComponent {
 
   static propTypes = {
@@ -60,3 +59,5 @@ class Report extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Report);
diff --git a/app/javascript/mastodon/features/notifications/index.jsx b/app/javascript/mastodon/features/notifications/index.jsx
index fee016a02..bb8852abf 100644
--- a/app/javascript/mastodon/features/notifications/index.jsx
+++ b/app/javascript/mastodon/features/notifications/index.jsx
@@ -67,8 +67,6 @@ const mapStateToProps = state => ({
   needsNotificationPermission: state.getIn(['settings', 'notifications', 'alerts']).includes(true) && state.getIn(['notifications', 'browserSupport']) && state.getIn(['notifications', 'browserPermission']) === 'default' && !state.getIn(['settings', 'notifications', 'dismissPermissionBanner']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Notifications extends React.PureComponent {
 
   static contextTypes = {
@@ -288,3 +286,5 @@ class Notifications extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Notifications));
diff --git a/app/javascript/mastodon/features/picture_in_picture/components/footer.jsx b/app/javascript/mastodon/features/picture_in_picture/components/footer.jsx
index 0ee6d06c7..66124f331 100644
--- a/app/javascript/mastodon/features/picture_in_picture/components/footer.jsx
+++ b/app/javascript/mastodon/features/picture_in_picture/components/footer.jsx
@@ -37,8 +37,6 @@ const makeMapStateToProps = () => {
   return mapStateToProps;
 };
 
-export default @connect(makeMapStateToProps)
-@injectIntl
 class Footer extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -190,3 +188,5 @@ class Footer extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps)(injectIntl(Footer));
diff --git a/app/javascript/mastodon/features/picture_in_picture/components/header.jsx b/app/javascript/mastodon/features/picture_in_picture/components/header.jsx
index e05d8c62e..100f9db7a 100644
--- a/app/javascript/mastodon/features/picture_in_picture/components/header.jsx
+++ b/app/javascript/mastodon/features/picture_in_picture/components/header.jsx
@@ -17,8 +17,6 @@ const mapStateToProps = (state, { accountId }) => ({
   account: state.getIn(['accounts', accountId]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Header extends ImmutablePureComponent {
 
   static propTypes = {
@@ -45,3 +43,5 @@ class Header extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Header));
diff --git a/app/javascript/mastodon/features/picture_in_picture/index.jsx b/app/javascript/mastodon/features/picture_in_picture/index.jsx
index 01a7d43f2..ae48a1b4e 100644
--- a/app/javascript/mastodon/features/picture_in_picture/index.jsx
+++ b/app/javascript/mastodon/features/picture_in_picture/index.jsx
@@ -11,7 +11,6 @@ const mapStateToProps = state => ({
   ...state.get('picture_in_picture'),
 });
 
-export default @connect(mapStateToProps)
 class PictureInPicture extends React.Component {
 
   static propTypes = {
@@ -83,3 +82,5 @@ class PictureInPicture extends React.Component {
   }
 
 }
+
+export default connect(mapStateToProps)(PictureInPicture);
diff --git a/app/javascript/mastodon/features/pinned_statuses/index.jsx b/app/javascript/mastodon/features/pinned_statuses/index.jsx
index 504fda415..24a0b6d2e 100644
--- a/app/javascript/mastodon/features/pinned_statuses/index.jsx
+++ b/app/javascript/mastodon/features/pinned_statuses/index.jsx
@@ -19,8 +19,6 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['status_lists', 'pins', 'next']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class PinnedStatuses extends ImmutablePureComponent {
 
   static propTypes = {
@@ -63,3 +61,5 @@ class PinnedStatuses extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(PinnedStatuses));
diff --git a/app/javascript/mastodon/features/privacy_policy/index.jsx b/app/javascript/mastodon/features/privacy_policy/index.jsx
index 3df487e8f..d5bbda6a3 100644
--- a/app/javascript/mastodon/features/privacy_policy/index.jsx
+++ b/app/javascript/mastodon/features/privacy_policy/index.jsx
@@ -10,7 +10,6 @@ const messages = defineMessages({
   title: { id: 'privacy_policy.title', defaultMessage: 'Privacy Policy' },
 });
 
-export default @injectIntl
 class PrivacyPolicy extends React.PureComponent {
 
   static propTypes = {
@@ -59,3 +58,5 @@ class PrivacyPolicy extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(PrivacyPolicy);
diff --git a/app/javascript/mastodon/features/public_timeline/components/column_settings.jsx b/app/javascript/mastodon/features/public_timeline/components/column_settings.jsx
index 756b6fe06..bf8a8323e 100644
--- a/app/javascript/mastodon/features/public_timeline/components/column_settings.jsx
+++ b/app/javascript/mastodon/features/public_timeline/components/column_settings.jsx
@@ -4,7 +4,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import { injectIntl, FormattedMessage } from 'react-intl';
 import SettingToggle from '../../notifications/components/setting_toggle';
 
-export default @injectIntl
 class ColumnSettings extends React.PureComponent {
 
   static propTypes = {
@@ -28,3 +27,5 @@ class ColumnSettings extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ColumnSettings);
diff --git a/app/javascript/mastodon/features/public_timeline/index.jsx b/app/javascript/mastodon/features/public_timeline/index.jsx
index aaef45c86..f89caa2c9 100644
--- a/app/javascript/mastodon/features/public_timeline/index.jsx
+++ b/app/javascript/mastodon/features/public_timeline/index.jsx
@@ -31,8 +31,6 @@ const mapStateToProps = (state, { columnId }) => {
   };
 };
 
-export default @connect(mapStateToProps)
-@injectIntl
 class PublicTimeline extends React.PureComponent {
 
   static contextTypes = {
@@ -160,3 +158,5 @@ class PublicTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(PublicTimeline));
diff --git a/app/javascript/mastodon/features/reblogs/index.jsx b/app/javascript/mastodon/features/reblogs/index.jsx
index 31e5dc1d4..35edb0d6a 100644
--- a/app/javascript/mastodon/features/reblogs/index.jsx
+++ b/app/javascript/mastodon/features/reblogs/index.jsx
@@ -21,8 +21,6 @@ const mapStateToProps = (state, props) => ({
   accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Reblogs extends ImmutablePureComponent {
 
   static propTypes = {
@@ -90,3 +88,5 @@ class Reblogs extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Reblogs));
diff --git a/app/javascript/mastodon/features/report/category.jsx b/app/javascript/mastodon/features/report/category.jsx
index c6c0a506f..492a533f2 100644
--- a/app/javascript/mastodon/features/report/category.jsx
+++ b/app/javascript/mastodon/features/report/category.jsx
@@ -24,8 +24,6 @@ const mapStateToProps = state => ({
   rules: state.getIn(['server', 'server', 'rules'], ImmutableList()),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Category extends React.PureComponent {
 
   static propTypes = {
@@ -104,3 +102,5 @@ class Category extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Category));
diff --git a/app/javascript/mastodon/features/report/comment.jsx b/app/javascript/mastodon/features/report/comment.jsx
index cde156415..ab29f6c22 100644
--- a/app/javascript/mastodon/features/report/comment.jsx
+++ b/app/javascript/mastodon/features/report/comment.jsx
@@ -8,7 +8,6 @@ const messages = defineMessages({
   placeholder: { id: 'report.placeholder', defaultMessage: 'Type or paste additional comments' },
 });
 
-export default @injectIntl
 class Comment extends React.PureComponent {
 
   static propTypes = {
@@ -81,3 +80,5 @@ class Comment extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(Comment);
diff --git a/app/javascript/mastodon/features/report/components/status_check_box.jsx b/app/javascript/mastodon/features/report/components/status_check_box.jsx
index 5366da90b..28d572e89 100644
--- a/app/javascript/mastodon/features/report/components/status_check_box.jsx
+++ b/app/javascript/mastodon/features/report/components/status_check_box.jsx
@@ -17,7 +17,6 @@ const messages = defineMessages({
   direct_short: { id: 'privacy.direct.short', defaultMessage: 'Mentioned people only' },
 });
 
-export default @injectIntl
 class StatusCheckBox extends React.PureComponent {
 
   static propTypes = {
@@ -80,3 +79,5 @@ class StatusCheckBox extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(StatusCheckBox);
diff --git a/app/javascript/mastodon/features/report/rules.jsx b/app/javascript/mastodon/features/report/rules.jsx
index 920da68d6..b500c0503 100644
--- a/app/javascript/mastodon/features/report/rules.jsx
+++ b/app/javascript/mastodon/features/report/rules.jsx
@@ -10,7 +10,6 @@ const mapStateToProps = state => ({
   rules: state.getIn(['server', 'server', 'rules']),
 });
 
-export default @connect(mapStateToProps)
 class Rules extends React.PureComponent {
 
   static propTypes = {
@@ -62,3 +61,5 @@ class Rules extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Rules);
diff --git a/app/javascript/mastodon/features/report/statuses.jsx b/app/javascript/mastodon/features/report/statuses.jsx
index d5d86034f..87163aeb8 100644
--- a/app/javascript/mastodon/features/report/statuses.jsx
+++ b/app/javascript/mastodon/features/report/statuses.jsx
@@ -13,7 +13,6 @@ const mapStateToProps = (state, { accountId }) => ({
   isLoading: state.getIn(['timelines', `account:${accountId}:with_replies`, 'isLoading']),
 });
 
-export default @connect(mapStateToProps)
 class Statuses extends React.PureComponent {
 
   static propTypes = {
@@ -59,3 +58,5 @@ class Statuses extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Statuses);
diff --git a/app/javascript/mastodon/features/report/thanks.jsx b/app/javascript/mastodon/features/report/thanks.jsx
index d169b1e32..4e5b6e864 100644
--- a/app/javascript/mastodon/features/report/thanks.jsx
+++ b/app/javascript/mastodon/features/report/thanks.jsx
@@ -12,7 +12,6 @@ import {
 
 const mapStateToProps = () => ({});
 
-export default @connect(mapStateToProps)
 class Thanks extends React.PureComponent {
 
   static propTypes = {
@@ -82,3 +81,5 @@ class Thanks extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Thanks);
diff --git a/app/javascript/mastodon/features/status/components/action_bar.jsx b/app/javascript/mastodon/features/status/components/action_bar.jsx
index 0d4767331..5cbe4984c 100644
--- a/app/javascript/mastodon/features/status/components/action_bar.jsx
+++ b/app/javascript/mastodon/features/status/components/action_bar.jsx
@@ -47,8 +47,6 @@ const mapStateToProps = (state, { status }) => ({
   relationship: state.getIn(['relationships', status.getIn(['account', 'id'])]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class ActionBar extends React.PureComponent {
 
   static contextTypes = {
@@ -298,3 +296,5 @@ class ActionBar extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(ActionBar));
diff --git a/app/javascript/mastodon/features/status/components/detailed_status.jsx b/app/javascript/mastodon/features/status/components/detailed_status.jsx
index f9ff57261..8a2194450 100644
--- a/app/javascript/mastodon/features/status/components/detailed_status.jsx
+++ b/app/javascript/mastodon/features/status/components/detailed_status.jsx
@@ -25,7 +25,6 @@ const messages = defineMessages({
   direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
 });
 
-export default  @injectIntl
 class DetailedStatus extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -289,3 +288,5 @@ class DetailedStatus extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(DetailedStatus);
diff --git a/app/javascript/mastodon/features/status/index.jsx b/app/javascript/mastodon/features/status/index.jsx
index 2c6728fc0..358ad14c4 100644
--- a/app/javascript/mastodon/features/status/index.jsx
+++ b/app/javascript/mastodon/features/status/index.jsx
@@ -176,8 +176,6 @@ const titleFromStatus = status => {
   return `${prefix}: "${truncate(text, 30)}"`;
 };
 
-export default @injectIntl
-@connect(makeMapStateToProps)
 class Status extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -684,3 +682,5 @@ class Status extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(connect(makeMapStateToProps)(Status));
diff --git a/app/javascript/mastodon/features/subscribed_languages_modal/index.jsx b/app/javascript/mastodon/features/subscribed_languages_modal/index.jsx
index f1360613e..04fe31b5c 100644
--- a/app/javascript/mastodon/features/subscribed_languages_modal/index.jsx
+++ b/app/javascript/mastodon/features/subscribed_languages_modal/index.jsx
@@ -36,8 +36,6 @@ const mapDispatchToProps = (dispatch, { accountId }) => ({
 
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class SubscribedLanguagesModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -123,3 +121,5 @@ class SubscribedLanguagesModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(SubscribedLanguagesModal));
diff --git a/app/javascript/mastodon/features/ui/components/audio_modal.jsx b/app/javascript/mastodon/features/ui/components/audio_modal.jsx
index a2fa8c49c..c0ac12ba7 100644
--- a/app/javascript/mastodon/features/ui/components/audio_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/audio_modal.jsx
@@ -11,7 +11,6 @@ const mapStateToProps = (state, { statusId }) => ({
   accountStaticAvatar: state.getIn(['accounts', state.getIn(['statuses', statusId, 'account']), 'avatar_static']),
 });
 
-export default @connect(mapStateToProps, null, null, { forwardRef: true })
 class AudioModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -55,3 +54,5 @@ class AudioModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, null, null, { forwardRef: true })(AudioModal);
diff --git a/app/javascript/mastodon/features/ui/components/block_modal.jsx b/app/javascript/mastodon/features/ui/components/block_modal.jsx
index 6c9d2043c..a9506aa69 100644
--- a/app/javascript/mastodon/features/ui/components/block_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/block_modal.jsx
@@ -36,8 +36,6 @@ const mapDispatchToProps = dispatch => {
   };
 };
 
-export default @connect(makeMapStateToProps, mapDispatchToProps)
-@injectIntl
 class BlockModal extends React.PureComponent {
 
   static propTypes = {
@@ -101,3 +99,5 @@ class BlockModal extends React.PureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps, mapDispatchToProps)(injectIntl(BlockModal));
diff --git a/app/javascript/mastodon/features/ui/components/boost_modal.jsx b/app/javascript/mastodon/features/ui/components/boost_modal.jsx
index d6a6cea31..fe55c963a 100644
--- a/app/javascript/mastodon/features/ui/components/boost_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/boost_modal.jsx
@@ -38,8 +38,6 @@ const mapDispatchToProps = dispatch => {
   };
 };
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class BoostModal extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -140,3 +138,5 @@ class BoostModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(BoostModal));
diff --git a/app/javascript/mastodon/features/ui/components/bundle_column_error.jsx b/app/javascript/mastodon/features/ui/components/bundle_column_error.jsx
index 9955173eb..dc9b0e539 100644
--- a/app/javascript/mastodon/features/ui/components/bundle_column_error.jsx
+++ b/app/javascript/mastodon/features/ui/components/bundle_column_error.jsx
@@ -92,7 +92,6 @@ class CopyButton extends React.PureComponent {
 
 }
 
-export default @injectIntl
 class BundleColumnError extends React.PureComponent {
 
   static propTypes = {
@@ -160,3 +159,5 @@ class BundleColumnError extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(BundleColumnError);
diff --git a/app/javascript/mastodon/features/ui/components/compare_history_modal.jsx b/app/javascript/mastodon/features/ui/components/compare_history_modal.jsx
index cbb1567b7..1802c167c 100644
--- a/app/javascript/mastodon/features/ui/components/compare_history_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/compare_history_modal.jsx
@@ -24,7 +24,6 @@ const mapDispatchToProps = dispatch => ({
 
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
 class CompareHistoryModal extends React.PureComponent {
 
   static propTypes = {
@@ -100,3 +99,5 @@ class CompareHistoryModal extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(CompareHistoryModal);
diff --git a/app/javascript/mastodon/features/ui/components/compose_panel.jsx b/app/javascript/mastodon/features/ui/components/compose_panel.jsx
index 6cb352322..1b0a0c74a 100644
--- a/app/javascript/mastodon/features/ui/components/compose_panel.jsx
+++ b/app/javascript/mastodon/features/ui/components/compose_panel.jsx
@@ -8,7 +8,6 @@ import LinkFooter from './link_footer';
 import ServerBanner from 'mastodon/components/server_banner';
 import { changeComposing, mountCompose, unmountCompose } from 'mastodon/actions/compose';
 
-export default @connect()
 class ComposePanel extends React.PureComponent {
 
   static contextTypes = {
@@ -66,3 +65,5 @@ class ComposePanel extends React.PureComponent {
   }
 
 }
+
+export default connect(ComposePanel);
diff --git a/app/javascript/mastodon/features/ui/components/confirmation_modal.jsx b/app/javascript/mastodon/features/ui/components/confirmation_modal.jsx
index b023b00b2..4437567a1 100644
--- a/app/javascript/mastodon/features/ui/components/confirmation_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/confirmation_modal.jsx
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
 import { injectIntl, FormattedMessage } from 'react-intl';
 import Button from '../../../components/button';
 
-export default @injectIntl
 class ConfirmationModal extends React.PureComponent {
 
   static propTypes = {
@@ -68,3 +67,5 @@ class ConfirmationModal extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ConfirmationModal);
diff --git a/app/javascript/mastodon/features/ui/components/disabled_account_banner.jsx b/app/javascript/mastodon/features/ui/components/disabled_account_banner.jsx
index 35520478b..ea11cea44 100644
--- a/app/javascript/mastodon/features/ui/components/disabled_account_banner.jsx
+++ b/app/javascript/mastodon/features/ui/components/disabled_account_banner.jsx
@@ -28,8 +28,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
   },
 });
 
-export default @injectIntl
-@connect(mapStateToProps, mapDispatchToProps)
 class DisabledAccountBanner extends React.PureComponent {
 
   static propTypes = {
@@ -90,3 +88,5 @@ class DisabledAccountBanner extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(DisabledAccountBanner));
diff --git a/app/javascript/mastodon/features/ui/components/embed_modal.jsx b/app/javascript/mastodon/features/ui/components/embed_modal.jsx
index a054dd3cf..baf6be411 100644
--- a/app/javascript/mastodon/features/ui/components/embed_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/embed_modal.jsx
@@ -9,7 +9,6 @@ const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
 });
 
-export default @injectIntl
 class EmbedModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -95,3 +94,5 @@ class EmbedModal extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(EmbedModal);
diff --git a/app/javascript/mastodon/features/ui/components/filter_modal.jsx b/app/javascript/mastodon/features/ui/components/filter_modal.jsx
index 376db961d..32ebaf7b7 100644
--- a/app/javascript/mastodon/features/ui/components/filter_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/filter_modal.jsx
@@ -13,8 +13,6 @@ const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
 });
 
-export default @connect(undefined)
-@injectIntl
 class FilterModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -132,3 +130,5 @@ class FilterModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(injectIntl(FilterModal));
diff --git a/app/javascript/mastodon/features/ui/components/focal_point_modal.jsx b/app/javascript/mastodon/features/ui/components/focal_point_modal.jsx
index 6e8d017ee..11c4c5237 100644
--- a/app/javascript/mastodon/features/ui/components/focal_point_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/focal_point_modal.jsx
@@ -100,8 +100,6 @@ class ImageLoader extends React.PureComponent {
 
 }
 
-export default @connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true })
-@(component => injectIntl(component, { withRef: true }))
 class FocalPointModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -428,3 +426,7 @@ class FocalPointModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps, null, {
+  forwardRef: true,
+})(injectIntl(FocalPointModal, { withRef: true }));
diff --git a/app/javascript/mastodon/features/ui/components/follow_requests_column_link.jsx b/app/javascript/mastodon/features/ui/components/follow_requests_column_link.jsx
index 8d4057782..1cd1b79bc 100644
--- a/app/javascript/mastodon/features/ui/components/follow_requests_column_link.jsx
+++ b/app/javascript/mastodon/features/ui/components/follow_requests_column_link.jsx
@@ -15,8 +15,6 @@ const mapStateToProps = state => ({
   count: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableList()).size,
 });
 
-export default @injectIntl
-@connect(mapStateToProps)
 class FollowRequestsColumnLink extends React.Component {
 
   static propTypes = {
@@ -49,3 +47,5 @@ class FollowRequestsColumnLink extends React.Component {
   }
 
 }
+
+export default injectIntl(connect(mapStateToProps)(FollowRequestsColumnLink));
diff --git a/app/javascript/mastodon/features/ui/components/header.jsx b/app/javascript/mastodon/features/ui/components/header.jsx
index 92adc47a9..c14c6faa7 100644
--- a/app/javascript/mastodon/features/ui/components/header.jsx
+++ b/app/javascript/mastodon/features/ui/components/header.jsx
@@ -22,8 +22,6 @@ const mapDispatchToProps = (dispatch) => ({
   },
 });
 
-export default @withRouter
-@connect(null, mapDispatchToProps)
 class Header extends React.PureComponent {
 
   static contextTypes = {
@@ -85,3 +83,5 @@ class Header extends React.PureComponent {
   }
 
 }
+
+export default withRouter(connect(null, mapDispatchToProps)(Header));
diff --git a/app/javascript/mastodon/features/ui/components/image_modal.jsx b/app/javascript/mastodon/features/ui/components/image_modal.jsx
index 7522c3da5..ca93d7b4e 100644
--- a/app/javascript/mastodon/features/ui/components/image_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/image_modal.jsx
@@ -9,7 +9,6 @@ const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
 });
 
-export default @injectIntl
 class ImageModal extends React.PureComponent {
 
   static propTypes = {
@@ -57,3 +56,5 @@ class ImageModal extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ImageModal);
diff --git a/app/javascript/mastodon/features/ui/components/link_footer.jsx b/app/javascript/mastodon/features/ui/components/link_footer.jsx
index be2111207..68ef015ab 100644
--- a/app/javascript/mastodon/features/ui/components/link_footer.jsx
+++ b/app/javascript/mastodon/features/ui/components/link_footer.jsx
@@ -24,8 +24,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
   },
 });
 
-export default @injectIntl
-@connect(null, mapDispatchToProps)
 class LinkFooter extends React.PureComponent {
 
   static contextTypes = {
@@ -100,3 +98,5 @@ class LinkFooter extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(connect(null, mapDispatchToProps)(LinkFooter));
diff --git a/app/javascript/mastodon/features/ui/components/list_panel.jsx b/app/javascript/mastodon/features/ui/components/list_panel.jsx
index 2f92a9254..fcff4e37d 100644
--- a/app/javascript/mastodon/features/ui/components/list_panel.jsx
+++ b/app/javascript/mastodon/features/ui/components/list_panel.jsx
@@ -20,8 +20,6 @@ const mapStateToProps = state => ({
   lists: getOrderedLists(state),
 });
 
-export default @withRouter
-@connect(mapStateToProps)
 class ListPanel extends ImmutablePureComponent {
 
   static propTypes = {
@@ -53,3 +51,5 @@ class ListPanel extends ImmutablePureComponent {
   }
 
 }
+
+export default withRouter(connect(mapStateToProps)(ListPanel));
diff --git a/app/javascript/mastodon/features/ui/components/media_modal.jsx b/app/javascript/mastodon/features/ui/components/media_modal.jsx
index 086da8cf5..aa179a965 100644
--- a/app/javascript/mastodon/features/ui/components/media_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/media_modal.jsx
@@ -25,8 +25,6 @@ const mapStateToProps = (state, { statusId }) => ({
   language: state.getIn(['statuses', statusId, 'language']),
 });
 
-export default @connect(mapStateToProps, null, null, { forwardRef: true })
-@injectIntl
 class MediaModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -257,3 +255,5 @@ class MediaModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, null, null, { forwardRef: true })(injectIntl(MediaModal));
diff --git a/app/javascript/mastodon/features/ui/components/mute_modal.jsx b/app/javascript/mastodon/features/ui/components/mute_modal.jsx
index b3e0ef56b..fa64bf0f4 100644
--- a/app/javascript/mastodon/features/ui/components/mute_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/mute_modal.jsx
@@ -43,8 +43,6 @@ const mapDispatchToProps = dispatch => {
   };
 };
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class MuteModal extends React.PureComponent {
 
   static propTypes = {
@@ -138,3 +136,5 @@ class MuteModal extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(MuteModal));
diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx
index 755b19349..90a3fcdf7 100644
--- a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx
+++ b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx
@@ -28,7 +28,6 @@ const messages = defineMessages({
   search: { id: 'navigation_bar.search', defaultMessage: 'Search' },
 });
 
-export default @injectIntl
 class NavigationPanel extends React.Component {
 
   static contextTypes = {
@@ -105,3 +104,5 @@ class NavigationPanel extends React.Component {
   }
 
 }
+
+export default injectIntl(NavigationPanel);
diff --git a/app/javascript/mastodon/features/ui/components/report_modal.jsx b/app/javascript/mastodon/features/ui/components/report_modal.jsx
index 22c31eb52..8b505b8bd 100644
--- a/app/javascript/mastodon/features/ui/components/report_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/report_modal.jsx
@@ -30,8 +30,6 @@ const makeMapStateToProps = () => {
   return mapStateToProps;
 };
 
-export default @connect(makeMapStateToProps)
-@injectIntl
 class ReportModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -217,3 +215,5 @@ class ReportModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps)(injectIntl(ReportModal));
diff --git a/app/javascript/mastodon/features/ui/components/video_modal.jsx b/app/javascript/mastodon/features/ui/components/video_modal.jsx
index 1737d52db..99359a58c 100644
--- a/app/javascript/mastodon/features/ui/components/video_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/video_modal.jsx
@@ -11,7 +11,6 @@ const mapStateToProps = (state, { statusId }) => ({
   language: state.getIn(['statuses', statusId, 'language']),
 });
 
-export default @connect(mapStateToProps, null, null, { forwardRef: true })
 class VideoModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -68,3 +67,5 @@ class VideoModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, null, null, { forwardRef: true })(VideoModal);
diff --git a/app/javascript/mastodon/features/ui/components/zoomable_image.jsx b/app/javascript/mastodon/features/ui/components/zoomable_image.jsx
index 7faba4c10..b41d0fe31 100644
--- a/app/javascript/mastodon/features/ui/components/zoomable_image.jsx
+++ b/app/javascript/mastodon/features/ui/components/zoomable_image.jsx
@@ -91,7 +91,6 @@ const normalizeWheel = event => {
   };
 };
 
-export default @injectIntl
 class ZoomableImage extends React.PureComponent {
 
   static propTypes = {
@@ -451,3 +450,5 @@ class ZoomableImage extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ZoomableImage);
diff --git a/app/javascript/mastodon/features/ui/index.jsx b/app/javascript/mastodon/features/ui/index.jsx
index 083707220..0e73f4c09 100644
--- a/app/javascript/mastodon/features/ui/index.jsx
+++ b/app/javascript/mastodon/features/ui/index.jsx
@@ -229,9 +229,6 @@ class SwitchingColumnsArea extends React.PureComponent {
 
 }
 
-export default @connect(mapStateToProps)
-@injectIntl
-@withRouter
 class UI extends React.PureComponent {
 
   static contextTypes = {
@@ -588,3 +585,5 @@ class UI extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(withRouter(UI)));
diff --git a/app/javascript/mastodon/features/video/index.jsx b/app/javascript/mastodon/features/video/index.jsx
index dc722a7a7..58bb45908 100644
--- a/app/javascript/mastodon/features/video/index.jsx
+++ b/app/javascript/mastodon/features/video/index.jsx
@@ -94,7 +94,6 @@ export const fileNameFromURL = str => {
   return pathname.slice(index + 1);
 };
 
-export default @injectIntl
 class Video extends React.PureComponent {
 
   static propTypes = {
@@ -655,3 +654,5 @@ class Video extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(Video);
diff --git a/babel.config.js b/babel.config.js
index b040cc159..3ff98802c 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -17,7 +17,6 @@ module.exports = (api) => {
       ['@babel/env', envOptions],
     ],
     plugins: [
-      ['@babel/proposal-decorators', { legacy: true }],
       ['react-intl', { messagesDir: './build/messages' }],
       'preval',
     ],
diff --git a/package.json b/package.json
index ed9f72e8c..045db8d14 100644
--- a/package.json
+++ b/package.json
@@ -25,7 +25,6 @@
   "private": true,
   "dependencies": {
     "@babel/core": "^7.21.3",
-    "@babel/plugin-proposal-decorators": "^7.21.0",
     "@babel/plugin-transform-react-inline-elements": "^7.21.0",
     "@babel/plugin-transform-runtime": "^7.21.0",
     "@babel/preset-env": "^7.20.2",
diff --git a/yarn.lock b/yarn.lock
index 3d6379246..d2be342c9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -24,7 +24,7 @@
     jsonpointer "^5.0.0"
     leven "^3.1.0"
 
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6":
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
   integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
@@ -71,7 +71,16 @@
     eslint-visitor-keys "^2.1.0"
     semver "^6.3.0"
 
-"@babel/generator@^7.21.3", "@babel/generator@^7.7.2":
+"@babel/generator@^7.17.10":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.6.tgz#9ab2d46d3cbf631f0e80f72e72874a04c3fc12a9"
+  integrity sha512-AIwwoOS8axIC5MZbhNHRLKi3D+DMpvDf9XUcu3pIVAfOHFT45f4AoDAltRbHIQomCipkCZxrNkfpOEHhJz/VKw==
+  dependencies:
+    "@babel/types" "^7.18.6"
+    "@jridgewell/gen-mapping" "^0.3.0"
+    jsesc "^2.5.1"
+
+"@babel/generator@^7.21.3":
   version "7.21.3"
   resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.3.tgz#232359d0874b392df04045d72ce2fd9bb5045fce"
   integrity sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==
@@ -81,6 +90,15 @@
     "@jridgewell/trace-mapping" "^0.3.17"
     jsesc "^2.5.1"
 
+"@babel/generator@^7.7.2":
+  version "7.17.10"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.10.tgz#c281fa35b0c349bbe9d02916f4ae08fc85ed7189"
+  integrity sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg==
+  dependencies:
+    "@babel/types" "^7.17.10"
+    "@jridgewell/gen-mapping" "^0.1.0"
+    jsesc "^2.5.1"
+
 "@babel/helper-annotate-as-pure@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb"
@@ -115,7 +133,7 @@
     lru-cache "^5.1.1"
     semver "^6.3.0"
 
-"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0":
+"@babel/helper-create-class-features-plugin@^7.18.6":
   version "7.21.0"
   resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz#64f49ecb0020532f19b1d014b03bccaa1ab85fb9"
   integrity sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==
@@ -149,6 +167,11 @@
     resolve "^1.14.2"
     semver "^6.1.2"
 
+"@babel/helper-environment-visitor@^7.16.7":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.6.tgz#b7eee2b5b9d70602e59d1a6cad7dd24de7ca6cd7"
+  integrity sha512-8n6gSfn2baOY+qlp+VSzsosjCVGFqWKmDF0cCWOybh52Dw3SEyoWR1KrhMJASjLwIEkkAufZ0xvr+SxLHSpy2Q==
+
 "@babel/helper-environment-visitor@^7.18.9":
   version "7.18.9"
   resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be"
@@ -161,6 +184,14 @@
   dependencies:
     "@babel/types" "^7.18.6"
 
+"@babel/helper-function-name@^7.17.9":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.6.tgz#8334fecb0afba66e6d87a7e8c6bb7fed79926b83"
+  integrity sha512-0mWMxV1aC97dhjCah5U5Ua7668r5ZmSC2DLfH2EZnf9c3/dHZKiFa5pRLMH5tjSl471tY6496ZWk/kjNONBxhw==
+  dependencies:
+    "@babel/template" "^7.18.6"
+    "@babel/types" "^7.18.6"
+
 "@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0":
   version "7.19.0"
   resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c"
@@ -177,7 +208,7 @@
     "@babel/template" "^7.20.7"
     "@babel/types" "^7.21.0"
 
-"@babel/helper-hoist-variables@^7.18.6":
+"@babel/helper-hoist-variables@^7.16.7", "@babel/helper-hoist-variables@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678"
   integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==
@@ -274,7 +305,7 @@
   dependencies:
     "@babel/types" "^7.20.0"
 
-"@babel/helper-split-export-declaration@^7.18.6":
+"@babel/helper-split-export-declaration@^7.16.7", "@babel/helper-split-export-declaration@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075"
   integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==
@@ -286,7 +317,12 @@
   resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63"
   integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==
 
-"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1":
+"@babel/helper-validator-identifier@^7.16.7", "@babel/helper-validator-identifier@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076"
+  integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==
+
+"@babel/helper-validator-identifier@^7.19.1":
   version "7.19.1"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
   integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
@@ -324,7 +360,17 @@
     chalk "^2.0.0"
     js-tokens "^4.0.0"
 
-"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.3":
+"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7":
+  version "7.17.10"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.10.tgz#873b16db82a8909e0fbd7f115772f4b739f6ce78"
+  integrity sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ==
+
+"@babel/parser@^7.17.10":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.6.tgz#845338edecad65ebffef058d3be851f1d28a63bc"
+  integrity sha512-uQVSa9jJUe/G/304lXspfWVpKpK4euFLgGiMQFOCpM/bgcAdeoHwi/OQz23O9GK2osz26ZiXRRV9aV+Yl1O8tw==
+
+"@babel/parser@^7.20.7", "@babel/parser@^7.21.3":
   version "7.21.3"
   resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3"
   integrity sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==
@@ -372,17 +418,6 @@
     "@babel/helper-plugin-utils" "^7.18.6"
     "@babel/plugin-syntax-class-static-block" "^7.14.5"
 
-"@babel/plugin-proposal-decorators@^7.21.0":
-  version "7.21.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.21.0.tgz#70e0c89fdcd7465c97593edb8f628ba6e4199d63"
-  integrity sha512-MfgX49uRrFUTL/HvWtmx3zmpyzMMr4MTj3d527MLlr/4RTT9G/ytFFP7qet2uM2Ve03b+BkpWUpK+lRXnQ+v9w==
-  dependencies:
-    "@babel/helper-create-class-features-plugin" "^7.21.0"
-    "@babel/helper-plugin-utils" "^7.20.2"
-    "@babel/helper-replace-supers" "^7.20.7"
-    "@babel/helper-split-export-declaration" "^7.18.6"
-    "@babel/plugin-syntax-decorators" "^7.21.0"
-
 "@babel/plugin-proposal-dynamic-import@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94"
@@ -513,13 +548,6 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.14.5"
 
-"@babel/plugin-syntax-decorators@^7.21.0":
-  version "7.21.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.21.0.tgz#d2b3f31c3e86fa86e16bb540b7660c55bd7d0e78"
-  integrity sha512-tIoPpGBR8UuM4++ccWN3gifhVvQu7ZizuR1fklhRJrd5ewgbkUS+0KVFeWWxELtn18NTLoW32XV7zyOgIAiz+w==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.20.2"
-
 "@babel/plugin-syntax-dynamic-import@^7.8.3":
   version "7.8.3"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3"
@@ -1054,7 +1082,7 @@
   dependencies:
     regenerator-runtime "^0.13.11"
 
-"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3":
+"@babel/template@^7.18.10", "@babel/template@^7.20.7":
   version "7.20.7"
   resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8"
   integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==
@@ -1063,7 +1091,16 @@
     "@babel/parser" "^7.20.7"
     "@babel/types" "^7.20.7"
 
-"@babel/traverse@^7.18.10", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3", "@babel/traverse@^7.7.2":
+"@babel/template@^7.3.3":
+  version "7.16.7"
+  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
+  integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==
+  dependencies:
+    "@babel/code-frame" "^7.16.7"
+    "@babel/parser" "^7.16.7"
+    "@babel/types" "^7.16.7"
+
+"@babel/traverse@^7.18.10", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3":
   version "7.21.3"
   resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.3.tgz#4747c5e7903d224be71f90788b06798331896f67"
   integrity sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==
@@ -1079,7 +1116,39 @@
     debug "^4.1.0"
     globals "^11.1.0"
 
-"@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
+"@babel/traverse@^7.7.2":
+  version "7.17.10"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.10.tgz#1ee1a5ac39f4eac844e6cf855b35520e5eb6f8b5"
+  integrity sha512-VmbrTHQteIdUUQNTb+zE12SHS/xQVIShmBPhlNP12hD5poF2pbITW1Z4172d03HegaQWhLffdkRJYtAzp0AGcw==
+  dependencies:
+    "@babel/code-frame" "^7.16.7"
+    "@babel/generator" "^7.17.10"
+    "@babel/helper-environment-visitor" "^7.16.7"
+    "@babel/helper-function-name" "^7.17.9"
+    "@babel/helper-hoist-variables" "^7.16.7"
+    "@babel/helper-split-export-declaration" "^7.16.7"
+    "@babel/parser" "^7.17.10"
+    "@babel/types" "^7.17.10"
+    debug "^4.1.0"
+    globals "^11.1.0"
+
+"@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.16.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
+  version "7.17.10"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.10.tgz#d35d7b4467e439fcf06d195f8100e0fea7fc82c4"
+  integrity sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.16.7"
+    to-fast-properties "^2.0.0"
+
+"@babel/types@^7.17.10", "@babel/types@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.6.tgz#5d781dd10a3f0c9f1f931bd19de5eb26ec31acf0"
+  integrity sha512-NdBNzPDwed30fZdDQtVR7ZgaO4UKjuaQFH9VArS+HMnurlOY0JWN+4ROlu/iapMFwjRQU4pOG4StZfDmulEwGA==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.18.6"
+    to-fast-properties "^2.0.0"
+
+"@babel/types@^7.18.10", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3":
   version "7.21.3"
   resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05"
   integrity sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==
-- 
cgit 


From 68a192e7186733885b1d70160170c4772fab7242 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Thu, 30 Mar 2023 15:16:20 +0200
Subject: Change "direct message" nomenclature to "private mention" in web UI
 (#24248)

---
 app/javascript/mastodon/components/status.jsx      |  7 ++++
 .../mastodon/components/status_action_bar.jsx      |  2 +-
 .../features/account/components/header.jsx         |  2 +-
 .../mastodon/features/direct_timeline/index.jsx    |  4 +-
 .../mastodon/features/getting_started/index.jsx    |  2 +-
 .../features/status/components/action_bar.jsx      |  2 +-
 .../features/status/components/detailed_status.jsx | 10 ++++-
 app/javascript/mastodon/features/status/index.jsx  |  2 +-
 .../features/ui/components/navigation_panel.jsx    |  2 +-
 app/javascript/mastodon/locales/af.json            |  1 +
 app/javascript/mastodon/locales/an.json            |  1 +
 app/javascript/mastodon/locales/ar.json            |  1 +
 app/javascript/mastodon/locales/ast.json           |  1 +
 app/javascript/mastodon/locales/be.json            |  1 +
 app/javascript/mastodon/locales/bg.json            |  1 +
 app/javascript/mastodon/locales/bn.json            |  1 +
 app/javascript/mastodon/locales/br.json            |  1 +
 app/javascript/mastodon/locales/bs.json            |  1 +
 app/javascript/mastodon/locales/ca.json            |  1 +
 app/javascript/mastodon/locales/ckb.json           |  1 +
 app/javascript/mastodon/locales/co.json            |  1 +
 app/javascript/mastodon/locales/cs.json            |  1 +
 app/javascript/mastodon/locales/csb.json           |  1 +
 app/javascript/mastodon/locales/cy.json            |  1 +
 app/javascript/mastodon/locales/da.json            |  1 +
 app/javascript/mastodon/locales/de.json            |  1 +
 .../mastodon/locales/defaultMessages.json          | 20 ++++++----
 app/javascript/mastodon/locales/el.json            |  1 +
 app/javascript/mastodon/locales/en-GB.json         |  1 +
 app/javascript/mastodon/locales/en.json            | 13 ++++---
 app/javascript/mastodon/locales/eo.json            |  1 +
 app/javascript/mastodon/locales/es-AR.json         |  1 +
 app/javascript/mastodon/locales/es-MX.json         |  1 +
 app/javascript/mastodon/locales/es.json            |  1 +
 app/javascript/mastodon/locales/et.json            |  1 +
 app/javascript/mastodon/locales/eu.json            |  1 +
 app/javascript/mastodon/locales/fa.json            |  1 +
 app/javascript/mastodon/locales/fi.json            |  1 +
 app/javascript/mastodon/locales/fo.json            |  1 +
 app/javascript/mastodon/locales/fr-QC.json         |  1 +
 app/javascript/mastodon/locales/fr.json            |  1 +
 app/javascript/mastodon/locales/fy.json            |  1 +
 app/javascript/mastodon/locales/ga.json            |  1 +
 app/javascript/mastodon/locales/gd.json            |  1 +
 app/javascript/mastodon/locales/gl.json            |  1 +
 app/javascript/mastodon/locales/he.json            |  1 +
 app/javascript/mastodon/locales/hi.json            |  1 +
 app/javascript/mastodon/locales/hr.json            |  1 +
 app/javascript/mastodon/locales/hu.json            |  1 +
 app/javascript/mastodon/locales/hy.json            |  1 +
 app/javascript/mastodon/locales/id.json            |  1 +
 app/javascript/mastodon/locales/ig.json            |  1 +
 app/javascript/mastodon/locales/io.json            |  1 +
 app/javascript/mastodon/locales/is.json            |  1 +
 app/javascript/mastodon/locales/it.json            |  1 +
 app/javascript/mastodon/locales/ja.json            |  1 +
 app/javascript/mastodon/locales/ka.json            |  1 +
 app/javascript/mastodon/locales/kab.json           |  1 +
 app/javascript/mastodon/locales/kk.json            |  1 +
 app/javascript/mastodon/locales/kn.json            |  1 +
 app/javascript/mastodon/locales/ko.json            |  1 +
 app/javascript/mastodon/locales/ku.json            |  1 +
 app/javascript/mastodon/locales/kw.json            |  1 +
 app/javascript/mastodon/locales/la.json            |  1 +
 app/javascript/mastodon/locales/lt.json            |  1 +
 app/javascript/mastodon/locales/lv.json            |  1 +
 app/javascript/mastodon/locales/mk.json            |  1 +
 app/javascript/mastodon/locales/ml.json            |  1 +
 app/javascript/mastodon/locales/mr.json            |  1 +
 app/javascript/mastodon/locales/ms.json            |  1 +
 app/javascript/mastodon/locales/my.json            |  1 +
 app/javascript/mastodon/locales/nl.json            |  1 +
 app/javascript/mastodon/locales/nn.json            |  1 +
 app/javascript/mastodon/locales/no.json            |  1 +
 app/javascript/mastodon/locales/oc.json            |  1 +
 app/javascript/mastodon/locales/pa.json            |  1 +
 app/javascript/mastodon/locales/pl.json            |  1 +
 app/javascript/mastodon/locales/pt-BR.json         |  1 +
 app/javascript/mastodon/locales/pt-PT.json         |  1 +
 app/javascript/mastodon/locales/ro.json            |  1 +
 app/javascript/mastodon/locales/ru.json            |  1 +
 app/javascript/mastodon/locales/sa.json            |  1 +
 app/javascript/mastodon/locales/sc.json            |  1 +
 app/javascript/mastodon/locales/sco.json           |  1 +
 app/javascript/mastodon/locales/si.json            |  1 +
 app/javascript/mastodon/locales/sk.json            |  1 +
 app/javascript/mastodon/locales/sl.json            |  1 +
 app/javascript/mastodon/locales/sq.json            |  1 +
 app/javascript/mastodon/locales/sr-Latn.json       |  1 +
 app/javascript/mastodon/locales/sr.json            |  1 +
 app/javascript/mastodon/locales/sv.json            |  1 +
 app/javascript/mastodon/locales/szl.json           |  1 +
 app/javascript/mastodon/locales/ta.json            |  1 +
 app/javascript/mastodon/locales/tai.json           |  1 +
 app/javascript/mastodon/locales/te.json            |  1 +
 app/javascript/mastodon/locales/th.json            |  1 +
 app/javascript/mastodon/locales/tr.json            |  1 +
 app/javascript/mastodon/locales/tt.json            |  1 +
 app/javascript/mastodon/locales/ug.json            |  1 +
 app/javascript/mastodon/locales/uk.json            |  1 +
 app/javascript/mastodon/locales/ur.json            |  1 +
 app/javascript/mastodon/locales/uz.json            |  1 +
 app/javascript/mastodon/locales/vi.json            |  1 +
 app/javascript/mastodon/locales/zgh.json           |  1 +
 app/javascript/mastodon/locales/zh-CN.json         |  1 +
 app/javascript/mastodon/locales/zh-HK.json         |  1 +
 app/javascript/mastodon/locales/zh-TW.json         |  1 +
 app/javascript/styles/mastodon-light/diff.scss     | 10 ++++-
 app/javascript/styles/mastodon/components.scss     | 45 +++++++++++++++++++++-
 109 files changed, 191 insertions(+), 26 deletions(-)

(limited to 'app/javascript/mastodon/features/status/components/detailed_status.jsx')

diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status.jsx
index 2d200a55b..281e69874 100644
--- a/app/javascript/mastodon/components/status.jsx
+++ b/app/javascript/mastodon/components/status.jsx
@@ -386,6 +386,13 @@ class Status extends ImmutablePureComponent {
 
       account = status.get('account');
       status  = status.get('reblog');
+    } else if (status.get('visibility') === 'direct') {
+      prepend = (
+        <div className='status__prepend'>
+          <div className='status__prepend-icon-wrapper'><Icon id='at' className='status__prepend-icon' fixedWidth /></div>
+          <FormattedMessage id='status.direct_indicator' defaultMessage='Private mention' />
+        </div>
+      );
     } else if (showThread && status.get('in_reply_to_id') && status.get('in_reply_to_account_id') === status.getIn(['account', 'id'])) {
       const display_name_html = { __html: status.getIn(['account', 'display_name_html']) };
 
diff --git a/app/javascript/mastodon/components/status_action_bar.jsx b/app/javascript/mastodon/components/status_action_bar.jsx
index 08e37e089..7b4031b68 100644
--- a/app/javascript/mastodon/components/status_action_bar.jsx
+++ b/app/javascript/mastodon/components/status_action_bar.jsx
@@ -14,7 +14,7 @@ const messages = defineMessages({
   delete: { id: 'status.delete', defaultMessage: 'Delete' },
   redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
   edit: { id: 'status.edit', defaultMessage: 'Edit' },
-  direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' },
+  direct: { id: 'status.direct', defaultMessage: 'Privately mention @{name}' },
   mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
   mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
   block: { id: 'account.block', defaultMessage: 'Block @{name}' },
diff --git a/app/javascript/mastodon/features/account/components/header.jsx b/app/javascript/mastodon/features/account/components/header.jsx
index be6b17896..72eb7e6b6 100644
--- a/app/javascript/mastodon/features/account/components/header.jsx
+++ b/app/javascript/mastodon/features/account/components/header.jsx
@@ -28,7 +28,7 @@ const messages = defineMessages({
   linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' },
   account_locked: { id: 'account.locked_info', defaultMessage: 'This account privacy status is set to locked. The owner manually reviews who can follow them.' },
   mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
-  direct: { id: 'account.direct', defaultMessage: 'Direct message @{name}' },
+  direct: { id: 'account.direct', defaultMessage: 'Privately mention @{name}' },
   unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
   block: { id: 'account.block', defaultMessage: 'Block @{name}' },
   mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
diff --git a/app/javascript/mastodon/features/direct_timeline/index.jsx b/app/javascript/mastodon/features/direct_timeline/index.jsx
index e2667d8e9..5d4a7c49a 100644
--- a/app/javascript/mastodon/features/direct_timeline/index.jsx
+++ b/app/javascript/mastodon/features/direct_timeline/index.jsx
@@ -11,7 +11,7 @@ import ColumnHeader from 'mastodon/components/column_header';
 import ConversationsListContainer from './containers/conversations_list_container';
 
 const messages = defineMessages({
-  title: { id: 'column.direct', defaultMessage: 'Direct messages' },
+  title: { id: 'column.direct', defaultMessage: 'Private mentions' },
 });
 
 class DirectTimeline extends React.PureComponent {
@@ -91,7 +91,7 @@ class DirectTimeline extends React.PureComponent {
           timelineId='direct'
           onLoadMore={this.handleLoadMore}
           prepend={<div className='follow_requests-unlocked_explanation'><span><FormattedMessage id='compose_form.encryption_warning' defaultMessage='Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.' /> <a href='/terms' target='_blank'><FormattedMessage id='compose_form.direct_message_warning_learn_more' defaultMessage='Learn more' /></a></span></div>}
-          emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
+          emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any private mentions yet. When you send or receive one, it will show up here." />}
         />
 
         <Helmet>
diff --git a/app/javascript/mastodon/features/getting_started/index.jsx b/app/javascript/mastodon/features/getting_started/index.jsx
index ee8a009ee..29659acc7 100644
--- a/app/javascript/mastodon/features/getting_started/index.jsx
+++ b/app/javascript/mastodon/features/getting_started/index.jsx
@@ -23,7 +23,7 @@ const messages = defineMessages({
   settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings' },
   community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
   explore: { id: 'navigation_bar.explore', defaultMessage: 'Explore' },
-  direct: { id: 'navigation_bar.direct', defaultMessage: 'Direct messages' },
+  direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' },
   bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
   preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
   follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
diff --git a/app/javascript/mastodon/features/status/components/action_bar.jsx b/app/javascript/mastodon/features/status/components/action_bar.jsx
index 5cbe4984c..0c74c4cc4 100644
--- a/app/javascript/mastodon/features/status/components/action_bar.jsx
+++ b/app/javascript/mastodon/features/status/components/action_bar.jsx
@@ -13,7 +13,7 @@ const messages = defineMessages({
   delete: { id: 'status.delete', defaultMessage: 'Delete' },
   redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
   edit: { id: 'status.edit', defaultMessage: 'Edit' },
-  direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' },
+  direct: { id: 'status.direct', defaultMessage: 'Privately mention @{name}' },
   mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
   reply: { id: 'status.reply', defaultMessage: 'Reply' },
   reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
diff --git a/app/javascript/mastodon/features/status/components/detailed_status.jsx b/app/javascript/mastodon/features/status/components/detailed_status.jsx
index 8a2194450..e4e572026 100644
--- a/app/javascript/mastodon/features/status/components/detailed_status.jsx
+++ b/app/javascript/mastodon/features/status/components/detailed_status.jsx
@@ -6,7 +6,7 @@ import DisplayName from '../../../components/display_name';
 import StatusContent from '../../../components/status_content';
 import MediaGallery from '../../../components/media_gallery';
 import { Link } from 'react-router-dom';
-import { injectIntl, defineMessages, FormattedDate } from 'react-intl';
+import { injectIntl, defineMessages, FormattedDate, FormattedMessage } from 'react-intl';
 import Card from './card';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import Video from '../../video';
@@ -262,7 +262,13 @@ class DetailedStatus extends ImmutablePureComponent {
 
     return (
       <div style={outerStyle}>
-        <div ref={this.setRef} className={classNames('detailed-status', `detailed-status-${status.get('visibility')}`, { compact })}>
+        <div ref={this.setRef} className={classNames('detailed-status', { compact })}>
+          {status.get('visibility') === 'direct' && (
+            <div className='status__prepend'>
+              <div className='status__prepend-icon-wrapper'><Icon id='at' className='status__prepend-icon' fixedWidth /></div>
+              <FormattedMessage id='status.direct_indicator' defaultMessage='Private mention' />
+            </div>
+          )}
           <a href={`/@${status.getIn(['account', 'acct'])}`} onClick={this.handleAccountClick} className='detailed-status__display-name'>
             <div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={46} /></div>
             <DisplayName account={status.get('account')} localDomain={this.props.domain} />
diff --git a/app/javascript/mastodon/features/status/index.jsx b/app/javascript/mastodon/features/status/index.jsx
index 358ad14c4..1d7cb685f 100644
--- a/app/javascript/mastodon/features/status/index.jsx
+++ b/app/javascript/mastodon/features/status/index.jsx
@@ -630,7 +630,7 @@ class Status extends ImmutablePureComponent {
             {ancestors}
 
             <HotKeys handlers={handlers}>
-              <div className={classNames('focusable', 'detailed-status__wrapper')} tabIndex='0' aria-label={textForScreenReader(intl, status, false)}>
+              <div className={classNames('focusable', 'detailed-status__wrapper', `detailed-status__wrapper-${status.get('visibility')}`)} tabIndex='0' aria-label={textForScreenReader(intl, status, false)}>
                 <DetailedStatus
                   key={`details-${status.get('id')}`}
                   status={status}
diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx
index 90a3fcdf7..ee1a83cc6 100644
--- a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx
+++ b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx
@@ -18,7 +18,7 @@ const messages = defineMessages({
   explore: { id: 'explore.title', defaultMessage: 'Explore' },
   local: { id: 'tabs_bar.local_timeline', defaultMessage: 'Local' },
   federated: { id: 'tabs_bar.federated_timeline', defaultMessage: 'Federated' },
-  direct: { id: 'navigation_bar.direct', defaultMessage: 'Direct messages' },
+  direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' },
   favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
   bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
   lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
diff --git a/app/javascript/mastodon/locales/af.json b/app/javascript/mastodon/locales/af.json
index c04cae948..59dbdfa63 100644
--- a/app/javascript/mastodon/locales/af.json
+++ b/app/javascript/mastodon/locales/af.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/an.json b/app/javascript/mastodon/locales/an.json
index 9f66c8d13..e1414a8f5 100644
--- a/app/javascript/mastodon/locales/an.json
+++ b/app/javascript/mastodon/locales/an.json
@@ -558,6 +558,7 @@
   "status.delete": "Borrar",
   "status.detailed_status": "Vista de conversación detallada",
   "status.direct": "Mensache directo a @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Editar",
   "status.edited": "Editau {date}",
   "status.edited_x_times": "Editau {count, plural, one {{count} vez} other {{count} veces}}",
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index d7d3baf4e..4aec5838d 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -558,6 +558,7 @@
   "status.delete": "احذف",
   "status.detailed_status": "تفاصيل المحادثة",
   "status.direct": "رسالة خاصة إلى @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "تعديل",
   "status.edited": "عُدّل في {date}",
   "status.edited_x_times": "عُدّل {count, plural, zero {} one {مرةً واحدة} two {مرّتان} few {{count} مرات} many {{count} مرة} other {{count} مرة}}",
diff --git a/app/javascript/mastodon/locales/ast.json b/app/javascript/mastodon/locales/ast.json
index 2881911a6..9e356881a 100644
--- a/app/javascript/mastodon/locales/ast.json
+++ b/app/javascript/mastodon/locales/ast.json
@@ -558,6 +558,7 @@
   "status.delete": "Desaniciar",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Unviar un mensaxe direutu a @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Editóse'l {date}",
   "status.edited_x_times": "Editóse {count, plural, one {{count} vegada} other {{count} vegaes}}",
diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json
index ad049dd25..ce51ee507 100644
--- a/app/javascript/mastodon/locales/be.json
+++ b/app/javascript/mastodon/locales/be.json
@@ -558,6 +558,7 @@
   "status.delete": "Выдаліць",
   "status.detailed_status": "Дэтальны агляд размовы",
   "status.direct": "Асабістае паведамленне @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Рэдагаваць",
   "status.edited": "Адрэдагавана {date}",
   "status.edited_x_times": "Рэдагавана {count, plural, one {{count} раз} few {{count} разы} many {{count} разоў} other {{count} разу}}",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index 248fd1ccb..6b6bd1011 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -558,6 +558,7 @@
   "status.delete": "Изтриване",
   "status.detailed_status": "Подробен изглед на разговора",
   "status.direct": "Директно съобщение до @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Редактиране",
   "status.edited": "Редактирано на {date}",
   "status.edited_x_times": "Редактирано {count, plural,one {{count} път} other {{count} пъти}}",
diff --git a/app/javascript/mastodon/locales/bn.json b/app/javascript/mastodon/locales/bn.json
index 7da6f78dc..8dbac2d58 100644
--- a/app/javascript/mastodon/locales/bn.json
+++ b/app/javascript/mastodon/locales/bn.json
@@ -558,6 +558,7 @@
   "status.delete": "মুছে ফেলতে",
   "status.detailed_status": "বিস্তারিত কথোপকথনের হিসেবে দেখতে",
   "status.direct": "@{name} কে সরাসরি লেখা পাঠাতে",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/br.json b/app/javascript/mastodon/locales/br.json
index 5cc331af4..0884047ac 100644
--- a/app/javascript/mastodon/locales/br.json
+++ b/app/javascript/mastodon/locales/br.json
@@ -558,6 +558,7 @@
   "status.delete": "Dilemel",
   "status.detailed_status": "Gwel kaozeadenn munudek",
   "status.direct": "Kas ur c'hannad eeun da @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Kemmañ",
   "status.edited": "Aozet {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/bs.json b/app/javascript/mastodon/locales/bs.json
index d67f73c00..e4dda7c77 100644
--- a/app/javascript/mastodon/locales/bs.json
+++ b/app/javascript/mastodon/locales/bs.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index ebfafcbd3..a65a41111 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -558,6 +558,7 @@
   "status.delete": "Elimina",
   "status.detailed_status": "Vista detallada de la conversa",
   "status.direct": "Missatge directe a @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edita",
   "status.edited": "Editat {date}",
   "status.edited_x_times": "Editat {count, plural, one {{count} vegada} other {{count} vegades}}",
diff --git a/app/javascript/mastodon/locales/ckb.json b/app/javascript/mastodon/locales/ckb.json
index 7623b5b99..095e73688 100644
--- a/app/javascript/mastodon/locales/ckb.json
+++ b/app/javascript/mastodon/locales/ckb.json
@@ -558,6 +558,7 @@
   "status.delete": "سڕینەوە",
   "status.detailed_status": "ڕوانگەی گفتوگۆ بە وردەکاری",
   "status.direct": "پەیامی ڕاستەوخۆ @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "دەستکاری",
   "status.edited": "بەشداری {date}",
   "status.edited_x_times": "دەستکاریکراوە {count, plural, one {{count} کات} other {{count} کات}}",
diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json
index 2ede2311e..dfa8bcd33 100644
--- a/app/javascript/mastodon/locales/co.json
+++ b/app/javascript/mastodon/locales/co.json
@@ -558,6 +558,7 @@
   "status.delete": "Toglie",
   "status.detailed_status": "Vista in ditagliu di a cunversazione",
   "status.direct": "Mandà un missaghju @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json
index 97bc50c86..392312650 100644
--- a/app/javascript/mastodon/locales/cs.json
+++ b/app/javascript/mastodon/locales/cs.json
@@ -558,6 +558,7 @@
   "status.delete": "Smazat",
   "status.detailed_status": "Podrobné zobrazení konverzace",
   "status.direct": "Poslat @{name} přímou zprávu",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Upravit",
   "status.edited": "Upraveno {date}",
   "status.edited_x_times": "Upraveno {count, plural, one {{count}krát} few {{count}krát} many {{count}krát} other {{count}krát}}",
diff --git a/app/javascript/mastodon/locales/csb.json b/app/javascript/mastodon/locales/csb.json
index 03c5af685..b5078462f 100644
--- a/app/javascript/mastodon/locales/csb.json
+++ b/app/javascript/mastodon/locales/csb.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json
index 21c0cd2be..ace8185d9 100644
--- a/app/javascript/mastodon/locales/cy.json
+++ b/app/javascript/mastodon/locales/cy.json
@@ -558,6 +558,7 @@
   "status.delete": "Dileu",
   "status.detailed_status": "Golwg manwl o'r sgwrs",
   "status.direct": "Neges breifat @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Golygu",
   "status.edited": "Golygwyd {date}",
   "status.edited_x_times": "Golygwyd {count, plural, one {waith} two {waith} other {{count} gwaith}}",
diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json
index 6caa763f0..97fb90a8f 100644
--- a/app/javascript/mastodon/locales/da.json
+++ b/app/javascript/mastodon/locales/da.json
@@ -558,6 +558,7 @@
   "status.delete": "Slet",
   "status.detailed_status": "Detaljeret samtalevisning",
   "status.direct": "Direkte besked til @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Redigér",
   "status.edited": "Redigeret {date}",
   "status.edited_x_times": "Redigeret {count, plural, one {{count} gang} other {{count} gange}}",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index dfc478ae5..24cec0228 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -558,6 +558,7 @@
   "status.delete": "Beitrag löschen",
   "status.detailed_status": "Detaillierte Ansicht der Unterhaltung",
   "status.direct": "Direktnachricht an @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Beitrag bearbeiten",
   "status.edited": "Bearbeitet {date}",
   "status.edited_x_times": "{count, plural, one {{count} mal} other {{count} mal}} bearbeitet",
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index 536566945..d8f40aa51 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -560,7 +560,7 @@
         "id": "status.edit"
       },
       {
-        "defaultMessage": "Direct message @{name}",
+        "defaultMessage": "Privately mention @{name}",
         "id": "status.direct"
       },
       {
@@ -761,6 +761,10 @@
         "defaultMessage": "{name} boosted",
         "id": "status.reblogged_by"
       },
+      {
+        "defaultMessage": "Private mention",
+        "id": "status.direct_indicator"
+      },
       {
         "defaultMessage": "Replied to {name}",
         "id": "status.replied_to"
@@ -1109,7 +1113,7 @@
         "id": "account.mention"
       },
       {
-        "defaultMessage": "Direct message @{name}",
+        "defaultMessage": "Privately mention @{name}",
         "id": "account.direct"
       },
       {
@@ -1627,7 +1631,7 @@
         "id": "privacy.private.long"
       },
       {
-        "defaultMessage": "Mentioned people only",
+        "defaultMessage": "Private mention",
         "id": "privacy.direct.short"
       },
       {
@@ -1925,7 +1929,7 @@
   {
     "descriptors": [
       {
-        "defaultMessage": "Direct messages",
+        "defaultMessage": "Private mentions",
         "id": "column.direct"
       },
       {
@@ -1937,7 +1941,7 @@
         "id": "compose_form.direct_message_warning_learn_more"
       },
       {
-        "defaultMessage": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+        "defaultMessage": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
         "id": "empty_column.direct"
       }
     ],
@@ -2432,7 +2436,7 @@
         "id": "navigation_bar.explore"
       },
       {
-        "defaultMessage": "Direct messages",
+        "defaultMessage": "Private mentions",
         "id": "navigation_bar.direct"
       },
       {
@@ -3550,7 +3554,7 @@
         "id": "status.edit"
       },
       {
-        "defaultMessage": "Direct message @{name}",
+        "defaultMessage": "Privately mention @{name}",
         "id": "status.direct"
       },
       {
@@ -4199,7 +4203,7 @@
         "id": "tabs_bar.federated_timeline"
       },
       {
-        "defaultMessage": "Direct messages",
+        "defaultMessage": "Private mentions",
         "id": "navigation_bar.direct"
       },
       {
diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json
index 0813a9857..e972494ad 100644
--- a/app/javascript/mastodon/locales/el.json
+++ b/app/javascript/mastodon/locales/el.json
@@ -558,6 +558,7 @@
   "status.delete": "Διαγραφή",
   "status.detailed_status": "Προβολή λεπτομερειών συζήτησης",
   "status.direct": "Προσωπικό μήνυμα προς @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Επεξεργασία",
   "status.edited": "Επεξεργάστηκε στις {date}",
   "status.edited_x_times": "Επεξεργάστηκε {count, plural, one {{count} φορά} other {{count} φορές}}",
diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json
index 974c6dd84..359cc0388 100644
--- a/app/javascript/mastodon/locales/en-GB.json
+++ b/app/javascript/mastodon/locales/en-GB.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index f4d051940..3bdba3b1d 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blocked",
   "account.browse_more_on_origin_server": "Browse more on the original profile",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Direct message @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "Domain blocked",
   "account.edit_profile": "Edit profile",
@@ -102,7 +102,7 @@
   "column.blocks": "Blocked users",
   "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "Blocked domains",
   "column.favourites": "Favourites",
@@ -216,7 +216,7 @@
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked posts yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no blocked domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "You don't have any favourite posts yet. When you favourite one, it will show up here.",
@@ -314,7 +314,7 @@
   "keyboard_shortcuts.column": "Focus column",
   "keyboard_shortcuts.compose": "Focus compose textarea",
   "keyboard_shortcuts.description": "Description",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "to open private mentions column",
   "keyboard_shortcuts.down": "Move down in the list",
   "keyboard_shortcuts.enter": "Open post",
   "keyboard_shortcuts.favourite": "Favourite post",
@@ -376,7 +376,7 @@
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new post",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Blocked domains",
   "navigation_bar.edit_profile": "Edit profile",
@@ -557,7 +557,8 @@
   "status.copy": "Copy link to post",
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index 93b668d35..29ecea294 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -558,6 +558,7 @@
   "status.delete": "Forigi",
   "status.detailed_status": "Detala konversacia vido",
   "status.direct": "Rekte mesaĝi @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Redakti",
   "status.edited": "Redaktita {date}",
   "status.edited_x_times": "Redactita {count, plural, one {{count} fojon} other {{count} fojojn}}",
diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json
index 93238efbc..f9d6b21d9 100644
--- a/app/javascript/mastodon/locales/es-AR.json
+++ b/app/javascript/mastodon/locales/es-AR.json
@@ -558,6 +558,7 @@
   "status.delete": "Eliminar",
   "status.detailed_status": "Vista de conversación detallada",
   "status.direct": "Mensaje directo para @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Editar",
   "status.edited": "Editado {date}",
   "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} veces}}",
diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json
index 56e891611..d10dfa9b3 100644
--- a/app/javascript/mastodon/locales/es-MX.json
+++ b/app/javascript/mastodon/locales/es-MX.json
@@ -558,6 +558,7 @@
   "status.delete": "Borrar",
   "status.detailed_status": "Vista de conversación detallada",
   "status.direct": "Enviar mensaje a @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Editar",
   "status.edited": "Editado {date}",
   "status.edited_x_times": "Editado {count, plural, one {{count} time} other {{count} veces}}",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index 61e831526..ffdecda88 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -558,6 +558,7 @@
   "status.delete": "Borrar",
   "status.detailed_status": "Vista de conversación detallada",
   "status.direct": "Mensaje directo a @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Editar",
   "status.edited": "Editado {date}",
   "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} veces}}",
diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json
index 845ac3a08..e8874a556 100644
--- a/app/javascript/mastodon/locales/et.json
+++ b/app/javascript/mastodon/locales/et.json
@@ -558,6 +558,7 @@
   "status.delete": "Kustuta",
   "status.detailed_status": "Detailne vestluskuva",
   "status.direct": "Saada otsesõnum @{name}'ile",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Muuda",
   "status.edited": "{date} muudetud",
   "status.edited_x_times": "Muudetud {count, plural, one{{count} kord} other {{count} korda}}",
diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json
index e5f2116bc..bf3f3e0e3 100644
--- a/app/javascript/mastodon/locales/eu.json
+++ b/app/javascript/mastodon/locales/eu.json
@@ -558,6 +558,7 @@
   "status.delete": "Ezabatu",
   "status.detailed_status": "Elkarrizketaren ikuspegi xehetsua",
   "status.direct": "Mezu zuzena @{name}(r)i",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Editatu",
   "status.edited": "Editatua {date}",
   "status.edited_x_times": "{count, plural, one {behin} other {{count} aldiz}} editatua",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index acb7525b2..a3eca3103 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -558,6 +558,7 @@
   "status.delete": "حذف",
   "status.detailed_status": "نمایش کامل گفتگو",
   "status.direct": "پیام مستقیم به ‎@{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "ویرایش",
   "status.edited": "ویرایش شده در {date}",
   "status.edited_x_times": "{count, plural, one {{count} مرتبه} other {{count} مرتبه}} ویرایش شد",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index f8c61c1e5..3d9506513 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -558,6 +558,7 @@
   "status.delete": "Poista",
   "status.detailed_status": "Yksityiskohtainen keskustelunäkymä",
   "status.direct": "Yksityisviesti käyttäjälle @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Muokkaa",
   "status.edited": "Muokattu {date}",
   "status.edited_x_times": "Muokattu {count, plural, one {{count} kerran} other {{count} kertaa}}",
diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json
index db2247c4b..778b0e028 100644
--- a/app/javascript/mastodon/locales/fo.json
+++ b/app/javascript/mastodon/locales/fo.json
@@ -558,6 +558,7 @@
   "status.delete": "Strika",
   "status.detailed_status": "Útgreinað samrøðusýni",
   "status.direct": "Beinleiðis boð @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Rætta",
   "status.edited": "Rættað {date}",
   "status.edited_x_times": "Rættað {count, plural, one {{count} ferð} other {{count} ferð}}",
diff --git a/app/javascript/mastodon/locales/fr-QC.json b/app/javascript/mastodon/locales/fr-QC.json
index fe7055d57..e14b816bb 100644
--- a/app/javascript/mastodon/locales/fr-QC.json
+++ b/app/javascript/mastodon/locales/fr-QC.json
@@ -558,6 +558,7 @@
   "status.delete": "Supprimer",
   "status.detailed_status": "Vue détaillée de la conversation",
   "status.direct": "Envoyer un message direct à @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Modifier",
   "status.edited": "Modifiée le {date}",
   "status.edited_x_times": "Modifiée {count, plural, one {{count} fois} other {{count} fois}}",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index 2c38ad496..8f2a886fb 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -558,6 +558,7 @@
   "status.delete": "Supprimer",
   "status.detailed_status": "Vue détaillée de la conversation",
   "status.direct": "Envoyer un message direct à @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Éditer",
   "status.edited": "Édité le {date}",
   "status.edited_x_times": "Edité {count, plural, one {{count} fois} other {{count} fois}}",
diff --git a/app/javascript/mastodon/locales/fy.json b/app/javascript/mastodon/locales/fy.json
index 4ac165471..f806494b7 100644
--- a/app/javascript/mastodon/locales/fy.json
+++ b/app/javascript/mastodon/locales/fy.json
@@ -558,6 +558,7 @@
   "status.delete": "Fuortsmite",
   "status.detailed_status": "Detaillearre petearoersjoch",
   "status.direct": "@{name} in direkt berjocht stjoere",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Bewurkje",
   "status.edited": "Bewurke op {date}",
   "status.edited_x_times": "{count, plural, one {{count} kear} other {{count} kearen}} bewurke",
diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json
index 12769ba13..8fc21a5a2 100644
--- a/app/javascript/mastodon/locales/ga.json
+++ b/app/javascript/mastodon/locales/ga.json
@@ -558,6 +558,7 @@
   "status.delete": "Scrios",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Seol teachtaireacht dhíreach chuig @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Cuir in eagar",
   "status.edited": "Curtha in eagar in {date}",
   "status.edited_x_times": "Curtha in eagar {count, plural, one {{count} uair amháin} two {{count} uair} few {{count} uair} many {{count} uair} other {{count} uair}}",
diff --git a/app/javascript/mastodon/locales/gd.json b/app/javascript/mastodon/locales/gd.json
index f5eb5c140..e4ae57952 100644
--- a/app/javascript/mastodon/locales/gd.json
+++ b/app/javascript/mastodon/locales/gd.json
@@ -558,6 +558,7 @@
   "status.delete": "Sguab às",
   "status.detailed_status": "Mion-shealladh a’ chòmhraidh",
   "status.direct": "Cuir teachdaireachd dhìreach gu @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Deasaich",
   "status.edited": "Air a dheasachadh {date}",
   "status.edited_x_times": "Chaidh a dheasachadh {count, plural, one {{counter} turas} two {{counter} thuras} few {{counter} tursan} other {{counter} turas}}",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index dc5bd31d6..b1ce477a6 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -558,6 +558,7 @@
   "status.delete": "Eliminar",
   "status.detailed_status": "Vista detallada da conversa",
   "status.direct": "Mensaxe directa a @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Editar",
   "status.edited": "Editado {date}",
   "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} veces}}",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index b8aced8fb..a827e4378 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -558,6 +558,7 @@
   "status.delete": "מחיקה",
   "status.detailed_status": "תצוגת שיחה מפורטת",
   "status.direct": "הודעה ישירה ל@{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "עריכה",
   "status.edited": "נערך ב{date}",
   "status.edited_x_times": "נערך {count, plural, one {פעם {count}} other {{count} פעמים}}",
diff --git a/app/javascript/mastodon/locales/hi.json b/app/javascript/mastodon/locales/hi.json
index 8b0569860..8e311c945 100644
--- a/app/javascript/mastodon/locales/hi.json
+++ b/app/javascript/mastodon/locales/hi.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index 2ee74d460..196d43e60 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -558,6 +558,7 @@
   "status.delete": "Obriši",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Uredi",
   "status.edited": "Uređeno {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index eaffa89e4..c69b58e43 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -558,6 +558,7 @@
   "status.delete": "Törlés",
   "status.detailed_status": "Részletes beszélgetési nézet",
   "status.direct": "Közvetlen üzenet @{name} számára",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Szerkesztés",
   "status.edited": "Szerkesztve: {date}",
   "status.edited_x_times": "{count, plural, one {{count} alkalommal} other {{count} alkalommal}} szerkesztve",
diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json
index 87ecee646..35c94f829 100644
--- a/app/javascript/mastodon/locales/hy.json
+++ b/app/javascript/mastodon/locales/hy.json
@@ -558,6 +558,7 @@
   "status.delete": "Ջնջել",
   "status.detailed_status": "Շղթայի ընդլայնուած դիտում",
   "status.direct": "Նամակ գրել {name} -ին",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Խմբագրել",
   "status.edited": "Խմբագրուել է՝ {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index 1f93219c8..038013448 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -558,6 +558,7 @@
   "status.delete": "Hapus",
   "status.detailed_status": "Tampilan detail percakapan",
   "status.direct": "Pesan langsung @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Diedit {date}",
   "status.edited_x_times": "Diedit {count, plural, other {{count} kali}}",
diff --git a/app/javascript/mastodon/locales/ig.json b/app/javascript/mastodon/locales/ig.json
index 3d2cd7772..830f0a19d 100644
--- a/app/javascript/mastodon/locales/ig.json
+++ b/app/javascript/mastodon/locales/ig.json
@@ -558,6 +558,7 @@
   "status.delete": "Hichapụ",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index b6f61d23b..00ae025f8 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -558,6 +558,7 @@
   "status.delete": "Efacar",
   "status.detailed_status": "Detala konversvido",
   "status.direct": "Direta mesajigez @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Modifikez",
   "status.edited": "Modifikesis ye {date}",
   "status.edited_x_times": "Modifikesis {count, plural, one {{count} foyo} other {{count} foyi}}",
diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json
index 3611b26ed..50e20aded 100644
--- a/app/javascript/mastodon/locales/is.json
+++ b/app/javascript/mastodon/locales/is.json
@@ -558,6 +558,7 @@
   "status.delete": "Eyða",
   "status.detailed_status": "Nákvæm spjallþráðasýn",
   "status.direct": "Bein skilaboð @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Breyta",
   "status.edited": "Breytt {date}",
   "status.edited_x_times": "Breytt {count, plural, one {{count} sinni} other {{count} sinnum}}",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index ce8f45fab..108cd73c8 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -558,6 +558,7 @@
   "status.delete": "Elimina",
   "status.detailed_status": "Vista conversazione dettagliata",
   "status.direct": "Messaggio diretto a @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Modifica",
   "status.edited": "Modificato il {date}",
   "status.edited_x_times": "Modificato {count, plural, one {{count} volta} other {{count} volte}}",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index 92e9da6ee..64f073fd3 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -558,6 +558,7 @@
   "status.delete": "削除",
   "status.detailed_status": "詳細な会話ビュー",
   "status.direct": "@{name}さんにダイレクトメッセージ",
+  "status.direct_indicator": "Private mention",
   "status.edit": "編集",
   "status.edited": "{date}に編集",
   "status.edited_x_times": "{count}回編集",
diff --git a/app/javascript/mastodon/locales/ka.json b/app/javascript/mastodon/locales/ka.json
index 9ebdfc989..b6829f5de 100644
--- a/app/javascript/mastodon/locales/ka.json
+++ b/app/javascript/mastodon/locales/ka.json
@@ -558,6 +558,7 @@
   "status.delete": "წაშლა",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "პირდაპირი წერილი @{name}-ს",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json
index cf02895c5..439da7ce9 100644
--- a/app/javascript/mastodon/locales/kab.json
+++ b/app/javascript/mastodon/locales/kab.json
@@ -558,6 +558,7 @@
   "status.delete": "Kkes",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Izen usrid i @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Ẓreg",
   "status.edited": "Tettwaẓreg deg {date}",
   "status.edited_x_times": "Tettwaẓreg {count, plural, one {{count} n tikkelt} other {{count} n tikkal}}",
diff --git a/app/javascript/mastodon/locales/kk.json b/app/javascript/mastodon/locales/kk.json
index 6a9195c4b..f360fa209 100644
--- a/app/javascript/mastodon/locales/kk.json
+++ b/app/javascript/mastodon/locales/kk.json
@@ -558,6 +558,7 @@
   "status.delete": "Өшіру",
   "status.detailed_status": "Толық пікірталас көрінісі",
   "status.direct": "Хат жіберу @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/kn.json b/app/javascript/mastodon/locales/kn.json
index f81a47fa9..b89b4a31d 100644
--- a/app/javascript/mastodon/locales/kn.json
+++ b/app/javascript/mastodon/locales/kn.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index f26f0b16a..edb96f116 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -558,6 +558,7 @@
   "status.delete": "삭제",
   "status.detailed_status": "대화 자세히 보기",
   "status.direct": "@{name}에게 다이렉트 메시지",
+  "status.direct_indicator": "Private mention",
   "status.edit": "수정",
   "status.edited": "{date}에 편집됨",
   "status.edited_x_times": "{count}번 수정됨",
diff --git a/app/javascript/mastodon/locales/ku.json b/app/javascript/mastodon/locales/ku.json
index c927d9658..5fb8aff26 100644
--- a/app/javascript/mastodon/locales/ku.json
+++ b/app/javascript/mastodon/locales/ku.json
@@ -558,6 +558,7 @@
   "status.delete": "Jê bibe",
   "status.detailed_status": "Dîtina axaftina berfireh",
   "status.direct": "Peyama rasterast @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Serrast bike",
   "status.edited": "Di {date} de hate serrastkirin",
   "status.edited_x_times": "{count, plural, one {{count} car} other {{count} car}} hate serrastkirin",
diff --git a/app/javascript/mastodon/locales/kw.json b/app/javascript/mastodon/locales/kw.json
index 9a20c2ebf..c1f25017d 100644
--- a/app/javascript/mastodon/locales/kw.json
+++ b/app/javascript/mastodon/locales/kw.json
@@ -558,6 +558,7 @@
   "status.delete": "Dilea",
   "status.detailed_status": "Gwel kesklapp a-vanyl",
   "status.direct": "Messach didro dhe @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/la.json b/app/javascript/mastodon/locales/la.json
index 173ca7c61..84a8bc560 100644
--- a/app/javascript/mastodon/locales/la.json
+++ b/app/javascript/mastodon/locales/la.json
@@ -558,6 +558,7 @@
   "status.delete": "Oblitterare",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Recolere",
   "status.edited": "Recultum {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json
index fd4bbdd95..99e669c95 100644
--- a/app/javascript/mastodon/locales/lt.json
+++ b/app/javascript/mastodon/locales/lt.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json
index 9a3e3219e..2f1f70f14 100644
--- a/app/javascript/mastodon/locales/lv.json
+++ b/app/javascript/mastodon/locales/lv.json
@@ -558,6 +558,7 @@
   "status.delete": "Dzēst",
   "status.detailed_status": "Detalizēts sarunas skats",
   "status.direct": "Privāta ziņa @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Rediģēt",
   "status.edited": "Rediģēts {date}",
   "status.edited_x_times": "Rediģēts {count, plural, one {{count} reizi} other {{count} reizes}}",
diff --git a/app/javascript/mastodon/locales/mk.json b/app/javascript/mastodon/locales/mk.json
index a84cc9168..138d17b9d 100644
--- a/app/javascript/mastodon/locales/mk.json
+++ b/app/javascript/mastodon/locales/mk.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/ml.json b/app/javascript/mastodon/locales/ml.json
index 5d29c55d0..6c4975de2 100644
--- a/app/javascript/mastodon/locales/ml.json
+++ b/app/javascript/mastodon/locales/ml.json
@@ -558,6 +558,7 @@
   "status.delete": "മായ്ക്കുക",
   "status.detailed_status": "വിശദമായ സംഭാഷണ കാഴ്‌ച",
   "status.direct": "@{name} ന് നേരിട്ട് മെസേജ് അയക്കുക",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/mr.json b/app/javascript/mastodon/locales/mr.json
index 12a29f503..9388f4792 100644
--- a/app/javascript/mastodon/locales/mr.json
+++ b/app/javascript/mastodon/locales/mr.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/ms.json b/app/javascript/mastodon/locales/ms.json
index 3a374884b..317efb4c4 100644
--- a/app/javascript/mastodon/locales/ms.json
+++ b/app/javascript/mastodon/locales/ms.json
@@ -558,6 +558,7 @@
   "status.delete": "Padam",
   "status.detailed_status": "Paparan perbualan terperinci",
   "status.direct": "Mesej terus @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Sunting",
   "status.edited": "Disunting {date}",
   "status.edited_x_times": "Disunting {count, plural, other {{count} kali}}",
diff --git a/app/javascript/mastodon/locales/my.json b/app/javascript/mastodon/locales/my.json
index 3679fcce0..502e8ca97 100644
--- a/app/javascript/mastodon/locales/my.json
+++ b/app/javascript/mastodon/locales/my.json
@@ -558,6 +558,7 @@
   "status.delete": "ဖျက်ရန်",
   "status.detailed_status": "အသေးစိတ်စကားပြောဆိုမှုမြင်ကွင်း",
   "status.direct": "@{name} ကို တိုက်ရိုက်စာပို့မည်",
+  "status.direct_indicator": "Private mention",
   "status.edit": "ပြင်ဆင်ရန်",
   "status.edited": "{date} ကို ပြင်ဆင်ပြီးပါပြီ",
   "status.edited_x_times": "{count, plural, one {{count} time} other {{count} times}} ပြင်ဆင်ခဲ့သည်",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index c2679ddef..359a1f200 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -558,6 +558,7 @@
   "status.delete": "Verwijderen",
   "status.detailed_status": "Uitgebreide gespreksweergave",
   "status.direct": "@{name} een direct bericht sturen",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Bewerken",
   "status.edited": "Bewerkt op {date}",
   "status.edited_x_times": "{count, plural, one {{count} keer} other {{count} keer}} bewerkt",
diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json
index 76b1ebf2a..d3b74204a 100644
--- a/app/javascript/mastodon/locales/nn.json
+++ b/app/javascript/mastodon/locales/nn.json
@@ -558,6 +558,7 @@
   "status.delete": "Slett",
   "status.detailed_status": "Detaljert samtalevisning",
   "status.direct": "Send melding til @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Rediger",
   "status.edited": "Redigert {date}",
   "status.edited_x_times": "Redigert {count, plural, one {{count} gong} other {{count} gonger}}",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index 372902e8b..5b0821946 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -558,6 +558,7 @@
   "status.delete": "Slett",
   "status.detailed_status": "Detaljert samtalevisning",
   "status.direct": "Send direktemelding til @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Rediger",
   "status.edited": "Redigert {date}",
   "status.edited_x_times": "Redigert {count, plural,one {{count} gang} other {{count} ganger}}",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index cde922c05..255d6f276 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -558,6 +558,7 @@
   "status.delete": "Escafar",
   "status.detailed_status": "Vista detalhada de la convèrsa",
   "status.direct": "Messatge per @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Modificar",
   "status.edited": "Modificat {date}",
   "status.edited_x_times": "Modificat {count, plural, un {{count} còp} other {{count} còps}}",
diff --git a/app/javascript/mastodon/locales/pa.json b/app/javascript/mastodon/locales/pa.json
index b81bb1356..ee4d3a7f9 100644
--- a/app/javascript/mastodon/locales/pa.json
+++ b/app/javascript/mastodon/locales/pa.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index 3e979dbb2..702c5b4d2 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -558,6 +558,7 @@
   "status.delete": "Usuń",
   "status.detailed_status": "Szczegółowy widok konwersacji",
   "status.direct": "Wyślij wiadomość bezpośrednią do @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edytuj",
   "status.edited": "Edytowano {date}",
   "status.edited_x_times": "Edytowano {count, plural, one {{count} raz} other {{count} razy}}",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index 249787438..8913f21e5 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -558,6 +558,7 @@
   "status.delete": "Excluir",
   "status.detailed_status": "Visão detalhada da conversa",
   "status.direct": "Enviar toot direto para @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Editar",
   "status.edited": "Editado em {date}",
   "status.edited_x_times": "Editado {count, plural, one {{count} hora} other {{count} vezes}}",
diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json
index d72c5c700..99f9c156d 100644
--- a/app/javascript/mastodon/locales/pt-PT.json
+++ b/app/javascript/mastodon/locales/pt-PT.json
@@ -558,6 +558,7 @@
   "status.delete": "Eliminar",
   "status.detailed_status": "Vista pormenorizada da conversa",
   "status.direct": "Mensagem direta @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Editar",
   "status.edited": "Editado em {date}",
   "status.edited_x_times": "Editado {count, plural,one {{count} vez} other {{count} vezes}}",
diff --git a/app/javascript/mastodon/locales/ro.json b/app/javascript/mastodon/locales/ro.json
index c50a6a12b..0c331b425 100644
--- a/app/javascript/mastodon/locales/ro.json
+++ b/app/javascript/mastodon/locales/ro.json
@@ -558,6 +558,7 @@
   "status.delete": "Șterge",
   "status.detailed_status": "Conversația detaliată",
   "status.direct": "Mesaj direct către @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Modifică",
   "status.edited": "Modificat în data de {date}",
   "status.edited_x_times": "Modificată {count, plural, one {o dată} few {de {count} ori} other {de {count} de ori}}",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index f84be48f7..8820a67e6 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -558,6 +558,7 @@
   "status.delete": "Удалить",
   "status.detailed_status": "Подробный просмотр обсуждения",
   "status.direct": "Написать @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Изменить",
   "status.edited": "Последнее изменение: {date}",
   "status.edited_x_times": "{count, plural, one {{count} изменение} many {{count} изменений} other {{count} изменения}}",
diff --git a/app/javascript/mastodon/locales/sa.json b/app/javascript/mastodon/locales/sa.json
index 13e5cbb57..15c6fc02e 100644
--- a/app/javascript/mastodon/locales/sa.json
+++ b/app/javascript/mastodon/locales/sa.json
@@ -558,6 +558,7 @@
   "status.delete": "मार्जय",
   "status.detailed_status": "विस्तृतसंभाषणदृश्यम्",
   "status.direct": "प्रत्यक्षसन्देशः @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "सम्पादय",
   "status.edited": "सम्पादितं {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} वारम्} other {{count} वारम्}}",
diff --git a/app/javascript/mastodon/locales/sc.json b/app/javascript/mastodon/locales/sc.json
index 38aa39f3f..b61ac537c 100644
--- a/app/javascript/mastodon/locales/sc.json
+++ b/app/javascript/mastodon/locales/sc.json
@@ -558,6 +558,7 @@
   "status.delete": "Cantzella",
   "status.detailed_status": "Visualizatzione de detàlliu de arresonada",
   "status.direct": "Messàgiu deretu a @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/sco.json b/app/javascript/mastodon/locales/sco.json
index 1abcac47e..f32d8d2e7 100644
--- a/app/javascript/mastodon/locales/sco.json
+++ b/app/javascript/mastodon/locales/sco.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailt conversation view",
   "status.direct": "Direck message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Editit {date}",
   "status.edited_x_times": "Editit {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/si.json b/app/javascript/mastodon/locales/si.json
index f13aca561..3e96be5fe 100644
--- a/app/javascript/mastodon/locales/si.json
+++ b/app/javascript/mastodon/locales/si.json
@@ -558,6 +558,7 @@
   "status.delete": "මකන්න",
   "status.detailed_status": "විස්තරාත්මක සංවාද දැක්ම",
   "status.direct": "@{name} සෘජු පණිවිඩයක්",
+  "status.direct_indicator": "Private mention",
   "status.edit": "සංස්කරණය",
   "status.edited": "සංශෝධිතයි {date}",
   "status.edited_x_times": "සංශෝධිතයි {count, plural, one {වාර {count}} other {වාර {count}}}",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index b3da4e07b..e2f558b9b 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -558,6 +558,7 @@
   "status.delete": "Zmazať",
   "status.detailed_status": "Podrobný náhľad celej konverzácie",
   "status.direct": "Priama správa pre @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Uprav",
   "status.edited": "Upravené {date}",
   "status.edited_x_times": "Upravený {count, plural, one {{count} krát} other {{count} krát}}",
diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json
index 331d9aa20..78502dd07 100644
--- a/app/javascript/mastodon/locales/sl.json
+++ b/app/javascript/mastodon/locales/sl.json
@@ -558,6 +558,7 @@
   "status.delete": "Izbriši",
   "status.detailed_status": "Podroben pogled pogovora",
   "status.direct": "Neposredno sporočilo @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Uredi",
   "status.edited": "Urejeno {date}",
   "status.edited_x_times": "Urejeno {count, plural, one {#-krat} two {#-krat} few {#-krat} other {#-krat}}",
diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json
index 2c4c8cd89..7322b12ae 100644
--- a/app/javascript/mastodon/locales/sq.json
+++ b/app/javascript/mastodon/locales/sq.json
@@ -558,6 +558,7 @@
   "status.delete": "Fshije",
   "status.detailed_status": "Pamje e hollësishme bisede",
   "status.direct": "Mesazh i drejtpërdrejtë për @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Përpunojeni",
   "status.edited": "Përpunuar më {date}",
   "status.edited_x_times": "Përpunuar {count, plural, one {{count} herë} other {{count} herë}}",
diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json
index 16f7a0fd9..904396610 100644
--- a/app/javascript/mastodon/locales/sr-Latn.json
+++ b/app/javascript/mastodon/locales/sr-Latn.json
@@ -558,6 +558,7 @@
   "status.delete": "Izbriši",
   "status.detailed_status": "Detaljan prikaz razgovora",
   "status.direct": "Pošalji poruku @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Uredi",
   "status.edited": "Uređeno {date}",
   "status.edited_x_times": "Uređeno {count, plural, one {{count} put} other {{count} puta}}",
diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json
index 6d983496f..665d561c6 100644
--- a/app/javascript/mastodon/locales/sr.json
+++ b/app/javascript/mastodon/locales/sr.json
@@ -558,6 +558,7 @@
   "status.delete": "Избриши",
   "status.detailed_status": "Детаљан приказ разговора",
   "status.direct": "Пошаљи поруку @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Уреди",
   "status.edited": "Уређено {date}",
   "status.edited_x_times": "Уређено {count, plural, one {{count} пут} other {{count} пута}}",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index fe573949d..90462f026 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -558,6 +558,7 @@
   "status.delete": "Radera",
   "status.detailed_status": "Detaljerad samtalsvy",
   "status.direct": "Direktmeddela @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Redigera",
   "status.edited": "Ändrad {date}",
   "status.edited_x_times": "Redigerad {count, plural, one {{count} gång} other {{count} gånger}}",
diff --git a/app/javascript/mastodon/locales/szl.json b/app/javascript/mastodon/locales/szl.json
index 587cae5b8..6ec6e8439 100644
--- a/app/javascript/mastodon/locales/szl.json
+++ b/app/javascript/mastodon/locales/szl.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/ta.json b/app/javascript/mastodon/locales/ta.json
index fa2ad1b1c..521b58fe5 100644
--- a/app/javascript/mastodon/locales/ta.json
+++ b/app/javascript/mastodon/locales/ta.json
@@ -558,6 +558,7 @@
   "status.delete": "நீக்கு",
   "status.detailed_status": "விரிவான உரையாடல் காட்சி",
   "status.direct": "நேரடி செய்தி @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/tai.json b/app/javascript/mastodon/locales/tai.json
index 37f8add8f..4c32ffcb5 100644
--- a/app/javascript/mastodon/locales/tai.json
+++ b/app/javascript/mastodon/locales/tai.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/te.json b/app/javascript/mastodon/locales/te.json
index 190db00a0..2a6f612d3 100644
--- a/app/javascript/mastodon/locales/te.json
+++ b/app/javascript/mastodon/locales/te.json
@@ -558,6 +558,7 @@
   "status.delete": "తొలగించు",
   "status.detailed_status": "వివరణాత్మక సంభాషణ వీక్షణ",
   "status.direct": "@{name}కు నేరుగా సందేశం పంపు",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index c7484b118..eb7de5aa5 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -558,6 +558,7 @@
   "status.delete": "ลบ",
   "status.detailed_status": "มุมมองการสนทนาโดยละเอียด",
   "status.direct": "ส่งข้อความโดยตรงถึง @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "แก้ไข",
   "status.edited": "แก้ไขเมื่อ {date}",
   "status.edited_x_times": "แก้ไข {count, plural, other {{count} ครั้ง}}",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index fd7546e1d..7e406f5f8 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -558,6 +558,7 @@
   "status.delete": "Sil",
   "status.detailed_status": "Ayrıntılı sohbet görünümü",
   "status.direct": "@{name} adlı kişiye direkt mesaj",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Düzenle",
   "status.edited": "{date} tarihinde düzenlenmiş",
   "status.edited_x_times": "{count, plural, one {{count} kez} other {{count} kez}} düzenlendi",
diff --git a/app/javascript/mastodon/locales/tt.json b/app/javascript/mastodon/locales/tt.json
index beffae95c..8307a455c 100644
--- a/app/javascript/mastodon/locales/tt.json
+++ b/app/javascript/mastodon/locales/tt.json
@@ -558,6 +558,7 @@
   "status.delete": "Бетерү",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Үзгәртү",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/ug.json b/app/javascript/mastodon/locales/ug.json
index b81bb1356..ee4d3a7f9 100644
--- a/app/javascript/mastodon/locales/ug.json
+++ b/app/javascript/mastodon/locales/ug.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index cbdfef527..9cc83b39d 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -558,6 +558,7 @@
   "status.delete": "Видалити",
   "status.detailed_status": "Детальний вигляд бесіди",
   "status.direct": "Пряме повідомлення до @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Редагувати",
   "status.edited": "Відредаговано {date}",
   "status.edited_x_times": "Відредаговано {count, plural, one {{count} раз} few {{count} рази} many {{counter} разів} other {{counter} разів}}",
diff --git a/app/javascript/mastodon/locales/ur.json b/app/javascript/mastodon/locales/ur.json
index bbe7fbd23..ad21ef963 100644
--- a/app/javascript/mastodon/locales/ur.json
+++ b/app/javascript/mastodon/locales/ur.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/uz.json b/app/javascript/mastodon/locales/uz.json
index 6dcc0e3d8..9c8675208 100644
--- a/app/javascript/mastodon/locales/uz.json
+++ b/app/javascript/mastodon/locales/uz.json
@@ -558,6 +558,7 @@
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json
index fec79a2af..ad7518d98 100644
--- a/app/javascript/mastodon/locales/vi.json
+++ b/app/javascript/mastodon/locales/vi.json
@@ -558,6 +558,7 @@
   "status.delete": "Xóa",
   "status.detailed_status": "Xem chi tiết thêm",
   "status.direct": "Nhắn riêng @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Sửa",
   "status.edited": "Đã sửa {date}",
   "status.edited_x_times": "Đã sửa {count, plural, other {{count} lần}}",
diff --git a/app/javascript/mastodon/locales/zgh.json b/app/javascript/mastodon/locales/zgh.json
index f5ca66b3a..700a6d6ab 100644
--- a/app/javascript/mastodon/locales/zgh.json
+++ b/app/javascript/mastodon/locales/zgh.json
@@ -558,6 +558,7 @@
   "status.delete": "ⴽⴽⵙ",
   "status.detailed_status": "Detailed conversation view",
   "status.direct": "ⵜⵓⵣⵉⵏⵜ ⵜⵓⵙⵔⵉⴷⵜ ⵉ @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index 3d0290e5d..75554c9ea 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -558,6 +558,7 @@
   "status.delete": "删除",
   "status.detailed_status": "详细的对话视图",
   "status.direct": "私信 @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "编辑",
   "status.edited": "编辑于 {date}",
   "status.edited_x_times": "共编辑 {count, plural, one {{count} 次} other {{count} 次}}",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index 86c14b180..db272a35a 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -558,6 +558,7 @@
   "status.delete": "刪除",
   "status.detailed_status": "詳細對話內容",
   "status.direct": "私訊 @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "編輯",
   "status.edited": "編輯於 {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} 次} other {{count} 次}}",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index 6c6871699..1a73ed6f2 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -558,6 +558,7 @@
   "status.delete": "刪除",
   "status.detailed_status": "詳細的對話內容",
   "status.direct": "發送私訊給 @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "編輯",
   "status.edited": "編輯於 {date}",
   "status.edited_x_times": "已編輯 {count, plural, one {{count} 次} other {{count} 次}}",
diff --git a/app/javascript/styles/mastodon-light/diff.scss b/app/javascript/styles/mastodon-light/diff.scss
index 58f161f81..7498477ca 100644
--- a/app/javascript/styles/mastodon-light/diff.scss
+++ b/app/javascript/styles/mastodon-light/diff.scss
@@ -264,7 +264,7 @@ html {
 
 // Change the background colors of statuses
 .focusable:focus {
-  background: $ui-base-color;
+  background: lighten($white, 4%);
 }
 
 .detailed-status,
@@ -697,3 +697,11 @@ html {
     url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 8%))}'/></svg>")
     no-repeat right 8px center / auto 16px;
 }
+
+.status__wrapper-direct {
+  background-color: rgba($ui-highlight-color, 0.1);
+
+  &:focus {
+    background-color: rgba($ui-highlight-color, 0.15);
+  }
+}
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index b9303f2ef..6d20be541 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -1244,7 +1244,7 @@ body > [data-popper-placement] {
 .status__prepend {
   padding: 16px;
   padding-bottom: 0;
-  display: flex;
+  display: inline-flex;
   gap: 10px;
   font-size: 15px;
   line-height: 22px;
@@ -1262,6 +1262,18 @@ body > [data-popper-placement] {
   }
 }
 
+.status__wrapper-direct {
+  background: mix($ui-base-color, $ui-highlight-color, 95%);
+
+  &:focus {
+    background: mix(lighten($ui-base-color, 4%), $ui-highlight-color, 95%);
+  }
+
+  .status__prepend {
+    color: $highlight-text-color;
+  }
+}
+
 .status__action-bar {
   display: flex;
   justify-content: space-between;
@@ -1315,6 +1327,11 @@ body > [data-popper-placement] {
   .audio-player {
     margin-top: 16px;
   }
+
+  .status__prepend {
+    padding: 0;
+    margin-bottom: 16px;
+  }
 }
 
 .detailed-status__meta {
@@ -1333,6 +1350,32 @@ body > [data-popper-placement] {
   padding: 10px 0;
 }
 
+.detailed-status__wrapper-direct {
+  .detailed-status,
+  .detailed-status__action-bar {
+    background: mix($ui-base-color, $ui-highlight-color, 95%);
+  }
+
+  &:focus {
+    .detailed-status,
+    .detailed-status__action-bar {
+      background: mix(lighten($ui-base-color, 4%), $ui-highlight-color, 95%);
+    }
+  }
+
+  .detailed-status__action-bar {
+    border-top-color: mix(
+      lighten($ui-base-color, 8%),
+      $ui-highlight-color,
+      95%
+    );
+  }
+
+  .status__prepend {
+    color: $highlight-text-color;
+  }
+}
+
 .detailed-status__link {
   color: inherit;
   text-decoration: none;
-- 
cgit 

