diff --git a/app/javascript/mastodon/api_types/custom_emoji.ts b/app/javascript/mastodon/api_types/custom_emoji.ts index 05144d6f68..099ef0b88b 100644 --- a/app/javascript/mastodon/api_types/custom_emoji.ts +++ b/app/javascript/mastodon/api_types/custom_emoji.ts @@ -1,8 +1,9 @@ -// See app/serializers/rest/account_serializer.rb +// See app/serializers/rest/custom_emoji_serializer.rb export interface ApiCustomEmojiJSON { shortcode: string; static_url: string; url: string; category?: string; + featured?: boolean; visible_in_picker: boolean; } diff --git a/app/javascript/mastodon/models/custom_emoji.ts b/app/javascript/mastodon/models/custom_emoji.ts index 5297dcd470..19ca951a5c 100644 --- a/app/javascript/mastodon/models/custom_emoji.ts +++ b/app/javascript/mastodon/models/custom_emoji.ts @@ -11,6 +11,7 @@ export const CustomEmojiFactory = ImmutableRecord({ static_url: '', url: '', category: '', + featured: false, visible_in_picker: false, }); diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb index 25ba3d921b..65a2faa9fd 100644 --- a/app/models/custom_emoji.rb +++ b/app/models/custom_emoji.rb @@ -66,6 +66,10 @@ class CustomEmoji < ApplicationRecord :emoji end + def featured? + category&.featured_emoji_id == id + end + def copy! copy = self.class.find_or_initialize_by(domain: nil, shortcode: shortcode) copy.image = image diff --git a/app/models/custom_emoji_category.rb b/app/models/custom_emoji_category.rb index dfcc156080..cc7db33ece 100644 --- a/app/models/custom_emoji_category.rb +++ b/app/models/custom_emoji_category.rb @@ -4,14 +4,16 @@ # # Table name: custom_emoji_categories # -# id :bigint(8) not null, primary key -# name :string -# created_at :datetime not null -# updated_at :datetime not null +# id :bigint(8) not null, primary key +# name :string +# created_at :datetime not null +# updated_at :datetime not null +# featured_emoji_id :bigint(8) # class CustomEmojiCategory < ApplicationRecord has_many :emojis, class_name: 'CustomEmoji', foreign_key: 'category_id', inverse_of: :category, dependent: nil + belongs_to :featured_emoji, class_name: 'CustomEmoji', optional: true, inverse_of: :category validates :name, presence: true, uniqueness: true diff --git a/app/serializers/rest/custom_emoji_serializer.rb b/app/serializers/rest/custom_emoji_serializer.rb index 33da69da53..a173aeae19 100644 --- a/app/serializers/rest/custom_emoji_serializer.rb +++ b/app/serializers/rest/custom_emoji_serializer.rb @@ -8,6 +8,7 @@ class REST::CustomEmojiSerializer < ActiveModel::Serializer attributes :shortcode, :url, :static_url, :visible_in_picker attribute :category, if: :category_loaded? + attribute :featured, if: :category_loaded? def url full_asset_url(object.image.url) @@ -21,6 +22,10 @@ class REST::CustomEmojiSerializer < ActiveModel::Serializer object.category.name end + def featured + object.featured? + end + def category_loaded? object.association(:category).loaded? && object.category.present? end diff --git a/db/migrate/20251201154910_add_featured_emoji_to_custom_emoji_categories.rb b/db/migrate/20251201154910_add_featured_emoji_to_custom_emoji_categories.rb new file mode 100644 index 0000000000..149b0b994e --- /dev/null +++ b/db/migrate/20251201154910_add_featured_emoji_to_custom_emoji_categories.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class AddFeaturedEmojiToCustomEmojiCategories < ActiveRecord::Migration[8.0] + def change + add_column :custom_emoji_categories, :featured_emoji_id, :bigint, null: true + add_foreign_key :custom_emoji_categories, :custom_emojis, column: :featured_emoji_id, on_delete: :nullify, validate: false + end +end diff --git a/db/migrate/20251201155054_validate_add_featured_emoji_to_custom_emoji_categories.rb b/db/migrate/20251201155054_validate_add_featured_emoji_to_custom_emoji_categories.rb new file mode 100644 index 0000000000..3ff5ce7b5e --- /dev/null +++ b/db/migrate/20251201155054_validate_add_featured_emoji_to_custom_emoji_categories.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class ValidateAddFeaturedEmojiToCustomEmojiCategories < ActiveRecord::Migration[8.0] + def change + validate_foreign_key :custom_emoji_categories, :custom_emojis + end +end diff --git a/db/schema.rb b/db/schema.rb index e4e7db3868..669a6dbf2e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_11_19_093332) do +ActiveRecord::Schema[8.0].define(version: 2025_12_01_155054) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -404,6 +404,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_11_19_093332) do t.string "name" t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false + t.bigint "featured_emoji_id" t.index ["name"], name: "index_custom_emoji_categories_on_name", unique: true end @@ -1426,6 +1427,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_11_19_093332) do add_foreign_key "collections", "tags" add_foreign_key "conversation_mutes", "accounts", name: "fk_225b4212bb", on_delete: :cascade add_foreign_key "conversation_mutes", "conversations", on_delete: :cascade + add_foreign_key "custom_emoji_categories", "custom_emojis", column: "featured_emoji_id", on_delete: :nullify add_foreign_key "custom_filter_keywords", "custom_filters", on_delete: :cascade add_foreign_key "custom_filter_statuses", "custom_filters", on_delete: :cascade add_foreign_key "custom_filter_statuses", "statuses", on_delete: :cascade