From e1db6cf320d5a1b3f7c87f4bd9e6f2f1a0c0585f Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 11 Oct 2022 13:33:21 +0200 Subject: Move flavours/glitch/features/emoji_picker back to flavours/glitch/features/compose/containers/emoji_picker_dropdown_container --- .../flavours/glitch/features/emoji_picker/index.js | 494 --------------------- 1 file changed, 494 deletions(-) delete mode 100644 app/javascript/flavours/glitch/features/emoji_picker/index.js (limited to 'app/javascript/flavours/glitch/features/emoji_picker/index.js') diff --git a/app/javascript/flavours/glitch/features/emoji_picker/index.js b/app/javascript/flavours/glitch/features/emoji_picker/index.js deleted file mode 100644 index 05ae2c09f..000000000 --- a/app/javascript/flavours/glitch/features/emoji_picker/index.js +++ /dev/null @@ -1,494 +0,0 @@ -import { connect } from 'react-redux'; -import { changeSetting } from 'flavours/glitch/actions/settings'; -import { createSelector } from 'reselect'; -import { Map as ImmutableMap } from 'immutable'; -import { useEmoji } from 'flavours/glitch/actions/emojis'; -import React from 'react'; -import PropTypes from 'prop-types'; -import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; -import { EmojiPicker as EmojiPickerAsync } from '../ui/util/async-components'; -import Overlay from 'react-overlays/lib/Overlay'; -import classNames from 'classnames'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import { supportsPassiveEvents } from 'detect-passive-events'; -import { buildCustomEmojis, categoriesFromEmojis } from 'flavours/glitch/features/emoji/emoji'; -import { useSystemEmojiFont } from 'flavours/glitch/initial_state'; -import { assetHost } from 'flavours/glitch/utils/config'; - -const messages = defineMessages({ - emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' }, - emoji_search: { id: 'emoji_button.search', defaultMessage: 'Search...' }, - custom: { id: 'emoji_button.custom', defaultMessage: 'Custom' }, - recent: { id: 'emoji_button.recent', defaultMessage: 'Frequently used' }, - search_results: { id: 'emoji_button.search_results', defaultMessage: 'Search results' }, - people: { id: 'emoji_button.people', defaultMessage: 'People' }, - nature: { id: 'emoji_button.nature', defaultMessage: 'Nature' }, - food: { id: 'emoji_button.food', defaultMessage: 'Food & Drink' }, - activity: { id: 'emoji_button.activity', defaultMessage: 'Activity' }, - travel: { id: 'emoji_button.travel', defaultMessage: 'Travel & Places' }, - objects: { id: 'emoji_button.objects', defaultMessage: 'Objects' }, - symbols: { id: 'emoji_button.symbols', defaultMessage: 'Symbols' }, - flags: { id: 'emoji_button.flags', defaultMessage: 'Flags' }, -}); - -const perLine = 8; -const lines = 2; - -const DEFAULTS = [ - '+1', - 'grinning', - 'kissing_heart', - 'heart_eyes', - 'laughing', - 'stuck_out_tongue_winking_eye', - 'sweat_smile', - 'joy', - 'yum', - 'disappointed', - 'thinking_face', - 'weary', - 'sob', - 'sunglasses', - 'heart', - 'ok_hand', -]; - -const getFrequentlyUsedEmojis = createSelector([ - state => state.getIn(['settings', 'frequentlyUsedEmojis'], ImmutableMap()), -], emojiCounters => { - let emojis = emojiCounters - .keySeq() - .sort((a, b) => emojiCounters.get(a) - emojiCounters.get(b)) - .reverse() - .slice(0, perLine * lines) - .toArray(); - - if (emojis.length < DEFAULTS.length) { - emojis = emojis.concat(DEFAULTS.slice(0, DEFAULTS.length - emojis.length)); - } - - return emojis; -}); - -const getCustomEmojis = createSelector([ - state => state.get('custom_emojis'), -], emojis => emojis.filter(e => e.get('visible_in_picker')).sort((a, b) => { - const aShort = a.get('shortcode').toLowerCase(); - const bShort = b.get('shortcode').toLowerCase(); - - if (aShort < bShort) { - return -1; - } else if (aShort > bShort ) { - return 1; - } else { - return 0; - } -})); - -const mapStateToProps = state => ({ - custom_emojis: getCustomEmojis(state), - skinTone: state.getIn(['settings', 'skinTone']), - frequentlyUsedEmojis: getFrequentlyUsedEmojis(state), -}); - -const mapDispatchToProps = (dispatch, { onPickEmoji }) => ({ - onSkinTone: skinTone => { - dispatch(changeSetting(['skinTone'], skinTone)); - }, - - onPickEmoji: emoji => { - dispatch(useEmoji(emoji)); - - if (onPickEmoji) { - onPickEmoji(emoji); - } - }, -}); - -let EmojiPicker, Emoji; // load asynchronously - -const listenerOptions = supportsPassiveEvents ? { passive: true } : false; - -const backgroundImageFn = () => `${assetHost}/emoji/sheet_13.png`; - -const notFoundFn = () => ( -
- - -
- -
-
-); - -class ModifierPickerMenu extends React.PureComponent { - - static propTypes = { - active: PropTypes.bool, - onSelect: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired, - }; - - handleClick = e => { - this.props.onSelect(e.currentTarget.getAttribute('data-index') * 1); - } - - componentWillReceiveProps (nextProps) { - if (nextProps.active) { - this.attachListeners(); - } else { - this.removeListeners(); - } - } - - componentWillUnmount () { - this.removeListeners(); - } - - handleDocumentClick = e => { - if (this.node && !this.node.contains(e.target)) { - this.props.onClose(); - } - } - - attachListeners () { - document.addEventListener('click', this.handleDocumentClick, false); - document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); - } - - removeListeners () { - document.removeEventListener('click', this.handleDocumentClick, false); - document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); - } - - setRef = c => { - this.node = c; - } - - render () { - const { active } = this.props; - - return ( -
- - - - - - -
- ); - } - -} - -class ModifierPicker extends React.PureComponent { - - static propTypes = { - active: PropTypes.bool, - modifier: PropTypes.number, - onChange: PropTypes.func, - onClose: PropTypes.func, - onOpen: PropTypes.func, - }; - - handleClick = () => { - if (this.props.active) { - this.props.onClose(); - } else { - this.props.onOpen(); - } - } - - handleSelect = modifier => { - this.props.onChange(modifier); - this.props.onClose(); - } - - render () { - const { active, modifier } = this.props; - - return ( -
- - -
- ); - } - -} - -@injectIntl -class EmojiPickerMenu extends React.PureComponent { - - static propTypes = { - custom_emojis: ImmutablePropTypes.list, - frequentlyUsedEmojis: PropTypes.arrayOf(PropTypes.string), - loading: PropTypes.bool, - onClose: PropTypes.func.isRequired, - onPick: PropTypes.func.isRequired, - style: PropTypes.object, - placement: PropTypes.string, - arrowOffsetLeft: PropTypes.string, - arrowOffsetTop: PropTypes.string, - intl: PropTypes.object.isRequired, - skinTone: PropTypes.number.isRequired, - onSkinTone: PropTypes.func.isRequired, - }; - - static defaultProps = { - style: {}, - loading: true, - frequentlyUsedEmojis: [], - }; - - state = { - modifierOpen: false, - readyToFocus: false, - }; - - handleDocumentClick = e => { - if (this.node && !this.node.contains(e.target)) { - this.props.onClose(); - } - } - - componentDidMount () { - document.addEventListener('click', this.handleDocumentClick, false); - document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); - - // Because of https://github.com/react-bootstrap/react-bootstrap/issues/2614 we need - // to wait for a frame before focusing - requestAnimationFrame(() => { - this.setState({ readyToFocus: true }); - if (this.node) { - const element = this.node.querySelector('input[type="search"]'); - if (element) element.focus(); - } - }); - } - - componentWillUnmount () { - document.removeEventListener('click', this.handleDocumentClick, false); - document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); - } - - setRef = c => { - this.node = c; - } - - getI18n = () => { - const { intl } = this.props; - - return { - search: intl.formatMessage(messages.emoji_search), - categories: { - search: intl.formatMessage(messages.search_results), - recent: intl.formatMessage(messages.recent), - people: intl.formatMessage(messages.people), - nature: intl.formatMessage(messages.nature), - foods: intl.formatMessage(messages.food), - activity: intl.formatMessage(messages.activity), - places: intl.formatMessage(messages.travel), - objects: intl.formatMessage(messages.objects), - symbols: intl.formatMessage(messages.symbols), - flags: intl.formatMessage(messages.flags), - custom: intl.formatMessage(messages.custom), - }, - }; - } - - handleClick = (emoji, event) => { - if (!emoji.native) { - emoji.native = emoji.colons; - } - if (!(event.ctrlKey || event.metaKey)) { - this.props.onClose(); - } - this.props.onPick(emoji); - } - - handleModifierOpen = () => { - this.setState({ modifierOpen: true }); - } - - handleModifierClose = () => { - this.setState({ modifierOpen: false }); - } - - handleModifierChange = modifier => { - this.props.onSkinTone(modifier); - } - - render () { - const { loading, style, intl, custom_emojis, skinTone, frequentlyUsedEmojis } = this.props; - - if (loading) { - return
; - } - - const title = intl.formatMessage(messages.emoji); - - const { modifierOpen } = this.state; - - const categoriesSort = [ - 'recent', - 'people', - 'nature', - 'foods', - 'activity', - 'places', - 'objects', - 'symbols', - 'flags', - ]; - - categoriesSort.splice(1, 0, ...Array.from(categoriesFromEmojis(custom_emojis)).sort()); - - return ( -
- - - -
- ); - } - -} - -export default @connect(mapStateToProps, mapDispatchToProps) -@injectIntl -class EmojiPickerDropdown extends React.PureComponent { - - static propTypes = { - custom_emojis: ImmutablePropTypes.list, - frequentlyUsedEmojis: PropTypes.arrayOf(PropTypes.string), - intl: PropTypes.object.isRequired, - onPickEmoji: PropTypes.func.isRequired, - onSkinTone: PropTypes.func.isRequired, - skinTone: PropTypes.number.isRequired, - button: PropTypes.node, - }; - - state = { - active: false, - loading: false, - placement: null, - }; - - setRef = (c) => { - this.dropdown = c; - } - - onShowDropdown = ({ target }) => { - this.setState({ active: true }); - - if (!EmojiPicker) { - this.setState({ loading: true }); - - EmojiPickerAsync().then(EmojiMart => { - EmojiPicker = EmojiMart.Picker; - Emoji = EmojiMart.Emoji; - - this.setState({ loading: false }); - }).catch(() => { - this.setState({ loading: false }); - }); - } - - const { top } = target.getBoundingClientRect(); - this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' }); - } - - onHideDropdown = () => { - this.setState({ active: false }); - } - - onToggle = (e) => { - if (!this.state.loading && (!e.key || e.key === 'Enter')) { - if (this.state.active) { - this.onHideDropdown(); - } else { - this.onShowDropdown(e); - } - } - } - - handleKeyDown = e => { - if (e.key === 'Escape') { - this.onHideDropdown(); - } - } - - setTargetRef = c => { - this.target = c; - } - - findTarget = () => { - return this.target; - } - - render () { - const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis, button } = this.props; - const title = intl.formatMessage(messages.emoji); - const { active, loading, placement } = this.state; - - return ( -
-
- {button || 🙂} -
- - - - -
- ); - } - -} -- cgit