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 committed by neatchee
parent dbbfea1b48
commit d0f3f649f5
9 changed files with 37 additions and 42 deletions

View file

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

View file

@ -118,13 +118,7 @@ class StatusActionBar extends ImmutablePureComponent {
}; };
handleEmojiPick = data => { handleEmojiPick = data => {
const { signedIn } = this.context.identity; this.props.onReactionAdd(this.props.status.get('id'), data.native.replace(/:/g, ''));
if (signedIn) {
this.props.onReactionAdd(this.props.status.get('id'), data.native.replace(/:/g, ''));
} else {
this.props.onInteractionModal('favourite', this.props.status);
}
} }
handleReblogClick = e => { handleReblogClick = e => {
@ -213,7 +207,6 @@ class StatusActionBar extends ImmutablePureComponent {
render () { render () {
const { status, intl, withDismiss, withCounters, showReplyCount, scrollKey } = this.props; const { status, intl, withDismiss, withCounters, showReplyCount, scrollKey } = this.props;
const { permissions } = this.context.identity; const { permissions } = this.context.identity;
const anonymousAccess = !me; const anonymousAccess = !me;
const mutingConversation = status.get('muted'); const mutingConversation = status.get('muted');
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility')); const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
@ -314,7 +307,7 @@ class StatusActionBar extends ImmutablePureComponent {
<IconButton className='status__action-bar-button' title={intl.formatMessage(messages.hide)} icon='eye' onClick={this.handleHideClick} /> <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 = permissions && status.get('reactions').filter(r => r.get('count') > 0 && r.get('me')).size < maxReactions;
const reactButton = ( const reactButton = (
<IconButton <IconButton
className='status__action-bar-button' className='status__action-bar-button'

View file

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

View file

@ -44,7 +44,6 @@ import { showAlertForError } from '../actions/alerts';
import AccountContainer from 'flavours/glitch/containers/account_container'; import AccountContainer from 'flavours/glitch/containers/account_container';
import Spoilers from '../components/spoilers'; import Spoilers from '../components/spoilers';
import Icon from 'flavours/glitch/components/icon'; import Icon from 'flavours/glitch/components/icon';
import { makeCustomEmojiMap } from '../selectors';
const messages = defineMessages({ const messages = defineMessages({
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
@ -88,7 +87,6 @@ const makeMapStateToProps = () => {
account: account || props.account, account: account || props.account,
settings: state.get('local_settings'), settings: state.get('local_settings'),
prepend: prepend || props.prepend, prepend: prepend || props.prepend,
emojiMap: makeCustomEmojiMap(state),
pictureInPicture: getPictureInPicture(state, props), pictureInPicture: getPictureInPicture(state, props),
}; };
}; };

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 = ( const reactButton = (
<IconButton <IconButton
className='plus-icon' className='plus-icon'

View file

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

View file

@ -42,7 +42,11 @@ import { initMuteModal } from 'flavours/glitch/actions/mutes';
import { initBlockModal } from 'flavours/glitch/actions/blocks'; import { initBlockModal } from 'flavours/glitch/actions/blocks';
import { initReport } from 'flavours/glitch/actions/reports'; import { initReport } from 'flavours/glitch/actions/reports';
import { initBoostModal } from 'flavours/glitch/actions/boosts'; import { initBoostModal } from 'flavours/glitch/actions/boosts';
<<<<<<< HEAD
import { makeCustomEmojiMap, makeGetStatus, makeGetPictureInPicture } from 'flavours/glitch/selectors'; import { makeCustomEmojiMap, makeGetStatus, makeGetPictureInPicture } from 'flavours/glitch/selectors';
=======
import { makeGetStatus } from 'flavours/glitch/selectors';
>>>>>>> f0197c80d (display external custom emoji reactions properly)
import ScrollContainer from 'flavours/glitch/containers/scroll_container'; import ScrollContainer from 'flavours/glitch/containers/scroll_container';
import ColumnHeader from '../../components/column_header'; import ColumnHeader from '../../components/column_header';
import StatusContainer from 'flavours/glitch/containers/status_container'; import StatusContainer from 'flavours/glitch/containers/status_container';
@ -151,7 +155,6 @@ const makeMapStateToProps = () => {
askReplyConfirmation: state.getIn(['local_settings', 'confirm_before_clearing_draft']) && state.getIn(['compose', 'text']).trim().length !== 0, askReplyConfirmation: state.getIn(['local_settings', 'confirm_before_clearing_draft']) && state.getIn(['compose', 'text']).trim().length !== 0,
domain: state.getIn(['meta', 'domain']), domain: state.getIn(['meta', 'domain']),
pictureInPicture: getPictureInPicture(state, { id: props.params.statusId }), pictureInPicture: getPictureInPicture(state, { id: props.params.statusId }),
emojiMap: makeCustomEmojiMap(state),
}; };
}; };
@ -713,8 +716,12 @@ class Status extends ImmutablePureComponent {
domain={domain} domain={domain}
showMedia={this.state.showMedia} showMedia={this.state.showMedia}
onToggleMediaVisibility={this.handleToggleMediaVisibility} onToggleMediaVisibility={this.handleToggleMediaVisibility}
<<<<<<< HEAD
pictureInPicture={pictureInPicture} pictureInPicture={pictureInPicture}
emojiMap={this.props.emojiMap} emojiMap={this.props.emojiMap}
=======
usingPiP={usingPiP}
>>>>>>> f0197c80d (display external custom emoji reactions properly)
/> />
<ActionBar <ActionBar

View file

@ -136,11 +136,3 @@ export const getAccountHidden = createSelector([
], (hidden, followingOrRequested, isSelf) => { ], (hidden, followingOrRequested, isSelf) => {
return hidden && !(isSelf || followingOrRequested); 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 class REST::ReactionSerializer < ActiveModel::Serializer
include RoutingHelper include RoutingHelper
attributes :name, :count attributes :name, :count, :extern
attribute :me, if: :current_user? attribute :me, if: :current_user?
attribute :url, if: :custom_emoji? attribute :url, if: :custom_emoji?
@ -21,6 +21,14 @@ class REST::ReactionSerializer < ActiveModel::Serializer
object.custom_emoji.present? object.custom_emoji.present?
end end
def extern
if custom_emoji?
object.custom_emoji.domain.present?
else
false
end
end
def url def url
full_asset_url(object.custom_emoji.image.url) full_asset_url(object.custom_emoji.image.url)
end end