Set Like button icon to chosen emoji

merge-requests/18/head
Alex Gleason 2020-05-22 18:44:24 -05:00
rodzic c0fe85ca29
commit 2060f15ebb
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 7211D1F99744FBB7
7 zmienionych plików z 58 dodań i 26 usunięć

Wyświetl plik

@ -13,9 +13,11 @@ export const EMOJI_REACTS_FETCH_REQUEST = 'EMOJI_REACTS_FETCH_REQUEST';
export const EMOJI_REACTS_FETCH_SUCCESS = 'EMOJI_REACTS_FETCH_SUCCESS'; export const EMOJI_REACTS_FETCH_SUCCESS = 'EMOJI_REACTS_FETCH_SUCCESS';
export const EMOJI_REACTS_FETCH_FAIL = 'EMOJI_REACTS_FETCH_FAIL'; export const EMOJI_REACTS_FETCH_FAIL = 'EMOJI_REACTS_FETCH_FAIL';
const noOp = () => () => new Promise(f => f());
export function fetchEmojiReacts(id, emoji) { export function fetchEmojiReacts(id, emoji) {
return (dispatch, getState) => { return (dispatch, getState) => {
if (!getState().get('me')) return; if (!getState().get('me')) return dispatch(noOp());
dispatch(fetchEmojiReactsRequest(id, emoji)); dispatch(fetchEmojiReactsRequest(id, emoji));
@ -23,7 +25,7 @@ export function fetchEmojiReacts(id, emoji) {
? `/api/v1/pleroma/statuses/${id}/reactions/${emoji}` ? `/api/v1/pleroma/statuses/${id}/reactions/${emoji}`
: `/api/v1/pleroma/statuses/${id}/reactions`; : `/api/v1/pleroma/statuses/${id}/reactions`;
api(getState).get(url).then(response => { return api(getState).get(url).then(response => {
response.data.forEach(emojiReact => { response.data.forEach(emojiReact => {
dispatch(importFetchedAccounts(emojiReact.accounts)); dispatch(importFetchedAccounts(emojiReact.accounts));
}); });
@ -36,11 +38,11 @@ export function fetchEmojiReacts(id, emoji) {
export function emojiReact(status, emoji) { export function emojiReact(status, emoji) {
return function(dispatch, getState) { return function(dispatch, getState) {
if (!getState().get('me')) return; if (!getState().get('me')) return dispatch(noOp());
dispatch(emojiReactRequest(status, emoji)); dispatch(emojiReactRequest(status, emoji));
api(getState) return api(getState)
.put(`/api/v1/pleroma/statuses/${status.get('id')}/reactions/${emoji}`) .put(`/api/v1/pleroma/statuses/${status.get('id')}/reactions/${emoji}`)
.then(function(response) { .then(function(response) {
dispatch(importFetchedStatus(response.data)); dispatch(importFetchedStatus(response.data));
@ -53,11 +55,11 @@ export function emojiReact(status, emoji) {
export function unEmojiReact(status, emoji) { export function unEmojiReact(status, emoji) {
return (dispatch, getState) => { return (dispatch, getState) => {
if (!getState().get('me')) return; if (!getState().get('me')) return dispatch(noOp());
dispatch(unEmojiReactRequest(status, emoji)); dispatch(unEmojiReactRequest(status, emoji));
api(getState) return api(getState)
.delete(`/api/v1/pleroma/statuses/${status.get('id')}/reactions/${emoji}`) .delete(`/api/v1/pleroma/statuses/${status.get('id')}/reactions/${emoji}`)
.then(response => { .then(response => {
dispatch(importFetchedStatus(response.data)); dispatch(importFetchedStatus(response.data));

Wyświetl plik

@ -4,6 +4,7 @@ import spring from 'react-motion/lib/spring';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import Icon from 'gabsocial/components/icon'; import Icon from 'gabsocial/components/icon';
import emojify from 'gabsocial/features/emoji/emoji';
export default class IconButton extends React.PureComponent { export default class IconButton extends React.PureComponent {
@ -24,6 +25,7 @@ export default class IconButton extends React.PureComponent {
overlay: PropTypes.bool, overlay: PropTypes.bool,
tabIndex: PropTypes.string, tabIndex: PropTypes.string,
text: PropTypes.string, text: PropTypes.string,
emoji: PropTypes.string,
}; };
static defaultProps = { static defaultProps = {
@ -66,6 +68,7 @@ export default class IconButton extends React.PureComponent {
tabIndex, tabIndex,
title, title,
text, text,
emoji,
} = this.props; } = this.props;
const classes = classNames(className, 'icon-button', { const classes = classNames(className, 'icon-button', {
@ -90,7 +93,9 @@ export default class IconButton extends React.PureComponent {
disabled={disabled} disabled={disabled}
> >
<div style={style}> <div style={style}>
<Icon id={icon} fixedWidth aria-hidden='true' /> {emoji
? <div className='icon-button__emoji' dangerouslySetInnerHTML={{ __html: emojify(emoji) }} aria-hidden='true' />
: <Icon id={icon} fixedWidth aria-hidden='true' />}
</div> </div>
{text && <span className='icon_button__text'>{text}</span>} {text && <span className='icon_button__text'>{text}</span>}
</button> </button>
@ -111,7 +116,9 @@ export default class IconButton extends React.PureComponent {
disabled={disabled} disabled={disabled}
> >
<div style={style}> <div style={style}>
<Icon id={icon} style={{ transform: `rotate(${rotate}deg)` }} fixedWidth aria-hidden='true' /> {emoji
? <div className='icon-button__emoji' style={{ transform: `rotate(${rotate}deg)` }} dangerouslySetInnerHTML={{ __html: emojify(emoji) }} aria-hidden='true' />
: <Icon id={icon} style={{ transform: `rotate(${rotate}deg)` }} fixedWidth aria-hidden='true' />}
</div> </div>
{text && <span className='icon_button__text'>{text}</span>} {text && <span className='icon_button__text'>{text}</span>}
</button> </button>

Wyświetl plik

@ -9,6 +9,7 @@ import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
import { defineMessages, injectIntl } from 'react-intl'; import { defineMessages, injectIntl } from 'react-intl';
import { isStaff } from 'gabsocial/utils/accounts'; import { isStaff } from 'gabsocial/utils/accounts';
import EmojiSelector from 'gabsocial/components/emoji_selector'; import EmojiSelector from 'gabsocial/components/emoji_selector';
import { getReactForStatus } from 'gabsocial/utils/emoji_reacts';
const messages = defineMessages({ const messages = defineMessages({
delete: { id: 'status.delete', defaultMessage: 'Delete' }, delete: { id: 'status.delete', defaultMessage: 'Delete' },
@ -189,6 +190,7 @@ class ActionBar extends React.PureComponent {
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);
let menu = []; let menu = [];
@ -267,10 +269,12 @@ class ActionBar extends React.PureComponent {
<EmojiSelector onReact={this.handleReactClick} /> <EmojiSelector onReact={this.handleReactClick} />
<IconButton <IconButton
className='star-icon' className='star-icon'
animate active={status.get('favourited')} animate
active={meEmojiReact}
title={intl.formatMessage(messages.favourite)} title={intl.formatMessage(messages.favourite)}
icon='thumbs-up' icon='thumbs-up'
onClick={this.handleFavouriteClick} emoji={meEmojiReact}
onClick={this.handleReactClick('👍')}
text='Like' text='Like'
/> />
</div> </div>

Wyświetl plik

@ -2,18 +2,20 @@ import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import emojify from 'gabsocial/features/emoji/emoji'; import emojify from 'gabsocial/features/emoji/emoji';
import { reduceEmoji } from 'gabsocial/utils/emoji_reacts'; import { reduceEmoji } from 'gabsocial/utils/emoji_reacts';
import SoapboxPropTypes from 'gabsocial/utils/soapbox_prop_types';
export class StatusInteractionBar extends React.Component { export class StatusInteractionBar extends React.Component {
static propTypes = { static propTypes = {
status: ImmutablePropTypes.map, status: ImmutablePropTypes.map,
me: SoapboxPropTypes.me,
} }
getNormalizedReacts = () => { getNormalizedReacts = () => {
const { status } = this.props; const { status, me } = this.props;
const emojiReacts = status.getIn(['pleroma', 'emoji_reactions']); const emojiReacts = status.getIn(['pleroma', 'emoji_reactions']);
const favouritesCount = status.get('favourites_count'); const favouritesCount = status.get('favourites_count');
return reduceEmoji(emojiReacts, favouritesCount).reverse(); return reduceEmoji(emojiReacts, favouritesCount, me).reverse();
} }
render() { render() {

Wyświetl plik

@ -163,17 +163,20 @@ class Status extends ImmutablePureComponent {
} }
handleEmojiReactClick = (status, emoji) => { handleEmojiReactClick = (status, emoji) => {
Promise.all(status.getIn(['pleroma', 'emoji_reactions'])
.filter(emojiReact => emojiReact.get('me') === true)
.map(emojiReact =>
this.props.dispatch(unEmojiReact(status, emojiReact.get('name')))))
.then(() => {
if (emoji === '👍') { if (emoji === '👍') {
this.handleFavouriteClick(status); return; this.handleFavouriteClick(status); return;
}
const hasReaction = status.getIn(['pleroma', 'emoji_reactions'])
.findIndex(e => e.get('name') === emoji && e.get('me') === true) > -1;
if (hasReaction) {
this.props.dispatch(unEmojiReact(status, emoji));
} else { } else {
this.props.dispatch(emojiReact(status, emoji)); this.props.dispatch(emojiReact(status, emoji));
} }
})
.catch(err => {
console.error(err);
});
} }
handleFavouriteClick = (status) => { handleFavouriteClick = (status) => {

Wyświetl plik

@ -154,4 +154,19 @@ describe('getReactForStatus', () => {
const status = fromJS({ favourites_count: 1, favourited: true }); const status = fromJS({ favourites_count: 1, favourited: true });
expect(getReactForStatus(status)).toEqual('👍'); expect(getReactForStatus(status)).toEqual('👍');
}); });
it('returns undefined when a status has no reacts (or favourites)', () => {
const status = fromJS([]);
expect(getReactForStatus(status)).toEqual(undefined);
});
it('returns undefined when a status has no valid reacts (or favourites)', () => {
const status = fromJS([
{ 'count': 1, 'me': true, 'name': '🔪' },
{ 'count': 1, 'me': true, 'name': '🌵' },
{ 'count': 1, 'me': false, 'name': '👀' },
{ 'count': 1, 'me': false, 'name': '🍩' },
]);
expect(getReactForStatus(status)).toEqual(undefined);
});
}); });

Wyświetl plik

@ -79,7 +79,6 @@ export const getReactForStatus = status => {
status.getIn(['pleroma', 'emoji_reactions'], ImmutableList()), status.getIn(['pleroma', 'emoji_reactions'], ImmutableList()),
status.get('favourites_count'), status.get('favourites_count'),
status.get('favourited') status.get('favourited')
).filter(e => e.get('me')) ).filter(e => e.get('me') === true)
.first(ImmutableMap()) .getIn([0, 'name']);
.get('name');
}; };