display external custom emoji reactions properly

Using an emoji map was completely unnecessary in
the first place, because the reaction list from
the API response includes URLs for every custom
emoji anyway.  The reaction list now also contains
a boolean field indicating whether it is an
external custom emoji, which is required because
people should only be able to react with Unicode
emojis and local custom ones, not with custom
emojis from other servers.
This commit is contained in:
fef 2022-12-03 11:57:00 +00:00
parent a688a0b880
commit 0ea02e608c
No known key found for this signature in database
GPG key ID: EC22E476DC2D3D84
9 changed files with 32 additions and 44 deletions

View file

@ -105,7 +105,6 @@ class Status extends ImmutablePureComponent {
scrollKey: PropTypes.string,
deployPictureInPicture: PropTypes.func,
settings: ImmutablePropTypes.map.isRequired,
emojiMap: ImmutablePropTypes.map.isRequired,
pictureInPicture: PropTypes.shape({
inUse: PropTypes.bool,
available: PropTypes.bool,
@ -811,7 +810,6 @@ class Status extends ImmutablePureComponent {
numVisible={visibleReactions}
addReaction={this.props.onReactionAdd}
removeReaction={this.props.onReactionRemove}
emojiMap={this.props.emojiMap}
/>
{!isCollapsed || !(muted || !settings.getIn(['collapsed', 'show_action_bar'])) ? (

View file

@ -118,13 +118,7 @@ class StatusActionBar extends ImmutablePureComponent {
}
handleEmojiPick = data => {
const { signedIn } = this.context.identity;
if (signedIn) {
this.props.onReactionAdd(this.props.status.get('id'), data.native.replace(/:/g, ''));
} else {
this.props.onInteractionModal('favourite', this.props.status);
}
this.props.onReactionAdd(this.props.status.get('id'), data.native.replace(/:/g, ''));
}
handleReblogClick = e => {
@ -212,6 +206,7 @@ class StatusActionBar extends ImmutablePureComponent {
render () {
const { status, intl, withDismiss, withCounters, showReplyCount, scrollKey } = this.props;
const { signedIn } = this.context.identity;
const anonymousAccess = !me;
const mutingConversation = status.get('muted');
@ -311,7 +306,7 @@ class StatusActionBar extends ImmutablePureComponent {
<IconButton className='status__action-bar-button' title={intl.formatMessage(messages.hide)} icon='eye' onClick={this.handleHideClick} />
);
const canReact = status.get('reactions').filter(r => r.get('count') > 0 && r.get('me')).size < maxReactions;
const canReact = signedIn && status.get('reactions').filter(r => r.get('count') > 0 && r.get('me')).size < maxReactions;
const reactButton = (
<IconButton
className='status__action-bar-button'

View file

@ -18,7 +18,6 @@ export default class StatusReactions extends ImmutablePureComponent {
numVisible: PropTypes.number,
addReaction: PropTypes.func.isRequired,
removeReaction: PropTypes.func.isRequired,
emojiMap: ImmutablePropTypes.map.isRequired,
};
willEnter() {
@ -57,7 +56,6 @@ export default class StatusReactions extends ImmutablePureComponent {
style={{ transform: `scale(${style.scale})`, position: style.scale < 0.5 ? 'absolute' : 'static' }}
addReaction={this.props.addReaction}
removeReaction={this.props.removeReaction}
emojiMap={this.props.emojiMap}
/>
))}
</div>
@ -75,7 +73,6 @@ class Reaction extends ImmutablePureComponent {
reaction: ImmutablePropTypes.map.isRequired,
addReaction: PropTypes.func.isRequired,
removeReaction: PropTypes.func.isRequired,
emojiMap: ImmutablePropTypes.map.isRequired,
style: PropTypes.object,
};
@ -86,10 +83,12 @@ class Reaction extends ImmutablePureComponent {
handleClick = () => {
const { reaction, statusId, addReaction, removeReaction } = this.props;
if (reaction.get('me')) {
removeReaction(statusId, reaction.get('name'));
} else {
addReaction(statusId, reaction.get('name'));
if (!reaction.get('extern')) {
if (reaction.get('me')) {
removeReaction(statusId, reaction.get('name'));
} else {
addReaction(statusId, reaction.get('name'));
}
}
}
@ -109,7 +108,12 @@ class Reaction extends ImmutablePureComponent {
style={this.props.style}
>
<span className='reactions-bar__item__emoji'>
<Emoji hovered={this.state.hovered} emoji={reaction.get('name')} emojiMap={this.props.emojiMap} />
<Emoji
hovered={this.state.hovered}
emoji={reaction.get('name')}
url={reaction.get('url')}
staticUrl={reaction.get('static_url')}
/>
</span>
<span className='reactions-bar__item__count'>
<AnimatedNumber value={reaction.get('count')} />
@ -124,12 +128,13 @@ class Emoji extends React.PureComponent {
static propTypes = {
emoji: PropTypes.string.isRequired,
emojiMap: ImmutablePropTypes.map.isRequired,
hovered: PropTypes.bool.isRequired,
url: PropTypes.string,
staticUrl: PropTypes.string,
};
render() {
const { emoji, emojiMap, hovered } = this.props;
const { emoji, hovered, url, staticUrl } = this.props;
if (unicodeMapping[emoji]) {
const { filename, shortCode } = unicodeMapping[this.props.emoji];
@ -144,10 +149,8 @@ class Emoji extends React.PureComponent {
src={`${assetHost}/emoji/${filename}.svg`}
/>
);
} else if (emojiMap.get(emoji)) {
const filename = (autoPlayGif || hovered)
? emojiMap.getIn([emoji, 'url'])
: emojiMap.getIn([emoji, 'static_url']);
} else {
const filename = (autoPlayGif || hovered) ? url : staticUrl;
const shortCode = `:${emoji}:`;
return (
@ -159,8 +162,6 @@ class Emoji extends React.PureComponent {
src={filename}
/>
);
} else {
return null;
}
}

View file

@ -45,7 +45,6 @@ import { showAlertForError } from '../actions/alerts';
import AccountContainer from 'flavours/glitch/containers/account_container';
import Spoilers from '../components/spoilers';
import Icon from 'flavours/glitch/components/icon';
import { makeCustomEmojiMap } from '../selectors';
const messages = defineMessages({
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
@ -85,7 +84,6 @@ const makeMapStateToProps = () => {
account: account || props.account,
settings: state.get('local_settings'),
prepend: prepend || props.prepend,
emojiMap: makeCustomEmojiMap(state),
pictureInPicture: {
inUse: state.getIn(['meta', 'layout']) !== 'mobile' && state.get('picture_in_picture').statusId === props.id,

View file

@ -203,7 +203,7 @@ class ActionBar extends React.PureComponent {
}
}
const canReact = status.get('reactions').filter(r => r.get('count') > 0 && r.get('me')).size < maxReactions;
const canReact = signedIn && status.get('reactions').filter(r => r.get('count') > 0 && r.get('me')).size < maxReactions;
const reactButton = (
<IconButton
className='plus-icon'

View file

@ -46,7 +46,6 @@ class DetailedStatus extends ImmutablePureComponent {
onToggleMediaVisibility: PropTypes.func,
onReactionAdd: PropTypes.func.isRequired,
onReactionRemove: PropTypes.func.isRequired,
emojiMap: ImmutablePropTypes.map.isRequired,
intl: PropTypes.object.isRequired,
};
@ -328,7 +327,6 @@ class DetailedStatus extends ImmutablePureComponent {
reactions={status.get('reactions')}
addReaction={this.props.onReactionAdd}
removeReaction={this.props.onReactionRemove}
emojiMap={this.props.emojiMap}
/>
<div className='detailed-status__meta'>

View file

@ -43,7 +43,7 @@ import { initMuteModal } from 'flavours/glitch/actions/mutes';
import { initBlockModal } from 'flavours/glitch/actions/blocks';
import { initReport } from 'flavours/glitch/actions/reports';
import { initBoostModal } from 'flavours/glitch/actions/boosts';
import { makeCustomEmojiMap, makeGetStatus } from 'flavours/glitch/selectors';
import { makeGetStatus } from 'flavours/glitch/selectors';
import ScrollContainer from 'flavours/glitch/containers/scroll_container';
import ColumnBackButton from 'flavours/glitch/components/column_back_button';
import ColumnHeader from '../../components/column_header';
@ -148,7 +148,6 @@ const makeMapStateToProps = () => {
askReplyConfirmation: state.getIn(['local_settings', 'confirm_before_clearing_draft']) && state.getIn(['compose', 'text']).trim().length !== 0,
domain: state.getIn(['meta', 'domain']),
usingPiP: state.get('picture_in_picture').statusId === props.params.statusId,
emojiMap: makeCustomEmojiMap(state),
};
};
@ -707,7 +706,6 @@ class Status extends ImmutablePureComponent {
showMedia={this.state.showMedia}
onToggleMediaVisibility={this.handleToggleMediaVisibility}
usingPiP={usingPiP}
emojiMap={this.props.emojiMap}
/>
<ActionBar

View file

@ -1,6 +1,6 @@
import escapeTextContentForBrowser from 'escape-html';
import { createSelector } from 'reselect';
import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
import { List as ImmutableList } from 'immutable';
import { toServerSideType } from 'flavours/glitch/utils/filters';
import { me } from 'flavours/glitch/initial_state';
@ -127,11 +127,3 @@ export const getAccountHidden = createSelector([
], (hidden, followingOrRequested, isSelf) => {
return hidden && !(isSelf || followingOrRequested);
});
export const makeCustomEmojiMap = createSelector(
[state => state.get('custom_emojis')],
items => items.reduce(
(map, emoji) => map.set(emoji.get('shortcode'), emoji),
ImmutableMap(),
),
);

View file

@ -3,7 +3,7 @@
class REST::ReactionSerializer < ActiveModel::Serializer
include RoutingHelper
attributes :name, :count
attributes :name, :count, :extern
attribute :me, if: :current_user?
attribute :url, if: :custom_emoji?
@ -21,6 +21,14 @@ class REST::ReactionSerializer < ActiveModel::Serializer
object.custom_emoji.present?
end
def extern
if custom_emoji?
object.custom_emoji.domain.present?
else
false
end
end
def url
full_asset_url(object.custom_emoji.image.url)
end