import PropTypes from 'prop-types'; import { useEffect, useState } from "react"; import { defineMessages, injectIntl } from 'react-intl'; import { Helmet } from 'react-helmet'; import { createSelector } from '@reduxjs/toolkit'; import { Map as ImmutableMap } from 'immutable'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { connect } from 'react-redux'; import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'; import MenuIcon from '@/material-icons/400-24px/menu.svg?react'; import EmojiReactionIcon from '@/material-icons/400-24px/mood.svg?react'; import { updateReactionDeck } from 'mastodon/actions/reaction_deck'; import { Button } from 'mastodon/components/button'; import ColumnHeader from 'mastodon/components/column_header'; import { Icon } from 'mastodon/components/icon'; import { LoadingIndicator } from 'mastodon/components/loading_indicator'; import ScrollableList from 'mastodon/components/scrollable_list'; import Column from 'mastodon/features/ui/components/column'; import ReactionEmoji from './components/reaction_emoji'; // https://medium.com/@wbern/getting-react-18s-strict-mode-to-work-with-react-beautiful-dnd-47bc909348e4 /* eslint react/prop-types: 0 */ const StrictModeDroppable = ({ children, ...props }) => { const [enabled, setEnabled] = useState(false); useEffect(() => { const animation = requestAnimationFrame(() => setEnabled(true)); return () => { cancelAnimationFrame(animation); setEnabled(false); }; }, []); if (!enabled) { return null; } return {children}; }; /* eslint react/prop-types: 0 */ const customEmojiMap = createSelector([state => state.get('custom_emojis')], items => items.reduce((map, emoji) => map.set(emoji.get('shortcode'), emoji), ImmutableMap())); const messages = defineMessages({ reaction_deck_add: { id: 'reaction_deck.add', defaultMessage: 'Add' }, heading: { id: 'column.reaction_deck', defaultMessage: 'Reaction deck' }, }); const mapStateToProps = (state, props) => ({ accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]), deck: state.get('reaction_deck'), emojiMap: customEmojiMap(state), }); const mapDispatchToProps = (dispatch) => ({ onChange: (emojis) => dispatch(updateReactionDeck(emojis)), }); class ReactionDeck extends ImmutablePureComponent { static propTypes = { params: PropTypes.object.isRequired, deck: ImmutablePropTypes.list, emojiMap: ImmutablePropTypes.map, multiColumn: PropTypes.bool, intl: PropTypes.object.isRequired, onChange: PropTypes.func.isRequired, }; deckToArray = () => { const { deck } = this.props; return deck.map((item) => item.get('name')).toArray(); }; handleReorder = (result) => { const newDeck = this.deckToArray(); const deleted = newDeck.splice(result.source.index, 1); newDeck.splice(result.destination.index, 0, deleted[0]); this.props.onChange(newDeck); }; handleChange = (index, emoji) => { const newDeck = this.deckToArray(); newDeck[index] = emoji.native || emoji.id.replace(':', ''); this.props.onChange(newDeck); }; handleRemove = (index) => { const newDeck = this.deckToArray(); newDeck.splice(index, 1); this.props.onChange(newDeck); }; handleAdd = () => { const newDeck = this.deckToArray(); newDeck.push('👍'); this.props.onChange(newDeck); }; render () { const { intl, deck, emojiMap, multiColumn } = this.props; if (!deck) { return ( ); } return ( {(provided) => ( {deck.map((emoji, index) => ( {(provided2) => ( )} ))} {provided.placeholder} )} ); } } export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(ReactionDeck));