diff --git a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
index 210721d9c1a..9be8909d8b4 100644
--- a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
+++ b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
@@ -31,6 +31,19 @@ let EmojiPicker, Emoji; // load asynchronously
const backgroundImageFn = () => `${assetHost}/emoji/sheet.png`;
const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false;
+const categoriesSort = [
+ 'recent',
+ 'custom',
+ 'people',
+ 'nature',
+ 'foods',
+ 'activity',
+ 'places',
+ 'objects',
+ 'symbols',
+ 'flags',
+];
+
class ModifierPickerMenu extends React.PureComponent {
static propTypes = {
@@ -141,6 +154,9 @@ class EmojiPickerMenu extends React.PureComponent {
arrowOffsetLeft: PropTypes.string,
arrowOffsetTop: PropTypes.string,
intl: PropTypes.object.isRequired,
+ skinTone: PropTypes.number.isRequired,
+ onSkinTone: PropTypes.func.isRequired,
+ autoPlay: PropTypes.bool,
};
static defaultProps = {
@@ -151,7 +167,6 @@ class EmojiPickerMenu extends React.PureComponent {
state = {
modifierOpen: false,
- modifier: 1,
};
handleDocumentClick = e => {
@@ -214,20 +229,18 @@ class EmojiPickerMenu extends React.PureComponent {
}
handleModifierChange = modifier => {
- if (modifier !== this.state.modifier) {
- this.setState({ modifier });
- }
+ this.props.onSkinTone(modifier);
}
render () {
- const { loading, style, intl } = this.props;
+ const { loading, style, intl, custom_emojis, autoPlay, skinTone } = this.props;
if (loading) {
return
;
}
const title = intl.formatMessage(messages.emoji);
- const { modifierOpen, modifier } = this.state;
+ const { modifierOpen } = this.state;
return (
@@ -235,20 +248,22 @@ class EmojiPickerMenu extends React.PureComponent {
perLine={8}
emojiSize={22}
sheetSize={32}
+ custom={buildCustomEmojis(custom_emojis, autoPlay)}
color=''
emoji=''
set='twitter'
title={title}
i18n={this.getI18n()}
onClick={this.handleClick}
- skin={modifier}
+ include={categoriesSort}
+ skin={skinTone}
showPreview={false}
backgroundImageFn={backgroundImageFn}
/>
{
- const { autoPlay } = this.props;
-
this.setState({ active: true });
if (!EmojiPicker) {
@@ -288,9 +303,8 @@ export default class EmojiPickerDropdown extends React.PureComponent {
EmojiPickerAsync().then(EmojiMart => {
EmojiPicker = EmojiMart.Picker;
- Emoji = EmojiMart.Emoji;
- // populate custom emoji in search
- EmojiMart.emojiIndex.search('', { custom: buildCustomEmojis(this.props.custom_emojis, autoPlay) });
+ Emoji = EmojiMart.Emoji;
+
this.setState({ loading: false });
}).catch(() => {
this.setState({ loading: false });
@@ -327,7 +341,7 @@ export default class EmojiPickerDropdown extends React.PureComponent {
}
render () {
- const { intl, onPickEmoji } = this.props;
+ const { intl, onPickEmoji, autoPlay, onSkinTone, skinTone } = this.props;
const title = intl.formatMessage(messages.emoji);
const { active, loading } = this.state;
@@ -347,6 +361,9 @@ export default class EmojiPickerDropdown extends React.PureComponent {
loading={loading}
onClose={this.onHideDropdown}
onPick={onPickEmoji}
+ autoPlay={autoPlay}
+ onSkinTone={onSkinTone}
+ skinTone={skinTone}
/>
diff --git a/app/javascript/mastodon/features/compose/containers/emoji_picker_dropdown_container.js b/app/javascript/mastodon/features/compose/containers/emoji_picker_dropdown_container.js
index cecc463201d..56cc6c3b10a 100644
--- a/app/javascript/mastodon/features/compose/containers/emoji_picker_dropdown_container.js
+++ b/app/javascript/mastodon/features/compose/containers/emoji_picker_dropdown_container.js
@@ -1,9 +1,17 @@
import { connect } from 'react-redux';
import EmojiPickerDropdown from '../components/emoji_picker_dropdown';
+import { changeSetting } from '../../../actions/settings';
const mapStateToProps = state => ({
custom_emojis: state.get('custom_emojis'),
autoPlay: state.getIn(['meta', 'auto_play_gif']),
+ skinTone: state.getIn(['settings', 'skinTone']),
});
-export default connect(mapStateToProps)(EmojiPickerDropdown);
+const mapDispatchToProps = dispatch => ({
+ onSkinTone: skinTone => {
+ dispatch(changeSetting(['skinTone'], skinTone));
+ },
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(EmojiPickerDropdown);
diff --git a/app/javascript/mastodon/reducers/settings.js b/app/javascript/mastodon/reducers/settings.js
index dd2d76ec02c..3063ddadd45 100644
--- a/app/javascript/mastodon/reducers/settings.js
+++ b/app/javascript/mastodon/reducers/settings.js
@@ -7,6 +7,8 @@ import uuid from '../uuid';
const initialState = ImmutableMap({
onboarded: false,
+ skinTone: 1,
+
home: ImmutableMap({
shows: ImmutableMap({
reblog: true,
diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss
index aecc98e760d..6c64528d6b1 100644
--- a/app/javascript/styles/components.scss
+++ b/app/javascript/styles/components.scss
@@ -2653,19 +2653,36 @@ button.icon-button.active i.fa-retweet {
flex-direction: column;
}
-@keyframes pulse {
- 0% {
- opacity: 1;
+@keyframes heartbeat {
+ from {
+ transform: scale(1);
+ transform-origin: center center;
+ animation-timing-function: ease-out;
}
- 100% {
- opacity: 0.5;
+ 10% {
+ transform: scale(0.91);
+ animation-timing-function: ease-in;
+ }
+
+ 17% {
+ transform: scale(0.98);
+ animation-timing-function: ease-out;
+ }
+
+ 33% {
+ transform: scale(0.87);
+ animation-timing-function: ease-in;
+ }
+
+ 45% {
+ transform: scale(1);
+ animation-timing-function: ease-out;
}
}
.pulse-loading {
- animation: pulse 1s ease-in-out infinite;
- animation-direction: alternate;
+ animation: heartbeat 1.5s ease-in-out infinite both;
}
.emoji-picker-dropdown__menu {