diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index c9f5b8564..8ee754362 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -98,6 +98,8 @@ class Settings::PreferencesController < Settings::BaseController :setting_content_emoji_reaction_size, :setting_emoji_scale, :setting_picker_emoji_size, + :setting_enable_wide_emoji, + :setting_enable_wide_emoji_reaction, :setting_hide_bot_on_public_timeline, :setting_confirm_follow_from_bot, :setting_default_search_searchability, diff --git a/app/helpers/accounts_helper.rb b/app/helpers/accounts_helper.rb index eb210a627..6120f24d3 100644 --- a/app/helpers/accounts_helper.rb +++ b/app/helpers/accounts_helper.rb @@ -123,12 +123,35 @@ module AccountsHelper return if user.nil? - ":root { + css = [] + + css << <<-EOS + :root { --content-font-size: #{h(user.setting_content_font_size)}px; --info-font-size: #{h(user.setting_info_font_size)}px; --content-emoji-reaction-size: #{h(user.setting_content_emoji_reaction_size)}px; --emoji-scale: #{h(user.setting_emoji_scale)}; - }" + } + EOS + + css << <<-EOS if user.setting_enable_wide_emoji + img.emojione.custom-emoji:not(.reaction) { + width: unset !important; + max-width: min(100%, 10em); + } + EOS + + css << <<-EOS if user.setting_enable_wide_emoji_reaction + span.reactions-bar__item__emoji { + width: unset !important; + } + span.reactions-bar__item__emoji img.emojione.custom-emoji { + width: unset !important; + max-width: 8em; + } + EOS + + css.join("\n") end def svg_logo diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index a479864cd..e3f902cf8 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -139,7 +139,7 @@ module ApplicationHelper elsif animate image_tag(reaction['url'], class: 'emojione', alt: ":#{reaction['name']}:") else - image_tag(reaction['static_url'], class: 'emojione custom-emoji', alt: ":#{reaction['name']}", 'data-original' => full_asset_url(reaction['url']), 'data-static' => full_asset_url(reaction['static_url'])) + image_tag(reaction['static_url'], class: 'emojione custom-emoji reaction', alt: ":#{reaction['name']}", 'data-original' => full_asset_url(reaction['url']), 'data-static' => full_asset_url(reaction['static_url'])) end end diff --git a/app/javascript/mastodon/components/emoji.js b/app/javascript/mastodon/components/emoji.js index 141a47195..0618060d7 100644 --- a/app/javascript/mastodon/components/emoji.js +++ b/app/javascript/mastodon/components/emoji.js @@ -4,12 +4,14 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import { autoPlayGif } from 'mastodon/initial_state'; import { assetHost } from 'mastodon/utils/config'; import unicodeMapping from 'mastodon/features/emoji/emoji_unicode_mapping_light'; +import classNames from 'classnames'; export default class Emoji extends React.PureComponent { static propTypes = { emoji: PropTypes.string.isRequired, emojiMap: ImmutablePropTypes.map.isRequired, + className: PropTypes.string, hovered: PropTypes.bool.isRequired, url: PropTypes.string, static_url: PropTypes.string, @@ -21,11 +23,12 @@ export default class Emoji extends React.PureComponent { if (unicodeMapping[emoji]) { const { filename, shortCode } = unicodeMapping[emoji]; const title = shortCode ? `:${shortCode}:` : ''; + const className = classNames('emojione', this.props.className); return ( {emoji}
diff --git a/app/javascript/mastodon/features/emoji_reactions/index.js b/app/javascript/mastodon/features/emoji_reactions/index.js index d57132f7e..ac69bc5de 100644 --- a/app/javascript/mastodon/features/emoji_reactions/index.js +++ b/app/javascript/mastodon/features/emoji_reactions/index.js @@ -61,7 +61,7 @@ class Reaction extends ImmutablePureComponent { return (
- +
); }; diff --git a/app/javascript/mastodon/features/notifications/components/notification.js b/app/javascript/mastodon/features/notifications/components/notification.js index f72078753..90badba32 100644 --- a/app/javascript/mastodon/features/notifications/components/notification.js +++ b/app/javascript/mastodon/features/notifications/components/notification.js @@ -352,18 +352,20 @@ class Notification extends ImmutablePureComponent { const { intl, unread, emojiMap } = this.props; if (!notification.get('emoji_reaction')) { - return + return ; } + const wide = notification.getIn(['emoji_reaction', 'width'], 1) / notification.getIn(['emoji_reaction', 'height'], 1) >= 1.4; + return (
-
+
- +
diff --git a/app/javascript/mastodon/initial_state.js b/app/javascript/mastodon/initial_state.js index b4a123753..41a3dfa5f 100644 --- a/app/javascript/mastodon/initial_state.js +++ b/app/javascript/mastodon/initial_state.js @@ -60,6 +60,8 @@ export const enableEmptyColumn = getMeta('enable_empty_column'); export const showReloadButton = getMeta('show_reload_button'); export const defaultColumnWidth = getMeta('default_column_width'); export const pickerEmojiSize = getMeta('picker_emoji_size'); +export const enableWideEmoji = getMeta('enable_wide_emoji'); +export const enableWideEmojiReaction = getMeta('enable_wide_emoji_reaction'); export const disablePost = getMeta('disable_post'); export const disableReactions = getMeta('disable_reactions'); export const disableFollow = getMeta('disable_follow'); diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 9d3f17507..2cee92136 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1946,7 +1946,8 @@ a.account__display-name { } } -.notification__favourite-icon-wrapper { +.notification__favourite-icon-wrapper, +.notification__reaction-icon-wrapper { left: -26px; position: absolute; @@ -1955,6 +1956,11 @@ a.account__display-name { } } +.notification__reaction-message-wrapper.wide { + display: block; + margin-top: 22px; +} + .icon-button.star-icon.active { color: $gold-star; } diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb index f82eb53f7..15a5da488 100644 --- a/app/lib/user_settings_decorator.rb +++ b/app/lib/user_settings_decorator.rb @@ -100,6 +100,8 @@ class UserSettingsDecorator hide_link_preview hide_photo_preview hide_video_preview + enable_wide_emoji + enable_wide_emoji_reaction ).freeze STRING_KEYS = %w( diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb index 0dbba4254..93e725392 100644 --- a/app/models/custom_emoji.rb +++ b/app/models/custom_emoji.rb @@ -18,13 +18,15 @@ # visible_in_picker :boolean default(TRUE), not null # category_id :bigint(8) # image_storage_schema_version :integer +# width :integer +# height :integer # class CustomEmoji < ApplicationRecord include Attachmentable - LOCAL_LIMIT = (ENV['MAX_EMOJI_SIZE'] || 50.kilobytes).to_i - LIMIT = [LOCAL_LIMIT, (ENV['MAX_REMOTE_EMOJI_SIZE'] || 200.kilobytes).to_i].max + LOCAL_LIMIT = (ENV['MAX_EMOJI_SIZE'] || 256.kilobytes).to_i + LIMIT = [LOCAL_LIMIT, (ENV['MAX_REMOTE_EMOJI_SIZE'] || 256.kilobytes).to_i].max SHORTCODE_RE_FRAGMENT = '[a-zA-Z0-9_]{2,}' @@ -37,7 +39,7 @@ class CustomEmoji < ApplicationRecord belongs_to :category, class_name: 'CustomEmojiCategory', optional: true has_one :local_counterpart, -> { where(domain: nil) }, class_name: 'CustomEmoji', primary_key: :shortcode, foreign_key: :shortcode - has_attached_file :image, styles: { static: { format: 'png', convert_options: '-coalesce +profile exif' } }, validate_media_type: false + has_attached_file :image, styles: { static: { format: 'png', convert_options: '-coalesce +profile exif', file_geometry_parser: FastGeometryParser } }, processors: [:dimension_extractor], validate_media_type: false before_validation :downcase_domain diff --git a/app/models/user.rb b/app/models/user.rb index e66679cc9..7be7c11ed 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -140,6 +140,7 @@ class User < ApplicationRecord :post_reference_modal, :add_reference_modal, :unselect_reference_modal, :delete_scheduled_status_modal, :hexagon_avatar, :enable_empty_column, :content_font_size, :info_font_size, :content_emoji_reaction_size, :emoji_scale, :picker_emoji_size, + :enable_wide_emoji, :enable_wide_emoji_reaction, :hide_bot_on_public_timeline, :confirm_follow_from_bot, :default_search_searchability, :default_expires_in, :default_expires_action, :show_reload_button, :default_column_width, diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index b508d8b9c..b116f8085 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -78,6 +78,8 @@ class InitialStateSerializer < ActiveModel::Serializer store[:content_emoji_reaction_size] = object.current_account.user.setting_content_emoji_reaction_size store[:emoji_scale] = object.current_account.user.setting_emoji_scale store[:picker_emoji_size] = object.current_account.user.setting_picker_emoji_size + store[:enable_wide_emoji] = object.current_account.user.setting_enable_wide_emoji + store[:enable_wide_emoji_reaction] = object.current_account.user.setting_enable_wide_emoji_reaction store[:hide_bot_on_public_timeline] = object.current_account.user.setting_hide_bot_on_public_timeline store[:confirm_follow_from_bot] = object.current_account.user.setting_confirm_follow_from_bot store[:show_reload_button] = object.current_account.user.setting_show_reload_button diff --git a/app/serializers/rest/custom_emoji_serializer.rb b/app/serializers/rest/custom_emoji_serializer.rb index aff58e4d9..3aec3ee2b 100644 --- a/app/serializers/rest/custom_emoji_serializer.rb +++ b/app/serializers/rest/custom_emoji_serializer.rb @@ -6,6 +6,8 @@ class REST::CustomEmojiSerializer < ActiveModel::Serializer attributes :shortcode, :url, :static_url, :visible_in_picker attribute :category, if: :category_loaded? + attribute :width, if: :width? + attribute :height, if: :height? def url full_asset_url(object.image.url) @@ -22,4 +24,20 @@ class REST::CustomEmojiSerializer < ActiveModel::Serializer def category_loaded? object.association(:category).loaded? && object.category.present? end + + def width + object.width + end + + def height + object.height + end + + def width? + !object.width.nil? + end + + def height? + !object.height.nil? + end end diff --git a/app/serializers/rest/emoji_reaction_serializer.rb b/app/serializers/rest/emoji_reaction_serializer.rb index 3e7e10927..57540a32a 100644 --- a/app/serializers/rest/emoji_reaction_serializer.rb +++ b/app/serializers/rest/emoji_reaction_serializer.rb @@ -8,6 +8,8 @@ class REST::EmojiReactionSerializer < ActiveModel::Serializer attribute :url, if: :custom_emoji? attribute :static_url, if: :custom_emoji? attribute :domain, if: :custom_emoji? + attribute :width, if: :width? + attribute :height, if: :height? belongs_to :account, serializer: REST::AccountSerializer @@ -26,4 +28,20 @@ class REST::EmojiReactionSerializer < ActiveModel::Serializer def domain object.custom_emoji.domain end + + def width + object.custom_emoji.width + end + + def height + object.custom_emoji.height + end + + def width? + custom_emoji? && object.custom_emoji.width + end + + def height? + custom_emoji? && object.custom_emoji.height + end end diff --git a/app/serializers/rest/grouped_emoji_reaction_serializer.rb b/app/serializers/rest/grouped_emoji_reaction_serializer.rb index f885f2deb..ef4634258 100644 --- a/app/serializers/rest/grouped_emoji_reaction_serializer.rb +++ b/app/serializers/rest/grouped_emoji_reaction_serializer.rb @@ -9,6 +9,8 @@ class REST::GroupedEmojiReactionSerializer < ActiveModel::Serializer attribute :url, if: :custom_emoji? attribute :static_url, if: :custom_emoji? attribute :domain, if: :custom_emoji? + attribute :width, if: :width? + attribute :height, if: :height? attribute :account_ids, if: :has_account_ids? def count @@ -38,4 +40,20 @@ class REST::GroupedEmojiReactionSerializer < ActiveModel::Serializer def domain object.custom_emoji.domain end + + def width + object.custom_emoji.width + end + + def height + object.custom_emoji.height + end + + def width? + custom_emoji? && object.custom_emoji.width + end + + def height? + custom_emoji? && object.custom_emoji.height + end end diff --git a/app/serializers/rest/instance_serializer.rb b/app/serializers/rest/instance_serializer.rb index ce52ebe96..9ef92ce28 100644 --- a/app/serializers/rest/instance_serializer.rb +++ b/app/serializers/rest/instance_serializer.rb @@ -148,6 +148,8 @@ class REST::InstanceSerializer < ActiveModel::Serializer :searchability, :status_compact_mode, :account_conversations, + :enable_wide_emoji, + :enable_wide_emoji_reaction, ] capabilities << :profile_search unless Chewy.enabled? diff --git a/app/views/settings/preferences/appearance/show.html.haml b/app/views/settings/preferences/appearance/show.html.haml index 100d78b00..deab54557 100644 --- a/app/views/settings/preferences/appearance/show.html.haml +++ b/app/views/settings/preferences/appearance/show.html.haml @@ -33,6 +33,12 @@ = f.input :setting_content_emoji_reaction_size, as: :range, input_html: { min: 10, max: 48, list: 'emoji_reaction_size_label' }, wrapper: :with_label, false: true, fedibird_features: true = f.input :setting_picker_emoji_size, as: :range, input_html: { min: 22, max: 48, list: 'picker_emoji_size_label' }, wrapper: :with_label, false: true, fedibird_features: true + .fields-group + = f.input :setting_enable_wide_emoji, as: :boolean, wrapper: :with_label, hint: true, fedibird_features: true + + .fields-group + = f.input :setting_enable_wide_emoji_reaction, as: :boolean, wrapper: :with_label, hint: true, fedibird_features: true + .fields-group = f.input :setting_theme_public, as: :boolean, wrapper: :with_label, hint: true, fedibird_features: true diff --git a/config/application.rb b/config/application.rb index 434e722f3..9245f498e 100644 --- a/config/application.rb +++ b/config/application.rb @@ -12,6 +12,7 @@ require_relative '../lib/sanitize_ext/sanitize_config' require_relative '../lib/redis/namespace_extensions' require_relative '../lib/paperclip/url_generator_extensions' require_relative '../lib/paperclip/attachment_extensions' +require_relative '../lib/paperclip/dimension_extractor' require_relative '../lib/paperclip/lazy_thumbnail' require_relative '../lib/paperclip/gif_transcoder' require_relative '../lib/paperclip/transcoder' diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 033f9ab96..75cb03b7d 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -91,6 +91,8 @@ en: setting_enable_personal_timeline: Enable a personal home to display personal post setting_enable_reaction: Enable the reaction display on the timeline and display the reaction button setting_enable_status_reference: Enable the feature where a post references another post + setting_enable_wide_emoji: Displays wide emoji derived from Misskey in their original proportions (for content) + setting_enable_wide_emoji_reaction: Displays wide emoji derived from Misskey in their original proportions (for reaction) setting_follow_button_to_list_adder: Change the behavior of the Follow / Subscribe button, open a dialog where you can select a list to follow / subscribe, or opt out of receiving at home setting_hexagon_avatar: Display everyone's avatar icon as a hollowed out hexagon (joke feature) setting_hide_bot_on_public_timeline: Disable Bot accounts from appearing on federation & hashtag & domain & group timelines (overridden by column setting) @@ -288,6 +290,8 @@ en: setting_enable_personal_timeline: Enable personal timeline setting_enable_reaction: Enable reaction setting_enable_status_reference: Enable reference + setting_enable_wide_emoji: Enable wide emoji (for content) + setting_enable_wide_emoji_reaction: Enable wide emoji (for reaction) setting_expand_spoilers: Always expand posts marked with content warnings setting_follow_button_to_list_adder: Open list add dialog with follow button setting_hexagon_avatar: Experience NFT Avatar diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index 520384d8d..32b8c6d21 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -87,6 +87,8 @@ ja: setting_enable_personal_timeline: 自分限定を表示する自分限定ホームを有効にします setting_enable_reaction: タイムラインでリアクションの表示を有効にし、リアクションボタンを表示する setting_enable_status_reference: 投稿が別の投稿を参照する機能を有効にします + setting_enable_wide_emoji: Misskey由来の横幅の広い絵文字を元の比率で表示します(本文) + setting_enable_wide_emoji_reaction: Misskey由来の横幅の広い絵文字を元の比率で表示します(リアクション) setting_follow_button_to_list_adder: フォロー・購読ボタンの動作を変更し、フォロー・購読するリストを選択したり、ホームで受け取らないよう設定するダイアログを開きます setting_hexagon_avatar: 全員のアバターアイコンを6角形にくりぬいて表示します(ジョーク機能) setting_hide_bot_on_public_timeline: 連合・ハッシュタグ・ドメイン・グループタイムライン上にBotアカウントが表示されないようにします(※カラム設定を優先) @@ -284,6 +286,8 @@ ja: setting_enable_personal_timeline: 自分限定ホームを有効にする setting_enable_reaction: リアクションを有効にする setting_enable_status_reference: 参照を有効にする + setting_enable_wide_emoji: ワイド絵文字を有効にする(本文) + setting_enable_wide_emoji_reaction: ワイド絵文字を有効にする(リアクション) setting_expand_spoilers: 閲覧注意としてマークされた投稿を常に展開する setting_follow_button_to_list_adder: フォローボタンでリスト追加ダイアログを開く setting_hexagon_avatar: NFTアイコンを体験する diff --git a/config/settings.yml b/config/settings.yml index b197cd695..f94fd0cb9 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -73,6 +73,8 @@ defaults: &defaults content_emoji_reaction_size: 16 emoji_scale: 1, picker_emoji_size: 22, + enable_wide_emoji: true + enable_wide_emoji_reaction: true notification_emails: follow: false reblog: false diff --git a/db/migrate/20230221031206_add_width_height_to_custom_emoji.rb b/db/migrate/20230221031206_add_width_height_to_custom_emoji.rb new file mode 100644 index 000000000..3dea9a698 --- /dev/null +++ b/db/migrate/20230221031206_add_width_height_to_custom_emoji.rb @@ -0,0 +1,6 @@ +class AddWidthHeightToCustomEmoji < ActiveRecord::Migration[6.1] + def change + add_column :custom_emojis, :width, :integer, null: true, default: nil + add_column :custom_emojis, :height, :integer, null: true, default: nil + end +end diff --git a/db/schema.rb b/db/schema.rb index e19670b44..fb1484e64 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.define(version: 2023_02_15_062659) do +ActiveRecord::Schema.define(version: 2023_02_21_031206) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -371,6 +371,8 @@ ActiveRecord::Schema.define(version: 2023_02_15_062659) do t.boolean "visible_in_picker", default: true, null: false t.bigint "category_id" t.integer "image_storage_schema_version" + t.integer "width" + t.integer "height" t.index ["shortcode", "domain"], name: "index_custom_emojis_on_shortcode_and_domain", unique: true end diff --git a/lib/mastodon/emoji_cli.rb b/lib/mastodon/emoji_cli.rb index 5bee70ea5..b54974fe0 100644 --- a/lib/mastodon/emoji_cli.rb +++ b/lib/mastodon/emoji_cli.rb @@ -7,6 +7,8 @@ require_relative 'cli_helper' module Mastodon class EmojiCLI < Thor + include CLIHelper + def self.exit_on_failure? true end @@ -132,6 +134,38 @@ module Mastodon say('OK', :green) end + option :local_only, type: :boolean + option :remote_only, type: :boolean + option :all, type: :boolean + option :concurrency, type: :numeric, default: 5, aliases: [:c] + option :verbose, type: :boolean, aliases: [:v] + desc 'fix-dimension', 'Fix dimension all custom emoji' + long_desc <<-LONG_DESC + Fix dimension all custom emoji. + + With the --local-only option, only local emoji will be fixed. + With the --remote-only option, only remote emoji will be fixed. + With the --all option, fix dimension of all emojis. + LONG_DESC + def fix_dimension + scope = CustomEmoji + scope = scope.local if options[:local_only] + scope = scope.remote if options[:remote_only] + scope = scope.where(width: nil) unless options[:all] + + processed, fixed = parallelize_with_progress(scope) do |emoji| + width, height = FastImage.size(emoji.image.url) + next if width.nil? + + emoji.update!(width: width, height: height) + 1 + rescue + next + end + + say("Checked #{processed} emojis, fixed #{fixed}", :green, true) + end + private def color(green, _yellow, red) diff --git a/lib/paperclip/dimension_extractor.rb b/lib/paperclip/dimension_extractor.rb new file mode 100644 index 000000000..4b3b06753 --- /dev/null +++ b/lib/paperclip/dimension_extractor.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Paperclip + class DimensionExtractor < Paperclip::Processor + def make + geometry = options.fetch(:file_geometry_parser).from_file(@file) + + attachment.instance.width = geometry.width if attachment.instance.respond_to?(:width) + attachment.instance.height = geometry.height if attachment.instance.respond_to?(:height) + + File.open(@file.path) + end + end +end