From c23ced5786b559d346e0c1e98af16ab6e2f3745e Mon Sep 17 00:00:00 2001 From: Ember Date: Tue, 9 Jan 2024 01:51:59 +1100 Subject: [PATCH] Squashed commit of the following: commit b8d7a1c07ad284bcb2b997b04b19387c8e6b1916 Author: Essem Date: Tue Dec 26 14:04:53 2023 -0600 Check for content attribute in Misskey likes commit 95b2d969b1b35878c8f2084b0e51d9faa1193a08 Author: Essem Date: Fri Dec 22 16:09:43 2023 -0600 Fix rubocop complaint commit 67190e09f0f1a5f2b62e4cfbcc3286f1cdfbe4d1 Author: Essem Date: Tue Dec 19 22:15:34 2023 -0600 Add reaction notification column settings This was in a previous PR. Not quite sure how it didn't carry over. commit 9670ba6a1bb780f1018e84f529b5e12b58af0b36 Author: Essem Date: Tue Dec 19 22:14:22 2023 -0600 More cleanup commit 01b6cbe577110c5b563919b4135b938b2e805bca Author: Essem Date: Mon Dec 18 18:27:02 2023 -0600 Linting fixes commit 8eda40cfb39b69038df399ae18658a8cb4a3edba Author: Essem Date: Sun Nov 12 20:59:36 2023 -0600 Refactor react services commit 421e3ea8ba511d829fd784ad6c3335380ac7d0d5 Author: Essem Date: Fri Nov 10 17:36:40 2023 -0600 Fix reblog reactions commit 08a21c1af6f10d412a63b7773207f4da3f6d3cfc Author: Essem Date: Fri Nov 10 15:16:29 2023 -0600 Add notification emails for reactions commit 6ae86f40d155075b57cb168929f918377384092a Author: Essem Date: Tue Nov 7 18:43:47 2023 -0600 Add support for emoji reactions Squashed, modified, and rebased from glitch-soc/mastodon#2221. Co-authored-by: fef Co-authored-by: Jeremy Kescher Co-authored-by: neatchee Co-authored-by: Ivan Rodriguez <104603218+IRod22@users.noreply.github.com> Co-authored-by: Plastikmensch --- .../flavours/glitch/components/status.jsx | 2 +- .../glitch/components/status_action_bar.jsx | 8 +++- .../glitch/components/status_reactions.jsx | 25 ++++++---- .../features/status/components/action_bar.jsx | 6 +-- .../flavours/glitch/initial_state.js | 2 + .../flavours/glitch/reducers/settings.js | 2 + .../flavours/glitch/reducers/statuses.js | 45 ++++++++++++++++++ app/javascript/images/mailer/icon_add.png | Bin 0 -> 1417 bytes app/lib/activitypub/activity/like.rb | 7 +-- app/lib/activitypub/activity/undo.rb | 2 +- app/mailers/notification_mailer.rb | 13 ++++- app/models/status.rb | 2 + app/models/status_reaction.rb | 4 ++ app/models/user_settings.rb | 1 + app/serializers/initial_state_serializer.rb | 1 + app/serializers/rest/status_serializer.rb | 4 ++ app/services/react_service.rb | 27 ++++++++--- app/services/unreact_service.rb | 18 ++++--- .../notification_mailer/reaction.html.haml | 45 ++++++++++++++++++ .../notification_mailer/reaction.text.erb | 5 ++ .../preferences/appearance/show.html.haml | 4 +- .../preferences/notifications/show.html.haml | 1 + config/locales-glitch/simple_form.en.yml | 1 + ...0215074425_move_emoji_reaction_settings.rb | 2 +- db/schema.rb | 1 - 25 files changed, 188 insertions(+), 40 deletions(-) create mode 100644 app/javascript/images/mailer/icon_add.png create mode 100644 app/views/notification_mailer/reaction.html.haml create mode 100644 app/views/notification_mailer/reaction.text.erb diff --git a/app/javascript/flavours/glitch/components/status.jsx b/app/javascript/flavours/glitch/components/status.jsx index 1ea95cff469e72..a784b100950bfd 100644 --- a/app/javascript/flavours/glitch/components/status.jsx +++ b/app/javascript/flavours/glitch/components/status.jsx @@ -852,7 +852,7 @@ class Status extends ImmutablePureComponent { numVisible={visibleReactions} addReaction={this.props.onReactionAdd} removeReaction={this.props.onReactionRemove} - canReact={signedIn} + canReact={this.context.identity.signedIn} /> {!isCollapsed || !(muted || !settings.getIn(['collapsed', 'show_action_bar'])) ? ( diff --git a/app/javascript/flavours/glitch/components/status_action_bar.jsx b/app/javascript/flavours/glitch/components/status_action_bar.jsx index 5e44f36f7e8bcb..6681dc6c097045 100644 --- a/app/javascript/flavours/glitch/components/status_action_bar.jsx +++ b/app/javascript/flavours/glitch/components/status_action_bar.jsx @@ -13,13 +13,12 @@ import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import DropdownMenuContainer from '../containers/dropdown_menu_container'; +import EmojiPickerDropdown from '../features/compose/containers/emoji_picker_dropdown_container'; import { me, maxReactions } from '../initial_state'; import { IconButton } from './icon_button'; import { RelativeTimestamp } from './relative_timestamp'; -import EmojiPickerDropdown from '../features/compose/containers/emoji_picker_dropdown_container'; - const messages = defineMessages({ delete: { id: 'status.delete', defaultMessage: 'Delete' }, redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' }, @@ -333,6 +332,11 @@ class StatusActionBar extends ImmutablePureComponent { /> + { + permissions + ? + : reactButton + } { permissions diff --git a/app/javascript/flavours/glitch/components/status_reactions.jsx b/app/javascript/flavours/glitch/components/status_reactions.jsx index 7aa7402bc79a6a..81443d20555e14 100644 --- a/app/javascript/flavours/glitch/components/status_reactions.jsx +++ b/app/javascript/flavours/glitch/components/status_reactions.jsx @@ -1,15 +1,20 @@ -import ImmutablePureComponent from 'react-immutable-pure-component'; import PropTypes from 'prop-types'; +import React from 'react'; + +import classNames from 'classnames'; + import ImmutablePropTypes from 'react-immutable-proptypes'; -import { autoPlayGif, reduceMotion } from '../initial_state'; -import spring from 'react-motion/lib/spring'; +import ImmutablePureComponent from 'react-immutable-pure-component'; + import TransitionMotion from 'react-motion/lib/TransitionMotion'; -import classNames from 'classnames'; -import React from 'react'; -import { unicodeMapping } from 'mastodon/features/emoji/emoji_unicode_mapping_light'; -import { AnimatedNumber } from './animated_number'; +import spring from 'react-motion/lib/spring'; + +import { unicodeMapping } from '../features/emoji/emoji_unicode_mapping_light'; +import { autoPlayGif, reduceMotion } from '../initial_state'; import { assetHost } from '../utils/config'; +import { AnimatedNumber } from './animated_number'; + export default class StatusReactions extends ImmutablePureComponent { static propTypes = { @@ -91,11 +96,11 @@ class Reaction extends ImmutablePureComponent { } else { addReaction(statusId, reaction.get('name')); } - } + }; - handleMouseEnter = () => this.setState({ hovered: true }) + handleMouseEnter = () => this.setState({ hovered: true }); - handleMouseLeave = () => this.setState({ hovered: false }) + handleMouseLeave = () => this.setState({ hovered: false }); render() { const { reaction } = this.props; diff --git a/app/javascript/flavours/glitch/features/status/components/action_bar.jsx b/app/javascript/flavours/glitch/features/status/components/action_bar.jsx index 94fb93ad054e2a..e42dcecb172055 100644 --- a/app/javascript/flavours/glitch/features/status/components/action_bar.jsx +++ b/app/javascript/flavours/glitch/features/status/components/action_bar.jsx @@ -10,12 +10,12 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions'; import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links'; -import EmojiPickerDropdown from '../../compose/containers/emoji_picker_dropdown_container'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import { IconButton } from '../../../components/icon_button'; import DropdownMenuContainer from '../../../containers/dropdown_menu_container'; import { me, maxReactions } from '../../../initial_state'; +import EmojiPickerDropdown from '../../compose/containers/emoji_picker_dropdown_container'; const messages = defineMessages({ delete: { id: 'status.delete', defaultMessage: 'Delete' }, @@ -89,7 +89,7 @@ class ActionBar extends PureComponent { handleEmojiPick = data => { this.props.onReactionAdd(this.props.status.get('id'), data.native.replace(/:/g, ''), data.imageUrl); - } + }; handleBookmarkClick = (e) => { this.props.onBookmark(this.props.status, e); @@ -150,7 +150,7 @@ class ActionBar extends PureComponent { navigator.clipboard.writeText(url); }; - handleNoOp = () => {} // hack for reaction add button + handleNoOp = () => {}; // hack for reaction add button render () { const { status, intl } = this.props; diff --git a/app/javascript/flavours/glitch/initial_state.js b/app/javascript/flavours/glitch/initial_state.js index 0b1d5edf9518d9..52066aac420546 100644 --- a/app/javascript/flavours/glitch/initial_state.js +++ b/app/javascript/flavours/glitch/initial_state.js @@ -24,6 +24,7 @@ * @property {boolean} limited_federation_mode * @property {string} locale * @property {string | null} mascot + * @property {number} max_reactions * @property {string=} me * @property {string=} moved_to_account_id * @property {string=} owner @@ -44,6 +45,7 @@ * @property {boolean} use_blurhash * @property {boolean=} use_pending_items * @property {string} version + * @property {number} visible_reactions * @property {string} sso_redirect * @property {number} visible_reactions * @property {boolean} translation_enabled diff --git a/app/javascript/flavours/glitch/reducers/settings.js b/app/javascript/flavours/glitch/reducers/settings.js index 0474f7af734229..e695d3bf9d714b 100644 --- a/app/javascript/flavours/glitch/reducers/settings.js +++ b/app/javascript/flavours/glitch/reducers/settings.js @@ -61,6 +61,7 @@ const initialState = ImmutableMap({ follow: true, follow_request: false, favourite: true, + reaction: true, reblog: true, reaction: true, mention: true, @@ -75,6 +76,7 @@ const initialState = ImmutableMap({ follow: true, follow_request: false, favourite: true, + reaction: true, reblog: true, reaction: true, mention: true, diff --git a/app/javascript/flavours/glitch/reducers/statuses.js b/app/javascript/flavours/glitch/reducers/statuses.js index a43b220199832e..ff41a57dfe61ae 100644 --- a/app/javascript/flavours/glitch/reducers/statuses.js +++ b/app/javascript/flavours/glitch/reducers/statuses.js @@ -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)))); @@ -137,6 +174,14 @@ export default function statuses(state = initialState, action) { return state.setIn([action.status.get('id'), 'reblogged'], true); case REBLOG_FAIL: return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'reblogged'], false); + case REACTION_UPDATE: + return updateReactionCount(state, action.reaction); + case REACTION_ADD_REQUEST: + case REACTION_REMOVE_FAIL: + return addReaction(state, action.id, action.name, action.url); + case REACTION_REMOVE_REQUEST: + case REACTION_ADD_FAIL: + return removeReaction(state, action.id, action.name); case UNREBLOG_REQUEST: return state.setIn([action.status.get('id'), 'reblogged'], false); case UNREBLOG_FAIL: diff --git a/app/javascript/images/mailer/icon_add.png b/app/javascript/images/mailer/icon_add.png new file mode 100644 index 0000000000000000000000000000000000000000..93263cffd8af13df492a1b093910a8e0badaa553 GIT binary patch literal 1417 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE4M+yv$zcaloCO|{#S9E(G9b*j{7}U!prB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&3=FJdo-U3d6^w8124=-Z^RQgB zsQ4FLc7liJP|nG%_sY*r+ZA>6?@49(`1ttxKkW`v58Tfx*eUqKzk%VMf^P%E%U`YP z4bmRM3J$yK9&$clyTrmVVO@L$(~d?HMy4gN#pf~P4Kigv{5k689QY!i^zX+<-owm) zm|9MY-`y|#hV2o{AEqPx3*_cAeC6(VuI})M;nhdZfc*^>bt6dl|7V7V^~~1)o^)OR zT>nR9&db|%jLTSR>t``A`dv@|smaLX!};qUFmC6pJO3$|iDib+m%o`zEEcizpDt(N zFi?H*`!Wkh!7jVg{+t2{UKhSE=M;EQQaf$Fkivl}3*P$+D;#+FOUGW-p+VE}{(Lou zhRa_g>%1Bmf}8f)`#w1IpMS%z_mk@v|9yG?`TlwTr91BZ72MD8{>JP*4GfH8QxaOs zSvWrQ?LEoOA&}t68CK!mFe*t_Ak^sHc({H2hmW_Jp642*i))4v-sGW7^nJuqTq zipe|wE}4l%Lix*qR3?@kcjnza4onhmFB(q+lf+Kj-TlBM(Q|=$IxtBLb_);K^G)Sw VbJ)bI3M@t$JYD@<);T3K0RVF@NC5x< literal 0 HcmV?d00001 diff --git a/app/lib/activitypub/activity/like.rb b/app/lib/activitypub/activity/like.rb index 86d70e0d70822a..f6c8769fee4cae 100644 --- a/app/lib/activitypub/activity/like.rb +++ b/app/lib/activitypub/activity/like.rb @@ -15,11 +15,12 @@ def perform Trends.statuses.register(original_status) end - # Misskey delivers reactions as likes with the emoji in _misskey_reaction - # see https://misskey-hub.net/ns.html#misskey-reaction for details + # 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 + # See https://misskey-hub.net/ns.html#misskey-reaction for details def maybe_process_misskey_reaction original_status = status_from_uri(object_uri) - name = @json['_misskey_reaction'] + name = @json['content'] || @json['_misskey_reaction'] return false if name.nil? if /^:.*:$/.match?(name) diff --git a/app/lib/activitypub/activity/undo.rb b/app/lib/activitypub/activity/undo.rb index 5efcfdc99f2d49..5f9f1bbecbfaf1 100644 --- a/app/lib/activitypub/activity/undo.rb +++ b/app/lib/activitypub/activity/undo.rb @@ -110,7 +110,7 @@ def undo_like if @account.favourited?(status) favourite = status.favourites.where(account: @account).first favourite&.destroy - elsif @object['_misskey_reaction'].present? + elsif @object['content'].present? || @object['_misskey_reaction'].present? undo_emoji_react else delete_later!(object_uri) diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb index 5eecfed10400bc..737ce9d418b9a7 100644 --- a/app/mailers/notification_mailer.rb +++ b/app/mailers/notification_mailer.rb @@ -6,8 +6,8 @@ class NotificationMailer < ApplicationMailer :routing before_action :process_params - before_action :set_status, only: [:mention, :favourite, :reblog] - before_action :set_account, only: [:follow, :favourite, :reblog, :follow_request] + before_action :set_status, only: [:mention, :favourite, :reaction, :reblog] + before_action :set_account, only: [:follow, :favourite, :reaction, :reblog, :follow_request] after_action :set_list_headers! default to: -> { email_address_with_name(@user.email, @me.username) } @@ -38,6 +38,15 @@ def favourite end end + def reaction + return unless @user.functional? && @status.present? + + locale_for_account(@me) do + thread_by_conversation(@status.conversation) + mail subject: default_i18n_subject(name: @account.acct) + end + end + def reblog return unless @user.functional? && @status.present? diff --git a/app/models/status.rb b/app/models/status.rb index 3005fd7c7f1219..9135173838fbb3 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -290,7 +290,9 @@ def reactions(account = nil) 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 end end diff --git a/app/models/status_reaction.rb b/app/models/status_reaction.rb index bc6eda2a1cc0f8..2fdb3da0ac62eb 100644 --- a/app/models/status_reaction.rb +++ b/app/models/status_reaction.rb @@ -22,6 +22,10 @@ class StatusReaction < ApplicationRecord validates :name, presence: true validates_with StatusReactionValidator + before_validation do + self.status = status.reblog if status&.reblog? + end + before_validation :set_custom_emoji private diff --git a/app/models/user_settings.rb b/app/models/user_settings.rb index d05df8ef9dac39..1a40962909b04b 100644 --- a/app/models/user_settings.rb +++ b/app/models/user_settings.rb @@ -45,6 +45,7 @@ class KeyError < Error; end setting :follow, default: true setting :reblog, default: false setting :favourite, default: false + setting :reaction, default: false setting :mention, default: true setting :follow_request, default: true setting :report, default: true diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index 560d571feb25dd..f96246f6eb9265 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -50,6 +50,7 @@ def meta store[:default_content_type] = object_account_user.setting_default_content_type store[:system_emoji_font] = object_account_user.setting_system_emoji_font store[:show_trends] = Setting.trends && object_account_user.setting_trends + store[:visible_reactions] = object_account_user.setting_visible_reactions else store[:auto_play_gif] = Setting.auto_play_gif store[:display_media] = Setting.display_media diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb index d037c870131552..1cf19ed5fc1349 100644 --- a/app/serializers/rest/status_serializer.rb +++ b/app/serializers/rest/status_serializer.rb @@ -157,6 +157,10 @@ def ordered_mentions object.active_mentions.to_a.sort_by(&:id) end + def reactions + object.reactions(current_user&.account) + end + private def relationships diff --git a/app/services/react_service.rb b/app/services/react_service.rb index de2fd1de9c6dcb..cfc3f99a78041f 100644 --- a/app/services/react_service.rb +++ b/app/services/react_service.rb @@ -16,16 +16,31 @@ def call(account, status, emoji) reaction = StatusReaction.create!(account: account, status: status, name: name, custom_emoji: custom_emoji) - json = Oj.dump(serialize_payload(reaction, ActivityPub::EmojiReactionSerializer)) + Trends.statuses.register(status) + + create_notification(reaction) + increment_statistics + + reaction + end + + private + + def create_notification(reaction) + status = reaction.status + if status.account.local? - NotifyService.new.call(status.account, :reaction, reaction) - ActivityPub::RawDistributionWorker.perform_async(json, status.account.id) - else - ActivityPub::DeliveryWorker.perform_async(json, reaction.account_id, status.account.inbox_url) + LocalNotificationWorker.perform_async(status.account_id, reaction.id, 'StatusReaction', 'reaction') + elsif status.account.activitypub? + ActivityPub::DeliveryWorker.perform_async(build_json(reaction), reaction.account_id, status.account.inbox_url) end + end + def increment_statistics ActivityTracker.increment('activity:interactions') + end - reaction + def build_json(reaction) + Oj.dump(serialize_payload(reaction, ActivityPub::EmojiReactionSerializer)) end end diff --git a/app/services/unreact_service.rb b/app/services/unreact_service.rb index 7c1b32e94f1adc..49d232e931418f 100644 --- a/app/services/unreact_service.rb +++ b/app/services/unreact_service.rb @@ -10,14 +10,18 @@ def call(account, status, emoji) return if reaction.nil? reaction.destroy! + create_notification(reaction) if !status.account.local? && status.account.activitypub? + reaction + end - json = Oj.dump(serialize_payload(reaction, ActivityPub::UndoEmojiReactionSerializer)) - if status.account.local? - ActivityPub::RawDistributionWorker.perform_async(json, status.account.id) - else - ActivityPub::DeliveryWorker.perform_async(json, reaction.account_id, status.account.inbox_url) - end + private - reaction + def create_notification(reaction) + status = reaction.status + ActivityPub::DeliveryWorker.perform_async(build_json(reaction), reaction.account_id, status.account.inbox_url) + end + + def build_json(reaction) + Oj.dump(serialize_payload(reaction, ActivityPub::UndoEmojiReactionSerializer)) end end diff --git a/app/views/notification_mailer/reaction.html.haml b/app/views/notification_mailer/reaction.html.haml new file mode 100644 index 00000000000000..83ae41927d3f99 --- /dev/null +++ b/app/views/notification_mailer/reaction.html.haml @@ -0,0 +1,45 @@ +%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.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' diff --git a/app/views/notification_mailer/reaction.text.erb b/app/views/notification_mailer/reaction.text.erb new file mode 100644 index 00000000000000..fa30d708eaa33c --- /dev/null +++ b/app/views/notification_mailer/reaction.text.erb @@ -0,0 +1,5 @@ +<%= raw t('application_mailer.salutation', name: display_name(@me)) %> + +<%= raw t('notification_mailer.reaction.body', name: @account.pretty_acct) %> + +<%= render 'status', status: @status %> diff --git a/app/views/settings/preferences/appearance/show.html.haml b/app/views/settings/preferences/appearance/show.html.haml index df20078aba1248..8b7b9edcc0b5f2 100644 --- a/app/views/settings/preferences/appearance/show.html.haml +++ b/app/views/settings/preferences/appearance/show.html.haml @@ -36,10 +36,8 @@ = ff.input :'web.use_system_font', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_system_font_ui') = ff.input :'web.use_system_emoji_font', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_system_emoji_font'), glitch_only: true - %h4= t 'appearance.toot_layout' - .fields-group.fields-row__column.fields-row__column-6 - = ff.input :'visible_reactions', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_visible_reactions'), input_html: { type: 'number', min: '0', data: { default: '6' } }, hint: false + = ff.input :visible_reactions, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_visible_reactions'), input_html: { type: 'number', min: '0', data: { default: '6' } }, hint: false %h4= t 'appearance.discovery' diff --git a/app/views/settings/preferences/notifications/show.html.haml b/app/views/settings/preferences/notifications/show.html.haml index e0cbe36d1e1b1d..11b3c6a947f668 100644 --- a/app/views/settings/preferences/notifications/show.html.haml +++ b/app/views/settings/preferences/notifications/show.html.haml @@ -17,6 +17,7 @@ = ff.input :'notification_emails.follow_request', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.follow_request') = ff.input :'notification_emails.reblog', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.reblog') = ff.input :'notification_emails.favourite', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.favourite') + = ff.input :'notification_emails.reaction', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.reaction') = ff.input :'notification_emails.mention', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.mention') .fields-group diff --git a/config/locales-glitch/simple_form.en.yml b/config/locales-glitch/simple_form.en.yml index 9de3f945ce9f60..a9361bf192a397 100644 --- a/config/locales-glitch/simple_form.en.yml +++ b/config/locales-glitch/simple_form.en.yml @@ -22,6 +22,7 @@ en: setting_system_emoji_font: Use system's default font for emojis (applies to Glitch flavour only) setting_visible_reactions: Number of visible emoji reactions notification_emails: + reaction: Someone reacted to your post trending_link: New trending link requires review trending_status: New trending post requires review trending_tag: New trending tag requires review diff --git a/db/migrate/20230215074425_move_emoji_reaction_settings.rb b/db/migrate/20230215074425_move_emoji_reaction_settings.rb index 420772b6923f1a..6d2d5e167ad9ca 100644 --- a/db/migrate/20230215074425_move_emoji_reaction_settings.rb +++ b/db/migrate/20230215074425_move_emoji_reaction_settings.rb @@ -42,7 +42,7 @@ def up end end - user.update_column('settings', Oj.dump(user_settings)) # rubocop:disable Rails/SkipsModelValidations + user.update_column('settings', Oj.dump(user_settings)) end end end diff --git a/db/schema.rb b/db/schema.rb index 28f534d57df0f0..f5aaba6b9997e1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -957,7 +957,6 @@ t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.index ["account_id", "status_id", "name"], name: "index_status_reactions_on_account_id_and_status_id", unique: true - t.index ["account_id"], name: "index_status_reactions_on_account_id" t.index ["custom_emoji_id"], name: "index_status_reactions_on_custom_emoji_id" t.index ["status_id"], name: "index_status_reactions_on_status_id" end