};
export default emojify;
+
+export const toCodePoint = (unicodeSurrogates, sep = '-') => {
+ let r = [], c = 0, p = 0, i = 0;
+
+ while (i < unicodeSurrogates.length) {
+ c = unicodeSurrogates.charCodeAt(i++);
+
+ if (p) {
+ r.push((0x10000 + ((p - 0xD800) << 10) + (c - 0xDC00)).toString(16));
+ p = 0;
+ } else if (0xD800 <= c && c <= 0xDBFF) {
+ p = c;
+ } else {
+ r.push(c.toString(16));
+ }
+ }
+
+ return r.join(sep);
+};
+
+export const buildCustomEmojis = customEmojis => {
+ const emojis = [];
+
+ customEmojis.forEach(emoji => {
+ const shortcode = emoji.get('shortcode');
+ const url = emoji.get('url');
+ const name = shortcode.replace(':', '');
+
+ emojis.push({
+ name,
+ short_names: [name],
+ text: '',
+ emoticons: [],
+ keywords: [name],
+ imageUrl: url,
+ });
+ });
+
+ return emojis;
+};
import SpoilerButtonContainer from '../containers/spoiler_button_container';
import PrivacyDropdownContainer from '../containers/privacy_dropdown_container';
import SensitiveButtonContainer from '../containers/sensitive_button_container';
-import EmojiPickerDropdown from './emoji_picker_dropdown';
+import EmojiPickerDropdown from '../containers/emoji_picker_dropdown_container';
import UploadFormContainer from '../containers/upload_form_container';
import WarningContainer from '../containers/warning_container';
import { isMobile } from '../../../is_mobile';
import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components';
import { Overlay } from 'react-overlays';
import classNames from 'classnames';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { buildCustomEmojis } from '../../../emoji';
const messages = defineMessages({
emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' },
class EmojiPickerMenu extends React.PureComponent {
static propTypes = {
+ custom_emojis: ImmutablePropTypes.list,
loading: PropTypes.bool,
onClose: PropTypes.func.isRequired,
onPick: PropTypes.func.isRequired,
}
handleClick = emoji => {
+ if (!emoji.native) {
+ emoji.native = emoji.colons;
+ }
+
this.props.onClose();
this.props.onPick(emoji);
}
return (
<div className={classNames('emoji-picker-dropdown__menu', { selecting: modifierOpen })} style={style} ref={this.setRef}>
<EmojiPicker
+ custom={buildCustomEmojis(this.props.custom_emojis)}
perLine={8}
emojiSize={22}
sheetSize={32}
export default class EmojiPickerDropdown extends React.PureComponent {
static propTypes = {
+ custom_emojis: ImmutablePropTypes.list,
intl: PropTypes.object.isRequired,
onPickEmoji: PropTypes.func.isRequired,
};
</div>
<Overlay show={active} placement='bottom' target={this.findTarget}>
- <EmojiPickerMenu loading={loading} onClose={this.onHideDropdown} onPick={onPickEmoji} />
+ <EmojiPickerMenu
+ custom_emojis={this.props.custom_emojis}
+ loading={loading}
+ onClose={this.onHideDropdown}
+ onPick={onPickEmoji}
+ />
</Overlay>
</div>
);
--- /dev/null
+import { connect } from 'react-redux';
+import EmojiPickerDropdown from '../components/emoji_picker_dropdown';
+
+const mapStateToProps = state => ({
+ custom_emojis: state.get('custom_emojis'),
+});
+
+export default connect(mapStateToProps)(EmojiPickerDropdown);
--- /dev/null
+import { List as ImmutableList } from 'immutable';
+import { STORE_HYDRATE } from '../actions/store';
+
+const initialState = ImmutableList();
+
+export default function statuses(state = initialState, action) {
+ switch(action.type) {
+ case STORE_HYDRATE:
+ return action.state.get('custom_emojis');
+ default:
+ return state;
+ }
+};
import media_attachments from './media_attachments';
import notifications from './notifications';
import height_cache from './height_cache';
+import custom_emojis from './custom_emojis';
const reducers = {
timelines,
media_attachments,
notifications,
height_cache,
+ custom_emojis,
};
export default combineReducers(reducers);
}
}
+.emoji-mart-emoji {
+ span {
+ background-repeat: no-repeat;
+ }
+}
+
.upload-area {
align-items: center;
background: rgba($base-overlay-background, 0.8);
attributes :meta, :compose, :accounts,
:media_attachments, :settings, :push_subscription
+ has_many :custom_emojis, serializer: REST::CustomEmojiSerializer
+
+ def custom_emojis
+ CustomEmoji.local
+ end
+
def meta
store = {
streaming_api_base_url: Rails.configuration.x.streaming_api_base_url,