Skip to content

Commit

Permalink
Merge branch 'emoji-reacts'
Browse files Browse the repository at this point in the history
  • Loading branch information
Ember-ruby committed Feb 1, 2024
2 parents 642c9eb + 9eef129 commit b52cd0f
Show file tree
Hide file tree
Showing 13 changed files with 102 additions and 78 deletions.
14 changes: 1 addition & 13 deletions app/controllers/api/v1/statuses/reactions_controller.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
# frozen_string_literal: true

class Api::V1::Statuses::ReactionsController < Api::BaseController
include Authorization

class Api::V1::Statuses::ReactionsController < Api::V1::Statuses::BaseController
before_action -> { doorkeeper_authorize! :write, :'write:favourites' }
before_action :require_user!
before_action :set_status

def create
ReactService.new.call(current_account, @status, params[:id])
Expand All @@ -19,13 +16,4 @@ def destroy
rescue Mastodon::NotPermittedError
not_found
end

private

def set_status
@status = Status.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
not_found
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ class StatusActionBar extends ImmutablePureComponent {
onClick={this.handleNoOp} // EmojiPickerDropdown handles that
title={intl.formatMessage(messages.react)}
disabled={!canReact}
icon='add-reaction'
icon='add_reaction'
iconComponent={AddReactionIcon}
/>
);
Expand Down
4 changes: 3 additions & 1 deletion app/javascript/flavours/glitch/components/status_prepend.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { FormattedMessage } from 'react-intl';

import ImmutablePropTypes from 'react-immutable-proptypes';

import AddReactionIcon from '@/material-icons/400-24px/add_reaction.svg?react';
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
Expand Down Expand Up @@ -130,7 +131,8 @@ export default class StatusPrepend extends PureComponent {
iconComponent = StarIcon;
break;
case 'reaction':
iconId = 'plus';
iconId = 'add_reaction';
iconComponent = AddReactionIcon;
break;
case 'featured':
iconId = 'thumb-tack';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { PureComponent } from 'react';

import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';

import AddReactionIcon from '@/material-icons/400-24px/add_reaction.svg?react';
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
import PersonAddIcon from '@/material-icons/400-24px/person_add.svg?react';
Expand Down Expand Up @@ -88,7 +89,7 @@ class FilterBar extends PureComponent {
onClick={this.onClick('reaction')}
title={intl.formatMessage(tooltips.reactions)}
>
<Icon id='plus' fixedWidth />
<Icon id='add_reaction' icon={AddReactionIcon} />
</button>
<button
className={selectedFilter === 'reblog' ? 'active' : ''}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,11 +244,11 @@ class ActionBar extends PureComponent {
const canReact = signedIn && status.get('reactions').filter(r => r.get('count') > 0 && r.get('me')).size < maxReactions;
const reactButton = (
<IconButton
className='plus-icon'
className='add-reaction-icon'
onClick={this.handleNoOp} // EmojiPickerDropdown handles that
title={intl.formatMessage(messages.react)}
disabled={!canReact}
icon='add-reaction'
icon='add_reaction'
iconComponent={AddReactionIcon}
/>
);
Expand Down
37 changes: 37 additions & 0 deletions app/javascript/flavours/glitch/reducers/statuses.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,43 @@ const deleteStatus = (state, id, references) => {
return state.delete(id);
};

const updateReaction = (state, id, name, updater) => state.update(
id,
status => status.update(
'reactions',
reactions => {
const index = reactions.findIndex(reaction => reaction.get('name') === name);
if (index > -1) {
return reactions.update(index, reaction => updater(reaction));
} else {
return reactions.push(updater(fromJS({ name, count: 0 })));
}
},
),
);

const updateReactionCount = (state, reaction) => updateReaction(state, reaction.status_id, reaction.name, x => x.set('count', reaction.count));

// The url parameter is only used when adding a new custom emoji reaction
// (one that wasn't in the reactions list before) because we don't have its
// URL yet. In all other cases, it's undefined.
const addReaction = (state, id, name, url) => updateReaction(
state,
id,
name,
x => x.set('me', true)
.update('count', n => n + 1)
.update('url', old => old ? old : url)
.update('static_url', old => old ? old : url),
);

const removeReaction = (state, id, name) => updateReaction(
state,
id,
name,
x => x.set('me', false).update('count', n => n - 1),
);

const statusTranslateSuccess = (state, id, translation) => {
return state.withMutations(map => {
map.setIn([id, 'translation'], fromJS(normalizeStatusTranslation(translation, map.get(id))));
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/javascript/material-icons/400-24px/add_reaction.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions app/lib/activitypub/activity/like.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ def perform
original_status = status_from_uri(object_uri)
return if original_status.nil? || !original_status.account.local? || delete_arrived_first?(@json['id'])

return if maybe_process_misskey_reaction
return if maybe_process_embedded_reaction

return if @account.favourited?(original_status)

Expand All @@ -15,10 +15,10 @@ def perform
Trends.statuses.register(original_status)
end

# Misskey delivers reactions as likes with the emoji in _misskey_reaction and content
# Versions of Misskey before 12.1.0 only specify emojis in _misskey_reaction, so we check both
# Some servers deliver reactions as likes with the emoji in content
# Versions of Misskey before 12.1.0 specify emojis in _misskey_reaction instead, so we check both
# See https://misskey-hub.net/ns.html#misskey-reaction for details
def maybe_process_misskey_reaction
def maybe_process_embedded_reaction
original_status = status_from_uri(object_uri)
name = @json['content'] || @json['_misskey_reaction']
return false if name.nil?
Expand Down
4 changes: 4 additions & 0 deletions app/models/concerns/account/interactions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,10 @@ def reacted?(status, name, custom_emoji = nil)
status.proper.status_reactions.where(account: self, name: name, custom_emoji: custom_emoji).exists?

Check failure on line 234 in app/models/concerns/account/interactions.rb

View workflow job for this annotation

GitHub Actions / lint

[Correctable] Rails/WhereExists: Prefer exists?(account: self, name: name, custom_emoji: custom_emoji) over where(account: self, name: name, custom_emoji: custom_emoji).exists?.
end

def reacted?(status, name, custom_emoji = nil)

Check warning on line 237 in app/models/concerns/account/interactions.rb

View workflow job for this annotation

GitHub Actions / lint

Lint/DuplicateMethods: Method Account::Interactions#reacted? is defined at both app/models/concerns/account/interactions.rb:233 and app/models/concerns/account/interactions.rb:237.
status.proper.status_reactions.exists?(account: self, name: name, custom_emoji: custom_emoji)
end

def bookmarked?(status)
status.proper.bookmarks.exists?(account: self)
end
Expand Down
46 changes: 34 additions & 12 deletions app/models/status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -284,20 +284,13 @@ def emojis
end

def reactions(account = nil)
records = begin
scope = status_reactions.group(:status_id, :name, :custom_emoji_id).order(Arel.sql('MIN(created_at) ASC'))

if account.nil?
scope.select('name, custom_emoji_id, count(*) as count, false as me')
else
# rubocop:disable Layout/LineLength
scope.select("name, custom_emoji_id, count(*) as count, exists(select 1 from status_reactions r where r.account_id = #{account.id} and r.status_id = status_reactions.status_id and r.name = status_reactions.name and (r.custom_emoji_id = status_reactions.custom_emoji_id or r.custom_emoji_id is null and status_reactions.custom_emoji_id is null)) as me")
# rubocop:enable Layout/LineLength
grouped_ordered_status_reactions.select(
[:name, :custom_emoji_id, 'COUNT(*) as count'].tap do |values|
values << value_for_reaction_me_column(account)
end
).to_a.tap do |records|
ActiveRecord::Associations::Preloader.new(records: records, associations: :custom_emoji).call
end

ActiveRecord::Associations::Preloader.new(records: records, associations: :custom_emoji)
records
end

def ordered_media_attachments
Expand Down Expand Up @@ -487,6 +480,35 @@ def unlink_from_conversations!

private

def grouped_ordered_status_reactions
status_reactions
.group(:status_id, :name, :custom_emoji_id)
.order(
Arel.sql('MIN(created_at)').asc
)
end

def value_for_reaction_me_column(account)
if account.nil?
'FALSE AS me'
else
<<~SQL.squish
EXISTS(
SELECT 1
FROM status_reactions inner_reactions
WHERE inner_reactions.account_id = #{account.id}
AND inner_reactions.status_id = status_reactions.status_id
AND inner_reactions.name = status_reactions.name
AND (
inner_reactions.custom_emoji_id = status_reactions.custom_emoji_id
OR inner_reactions.custom_emoji_id IS NULL
AND status_reactions.custom_emoji_id IS NULL
)
) AS me
SQL
end
end

def update_status_stat!(attrs)
return if marked_for_destruction? || destroyed?

Expand Down
56 changes: 12 additions & 44 deletions app/views/notification_mailer/reaction.html.haml
Original file line number Diff line number Diff line change
@@ -1,45 +1,13 @@
%table.email-table{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.email-body
.email-container
%table.content-section{ cellspacing: 0, cellpadding: 0 }
%tbody
= content_for :heading do
= render 'application/mailer/heading', heading_title: t('notification_mailer.reaction.title'), heading_subtitle: t('notification_mailer.reaction.body', name: @account.pretty_acct), heading_image_url: frontend_asset_url('images/mailer-new/heading/reaction.png')
%table.email-w-full{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' }
%tr
%td.email-body-padding-td
%table.email-inner-card-table{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' }
%tr
%td.email-inner-card-td
= render 'status', status: @status, time_zone: @me.user_time_zone
%table.email-w-full{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' }
%tr
%td.content-cell.hero
.email-row
.col-6
%table.column{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.column-cell.text-center.padded
%table.hero-icon{ align: 'center', cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td
= image_tag full_pack_url('media/images/mailer/icon_add.png'), alt: ''

%h1= t 'notification_mailer.reaction.title'
%p.lead= t('notification_mailer.reaction.body', name: @account.pretty_acct)

= render 'status', status: @status, time_zone: @me.user_time_zone

%table.email-table{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.email-body
.email-container
%table.content-section{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.content-cell.content-start.border-top
%table.column{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.column-cell.button-cell
%table.button{ align: 'center', cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.button-primary
= link_to web_url("@#{@status.account.pretty_acct}/#{@status.id}") do
%span= t 'application_mailer.view_status'
%td.email-padding-top-24
= render 'application/mailer/button', text: t('application_mailer.view_status'), url: web_url("@#{@status.account.pretty_acct}/#{@status.id}")

0 comments on commit b52cd0f

Please sign in to comment.