diff --git a/Gemfile b/Gemfile index 988b4d6b98..531d01ae05 100644 --- a/Gemfile +++ b/Gemfile @@ -28,6 +28,7 @@ gem 'devise', '~> 4.2' gem 'devise-two-factor', '~> 3.0' gem 'doorkeeper', '~> 4.2' gem 'fast_blank', '~> 1.0' +gem 'gemoji', '~> 3.0' gem 'goldfinger', '~> 1.2' gem 'hiredis', '~> 0.6' gem 'redis-namespace', '~> 1.5' diff --git a/Gemfile.lock b/Gemfile.lock index 5599e1db16..83202189d0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -106,9 +106,9 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) - charlock_holmes (0.7.3) case_transform (0.2) activesupport + charlock_holmes (0.7.3) chunky_png (1.3.8) cld3 (3.1.3) ffi (>= 1.1.0, < 1.10.0) @@ -163,6 +163,7 @@ GEM fuubar (2.2.0) rspec-core (~> 3.0) ruby-progressbar (~> 1.4) + gemoji (3.0.0) globalid (0.4.0) activesupport (>= 4.2.0) goldfinger (1.2.0) @@ -518,6 +519,7 @@ DEPENDENCIES faker (~> 1.7) fast_blank (~> 1.0) fuubar (~> 2.2) + gemoji (~> 3.0) goldfinger (~> 1.2) hamlit-rails (~> 0.2) hiredis (~> 0.6) diff --git a/app/helpers/emoji_helper.rb b/app/helpers/emoji_helper.rb new file mode 100644 index 0000000000..c1595851fd --- /dev/null +++ b/app/helpers/emoji_helper.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module EmojiHelper + EMOJI_PATTERN = /(?<=[^[:alnum:]:]|\n|^):([\w+-]+):(?=[^[:alnum:]:]|$)/x + + def emojify(text) + return text if text.blank? + + text.gsub(EMOJI_PATTERN) do |match| + emoji = Emoji.find_by_alias($1) # rubocop:disable Rails/DynamicFindBy,Style/PerlBackrefs + + if emoji + emoji.raw + else + match + end + end + end +end diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 647a52b932..9f05a53e96 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -2,8 +2,6 @@ import api from '../api'; import { updateTimeline } from './timelines'; -import * as emojione from 'emojione'; - export const COMPOSE_CHANGE = 'COMPOSE_CHANGE'; export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST'; export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS'; @@ -73,11 +71,14 @@ export function mentionCompose(account, router) { export function submitCompose() { return function (dispatch, getState) { - const status = emojione.shortnameToUnicode(getState().getIn(['compose', 'text'], '')); + const status = getState().getIn(['compose', 'text'], ''); + if (!status || !status.length) { return; } + dispatch(submitComposeRequest()); + api(getState).post('/api/v1/statuses', { status, in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null), diff --git a/app/javascript/mastodon/emoji.js b/app/javascript/mastodon/emoji.js index 7043d5f3a3..ed2180cd1d 100644 --- a/app/javascript/mastodon/emoji.js +++ b/app/javascript/mastodon/emoji.js @@ -6,36 +6,18 @@ const trie = new Trie(Object.keys(emojione.jsEscapeMap)); function emojify(str) { // This walks through the string from start to end, ignoring any tags (
, :smile: :smile: :wink:
, etc.)
- // and replacing valid shortnames like :smile: and :wink: as well as unicode strings
+ // and replacing valid unicode strings
// that _aren't_ within tags with an version.
- // The goal is to be the same as an emojione.regShortNames/regUnicode replacement, but faster.
+ // The goal is to be the same as an emojione.regUnicode replacement, but faster.
let i = -1;
let insideTag = false;
- let insideShortname = false;
- let shortnameStartIndex = -1;
let match;
while (++i < str.length) {
const char = str.charAt(i);
- if (insideShortname && char === ':') {
- const shortname = str.substring(shortnameStartIndex, i + 1);
- if (shortname in emojione.emojioneList) {
- const unicode = emojione.emojioneList[shortname].unicode[emojione.emojioneList[shortname].unicode.length - 1];
- const alt = emojione.convert(unicode.toUpperCase());
- const replacement = ``;
- str = str.substring(0, shortnameStartIndex) + replacement + str.substring(i + 1);
- i += (replacement.length - shortname.length - 1); // jump ahead the length we've added to the string
- } else {
- i--; // stray colon, try again
- }
- insideShortname = false;
- } else if (insideTag && char === '>') {
+ if (insideTag && char === '>') {
insideTag = false;
} else if (char === '<') {
insideTag = true;
- insideShortname = false;
- } else if (!insideTag && char === ':') {
- insideShortname = true;
- shortnameStartIndex = i;
} else if (!insideTag && (match = trie.search(str.substring(i)))) {
const unicodeStr = match;
if (unicodeStr in emojione.jsEscapeMap) {
diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js
index f7eeedc69f..f075529479 100644
--- a/app/javascript/mastodon/features/compose/components/compose_form.js
+++ b/app/javascript/mastodon/features/compose/components/compose_form.js
@@ -136,7 +136,8 @@ export default class ComposeForm extends ImmutablePureComponent {
handleEmojiPick = (data) => {
const position = this.autosuggestTextarea.textarea.selectionStart;
- this._restoreCaret = position + data.shortname.length + 1;
+ const emojiChar = String.fromCodePoint(parseInt(data.unicode, 16));
+ this._restoreCaret = position + emojiChar.length + 1;
this.props.onPickEmoji(position, data);
}
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 83c66a5d59..acc584f20c 100644
--- a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
+++ b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
@@ -109,11 +109,12 @@ export default class EmojiPickerDropdown extends React.PureComponent {