1
0
mirror of https://github.com/funamitech/mastodon synced 2024-12-15 15:18:30 +09:00

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 Jeremy Kescher
parent 56617243b5
commit a5c96afeac
No known key found for this signature in database
GPG Key ID: 80A419A7A613DFA4
9 changed files with 33 additions and 42 deletions

View File

@ -116,7 +116,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,
@ -843,7 +842,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

@ -122,13 +122,7 @@ class StatusActionBar extends ImmutablePureComponent {
}; };
handleEmojiPick = data => { handleEmojiPick = data => {
const { signedIn } = this.context.identity;
if (signedIn) {
this.props.onReactionAdd(this.props.status.get('id'), data.native.replace(/:/g, '')); this.props.onReactionAdd(this.props.status.get('id'), data.native.replace(/:/g, ''));
} else {
this.props.onInteractionModal('favourite', this.props.status);
}
} }
handleReblogClick = e => { handleReblogClick = e => {
@ -216,7 +210,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'));
@ -317,7 +310,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,12 +83,14 @@ class Reaction extends ImmutablePureComponent {
handleClick = () => { handleClick = () => {
const { reaction, statusId, addReaction, removeReaction } = this.props; const { reaction, statusId, addReaction, removeReaction } = this.props;
if (!reaction.get('extern')) {
if (reaction.get('me')) { if (reaction.get('me')) {
removeReaction(statusId, reaction.get('name')); removeReaction(statusId, reaction.get('name'));
} else { } else {
addReaction(statusId, reaction.get('name')); addReaction(statusId, reaction.get('name'));
} }
} }
}
handleMouseEnter = () => this.setState({ hovered: true }) handleMouseEnter = () => this.setState({ hovered: true })
@ -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

@ -47,7 +47,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' },
@ -91,7 +90,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

@ -211,7 +211,7 @@ class ActionBar extends 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

@ -53,7 +53,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,
}; };
@ -338,7 +337,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

@ -156,7 +156,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),
}; };
}; };
@ -747,8 +746,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

@ -141,11 +141,3 @@ export const getAccountHidden = createSelector([
export const getStatusList = createSelector([ export const getStatusList = createSelector([
(state, type) => state.getIn(['status_lists', type, 'items']), (state, type) => state.getIn(['status_lists', type, 'items']),
], (items) => items.toList()); ], (items) => items.toList());
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