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:
parent
728083ab53
commit
49324217c3
9 changed files with 38 additions and 43 deletions
|
@ -104,7 +104,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,
|
||||||
|
@ -820,7 +819,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'])) ? (
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,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' },
|
||||||
|
@ -89,7 +88,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),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -333,7 +332,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'>
|
||||||
|
|
|
@ -43,7 +43,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 ColumnBackButton from 'flavours/glitch/components/column_back_button';
|
import ColumnBackButton from 'flavours/glitch/components/column_back_button';
|
||||||
import ColumnHeader from '../../components/column_header';
|
import ColumnHeader from '../../components/column_header';
|
||||||
|
@ -150,7 +154,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),
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -709,8 +712,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
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import escapeTextContentForBrowser from 'escape-html';
|
import escapeTextContentForBrowser from 'escape-html';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { List as ImmutableList, Map as ImmutableMap, is } from 'immutable';
|
import { List as ImmutableList } from 'immutable';
|
||||||
import { toServerSideType } from 'flavours/glitch/utils/filters';
|
import { toServerSideType } from 'flavours/glitch/utils/filters';
|
||||||
import { me } from 'flavours/glitch/initial_state';
|
import { me } from 'flavours/glitch/initial_state';
|
||||||
|
|
||||||
|
@ -137,11 +137,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(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue