Merge branch 'heart-react' into 'develop'

Fix heart emoji react for Pleroma develop branch; let emojis be customizable the admin

Closes #538

See merge request soapbox-pub/soapbox-fe!398
merge-requests/403/merge
Alex Gleason 4 years ago
commit bdbc6eb79c

@ -18,10 +18,18 @@ export const defaultConfig = ImmutableMap({
navlinks: ImmutableMap({ navlinks: ImmutableMap({
homeFooter: ImmutableList(), homeFooter: ImmutableList(),
}), }),
allowedEmoji: ImmutableList([
'👍',
'❤️',
'😆',
'😮',
'😢',
'😩',
]),
}); });
export function getSoapboxConfig(state) { export function getSoapboxConfig(state) {
return defaultConfig.mergeDeep(state.get('soapbox')); return defaultConfig.merge(state.get('soapbox'));
} }
export function fetchSoapboxConfig() { export function fetchSoapboxConfig() {

@ -16,7 +16,7 @@ exports[`<EmojiSelector /> renders correctly 1`] = `
className="emoji-react-selector__emoji" className="emoji-react-selector__emoji"
dangerouslySetInnerHTML={ dangerouslySetInnerHTML={
Object { Object {
"__html": "<img draggable=\\"false\\" class=\\"emojione\\" alt=\\"❤\\" title=\\":heart:\\" src=\\"/emoji/2764.svg\\" />", "__html": "<img draggable=\\"false\\" class=\\"emojione\\" alt=\\"❤\\" title=\\":heart:\\" src=\\"/emoji/2764.svg\\" />",
} }
} }
/> />

@ -1,10 +1,10 @@
import React from 'react'; import React from 'react';
import renderer from 'react-test-renderer'; import { createComponent } from 'soapbox/test_helpers';
import EmojiSelector from '../emoji_selector'; import EmojiSelector from '../emoji_selector';
describe('<EmojiSelector />', () => { describe('<EmojiSelector />', () => {
it('renders correctly', () => { it('renders correctly', () => {
const component = renderer.create(<EmojiSelector />); const component = createComponent(<EmojiSelector />);
const tree = component.toJSON(); const tree = component.toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
}); });

@ -1,10 +1,17 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { ALLOWED_EMOJI } from 'soapbox/utils/emoji_reacts'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import emojify from 'soapbox/features/emoji/emoji'; import emojify from 'soapbox/features/emoji/emoji';
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
import classNames from 'classnames'; import classNames from 'classnames';
export default class EmojiSelector extends React.Component { const mapStateToProps = state => ({
allowedEmoji: getSoapboxConfig(state).get('allowedEmoji'),
});
export default @connect(mapStateToProps)
class EmojiSelector extends ImmutablePureComponent {
static propTypes = { static propTypes = {
onReact: PropTypes.func.isRequired, onReact: PropTypes.func.isRequired,
@ -17,11 +24,11 @@ export default class EmojiSelector extends React.Component {
} }
render() { render() {
const { onReact, visible } = this.props; const { onReact, visible, allowedEmoji } = this.props;
return ( return (
<div className={classNames('emoji-react-selector', { 'emoji-react-selector--visible': visible })}> <div className={classNames('emoji-react-selector', { 'emoji-react-selector--visible': visible })}>
{ALLOWED_EMOJI.map((emoji, i) => ( {allowedEmoji.map((emoji, i) => (
<button <button
key={i} key={i}
className='emoji-react-selector__emoji' className='emoji-react-selector__emoji'

@ -93,6 +93,7 @@ class Status extends ImmutablePureComponent {
cachedMediaWidth: PropTypes.number, cachedMediaWidth: PropTypes.number,
group: ImmutablePropTypes.map, group: ImmutablePropTypes.map,
displayMedia: PropTypes.string, displayMedia: PropTypes.string,
allowedEmoji: ImmutablePropTypes.list,
}; };
// Avoid checking props that are functions (and whose equality will always // Avoid checking props that are functions (and whose equality will always

@ -74,6 +74,7 @@ class StatusActionBar extends ImmutablePureComponent {
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
me: SoapboxPropTypes.me, me: SoapboxPropTypes.me,
isStaff: PropTypes.bool.isRequired, isStaff: PropTypes.bool.isRequired,
allowedEmoji: ImmutablePropTypes.list,
}; };
static defaultProps = { static defaultProps = {
@ -120,7 +121,7 @@ class StatusActionBar extends ImmutablePureComponent {
} }
handleLikeButtonClick = e => { handleLikeButtonClick = e => {
const meEmojiReact = getReactForStatus(this.props.status) || '👍'; const meEmojiReact = getReactForStatus(this.props.status, this.props.allowedEmoji) || '👍';
if (this.isMobile()) { if (this.isMobile()) {
if (this.state.emojiSelectorVisible) { if (this.state.emojiSelectorVisible) {
this.handleReactClick(meEmojiReact)(); this.handleReactClick(meEmojiReact)();
@ -314,7 +315,7 @@ class StatusActionBar extends ImmutablePureComponent {
} }
render() { render() {
const { status, intl } = this.props; const { status, intl, allowedEmoji } = this.props;
const { emojiSelectorVisible } = this.state; const { emojiSelectorVisible } = this.state;
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility')); const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
@ -326,8 +327,9 @@ class StatusActionBar extends ImmutablePureComponent {
status.getIn(['pleroma', 'emoji_reactions'], ImmutableList()), status.getIn(['pleroma', 'emoji_reactions'], ImmutableList()),
favouriteCount, favouriteCount,
status.get('favourited'), status.get('favourited'),
allowedEmoji,
).reduce((acc, cur) => acc + cur.get('count'), 0); ).reduce((acc, cur) => acc + cur.get('count'), 0);
const meEmojiReact = getReactForStatus(status); const meEmojiReact = getReactForStatus(status, allowedEmoji);
let menu = this._makeMenu(publicStatus); let menu = this._makeMenu(publicStatus);
let reblogIcon = 'retweet'; let reblogIcon = 'retweet';

@ -35,6 +35,7 @@ import {
groupRemoveStatus, groupRemoveStatus,
} from '../actions/groups'; } from '../actions/groups';
import { getSettings } from '../actions/settings'; import { getSettings } from '../actions/settings';
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
const messages = defineMessages({ const messages = defineMessages({
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
@ -53,6 +54,7 @@ const makeMapStateToProps = () => {
const mapStateToProps = (state, props) => ({ const mapStateToProps = (state, props) => ({
status: getStatus(state, props), status: getStatus(state, props),
displayMedia: getSettings(state).get('displayMedia'), displayMedia: getSettings(state).get('displayMedia'),
allowedEmoji: getSoapboxConfig(state).get('allowedEmoji'),
}); });
return mapStateToProps; return mapStateToProps;

@ -78,6 +78,7 @@ class ActionBar extends React.PureComponent {
onOpenUnauthorizedModal: PropTypes.func.isRequired, onOpenUnauthorizedModal: PropTypes.func.isRequired,
me: SoapboxPropTypes.me, me: SoapboxPropTypes.me,
isStaff: PropTypes.bool.isRequired, isStaff: PropTypes.bool.isRequired,
allowedEmoji: ImmutablePropTypes.list,
}; };
static defaultProps = { static defaultProps = {
@ -130,7 +131,7 @@ class ActionBar extends React.PureComponent {
} }
handleLikeButtonClick = e => { handleLikeButtonClick = e => {
const meEmojiReact = getReactForStatus(this.props.status) || '👍'; const meEmojiReact = getReactForStatus(this.props.status, this.props.allowedEmoji) || '👍';
if (this.isMobile()) { if (this.isMobile()) {
if (this.state.emojiSelectorVisible) { if (this.state.emojiSelectorVisible) {
this.handleReactClick(meEmojiReact)(); this.handleReactClick(meEmojiReact)();
@ -232,12 +233,12 @@ class ActionBar extends React.PureComponent {
} }
render() { render() {
const { status, intl, me, isStaff } = this.props; const { status, intl, me, isStaff, allowedEmoji } = this.props;
const { emojiSelectorVisible } = this.state; const { emojiSelectorVisible } = this.state;
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility')); const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
const mutingConversation = status.get('muted'); const mutingConversation = status.get('muted');
const meEmojiReact = getReactForStatus(status); const meEmojiReact = getReactForStatus(status, allowedEmoji);
let menu = []; let menu = [];

@ -15,7 +15,7 @@ import scheduleIdleTask from '../../ui/util/schedule_idle_task';
import classNames from 'classnames'; import classNames from 'classnames';
import Icon from 'soapbox/components/icon'; import Icon from 'soapbox/components/icon';
import PollContainer from 'soapbox/containers/poll_container'; import PollContainer from 'soapbox/containers/poll_container';
import { StatusInteractionBar } from './status_interaction_bar'; import StatusInteractionBar from './status_interaction_bar';
import { getDomain } from 'soapbox/utils/accounts'; import { getDomain } from 'soapbox/utils/accounts';
import HoverRefWrapper from 'soapbox/components/hover_ref_wrapper'; import HoverRefWrapper from 'soapbox/components/hover_ref_wrapper';

@ -1,17 +1,26 @@
import React from 'react'; import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import { FormattedNumber } from 'react-intl'; import { FormattedNumber } from 'react-intl';
import emojify from 'soapbox/features/emoji/emoji'; import emojify from 'soapbox/features/emoji/emoji';
import { reduceEmoji } from 'soapbox/utils/emoji_reacts'; import { reduceEmoji } from 'soapbox/utils/emoji_reacts';
import SoapboxPropTypes from 'soapbox/utils/soapbox_prop_types'; import SoapboxPropTypes from 'soapbox/utils/soapbox_prop_types';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import Icon from 'soapbox/components/icon'; import Icon from 'soapbox/components/icon';
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
export class StatusInteractionBar extends React.Component { const mapStateToProps = state => ({
allowedEmoji: getSoapboxConfig(state).get('allowedEmoji'),
});
export default @connect(mapStateToProps)
class StatusInteractionBar extends ImmutablePureComponent {
static propTypes = { static propTypes = {
status: ImmutablePropTypes.map, status: ImmutablePropTypes.map,
me: SoapboxPropTypes.me, me: SoapboxPropTypes.me,
allowedEmoji: ImmutablePropTypes.list,
} }
getNormalizedReacts = () => { getNormalizedReacts = () => {
@ -20,6 +29,7 @@ export class StatusInteractionBar extends React.Component {
status.getIn(['pleroma', 'emoji_reactions']), status.getIn(['pleroma', 'emoji_reactions']),
status.get('favourites_count'), status.get('favourites_count'),
status.get('favourited'), status.get('favourited'),
this.props.allowedEmoji,
).reverse(); ).reverse();
} }

@ -46,6 +46,7 @@ import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from
import { textForScreenReader, defaultMediaVisibility } from '../../components/status'; import { textForScreenReader, defaultMediaVisibility } from '../../components/status';
import Icon from 'soapbox/components/icon'; import Icon from 'soapbox/components/icon';
import { getSettings } from 'soapbox/actions/settings'; import { getSettings } from 'soapbox/actions/settings';
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
const messages = defineMessages({ const messages = defineMessages({
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
@ -111,6 +112,7 @@ const makeMapStateToProps = () => {
domain: state.getIn(['meta', 'domain']), domain: state.getIn(['meta', 'domain']),
me: state.get('me'), me: state.get('me'),
displayMedia: getSettings(state).get('displayMedia'), displayMedia: getSettings(state).get('displayMedia'),
allowedEmoji: getSoapboxConfig(state).get('allowedEmoji'),
}; };
}; };
@ -521,6 +523,7 @@ class Status extends ImmutablePureComponent {
onPin={this.handlePin} onPin={this.handlePin}
onBookmark={this.handleBookmark} onBookmark={this.handleBookmark}
onEmbed={this.handleEmbed} onEmbed={this.handleEmbed}
allowedEmoji={this.props.allowedEmoji}
/> />
</div> </div>
</HotKeys> </HotKeys>

@ -158,7 +158,7 @@ describe('getReactForStatus', () => {
], ],
}, },
}); });
expect(getReactForStatus(status)).toEqual('❤'); expect(getReactForStatus(status, ALLOWED_EMOJI)).toEqual('❤');
}); });
it('returns a thumbs-up for a favourite', () => { it('returns a thumbs-up for a favourite', () => {

@ -7,7 +7,7 @@ import {
// I've customized them. // I've customized them.
export const ALLOWED_EMOJI = [ export const ALLOWED_EMOJI = [
'👍', '👍',
'❤', '❤',
'😆', '😆',
'😮', '😮',
'😢', '😢',
@ -75,11 +75,12 @@ export const reduceEmoji = (emojiReacts, favouritesCount, favourited, allowedEmo
emojiReacts, favouritesCount, favourited, emojiReacts, favouritesCount, favourited,
))), allowedEmoji)); ))), allowedEmoji));
export const getReactForStatus = status => { export const getReactForStatus = (status, allowedEmoji=ALLOWED_EMOJI) => {
return reduceEmoji( return reduceEmoji(
status.getIn(['pleroma', 'emoji_reactions'], ImmutableList()), status.getIn(['pleroma', 'emoji_reactions'], ImmutableList()),
status.get('favourites_count'), status.get('favourites_count', 0),
status.get('favourited'), status.get('favourited'),
allowedEmoji,
).filter(e => e.get('me') === true) ).filter(e => e.get('me') === true)
.getIn([0, 'name']); .getIn([0, 'name']);
}; };

Loading…
Cancel
Save