Skip to content

Commit a672b35

Browse files
authored
Add categories for custom emojis (mastodon#11196)
Fix mastodon#7940
1 parent e25df6e commit a672b35

13 files changed

Lines changed: 86 additions & 18 deletions

File tree

app/controllers/api/v1/custom_emojis_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class Api::V1::CustomEmojisController < Api::BaseController
77

88
def index
99
render_cached_json('api:v1:custom_emojis', expires_in: 1.minute) do
10-
ActiveModelSerializers::SerializableResource.new(CustomEmoji.local.where(disabled: false), each_serializer: REST::CustomEmojiSerializer)
10+
ActiveModelSerializers::SerializableResource.new(CustomEmoji.local.where(disabled: false).includes(:category), each_serializer: REST::CustomEmojiSerializer)
1111
end
1212
end
1313
end

app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import Overlay from 'react-overlays/lib/Overlay';
66
import classNames from 'classnames';
77
import ImmutablePropTypes from 'react-immutable-proptypes';
88
import detectPassiveEvents from 'detect-passive-events';
9-
import { buildCustomEmojis } from '../../emoji/emoji';
9+
import { buildCustomEmojis, categoriesFromEmojis } from '../../emoji/emoji';
1010

1111
const messages = defineMessages({
1212
emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' },
@@ -31,19 +31,6 @@ let EmojiPicker, Emoji; // load asynchronously
3131
const backgroundImageFn = () => `${assetHost}/emoji/sheet_10.png`;
3232
const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false;
3333

34-
const categoriesSort = [
35-
'recent',
36-
'custom',
37-
'people',
38-
'nature',
39-
'foods',
40-
'activity',
41-
'places',
42-
'objects',
43-
'symbols',
44-
'flags',
45-
];
46-
4734
class ModifierPickerMenu extends React.PureComponent {
4835

4936
static propTypes = {
@@ -241,8 +228,23 @@ class EmojiPickerMenu extends React.PureComponent {
241228
}
242229

243230
const title = intl.formatMessage(messages.emoji);
231+
244232
const { modifierOpen } = this.state;
245233

234+
const categoriesSort = [
235+
'recent',
236+
'people',
237+
'nature',
238+
'foods',
239+
'activity',
240+
'places',
241+
'objects',
242+
'symbols',
243+
'flags',
244+
];
245+
246+
categoriesSort.splice(1, 0, ...Array.from(categoriesFromEmojis(custom_emojis)).sort());
247+
246248
return (
247249
<div className={classNames('emoji-picker-dropdown__menu', { selecting: modifierOpen })} style={style} ref={this.setRef}>
248250
<EmojiPicker

app/javascript/mastodon/features/emoji/emoji.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,11 @@ export const buildCustomEmojis = (customEmojis) => {
9292
keywords: [name],
9393
imageUrl: url,
9494
custom: true,
95+
customCategory: emoji.get('category'),
9596
});
9697
});
9798

9899
return emojis;
99100
};
101+
102+
export const categoriesFromEmojis = customEmojis => customEmojis.reduce((set, emoji) => set.add(emoji.get('category') ? `custom-${emoji.get('category')}` : 'custom'), new Set());

app/models/custom_emoji.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
# uri :string
1717
# image_remote_url :string
1818
# visible_in_picker :boolean default(TRUE), not null
19+
# category_id :bigint(8)
1920
#
2021

2122
class CustomEmoji < ApplicationRecord
@@ -27,6 +28,7 @@ class CustomEmoji < ApplicationRecord
2728
:(#{SHORTCODE_RE_FRAGMENT}):
2829
(?=[^[:alnum:]:]|$)/x
2930

31+
belongs_to :category, class_name: 'CustomEmojiCategory', optional: true
3032
has_one :local_counterpart, -> { where(domain: nil) }, class_name: 'CustomEmoji', primary_key: :shortcode, foreign_key: :shortcode
3133

3234
has_attached_file :image, styles: { static: { format: 'png', convert_options: '-coalesce -strip' } }
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# frozen_string_literal: true
2+
3+
# == Schema Information
4+
#
5+
# Table name: custom_emoji_categories
6+
#
7+
# id :bigint(8) not null, primary key
8+
# name :string
9+
# created_at :datetime not null
10+
# updated_at :datetime not null
11+
#
12+
13+
class CustomEmojiCategory < ApplicationRecord
14+
has_many :emojis, class_name: 'CustomEmoji', foreign_key: 'category_id', inverse_of: :category
15+
end

app/serializers/rest/custom_emoji_serializer.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,21 @@ class REST::CustomEmojiSerializer < ActiveModel::Serializer
55

66
attributes :shortcode, :url, :static_url, :visible_in_picker
77

8+
attribute :category, if: :category_loaded?
9+
810
def url
911
full_asset_url(object.image.url)
1012
end
1113

1214
def static_url
1315
full_asset_url(object.image.url(:static))
1416
end
17+
18+
def category
19+
object.category.name
20+
end
21+
22+
def category_loaded?
23+
object.association(:category).loaded? && object.category.present?
24+
end
1525
end
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class CreateCustomEmojiCategories < ActiveRecord::Migration[5.2]
2+
def change
3+
create_table :custom_emoji_categories do |t|
4+
t.string :name, index: { unique: true }
5+
6+
t.timestamps
7+
end
8+
end
9+
end
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class AddCategoryIdToCustomEmojis < ActiveRecord::Migration[5.2]
2+
def change
3+
add_column :custom_emojis, :category_id, :bigint
4+
end
5+
end

db/schema.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema.define(version: 2019_05_29_143559) do
13+
ActiveRecord::Schema.define(version: 2019_06_27_222826) do
1414

1515
# These are extensions that must be enabled in order to support this database
1616
enable_extension "plpgsql"
@@ -208,6 +208,13 @@
208208
t.index ["uri"], name: "index_conversations_on_uri", unique: true
209209
end
210210

211+
create_table "custom_emoji_categories", force: :cascade do |t|
212+
t.string "name"
213+
t.datetime "created_at", null: false
214+
t.datetime "updated_at", null: false
215+
t.index ["name"], name: "index_custom_emoji_categories_on_name", unique: true
216+
end
217+
211218
create_table "custom_emojis", force: :cascade do |t|
212219
t.string "shortcode", default: "", null: false
213220
t.string "domain"
@@ -221,6 +228,7 @@
221228
t.string "uri"
222229
t.string "image_remote_url"
223230
t.boolean "visible_in_picker", default: true, null: false
231+
t.bigint "category_id"
224232
t.index ["shortcode", "domain"], name: "index_custom_emojis_on_shortcode_and_domain", unique: true
225233
end
226234

lib/mastodon/emoji_cli.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@ def self.exit_on_failure?
1515
option :suffix
1616
option :overwrite, type: :boolean
1717
option :unlisted, type: :boolean
18+
option :category
1819
desc 'import PATH', 'Import emoji from a TAR GZIP archive at PATH'
1920
long_desc <<-LONG_DESC
2021
Imports custom emoji from a TAR GZIP archive specified by PATH.
2122
2223
Existing emoji will be skipped unless the --overwrite option
2324
is provided, in which case they will be overwritten.
2425
26+
You can specifiy a --category under which the emojis will be
27+
grouped together.
28+
2529
With the --prefix option, a prefix can be added to all
2630
generated shortcodes. Likewise, the --suffix option controls
2731
the suffix of all shortcodes.
@@ -33,6 +37,7 @@ def import(path)
3337
imported = 0
3438
skipped = 0
3539
failed = 0
40+
category = options[:category] ? CustomEmojiCategory.find_or_create_by(name: options[:category]) : nil
3641

3742
Gem::Package::TarReader.new(Zlib::GzipReader.open(path)) do |tar|
3843
tar.each do |entry|
@@ -50,6 +55,7 @@ def import(path)
5055
custom_emoji.image = StringIO.new(entry.read)
5156
custom_emoji.image_file_name = File.basename(entry.full_name)
5257
custom_emoji.visible_in_picker = !options[:unlisted]
58+
custom_emoji.category = category
5359

5460
if custom_emoji.save
5561
imported += 1

0 commit comments

Comments
 (0)