diff --git a/app/javascript/mastodon/actions/interactions.js b/app/javascript/mastodon/actions/interactions.js
index 5b1163b3ab..d9d7e14060 100644
--- a/app/javascript/mastodon/actions/interactions.js
+++ b/app/javascript/mastodon/actions/interactions.js
@@ -207,7 +207,9 @@ export function emojiReact(status, emoji) {
   return function (dispatch, getState) {
     dispatch(emojiReactRequest(status, emoji));
 
-    api(getState).post(`/api/v1/statuses/${status.get('id')}/emoji_reactions`, { emoji: emoji.custom ? (emoji.name + (emoji.domain || '')) : emoji.native }).then(function () {
+    const api_emoji = typeof emoji !== 'string' ? (emoji.custom ? (emoji.name + (emoji.domain || '')) : emoji.native) : emoji;
+
+    api(getState).post(`/api/v1/statuses/${status.get('id')}/emoji_reactions`, { emoji: api_emoji }).then(function () {
       dispatch(emojiReactSuccess(status, emoji));
     }).catch(function (error) {
       dispatch(emojiReactFail(status, emoji, error));
diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js
index cd9c1fd1a0..81bce49148 100644
--- a/app/javascript/mastodon/actions/notifications.js
+++ b/app/javascript/mastodon/actions/notifications.js
@@ -137,6 +137,7 @@ const excludeTypesFromFilter = filter => {
     'follow',
     'follow_request',
     'favourite',
+    'emoji_reaction',
     'reblog',
     'mention',
     'poll',
diff --git a/app/javascript/mastodon/features/notifications/components/column_settings.jsx b/app/javascript/mastodon/features/notifications/components/column_settings.jsx
index 9251847bad..b6a3124ccb 100644
--- a/app/javascript/mastodon/features/notifications/components/column_settings.jsx
+++ b/app/javascript/mastodon/features/notifications/components/column_settings.jsx
@@ -115,6 +115,17 @@ export default class ColumnSettings extends React.PureComponent {
           </div>
         </div>
 
+        <div role='group' aria-labelledby='notifications-emoji_reaction'>
+          <span id='notifications-emoji_reaction' className='column-settings__section'><FormattedMessage id='notifications.column_settings.emoji_reaction' defaultMessage='Emoji Reactions:' /></span>
+
+          <div className='column-settings__row'>
+            <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'emoji_reaction']} onChange={onChange} label={alertStr} />
+            {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'emoji_reaction']} onChange={this.onPushChange} label={pushStr} />}
+            <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'emoji_reaction']} onChange={onChange} label={showStr} />
+            <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'emoji_reaction']} onChange={onChange} label={soundStr} />
+          </div>
+        </div>
+
         <div role='group' aria-labelledby='notifications-mention'>
           <span id='notifications-mention' className='column-settings__section'><FormattedMessage id='notifications.column_settings.mention' defaultMessage='Mentions:' /></span>
 
diff --git a/app/javascript/mastodon/features/notifications/components/filter_bar.jsx b/app/javascript/mastodon/features/notifications/components/filter_bar.jsx
index 368eb0b7e6..f56ca6a69c 100644
--- a/app/javascript/mastodon/features/notifications/components/filter_bar.jsx
+++ b/app/javascript/mastodon/features/notifications/components/filter_bar.jsx
@@ -74,6 +74,13 @@ class FilterBar extends React.PureComponent {
         >
           <Icon id='star' fixedWidth />
         </button>
+        <button
+          className={selectedFilter === 'emoji_reaction' ? 'active' : ''}
+          onClick={this.onClick('emoji_reaction')}
+          title={intl.formatMessage(tooltips.emojiReactions)}
+        >
+          <Icon id='star' fixedWidth />
+        </button>
         <button
           className={selectedFilter === 'reblog' ? 'active' : ''}
           onClick={this.onClick('reblog')}
diff --git a/app/javascript/mastodon/features/notifications/components/notification.jsx b/app/javascript/mastodon/features/notifications/components/notification.jsx
index 9e2517f084..eab219dfbd 100644
--- a/app/javascript/mastodon/features/notifications/components/notification.jsx
+++ b/app/javascript/mastodon/features/notifications/components/notification.jsx
@@ -15,6 +15,7 @@ import classNames from 'classnames';
 
 const messages = defineMessages({
   favourite: { id: 'notification.favourite', defaultMessage: '{name} favourited your status' },
+  emojiReaction: { id: 'notification.emoji_reaction', defaultMessage: '{name} reacted your status with emoji' },
   follow: { id: 'notification.follow', defaultMessage: '{name} followed you' },
   ownPoll: { id: 'notification.own_poll', defaultMessage: 'Your poll has ended' },
   poll: { id: 'notification.poll', defaultMessage: 'A poll you have voted in has ended' },
@@ -213,6 +214,38 @@ class Notification extends ImmutablePureComponent {
     );
   }
 
+  renderEmojiReaction (notification, link) {
+    const { intl, unread } = this.props;
+
+    return (
+      <HotKeys handlers={this.getHandlers()}>
+        <div className={classNames('notification notification-emoji_reaction focusable', { unread })} tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.emojiReaction, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
+          <div className='notification__message'>
+            <div className='notification__emoji_reaction-icon-wrapper'>
+              <Icon id='star' className='star-icon' fixedWidth />
+            </div>
+
+            <span title={notification.get('created_at')}>
+              <FormattedMessage id='notification.emoji_reaction' defaultMessage='{name} reacted your status with emoji' values={{ name: link }} />
+            </span>
+          </div>
+
+          <StatusContainer
+            id={notification.get('status')}
+            account={notification.get('account')}
+            muted
+            withDismiss
+            hidden={!!this.props.hidden}
+            getScrollPosition={this.props.getScrollPosition}
+            updateScrollBottom={this.props.updateScrollBottom}
+            cachedMediaWidth={this.props.cachedMediaWidth}
+            cacheMediaWidth={this.props.cacheMediaWidth}
+          />
+        </div>
+      </HotKeys>
+    );
+  }
+
   renderReblog (notification, link) {
     const { intl, unread } = this.props;
 
@@ -429,6 +462,8 @@ class Notification extends ImmutablePureComponent {
       return this.renderMention(notification);
     case 'favourite':
       return this.renderFavourite(notification, link);
+    case 'emoji_reaction':
+      return this.renderEmojiReaction(notification, link);
     case 'reblog':
       return this.renderReblog(notification, link);
     case 'status':
diff --git a/app/javascript/mastodon/reducers/statuses.js b/app/javascript/mastodon/reducers/statuses.js
index fcb80ae841..28c760f9a0 100644
--- a/app/javascript/mastodon/reducers/statuses.js
+++ b/app/javascript/mastodon/reducers/statuses.js
@@ -45,11 +45,10 @@ const updateStatusEmojiReaction = (state, emoji_reaction, myId) => {
   let emoji_reactions = Array.from(status.get('emoji_reactions') || []);
 
   if (emoji_reaction.count > 0) {
-    const old_emoji = emoji_reactions.find((er) => er.name === emoji_reaction.name && er.url === emoji_reaction.url);
+    const old_emoji = emoji_reactions.find((er) => er.get('name') === emoji_reaction.name && (!er.get('domain') || er.get('domain') === emoji_reaction.domain));
     if (old_emoji) {
-      old_emoji.account_ids = emoji_reaction.account_ids;
-      old_emoji.count       = emoji_reaction.count;
-      old_emoji.me          = emoji_reaction.me;
+      const index = emoji_reactions.indexOf(old_emoji);
+      emoji_reactions[index] = old_emoji.merge({ account_ids: emoji_reaction.account_ids, count: emoji_reaction.count, me: emoji_reaction.me });
     } else {
       emoji_reactions.push(ImmutableMap(emoji_reaction));
     }
diff --git a/app/models/notification.rb b/app/models/notification.rb
index 88c5f44b6c..2fadcc65a5 100644
--- a/app/models/notification.rb
+++ b/app/models/notification.rb
@@ -25,6 +25,7 @@ class Notification < ApplicationRecord
     'Follow' => :follow,
     'FollowRequest' => :follow_request,
     'Favourite' => :favourite,
+    'EmojiReaction' => :emoji_reaction,
     'Poll' => :poll,
   }.freeze
 
@@ -35,6 +36,7 @@ class Notification < ApplicationRecord
     follow
     follow_request
     favourite
+    emoji_reaction
     poll
     update
     admin.sign_up
@@ -46,6 +48,7 @@ class Notification < ApplicationRecord
     reblog: [status: :reblog],
     mention: [mention: :status],
     favourite: [favourite: :status],
+    emoji_reaction: [emoji_reaction: :status],
     poll: [poll: :status],
     update: :status,
     'admin.report': [report: :target_account],
diff --git a/app/serializers/rest/notification_serializer.rb b/app/serializers/rest/notification_serializer.rb
index 137fc53dda..2d9ab53ef5 100644
--- a/app/serializers/rest/notification_serializer.rb
+++ b/app/serializers/rest/notification_serializer.rb
@@ -12,7 +12,7 @@ class REST::NotificationSerializer < ActiveModel::Serializer
   end
 
   def status_type?
-    [:favourite, :reblog, :status, :mention, :poll, :update].include?(object.type)
+    [:favourite, :emoji_reaction, :reblog, :status, :mention, :poll, :update].include?(object.type)
   end
 
   def report_type?
diff --git a/app/services/un_emoji_react_service.rb b/app/services/un_emoji_react_service.rb
index 308b3f73d4..ca58c3c1d0 100644
--- a/app/services/un_emoji_react_service.rb
+++ b/app/services/un_emoji_react_service.rb
@@ -4,14 +4,20 @@ class UnEmojiReactService < BaseService
   include Redisable
   include Payloadable
 
-  def call(account, status, emoji_reaction = nil)
+  def call(account_id, status_id, emoji_reaction = nil)
+    @account_id = account_id
+    @account    = Account.find(account_id)
+    @status     = Status.find(status_id)
+
     if emoji_reaction
+      p '================================ DEBUG2 G'
       emoji_reaction.destroy
-      create_notification(emoji_reaction) if !status.account.local? && status.account.activitypub?
-      notify_to_followers(emoji_reaction) if status.account.local?
+      p '================================ DEBUG2 H'
+      create_notification(emoji_reaction) if !@account.local? && @account.activitypub?
+      notify_to_followers(emoji_reaction) if @account.local?
       write_stream(emoji_reaction)
     else
-      bulk(account, status)
+      bulk(@account, @status)
     end
     emoji_reaction
   end
@@ -25,33 +31,31 @@ class UnEmojiReactService < BaseService
   end
 
   def create_notification(emoji_reaction)
-    status = emoji_reaction.status
-    ActivityPub::DeliveryWorker.perform_async(build_json(emoji_reaction), status.account_id, status.account.inbox_url)
+    ActivityPub::DeliveryWorker.perform_async(build_json(emoji_reaction), @account_id, @account.inbox_url)
   end
 
   def notify_to_followers(emoji_reaction)
-    status = emoji_reaction.status
-    ActivityPub::RawDistributionWorker.perform_async(build_json(emoji_reaction), status.account_id)
+    ActivityPub::RawDistributionWorker.perform_async(build_json(emoji_reaction), @account_id)
   end
 
   def write_stream(emoji_reaction)
-    emoji_group = emoji_reaction.status.emoji_reactions_grouped_by_name
+    emoji_group = @status.emoji_reactions_grouped_by_name
                                 .find { |reaction_group| reaction_group['name'] == emoji_reaction.name && (!reaction_group.key?(:domain) || reaction_group['domain'] == emoji_reaction.domain) }
     if emoji_group
-      emoji_group['status_id'] = emoji_reaction.status_id.to_s
+      emoji_group['status_id'] = @status.id.to_s
     else
       # name: emoji_reaction.name, count: 0, domain: emoji_reaction.domain
-      emoji_group = { 'name' => emoji_reaction.name, 'count' => 0, 'account_ids' => [], 'status_id' => emoji_reaction.status_id.to_s }
+      emoji_group = { 'name' => emoji_reaction.name, 'count' => 0, 'account_ids' => [], 'status_id' => @status.id.to_s }
       emoji_group['domain'] = emoji_reaction.custom_emoji.domain if emoji_reaction.custom_emoji
     end
-    FeedAnyJsonWorker.perform_async(render_emoji_reaction(emoji_group), emoji_reaction.status_id, emoji_reaction.account_id)
+    FeedAnyJsonWorker.perform_async(render_emoji_reaction(emoji_group), @status.id, @account_id)
   end
 
   def build_json(emoji_reaction)
     Oj.dump(serialize_payload(emoji_reaction, ActivityPub::UndoEmojiReactionSerializer))
   end
 
-  def render_emoji_reaction(_emoji_reaction, emoji_group)
+  def render_emoji_reaction(emoji_group)
     # @rendered_emoji_reaction ||= InlineRenderer.render(emoji_group, nil, :emoji_reaction)
     Oj.dump(event: :emoji_reaction, payload: emoji_group.to_json)
   end
diff --git a/app/workers/feed_any_json_worker.rb b/app/workers/feed_any_json_worker.rb
index 0492c52f2c..33aa254770 100644
--- a/app/workers/feed_any_json_worker.rb
+++ b/app/workers/feed_any_json_worker.rb
@@ -7,14 +7,29 @@ class FeedAnyJsonWorker
   include AccountLimitable
 
   def perform(payload_json, status_id, my_account_id = nil)
+    p '========================================= DEBUG AAA'
     redis.publish("timeline:#{my_account_id}", payload_json) if my_account_id.present?
+    p '========================================= DEBUG AA'
+    p status_id
+    p status_id.to_i
 
     status = Status.find(status_id.to_i)
+    p '========================================= DEBUG AAAAAAAA'
+    p status.present?
 
     if status.present?
-      scope_status(status).find_each do |account_id|
-        p account_id if redis.exists?("subscribed:timeline:#{account_id}")
-        redis.publish("timeline:#{account_id}", payload_json) if redis.exists?("subscribed:timeline:#{account_id}")
+      p '========================================= DEBUG A'
+      p scope_status(status)
+      p '========================================= DEBUG C'
+      scope_status(status).find_each do |account|
+        p '========================================= DEBUG D'
+        p redis.exists?("subscribed:timeline:#{account.id}")
+        redis.publish("timeline:#{account.id}", payload_json) if redis.exists?("subscribed:timeline:#{account.id}")
+      end
+
+      if status.visibility.to_sym != :public && status.visibility.to_sym != :unlisted && status.account_id != my_account_id &&
+         redis.exists?("subscribed:timeline:#{status.account_id}")
+        redis.publish("timeline:#{status.account_id}", payload_json)
       end
     end
 
diff --git a/app/workers/un_emoji_react_worker.rb b/app/workers/un_emoji_react_worker.rb
index c7c2671099..fce8019fb6 100644
--- a/app/workers/un_emoji_react_worker.rb
+++ b/app/workers/un_emoji_react_worker.rb
@@ -12,7 +12,7 @@ class UnEmojiReactWorker
                                     .find { |reaction| domain == '' ? reaction.custom_emoji.nil? : reaction.custom_emoji&.domain == domain }
     end
 
-    UnEmojiReactService.new.call(Account.find(account_id), Status.find(status_id), emoji_reaction)
+    UnEmojiReactService.new.call(account_id.to_i, status_id.to_i, emoji_reaction)
   rescue ActiveRecord::RecordNotFound
     true
   end