Add api for set emoji reactions to toot
This commit is contained in:
parent
f157a509d6
commit
5f7da7bff1
17 changed files with 276 additions and 18 deletions
|
@ -0,0 +1,45 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::Statuses::EmojiReactionsController < Api::BaseController
|
||||||
|
include Authorization
|
||||||
|
|
||||||
|
before_action -> { doorkeeper_authorize! :write, :'write:emoji_reactions' }
|
||||||
|
before_action :require_user!
|
||||||
|
before_action :set_status, only: [:update]
|
||||||
|
|
||||||
|
# For compatible with Fedibird API
|
||||||
|
def update
|
||||||
|
create_private
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: destroy emoji reaction api
|
||||||
|
def destroy
|
||||||
|
# fav = current_account.favourites.find_by(status_id: params[:status_id])
|
||||||
|
|
||||||
|
# if fav
|
||||||
|
# @status = fav.status
|
||||||
|
# UnfavouriteWorker.perform_async(current_account.id, @status.id)
|
||||||
|
# else
|
||||||
|
# @status = Status.find(params[:status_id])
|
||||||
|
# authorize @status, :show?
|
||||||
|
# end
|
||||||
|
|
||||||
|
# render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, favourites_map: { @status.id => false })
|
||||||
|
# rescue Mastodon::NotPermittedError
|
||||||
|
not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def create_private
|
||||||
|
EmojiReactService.new.call(current_account, @status, params[:id])
|
||||||
|
render json: @status, serializer: REST::StatusSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_status
|
||||||
|
@status = Status.find(params[:status_id])
|
||||||
|
authorize @status, :show?
|
||||||
|
rescue Mastodon::NotPermittedError
|
||||||
|
not_found
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,6 +9,10 @@ export const FAVOURITE_REQUEST = 'FAVOURITE_REQUEST';
|
||||||
export const FAVOURITE_SUCCESS = 'FAVOURITE_SUCCESS';
|
export const FAVOURITE_SUCCESS = 'FAVOURITE_SUCCESS';
|
||||||
export const FAVOURITE_FAIL = 'FAVOURITE_FAIL';
|
export const FAVOURITE_FAIL = 'FAVOURITE_FAIL';
|
||||||
|
|
||||||
|
export const EMOJIREACT_REQUEST = 'EMOJIREACT_REQUEST';
|
||||||
|
export const EMOJIREACT_SUCCESS = 'EMOJIREACT_SUCCESS';
|
||||||
|
export const EMOJIREACT_FAIL = 'EMOJIREACT_FAIL';
|
||||||
|
|
||||||
export const UNREBLOG_REQUEST = 'UNREBLOG_REQUEST';
|
export const UNREBLOG_REQUEST = 'UNREBLOG_REQUEST';
|
||||||
export const UNREBLOG_SUCCESS = 'UNREBLOG_SUCCESS';
|
export const UNREBLOG_SUCCESS = 'UNREBLOG_SUCCESS';
|
||||||
export const UNREBLOG_FAIL = 'UNREBLOG_FAIL';
|
export const UNREBLOG_FAIL = 'UNREBLOG_FAIL';
|
||||||
|
@ -17,6 +21,10 @@ export const UNFAVOURITE_REQUEST = 'UNFAVOURITE_REQUEST';
|
||||||
export const UNFAVOURITE_SUCCESS = 'UNFAVOURITE_SUCCESS';
|
export const UNFAVOURITE_SUCCESS = 'UNFAVOURITE_SUCCESS';
|
||||||
export const UNFAVOURITE_FAIL = 'UNFAVOURITE_FAIL';
|
export const UNFAVOURITE_FAIL = 'UNFAVOURITE_FAIL';
|
||||||
|
|
||||||
|
export const UNEMOJIREACT_REQUEST = 'UNEMOJIREACT_REQUEST';
|
||||||
|
export const UNEMOJIREACT_SUCCESS = 'UNEMOJIREACT_SUCCESS';
|
||||||
|
export const UNEMOJIREACT_FAIL = 'UNEMOJIREACT_FAIL';
|
||||||
|
|
||||||
export const REBLOGS_FETCH_REQUEST = 'REBLOGS_FETCH_REQUEST';
|
export const REBLOGS_FETCH_REQUEST = 'REBLOGS_FETCH_REQUEST';
|
||||||
export const REBLOGS_FETCH_SUCCESS = 'REBLOGS_FETCH_SUCCESS';
|
export const REBLOGS_FETCH_SUCCESS = 'REBLOGS_FETCH_SUCCESS';
|
||||||
export const REBLOGS_FETCH_FAIL = 'REBLOGS_FETCH_FAIL';
|
export const REBLOGS_FETCH_FAIL = 'REBLOGS_FETCH_FAIL';
|
||||||
|
@ -195,6 +203,89 @@ export function unfavouriteFail(status, error) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function emojiReact(status, emoji) {
|
||||||
|
return function (dispatch, getState) {
|
||||||
|
dispatch(emojiReactRequest(status, emoji));
|
||||||
|
console.dir(emoji.custom ? (emoji.name + (emoji.domain || '')) : emoji.native);
|
||||||
|
|
||||||
|
api(getState).put(`/api/v1/statuses/${status.get('id')}/emoji_reactions/${emoji.custom ? (emoji.name + (emoji.domain || '')) : emoji.native}`).then(function (response) {
|
||||||
|
dispatch(importFetchedStatus(response.data));
|
||||||
|
dispatch(emojiReactSuccess(status, emoji));
|
||||||
|
}).catch(function (error) {
|
||||||
|
dispatch(emojiReactFail(status, emoji, error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unEmojiReact(status, emoji) {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
dispatch(unEmojiReactRequest(status, emoji));
|
||||||
|
|
||||||
|
api(getState).post(`/api/v1/statuses/${status.get('id')}/emoji_unreactions/${emoji.native}`).then(response => {
|
||||||
|
dispatch(importFetchedStatus(response.data));
|
||||||
|
dispatch(unEmojiReactSuccess(status, emoji));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(unEmojiReactFail(status, emoji, error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function emojiReactRequest(status, emoji) {
|
||||||
|
return {
|
||||||
|
type: EMOJIREACT_REQUEST,
|
||||||
|
status: status,
|
||||||
|
emoji: emoji,
|
||||||
|
skipLoading: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function emojiReactSuccess(status, emoji) {
|
||||||
|
return {
|
||||||
|
type: EMOJIREACT_SUCCESS,
|
||||||
|
status: status,
|
||||||
|
emoji: emoji,
|
||||||
|
skipLoading: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function emojiReactFail(status, emoji, error) {
|
||||||
|
return {
|
||||||
|
type: EMOJIREACT_FAIL,
|
||||||
|
status: status,
|
||||||
|
emoji: emoji,
|
||||||
|
error: error,
|
||||||
|
skipLoading: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unEmojiReactRequest(status, emoji) {
|
||||||
|
return {
|
||||||
|
type: UNEMOJIREACT_REQUEST,
|
||||||
|
status: status,
|
||||||
|
emoji: emoji,
|
||||||
|
skipLoading: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unEmojiReactSuccess(status, emoji) {
|
||||||
|
return {
|
||||||
|
type: UNEMOJIREACT_SUCCESS,
|
||||||
|
status: status,
|
||||||
|
emoji: emoji,
|
||||||
|
skipLoading: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unEmojiReactFail(status, emoji, error) {
|
||||||
|
return {
|
||||||
|
type: UNEMOJIREACT_FAIL,
|
||||||
|
status: status,
|
||||||
|
emoji: emoji,
|
||||||
|
error: error,
|
||||||
|
skipLoading: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function bookmark(status) {
|
export function bookmark(status) {
|
||||||
return function (dispatch, getState) {
|
return function (dispatch, getState) {
|
||||||
dispatch(bookmarkRequest(status));
|
dispatch(bookmarkRequest(status));
|
||||||
|
|
|
@ -73,6 +73,8 @@ class Status extends ImmutablePureComponent {
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
onReply: PropTypes.func,
|
onReply: PropTypes.func,
|
||||||
onFavourite: PropTypes.func,
|
onFavourite: PropTypes.func,
|
||||||
|
onEmojiReact: PropTypes.func,
|
||||||
|
onUnEmojiReact: PropTypes.func,
|
||||||
onReblog: PropTypes.func,
|
onReblog: PropTypes.func,
|
||||||
onDelete: PropTypes.func,
|
onDelete: PropTypes.func,
|
||||||
onDirect: PropTypes.func,
|
onDirect: PropTypes.func,
|
||||||
|
|
|
@ -68,6 +68,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
relationship: ImmutablePropTypes.map,
|
relationship: ImmutablePropTypes.map,
|
||||||
onReply: PropTypes.func,
|
onReply: PropTypes.func,
|
||||||
onFavourite: PropTypes.func,
|
onFavourite: PropTypes.func,
|
||||||
|
onEmojiReact: PropTypes.func,
|
||||||
onReblog: PropTypes.func,
|
onReblog: PropTypes.func,
|
||||||
onDelete: PropTypes.func,
|
onDelete: PropTypes.func,
|
||||||
onDirect: PropTypes.func,
|
onDirect: PropTypes.func,
|
||||||
|
@ -129,6 +130,16 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleEmojiPick = (data) => {
|
||||||
|
const { signedIn } = this.context.identity;
|
||||||
|
|
||||||
|
if (signedIn) {
|
||||||
|
this.props.onEmojiReact(this.props.status, data);
|
||||||
|
} else {
|
||||||
|
this.props.onInteractionModal('favourite', this.props.status);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
handleReblogClick = e => {
|
handleReblogClick = e => {
|
||||||
const { signedIn } = this.context.identity;
|
const { signedIn } = this.context.identity;
|
||||||
|
|
||||||
|
@ -232,16 +243,6 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
this.props.onFilter();
|
this.props.onFilter();
|
||||||
};
|
};
|
||||||
|
|
||||||
handleEmojiPick = (data) => {
|
|
||||||
/*
|
|
||||||
const { text } = this.props;
|
|
||||||
const position = this.autosuggestTextarea.textarea.selectionStart;
|
|
||||||
const needsSpace = data.custom && position > 0 && !allowedAroundShortCode.includes(text[position - 1]);
|
|
||||||
|
|
||||||
this.props.onPickEmoji(position, data, needsSpace);
|
|
||||||
*/
|
|
||||||
};
|
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { status, relationship, intl, withDismiss, withCounters, scrollKey } = this.props;
|
const { status, relationship, intl, withDismiss, withCounters, scrollKey } = this.props;
|
||||||
const { signedIn, permissions } = this.context.identity;
|
const { signedIn, permissions } = this.context.identity;
|
||||||
|
|
|
@ -8,7 +8,7 @@ import classNames from 'classnames';
|
||||||
class EmojiReactionButton extends React.PureComponent {
|
class EmojiReactionButton extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
name: ImmutablePropTypes.map,
|
name: PropTypes.string,
|
||||||
url: PropTypes.string,
|
url: PropTypes.string,
|
||||||
staticUrl: PropTypes.string,
|
staticUrl: PropTypes.string,
|
||||||
count: PropTypes.number.isRequired,
|
count: PropTypes.number.isRequired,
|
||||||
|
@ -22,7 +22,7 @@ class EmojiReactionButton extends React.PureComponent {
|
||||||
let emojiHtml = null;
|
let emojiHtml = null;
|
||||||
if (url) {
|
if (url) {
|
||||||
let customEmojis = {};
|
let customEmojis = {};
|
||||||
customEmojis[name] = { url, static_url: staticUrl };
|
customEmojis[`:${name}:`] = { url, static_url: staticUrl };
|
||||||
emojiHtml = emojify(`:${name}:`, customEmojis);
|
emojiHtml = emojify(`:${name}:`, customEmojis);
|
||||||
} else {
|
} else {
|
||||||
emojiHtml = emojify(name);
|
emojiHtml = emojify(name);
|
||||||
|
@ -47,16 +47,16 @@ export default @injectIntl
|
||||||
class StatusEmojiReactionsBar extends React.PureComponent {
|
class StatusEmojiReactionsBar extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
emojiReactions: ImmutablePropTypes.map.isRequired,
|
emojiReactions: ImmutablePropTypes.list.isRequired,
|
||||||
statusId: PropTypes.string,
|
statusId: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { emojiReactions, statusId } = this.props;
|
const { emojiReactions, statusId } = this.props;
|
||||||
|
|
||||||
const emojiButtons = React.Children.map(emojiReactions, (emoji) => (
|
const emojiButtons = Array.from(emojiReactions).map((emoji, index) => (
|
||||||
<EmojiReactionButton
|
<EmojiReactionButton
|
||||||
key={emoji.get('id')}
|
key={index}
|
||||||
name={emoji.get('name')}
|
name={emoji.get('name')}
|
||||||
count={emoji.get('count')}
|
count={emoji.get('count')}
|
||||||
me={emoji.get('me')}
|
me={emoji.get('me')}
|
||||||
|
|
|
@ -10,9 +10,11 @@ import {
|
||||||
import {
|
import {
|
||||||
reblog,
|
reblog,
|
||||||
favourite,
|
favourite,
|
||||||
|
emojiReact,
|
||||||
bookmark,
|
bookmark,
|
||||||
unreblog,
|
unreblog,
|
||||||
unfavourite,
|
unfavourite,
|
||||||
|
unEmojiReact,
|
||||||
unbookmark,
|
unbookmark,
|
||||||
pin,
|
pin,
|
||||||
unpin,
|
unpin,
|
||||||
|
@ -113,6 +115,14 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onEmojiReact (status, emoji) {
|
||||||
|
dispatch(emojiReact(status, emoji));
|
||||||
|
},
|
||||||
|
|
||||||
|
onUnEmojiReact (status, emoji) {
|
||||||
|
dispatch(unEmojiReact(status, emoji));
|
||||||
|
},
|
||||||
|
|
||||||
onBookmark (status) {
|
onBookmark (status) {
|
||||||
if (status.get('bookmarked')) {
|
if (status.get('bookmarked')) {
|
||||||
dispatch(unbookmark(status));
|
dispatch(unbookmark(status));
|
||||||
|
|
|
@ -61,6 +61,10 @@ const mapDispatchToProps = dispatch => ({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onEmojiReact (status, emoji) {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
onToggleHidden (status) {
|
onToggleHidden (status) {
|
||||||
if (status.get('hidden')) {
|
if (status.get('hidden')) {
|
||||||
dispatch(revealStatus(status.get('id')));
|
dispatch(revealStatus(status.get('id')));
|
||||||
|
|
|
@ -93,6 +93,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onEmojiReact (status, emoji) {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
onPin (status) {
|
onPin (status) {
|
||||||
if (status.get('pinned')) {
|
if (status.get('pinned')) {
|
||||||
dispatch(unpin(status));
|
dispatch(unpin(status));
|
||||||
|
|
|
@ -6,6 +6,7 @@ class PotentialFriendshipTracker
|
||||||
|
|
||||||
WEIGHTS = {
|
WEIGHTS = {
|
||||||
reply: 1,
|
reply: 1,
|
||||||
|
emoji_reaction: 5,
|
||||||
favourite: 10,
|
favourite: 10,
|
||||||
reblog: 20,
|
reblog: 20,
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
|
@ -13,6 +13,7 @@ module AccountAssociations
|
||||||
# Timelines
|
# Timelines
|
||||||
has_many :statuses, inverse_of: :account, dependent: :destroy
|
has_many :statuses, inverse_of: :account, dependent: :destroy
|
||||||
has_many :favourites, inverse_of: :account, dependent: :destroy
|
has_many :favourites, inverse_of: :account, dependent: :destroy
|
||||||
|
has_many :emoji_reactions, inverse_of: :account, dependent: :destroy
|
||||||
has_many :bookmarks, inverse_of: :account, dependent: :destroy
|
has_many :bookmarks, inverse_of: :account, dependent: :destroy
|
||||||
has_many :mentions, inverse_of: :account, dependent: :destroy
|
has_many :mentions, inverse_of: :account, dependent: :destroy
|
||||||
has_many :notifications, inverse_of: :account, dependent: :destroy
|
has_many :notifications, inverse_of: :account, dependent: :destroy
|
||||||
|
|
|
@ -37,6 +37,7 @@ class CustomEmoji < ApplicationRecord
|
||||||
|
|
||||||
belongs_to :category, class_name: 'CustomEmojiCategory', optional: true
|
belongs_to :category, class_name: 'CustomEmojiCategory', optional: true
|
||||||
has_one :local_counterpart, -> { where(domain: nil) }, class_name: 'CustomEmoji', primary_key: :shortcode, foreign_key: :shortcode
|
has_one :local_counterpart, -> { where(domain: nil) }, class_name: 'CustomEmoji', primary_key: :shortcode, foreign_key: :shortcode
|
||||||
|
has_many :emoji_reactions, inverse_of: :custom_emoji, dependent: :destroy
|
||||||
|
|
||||||
has_attached_file :image, styles: { static: { format: 'png', convert_options: '-coalesce +profile "!icc,*" +set modify-date +set create-date' } }, validate_media_type: false
|
has_attached_file :image, styles: { static: { format: 'png', convert_options: '-coalesce +profile "!icc,*" +set modify-date +set create-date' } }, validate_media_type: false
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ class EmojiReaction < ApplicationRecord
|
||||||
|
|
||||||
belongs_to :account, inverse_of: :emoji_reactions
|
belongs_to :account, inverse_of: :emoji_reactions
|
||||||
belongs_to :status, inverse_of: :emoji_reactions
|
belongs_to :status, inverse_of: :emoji_reactions
|
||||||
belongs_to :custom_emojis, optional: true
|
belongs_to :custom_emoji, optional: true
|
||||||
|
|
||||||
has_one :notification, as: :activity, dependent: :destroy
|
has_one :notification, as: :activity, dependent: :destroy
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,7 @@ class Notification < ApplicationRecord
|
||||||
belongs_to :follow, foreign_key: 'activity_id', optional: true
|
belongs_to :follow, foreign_key: 'activity_id', optional: true
|
||||||
belongs_to :follow_request, foreign_key: 'activity_id', optional: true
|
belongs_to :follow_request, foreign_key: 'activity_id', optional: true
|
||||||
belongs_to :favourite, foreign_key: 'activity_id', optional: true
|
belongs_to :favourite, foreign_key: 'activity_id', optional: true
|
||||||
|
belongs_to :emoji_reaction, foreign_key: 'activity_id', optional: true
|
||||||
belongs_to :poll, foreign_key: 'activity_id', optional: true
|
belongs_to :poll, foreign_key: 'activity_id', optional: true
|
||||||
belongs_to :report, foreign_key: 'activity_id', optional: true
|
belongs_to :report, foreign_key: 'activity_id', optional: true
|
||||||
|
|
||||||
|
@ -79,6 +80,8 @@ class Notification < ApplicationRecord
|
||||||
status&.reblog
|
status&.reblog
|
||||||
when :favourite
|
when :favourite
|
||||||
favourite&.status
|
favourite&.status
|
||||||
|
when :emoji_reaction
|
||||||
|
emoji_reaction&.status
|
||||||
when :mention
|
when :mention
|
||||||
mention&.status
|
mention&.status
|
||||||
when :poll
|
when :poll
|
||||||
|
@ -128,6 +131,8 @@ class Notification < ApplicationRecord
|
||||||
notification.status.reblog = cached_status
|
notification.status.reblog = cached_status
|
||||||
when :favourite
|
when :favourite
|
||||||
notification.favourite.status = cached_status
|
notification.favourite.status = cached_status
|
||||||
|
when :emoji_reaction
|
||||||
|
notification.emoji_reaction.status = cached_status
|
||||||
when :mention
|
when :mention
|
||||||
notification.mention.status = cached_status
|
notification.mention.status = cached_status
|
||||||
when :poll
|
when :poll
|
||||||
|
@ -148,7 +153,7 @@ class Notification < ApplicationRecord
|
||||||
return unless new_record?
|
return unless new_record?
|
||||||
|
|
||||||
case activity_type
|
case activity_type
|
||||||
when 'Status', 'Follow', 'Favourite', 'FollowRequest', 'Poll', 'Report'
|
when 'Status', 'Follow', 'Favourite', 'EmojiReaction', 'FollowRequest', 'Poll', 'Report'
|
||||||
self.from_account_id = activity&.account_id
|
self.from_account_id = activity&.account_id
|
||||||
when 'Mention'
|
when 'Mention'
|
||||||
self.from_account_id = activity&.status&.account_id
|
self.from_account_id = activity&.status&.account_id
|
||||||
|
|
|
@ -27,6 +27,10 @@ class StatusPolicy < ApplicationPolicy
|
||||||
show? && !blocking_author?
|
show? && !blocking_author?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def emoji_reaction?
|
||||||
|
show? && !blocking_author?
|
||||||
|
end
|
||||||
|
|
||||||
def destroy?
|
def destroy?
|
||||||
owned?
|
owned?
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class REST::EmojiReactionsGroupedByNameSerializer < ActiveModel::Serializer
|
class REST::EmojiReactionsGroupedByNameSerializer < ActiveModel::Serializer
|
||||||
|
include RoutingHelper
|
||||||
|
|
||||||
attributes :name, :count
|
attributes :name, :count
|
||||||
|
|
||||||
attribute :me, if: :current_user?
|
attribute :me, if: :current_user?
|
||||||
|
@ -14,10 +16,22 @@ class REST::EmojiReactionsGroupedByNameSerializer < ActiveModel::Serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def custom_emoji?
|
def custom_emoji?
|
||||||
object.respond_to?(:custom_emoji)
|
object.custom_emoji.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def account_ids?
|
def account_ids?
|
||||||
object.respond_to?(:account_ids)
|
object.respond_to?(:account_ids)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def url
|
||||||
|
full_asset_url(object.custom_emoji.image.url)
|
||||||
|
end
|
||||||
|
|
||||||
|
def static_url
|
||||||
|
full_asset_url(object.custom_emoji.image.url(:static))
|
||||||
|
end
|
||||||
|
|
||||||
|
def domain
|
||||||
|
object.custom_emoji.domain
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
72
app/services/emoji_react_service.rb
Normal file
72
app/services/emoji_react_service.rb
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class EmojiReactService < BaseService
|
||||||
|
include Authorization
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
|
# React a status with emoji and notify remote user
|
||||||
|
# @param [Account] account
|
||||||
|
# @param [Status] status
|
||||||
|
# @param [string] name
|
||||||
|
# @return [Favourite]
|
||||||
|
def call(account, status, name)
|
||||||
|
authorize_with account, status, :emoji_reaction?
|
||||||
|
|
||||||
|
emoji_reaction = EmojiReaction.find_by(account: account, status: status, name: name)
|
||||||
|
|
||||||
|
return emoji_reaction unless emoji_reaction.nil?
|
||||||
|
|
||||||
|
shortcode, domain = name.split('@')
|
||||||
|
|
||||||
|
custom_emoji = CustomEmoji.find_by(shortcode: shortcode, domain: domain)
|
||||||
|
|
||||||
|
emoji_reaction = EmojiReaction.create!(account: account, status: status, name: shortcode, custom_emoji: custom_emoji)
|
||||||
|
|
||||||
|
Trends.statuses.register(status)
|
||||||
|
|
||||||
|
create_notification(emoji_reaction)
|
||||||
|
notify_to_followers(emoji_reaction)
|
||||||
|
bump_potential_friendship(account, status)
|
||||||
|
|
||||||
|
emoji_reaction
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def create_notification(emoji_reaction)
|
||||||
|
status = emoji_reaction.status
|
||||||
|
|
||||||
|
if status.account.local?
|
||||||
|
# TODO: Change favourite event to notify
|
||||||
|
LocalNotificationWorker.perform_async(status.account_id, emoji_reaction.id, 'Favourite', 'favourite')
|
||||||
|
elsif status.account.activitypub?
|
||||||
|
ActivityPub::DeliveryWorker.perform_async(build_json(emoji_reaction), emoji_reaction.account_id, status.account.inbox_url)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def notify_to_followers(emoji_reaction)
|
||||||
|
status = emoji_reaction.status
|
||||||
|
|
||||||
|
return unless status.account.local?
|
||||||
|
|
||||||
|
ActivityPub::RawDistributionWorker.perform_async(emoji_reaction, status.account_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def broadcast_updates!(emoji_reaction)
|
||||||
|
status = emoji_reaction.status
|
||||||
|
|
||||||
|
DistributionWorker.perform_async(status.id, { 'update' => true })
|
||||||
|
end
|
||||||
|
|
||||||
|
def bump_potential_friendship(account, status)
|
||||||
|
ActivityTracker.increment('activity:interactions')
|
||||||
|
return if account.following?(status.account_id)
|
||||||
|
|
||||||
|
PotentialFriendshipTracker.record(account.id, status.account_id, :emoji_reaction)
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_json(emoji_reaction)
|
||||||
|
# TODO: change to original serializer for other servers
|
||||||
|
Oj.dump(serialize_payload(emoji_reaction, ActivityPub::LikeSerializer))
|
||||||
|
end
|
||||||
|
end
|
|
@ -458,6 +458,9 @@ Rails.application.routes.draw do
|
||||||
resource :source, only: :show
|
resource :source, only: :show
|
||||||
|
|
||||||
post :translate, to: 'translations#create'
|
post :translate, to: 'translations#create'
|
||||||
|
|
||||||
|
resources :emoji_reactions, only: :update, constraints: { id: /[^\/]+/ }
|
||||||
|
post :emoji_unreaction, to: 'emoji_reactions#destroy'
|
||||||
end
|
end
|
||||||
|
|
||||||
member do
|
member do
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue