From bb4d005a8381091911697175416eb9c37379155e Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 20 Sep 2017 01:08:08 +0900 Subject: Introduce OStatus::TagManager (#5008) --- spec/lib/activitypub/tag_manager_spec.rb | 2 +- spec/lib/ostatus/atom_serializer_spec.rb | 90 ++++++++++++++++---------------- spec/lib/ostatus/tag_manager_spec.rb | 70 +++++++++++++++++++++++++ spec/lib/tag_manager_spec.rb | 65 ----------------------- 4 files changed, 116 insertions(+), 111 deletions(-) create mode 100644 spec/lib/ostatus/tag_manager_spec.rb (limited to 'spec/lib') diff --git a/spec/lib/activitypub/tag_manager_spec.rb b/spec/lib/activitypub/tag_manager_spec.rb index dea8abc65..0d1665216 100644 --- a/spec/lib/activitypub/tag_manager_spec.rb +++ b/spec/lib/activitypub/tag_manager_spec.rb @@ -108,7 +108,7 @@ RSpec.describe ActivityPub::TagManager do it 'returns the local status for OStatus tag: URI' do status = Fabricate(:status) - expect(subject.uri_to_resource(::TagManager.instance.uri_for(status), Status)).to eq status + expect(subject.uri_to_resource(OStatus::TagManager.instance.uri_for(status), Status)).to eq status end it 'returns the local status for OStatus StreamEntry URL' do diff --git a/spec/lib/ostatus/atom_serializer_spec.rb b/spec/lib/ostatus/atom_serializer_spec.rb index b2480a53b..00e6f09dc 100644 --- a/spec/lib/ostatus/atom_serializer_spec.rb +++ b/spec/lib/ostatus/atom_serializer_spec.rb @@ -17,7 +17,7 @@ RSpec.describe OStatus::AtomSerializer do follow_request_salmon = serialize(follow_request) object_type = follow_request_salmon.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq TagManager::TYPES[:activity] + expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity] end it 'appends activity:verb element with request_friend type' do @@ -26,7 +26,7 @@ RSpec.describe OStatus::AtomSerializer do follow_request_salmon = serialize(follow_request) verb = follow_request_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq TagManager::VERBS[:request_friend] + expect(verb.text).to eq OStatus::TagManager::VERBS[:request_friend] end it 'appends activity:object with target account' do @@ -44,13 +44,13 @@ RSpec.describe OStatus::AtomSerializer do it 'adds namespaces' do element = serialize - expect(element['xmlns']).to eq TagManager::XMLNS - expect(element['xmlns:thr']).to eq TagManager::THR_XMLNS - expect(element['xmlns:activity']).to eq TagManager::AS_XMLNS - expect(element['xmlns:poco']).to eq TagManager::POCO_XMLNS - expect(element['xmlns:media']).to eq TagManager::MEDIA_XMLNS - expect(element['xmlns:ostatus']).to eq TagManager::OS_XMLNS - expect(element['xmlns:mastodon']).to eq TagManager::MTDN_XMLNS + expect(element['xmlns']).to eq OStatus::TagManager::XMLNS + expect(element['xmlns:thr']).to eq OStatus::TagManager::THR_XMLNS + expect(element['xmlns:activity']).to eq OStatus::TagManager::AS_XMLNS + expect(element['xmlns:poco']).to eq OStatus::TagManager::POCO_XMLNS + expect(element['xmlns:media']).to eq OStatus::TagManager::MEDIA_XMLNS + expect(element['xmlns:ostatus']).to eq OStatus::TagManager::OS_XMLNS + expect(element['xmlns:mastodon']).to eq OStatus::TagManager::MTDN_XMLNS end end @@ -98,7 +98,7 @@ RSpec.describe OStatus::AtomSerializer do mentioned = element.nodes.find do |node| node.name == 'link' && node[:rel] == 'mentioned' && - node['ostatus:object-type'] == TagManager::TYPES[:person] + node['ostatus:object-type'] == OStatus::TagManager::TYPES[:person] end expect(mentioned[:href]).to eq 'https://cb6e6126.ngrok.io/users/username' @@ -188,7 +188,7 @@ RSpec.describe OStatus::AtomSerializer do author = OStatus::AtomSerializer.new.author(account) object_type = author.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq TagManager::TYPES[:person] + expect(object_type.text).to eq OStatus::TagManager::TYPES[:person] end it 'appends email element with username and domain for local account' do @@ -358,9 +358,9 @@ RSpec.describe OStatus::AtomSerializer do mentioned_person = entry.nodes.find do |node| node.name == 'link' && node[:rel] == 'mentioned' && - node['ostatus:object-type'] == TagManager::TYPES[:collection] + node['ostatus:object-type'] == OStatus::TagManager::TYPES[:collection] end - expect(mentioned_person[:href]).to eq TagManager::COLLECTIONS[:public] + expect(mentioned_person[:href]).to eq OStatus::TagManager::COLLECTIONS[:public] end it 'does not append link element for the public collection if status is not publicly visible' do @@ -371,8 +371,8 @@ RSpec.describe OStatus::AtomSerializer do entry.nodes.each do |node| if node.name == 'link' && node[:rel] == 'mentioned' && - node['ostatus:object-type'] == TagManager::TYPES[:collection] - expect(mentioned_collection[:href]).not_to eq TagManager::COLLECTIONS[:public] + node['ostatus:object-type'] == OStatus::TagManager::TYPES[:collection] + expect(mentioned_collection[:href]).not_to eq OStatus::TagManager::COLLECTIONS[:public] end end end @@ -506,7 +506,7 @@ RSpec.describe OStatus::AtomSerializer do status = Fabricate(:status) entry = OStatus::AtomSerializer.new.entry(status.stream_entry) object_type = entry.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq TagManager::TYPES[:note] + expect(object_type.text).to eq OStatus::TagManager::TYPES[:note] end it 'appends activity:verb element with object type' do @@ -515,7 +515,7 @@ RSpec.describe OStatus::AtomSerializer do entry = OStatus::AtomSerializer.new.entry(status.stream_entry) object_type = entry.nodes.find { |node| node.name == 'activity:verb' } - expect(object_type.text).to eq TagManager::VERBS[:post] + expect(object_type.text).to eq OStatus::TagManager::VERBS[:post] end it 'appends activity:object element with target if present' do @@ -739,8 +739,8 @@ RSpec.describe OStatus::AtomSerializer do time_after = Time.now expect(block_salmon.id.text).to( - eq(TagManager.instance.unique_tag(time_before.utc, block.id, 'Block')) - .or(eq(TagManager.instance.unique_tag(time_after.utc, block.id, 'Block'))) + eq(OStatus::TagManager.instance.unique_tag(time_before.utc, block.id, 'Block')) + .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, block.id, 'Block'))) ) end @@ -769,7 +769,7 @@ RSpec.describe OStatus::AtomSerializer do block_salmon = OStatus::AtomSerializer.new.block_salmon(block) object_type = block_salmon.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq TagManager::TYPES[:activity] + expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity] end it 'appends activity:verb element with block' do @@ -778,7 +778,7 @@ RSpec.describe OStatus::AtomSerializer do block_salmon = OStatus::AtomSerializer.new.block_salmon(block) verb = block_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq TagManager::VERBS[:block] + expect(verb.text).to eq OStatus::TagManager::VERBS[:block] end it 'appends activity:object element with target account' do @@ -826,8 +826,8 @@ RSpec.describe OStatus::AtomSerializer do time_after = Time.now expect(unblock_salmon.id.text).to( - eq(TagManager.instance.unique_tag(time_before.utc, block.id, 'Block')) - .or(eq(TagManager.instance.unique_tag(time_after.utc, block.id, 'Block'))) + eq(OStatus::TagManager.instance.unique_tag(time_before.utc, block.id, 'Block')) + .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, block.id, 'Block'))) ) end @@ -856,7 +856,7 @@ RSpec.describe OStatus::AtomSerializer do unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block) object_type = unblock_salmon.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq TagManager::TYPES[:activity] + expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity] end it 'appends activity:verb element with block' do @@ -865,7 +865,7 @@ RSpec.describe OStatus::AtomSerializer do unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block) verb = unblock_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq TagManager::VERBS[:unblock] + expect(verb.text).to eq OStatus::TagManager::VERBS[:unblock] end it 'appends activity:object element with target account' do @@ -934,7 +934,7 @@ RSpec.describe OStatus::AtomSerializer do favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite) verb = favourite_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq TagManager::VERBS[:favorite] + expect(verb.text).to eq OStatus::TagManager::VERBS[:favorite] end it 'appends activity:object element with status' do @@ -1005,8 +1005,8 @@ RSpec.describe OStatus::AtomSerializer do time_after = Time.now expect(unfavourite_salmon.id.text).to( - eq(TagManager.instance.unique_tag(time_before.utc, favourite.id, 'Favourite')) - .or(eq(TagManager.instance.unique_tag(time_after.utc, favourite.id, 'Favourite'))) + eq(OStatus::TagManager.instance.unique_tag(time_before.utc, favourite.id, 'Favourite')) + .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, favourite.id, 'Favourite'))) ) end @@ -1034,7 +1034,7 @@ RSpec.describe OStatus::AtomSerializer do unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite) verb = unfavourite_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq TagManager::VERBS[:unfavorite] + expect(verb.text).to eq OStatus::TagManager::VERBS[:unfavorite] end it 'appends activity:object element with status' do @@ -1117,7 +1117,7 @@ RSpec.describe OStatus::AtomSerializer do follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow) object_type = follow_salmon.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq TagManager::TYPES[:activity] + expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity] end it 'appends activity:verb element with follow' do @@ -1126,7 +1126,7 @@ RSpec.describe OStatus::AtomSerializer do follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow) verb = follow_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq TagManager::VERBS[:follow] + expect(verb.text).to eq OStatus::TagManager::VERBS[:follow] end it 'appends activity:object element with target account' do @@ -1190,8 +1190,8 @@ RSpec.describe OStatus::AtomSerializer do time_after = Time.now expect(unfollow_salmon.id.text).to( - eq(TagManager.instance.unique_tag(time_before.utc, follow.id, 'Follow')) - .or(eq(TagManager.instance.unique_tag(time_after.utc, follow.id, 'Follow'))) + eq(OStatus::TagManager.instance.unique_tag(time_before.utc, follow.id, 'Follow')) + .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, follow.id, 'Follow'))) ) end @@ -1234,7 +1234,7 @@ RSpec.describe OStatus::AtomSerializer do unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow) object_type = unfollow_salmon.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq TagManager::TYPES[:activity] + expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity] end it 'appends activity:verb element with follow' do @@ -1244,7 +1244,7 @@ RSpec.describe OStatus::AtomSerializer do unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow) verb = unfollow_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq TagManager::VERBS[:unfollow] + expect(verb.text).to eq OStatus::TagManager::VERBS[:unfollow] end it 'appends activity:object element with target account' do @@ -1338,8 +1338,8 @@ RSpec.describe OStatus::AtomSerializer do time_after = Time.now expect(authorize_follow_request_salmon.id.text).to( - eq(TagManager.instance.unique_tag(time_before.utc, follow_request.id, 'FollowRequest')) - .or(eq(TagManager.instance.unique_tag(time_after.utc, follow_request.id, 'FollowRequest'))) + eq(OStatus::TagManager.instance.unique_tag(time_before.utc, follow_request.id, 'FollowRequest')) + .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, follow_request.id, 'FollowRequest'))) ) end @@ -1359,7 +1359,7 @@ RSpec.describe OStatus::AtomSerializer do authorize_follow_request_salmon = OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request) object_type = authorize_follow_request_salmon.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq TagManager::TYPES[:activity] + expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity] end it 'appends activity:verb element with authorize' do @@ -1368,7 +1368,7 @@ RSpec.describe OStatus::AtomSerializer do authorize_follow_request_salmon = OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request) verb = authorize_follow_request_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq TagManager::VERBS[:authorize] + expect(verb.text).to eq OStatus::TagManager::VERBS[:authorize] end it 'returns element whose rendered view creates follow from follow request when processed' do @@ -1407,8 +1407,8 @@ RSpec.describe OStatus::AtomSerializer do time_after = Time.now expect(reject_follow_request_salmon.id.text).to( - eq(TagManager.instance.unique_tag(time_before.utc, follow_request.id, 'FollowRequest')) - .or(TagManager.instance.unique_tag(time_after.utc, follow_request.id, 'FollowRequest')) + eq(OStatus::TagManager.instance.unique_tag(time_before.utc, follow_request.id, 'FollowRequest')) + .or(OStatus::TagManager.instance.unique_tag(time_after.utc, follow_request.id, 'FollowRequest')) ) end @@ -1424,14 +1424,14 @@ RSpec.describe OStatus::AtomSerializer do follow_request = Fabricate(:follow_request) reject_follow_request_salmon = OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request) object_type = reject_follow_request_salmon.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq TagManager::TYPES[:activity] + expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity] end it 'appends activity:verb element with authorize' do follow_request = Fabricate(:follow_request) reject_follow_request_salmon = OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request) verb = reject_follow_request_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq TagManager::VERBS[:reject] + expect(verb.text).to eq OStatus::TagManager::VERBS[:reject] end it 'returns element whose rendered view deletes follow request when processed' do @@ -1503,7 +1503,7 @@ RSpec.describe OStatus::AtomSerializer do entry = OStatus::AtomSerializer.new.object(status) object_type = entry.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq TagManager::TYPES[:note] + expect(object_type.text).to eq OStatus::TagManager::TYPES[:note] end it 'appends activity:verb element with verb' do @@ -1512,7 +1512,7 @@ RSpec.describe OStatus::AtomSerializer do entry = OStatus::AtomSerializer.new.object(status) object_type = entry.nodes.find { |node| node.name == 'activity:verb' } - expect(object_type.text).to eq TagManager::VERBS[:post] + expect(object_type.text).to eq OStatus::TagManager::VERBS[:post] end it 'appends link element for an alternative' do diff --git a/spec/lib/ostatus/tag_manager_spec.rb b/spec/lib/ostatus/tag_manager_spec.rb new file mode 100644 index 000000000..31195bae2 --- /dev/null +++ b/spec/lib/ostatus/tag_manager_spec.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe OStatus::TagManager do + describe '#unique_tag' do + it 'returns a unique tag' do + expect(OStatus::TagManager.instance.unique_tag(Time.utc(2000), 12, 'Status')).to eq 'tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Status' + end + end + + describe '#unique_tag_to_local_id' do + it 'returns the ID part' do + expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Status', 'Status')).to eql '12' + end + + it 'returns nil if it is not local id' do + expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:remote,2000-01-01:objectId=12:objectType=Status', 'Status')).to eq nil + end + + it 'returns nil if it is not expected type' do + expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Block', 'Status')).to eq nil + end + + it 'returns nil if it does not have object ID' do + expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectType=Status', 'Status')).to eq nil + end + end + + describe '#local_id?' do + it 'returns true for a local ID' do + expect(OStatus::TagManager.instance.local_id?('tag:cb6e6126.ngrok.io;objectId=12:objectType=Status')).to be true + end + + it 'returns false for a foreign ID' do + expect(OStatus::TagManager.instance.local_id?('tag:foreign.tld;objectId=12:objectType=Status')).to be false + end + end + + describe '#uri_for' do + subject { OStatus::TagManager.instance.uri_for(target) } + + context 'comment object' do + let(:target) { Fabricate(:status, created_at: '2000-01-01T00:00:00Z', reply: true) } + + it 'returns the unique tag for status' do + expect(target.object_type).to eq :comment + is_expected.to eq target.uri + end + end + + context 'note object' do + let(:target) { Fabricate(:status, created_at: '2000-01-01T00:00:00Z', reply: false, thread: nil) } + + it 'returns the unique tag for status' do + expect(target.object_type).to eq :note + is_expected.to eq target.uri + end + end + + context 'person object' do + let(:target) { Fabricate(:account, username: 'alice') } + + it 'returns the URL for account' do + expect(target.object_type).to eq :person + is_expected.to eq 'https://cb6e6126.ngrok.io/users/alice' + end + end + end +end diff --git a/spec/lib/tag_manager_spec.rb b/spec/lib/tag_manager_spec.rb index 6c7830231..5427a2929 100644 --- a/spec/lib/tag_manager_spec.rb +++ b/spec/lib/tag_manager_spec.rb @@ -120,71 +120,6 @@ RSpec.describe TagManager do end end - describe '#unique_tag' do - it 'returns a unique tag' do - expect(TagManager.instance.unique_tag(Time.utc(2000), 12, 'Status')).to eq 'tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Status' - end - end - - describe '#unique_tag_to_local_id' do - it 'returns the ID part' do - expect(TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Status', 'Status')).to eql '12' - end - - it 'returns nil if it is not local id' do - expect(TagManager.instance.unique_tag_to_local_id('tag:remote,2000-01-01:objectId=12:objectType=Status', 'Status')).to eq nil - end - - it 'returns nil if it is not expected type' do - expect(TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Block', 'Status')).to eq nil - end - - it 'returns nil if it does not have object ID' do - expect(TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectType=Status', 'Status')).to eq nil - end - end - - describe '#local_id?' do - it 'returns true for a local ID' do - expect(TagManager.instance.local_id?('tag:cb6e6126.ngrok.io;objectId=12:objectType=Status')).to be true - end - - it 'returns false for a foreign ID' do - expect(TagManager.instance.local_id?('tag:foreign.tld;objectId=12:objectType=Status')).to be false - end - end - - describe '#uri_for' do - subject { TagManager.instance.uri_for(target) } - - context 'comment object' do - let(:target) { Fabricate(:status, created_at: '2000-01-01T00:00:00Z', reply: true) } - - it 'returns the unique tag for status' do - expect(target.object_type).to eq :comment - is_expected.to eq target.uri - end - end - - context 'note object' do - let(:target) { Fabricate(:status, created_at: '2000-01-01T00:00:00Z', reply: false, thread: nil) } - - it 'returns the unique tag for status' do - expect(target.object_type).to eq :note - is_expected.to eq target.uri - end - end - - context 'person object' do - let(:target) { Fabricate(:account, username: 'alice') } - - it 'returns the URL for account' do - expect(target.object_type).to eq :person - is_expected.to eq 'https://cb6e6126.ngrok.io/users/alice' - end - end - end - describe '#url_for' do let(:alice) { Fabricate(:account, username: 'alice') } -- cgit From 1e02ba111ae38ab758135b5b2b46f34c672ca02e Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 23 Sep 2017 14:47:32 +0200 Subject: Add emoji autosuggest (#5053) * Add emoji autosuggest Some credit goes to glitch-soc/mastodon#149 * Remove server-side shortcode->unicode conversion * Insert shortcode when suggestion is custom emoji * Remove remnant of server-side emojis * Update style of autosuggestions * Fix wrong emoji filenames generated in autosuggest item * Do not lazy load emoji picker, as that no longer works * Fix custom emoji autosuggest * Fix multiple "Custom" categories getting added to emoji index, only add once --- app/helpers/emoji_helper.rb | 24 ------------ app/javascript/mastodon/actions/compose.js | 35 ++++++++++++++--- .../mastodon/components/autosuggest_emoji.js | 37 ++++++++++++++++++ .../mastodon/components/autosuggest_textarea.js | 43 +++++++++++++-------- app/javascript/mastodon/emoji.js | 21 +--------- .../compose/components/emoji_picker_dropdown.js | 37 +++--------------- .../mastodon/features/ui/util/async-components.js | 4 -- app/javascript/mastodon/reducers/accounts.js | 2 +- .../mastodon/reducers/accounts_counters.js | 2 +- app/javascript/mastodon/reducers/compose.js | 2 +- app/javascript/mastodon/reducers/custom_emojis.js | 5 ++- app/javascript/styles/components.scss | 45 ++++++++++++---------- app/lib/emoji.rb | 40 ------------------- app/models/account.rb | 4 -- app/models/status.rb | 4 -- app/views/layouts/application.html.haml | 1 - lib/assets/emoji.json | 1 - spec/helpers/emoji_helper_spec.rb | 20 ---------- spec/lib/emoji_spec.rb | 15 -------- 19 files changed, 133 insertions(+), 209 deletions(-) delete mode 100644 app/helpers/emoji_helper.rb create mode 100644 app/javascript/mastodon/components/autosuggest_emoji.js delete mode 100644 app/lib/emoji.rb delete mode 100644 lib/assets/emoji.json delete mode 100644 spec/helpers/emoji_helper_spec.rb delete mode 100644 spec/lib/emoji_spec.rb (limited to 'spec/lib') diff --git a/app/helpers/emoji_helper.rb b/app/helpers/emoji_helper.rb deleted file mode 100644 index 848c03fce..000000000 --- a/app/helpers/emoji_helper.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -module EmojiHelper - def emojify(text) - return text if text.blank? - - text.gsub(emoji_pattern) do |match| - emoji = Emoji.instance.unicode($1) # rubocop:disable Style/PerlBackrefs - - if emoji - emoji - else - match - end - end - end - - def emoji_pattern - @emoji_pattern ||= - /(?<=[^[:alnum:]:]|\n|^) - (#{Emoji.instance.names.map { |name| Regexp.escape(name) }.join('|')}) - (?=[^[:alnum:]:]|$)/x - end -end diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 1f26907f2..9f10a8c15 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -1,4 +1,5 @@ import api from '../api'; +import { emojiIndex } from 'emoji-mart'; import { updateTimeline, @@ -210,19 +211,33 @@ export function clearComposeSuggestions() { export function fetchComposeSuggestions(token) { return (dispatch, getState) => { + if (token[0] === ':') { + const results = emojiIndex.search(token.replace(':', ''), { maxResults: 3 }); + dispatch(readyComposeSuggestionsEmojis(token, results)); + return; + } + api(getState).get('/api/v1/accounts/search', { params: { - q: token, + q: token.slice(1), resolve: false, limit: 4, }, }).then(response => { - dispatch(readyComposeSuggestions(token, response.data)); + dispatch(readyComposeSuggestionsAccounts(token, response.data)); }); }; }; -export function readyComposeSuggestions(token, accounts) { +export function readyComposeSuggestionsEmojis(token, emojis) { + return { + type: COMPOSE_SUGGESTIONS_READY, + token, + emojis, + }; +}; + +export function readyComposeSuggestionsAccounts(token, accounts) { return { type: COMPOSE_SUGGESTIONS_READY, token, @@ -230,13 +245,21 @@ export function readyComposeSuggestions(token, accounts) { }; }; -export function selectComposeSuggestion(position, token, accountId) { +export function selectComposeSuggestion(position, token, suggestion) { return (dispatch, getState) => { - const completion = getState().getIn(['accounts', accountId, 'acct']); + let completion, startPosition; + + if (typeof suggestion === 'object' && suggestion.id) { + completion = suggestion.native || suggestion.colons; + startPosition = position - 1; + } else { + completion = getState().getIn(['accounts', suggestion, 'acct']); + startPosition = position; + } dispatch({ type: COMPOSE_SUGGESTION_SELECT, - position, + position: startPosition, token, completion, }); diff --git a/app/javascript/mastodon/components/autosuggest_emoji.js b/app/javascript/mastodon/components/autosuggest_emoji.js new file mode 100644 index 000000000..e2866e8e4 --- /dev/null +++ b/app/javascript/mastodon/components/autosuggest_emoji.js @@ -0,0 +1,37 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { unicodeMapping } from '../emojione_light'; + +const assetHost = process.env.CDN_HOST || ''; + +export default class AutosuggestEmoji extends React.PureComponent { + + static propTypes = { + emoji: PropTypes.object.isRequired, + }; + + render () { + const { emoji } = this.props; + let url; + + if (emoji.custom) { + url = emoji.imageUrl; + } else { + const [ filename ] = unicodeMapping[emoji.native]; + url = `${assetHost}/emoji/${filename}.svg`; + } + + return ( +
+ {emoji.native + + {emoji.colons} +
+ ); + } + +} diff --git a/app/javascript/mastodon/components/autosuggest_textarea.js b/app/javascript/mastodon/components/autosuggest_textarea.js index 30e3049df..daeb6fd53 100644 --- a/app/javascript/mastodon/components/autosuggest_textarea.js +++ b/app/javascript/mastodon/components/autosuggest_textarea.js @@ -1,10 +1,12 @@ import React from 'react'; import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container'; +import AutosuggestEmoji from './autosuggest_emoji'; import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import { isRtl } from '../rtl'; import ImmutablePureComponent from 'react-immutable-pure-component'; import Textarea from 'react-textarea-autosize'; +import classNames from 'classnames'; const textAtCursorMatchesToken = (str, caretPosition) => { let word; @@ -18,11 +20,11 @@ const textAtCursorMatchesToken = (str, caretPosition) => { word = str.slice(left, right + caretPosition); } - if (!word || word.trim().length < 2 || word[0] !== '@') { + if (!word || word.trim().length < 2 || ['@', ':'].indexOf(word[0]) === -1) { return [null, null]; } - word = word.trim().toLowerCase().slice(1); + word = word.trim().toLowerCase(); if (word.length > 0) { return [left + 1, word]; @@ -128,7 +130,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { } onSuggestionClick = (e) => { - const suggestion = e.currentTarget.getAttribute('data-index'); + const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index')); e.preventDefault(); this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion); this.textarea.focus(); @@ -151,9 +153,28 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { } } + renderSuggestion = (suggestion, i) => { + const { selectedSuggestion } = this.state; + let inner, key; + + if (typeof suggestion === 'object') { + inner = ; + key = suggestion.id; + } else { + inner = ; + key = suggestion; + } + + return ( +
+ {inner} +
+ ); + } + render () { const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus } = this.props; - const { suggestionsHidden, selectedSuggestion } = this.state; + const { suggestionsHidden } = this.state; const style = { direction: 'ltr' }; if (isRtl(value)) { @@ -164,6 +185,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {