diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 6092a416..cf30c640 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -793,3 +793,8 @@ ul.pagination { display: inline; line-height: 1; } + +.custom-emoji { + height: 1.25em; + vertical-align: middle; +} \ No newline at end of file diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 428100fd..d41e672d 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -92,4 +92,17 @@ def hub_admin? def newsmast_admin? current_user && policy(current_user).newsmast_admin? end + + def render_custom_emojis(text) + emoji_map = MastodonEmoji.fetch_and_cache_emojis + + pattern = /:([a-zA-Z0-9_+-]+):/ + text.gsub(pattern) do |match| + if emoji_map[match] + image_tag emoji_map[match], alt: match, class: "custom-emoji" + else + match + end + end.html_safe + end end diff --git a/app/services/contributor_search_service.rb b/app/services/contributor_search_service.rb index 50383602..28595265 100644 --- a/app/services/contributor_search_service.rb +++ b/app/services/contributor_search_service.rb @@ -41,7 +41,7 @@ def find_saved_accounts_with_retry(accounts) { 'id' => account.id.to_s, 'username' => account.username, - 'display_name' => account.display_name, + 'display_name' => account.display_name.gsub(/:[^:\s]+:/, '').strip, 'domain' => account.domain, 'note' => account.note, 'avatar_url' => account.avatar_url, diff --git a/app/services/remote_account_verify_service.rb b/app/services/remote_account_verify_service.rb index b8dd1753..c2b4134a 100644 --- a/app/services/remote_account_verify_service.rb +++ b/app/services/remote_account_verify_service.rb @@ -25,10 +25,14 @@ def verify_account_credentials def fetch_remote_account_id # Find account in local server - account_id = if acc = Account.find_by(username: @remote_account["username"], domain: @domain) + domain = @domain + if @domain == 'backend.newsmast.org' + domain = 'newsmast.social' + end + account_id = if acc = Account.find_by(username: @remote_account["username"], domain: domain) acc.id else - account_handler = "@#{@remote_account["username"]}@#{@domain}" + account_handler = "@#{@remote_account["username"]}@#{domain}" search_target_account_id(account_handler) end account_id diff --git a/app/views/accounts/index.html.haml b/app/views/accounts/index.html.haml index 249c5682..c18ff795 100644 --- a/app/views/accounts/index.html.haml +++ b/app/views/accounts/index.html.haml @@ -29,7 +29,7 @@ %td = check_box_tag "selected_accounts[]", account.id, false, class: 'select_account' %td= account.username - %td= account.display_name + %td= render_custom_emojis(account.display_name) %td= account.user ? account.user.email : "-" %td= account.created_at.strftime('%Y-%m-%d %H:%M:%S') %td= account.domain ? account.domain : ENV['MASTODON_INSTANCE_URL'] diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml index 66ad2306..3276a64d 100644 --- a/app/views/accounts/show.html.haml +++ b/app/views/accounts/show.html.haml @@ -18,7 +18,7 @@ .col-sm-3.text-secondary.text-right Display name: .col-sm-9.text-left - = @account.display_name.presence || '-' + = @account.display_name.presence ? render_custom_emojis(@account.display_name) : '-' .row.border-bottom.mb-4 .col-sm-3.text-secondary.text-right Username: diff --git a/app/views/collections/_form.html.haml b/app/views/collections/_form.html.haml index 2b502a16..d230273f 100644 --- a/app/views/collections/_form.html.haml +++ b/app/views/collections/_form.html.haml @@ -26,9 +26,11 @@ .avatar-upload-container .rounded-circle.border.d-inline-flex.align-items-center.justify-content-center{ style: "width: 90px; height: 90px; border: dotted; overflow: hidden;" } - avatar_image_url = @collection.avatar_image.present? && @collection.avatar_image.respond_to?(:url) ? @collection.avatar_image.url : '' + - margin_left = avatar_image_url.present? ? '0' : '16px' - %img{ id: "currentAvatar", src: avatar_image_url, style: "display: #{avatar_image_url.present? ? 'block' : 'none'}; width: 100%; height: 100%; object-fit: cover; margin-left: 16px;" } - %i.fa-solid.fa-image + %img{ id: "currentAvatar", src: avatar_image_url, style: "display: #{avatar_image_url.present? ? 'block' : 'none'}; width: 100%; height: 100%; object-fit: cover; margin-left: #{margin_left};" } + - if avatar_image_url.blank? + %i.fa-solid.fa-image = f.input :avatar_image, as: :file, label: false, input_html: { id: "customFileAvatar", class: "form-control-file upload-input", style: "display: none;", accept: "image/*", 'data-preview-id': "currentAvatar", 'data-aspect-ratio': "1" } .avatar-image-upload diff --git a/app/views/communities/_form_step1.html.haml b/app/views/communities/_form_step1.html.haml index 364490eb..a5381642 100644 --- a/app/views/communities/_form_step1.html.haml +++ b/app/views/communities/_form_step1.html.haml @@ -155,8 +155,10 @@ .logo-upload-container .rounded.border.d-inline-flex.align-items-center.justify-content-center{ style: "width: 300px; height: 150px; border: dotted; overflow: hidden;" } - logo_image_url = @community_form.logo_image.present? && @community_form.logo_image.respond_to?(:url) ? @community_form.logo_image.url : '' - %img{ id: "currentlogo", src: logo_image_url, alt: "Logo Preview", style: "display: #{logo_image_url.present? ? 'block' : 'none'}; width: 100%; height: 100%; object-fit: cover; margin-left: 16px;" } - %i.fa-solid.fa-image + - margin_left = logo_image_url.present? ? '0' : '16px' + %img{ id: "currentlogo", src: logo_image_url, alt: "Logo Preview", style: "display: #{logo_image_url.present? ? 'block' : 'none'}; width: 100%; height: 100%; object-fit: cover; margin-left: #{margin_left};" } + - if logo_image_url.blank? + %i.fa-solid.fa-image = f.input :logo_image, as: :file, label: false, input_html: { id: "customFilelogo", class: "form-control-file upload-input", style: "display: none;", accept: "image/*", 'data-preview-id': "currentlogo", 'data-aspect-ratio': "2.1" } .logo-image-upload %button.btn.btn-primary.mt-2{ onclick: "event.preventDefault(); document.getElementById('customFilelogo').click();" } Upload image @@ -169,8 +171,10 @@ .avatar-upload-container .rounded-circle.border.d-inline-flex.align-items-center.justify-content-center{ style: "width: 90px; height: 90px; border: dotted; overflow: hidden;" } - avatar_image_url = @community_form.avatar_image.present? && @community_form.avatar_image.respond_to?(:url) ? @community_form.avatar_image.url : '' - %img{ id: "currentAvatar", src: avatar_image_url, alt: "Avatar Preview", style: "display: #{avatar_image_url.present? ? 'block' : 'none'}; width: 100%; height: 100%; object-fit: cover; margin-left: 16px;" } - %i.fa-solid.fa-image + - margin_left = avatar_image_url.present? ? '0' : '16px' + %img{ id: "currentAvatar", src: avatar_image_url, alt: "Avatar Preview", style: "display: #{avatar_image_url.present? ? 'block' : 'none'}; width: 100%; height: 100%; object-fit: cover; margin-left: #{margin_left};" } + - if avatar_image_url.blank? + %i.fa-solid.fa-image = f.input :avatar_image, as: :file, label: false, input_html: { id: "customFileAvatar", class: "form-control-file upload-input", style: "display: none;", accept: "image/*", 'data-preview-id': "currentAvatar", 'data-aspect-ratio': "1" } .avatar-image-upload %button.btn.btn-primary.mt-2{ onclick: "event.preventDefault(); document.getElementById('customFileAvatar').click();" } Upload image diff --git a/app/views/communities/_form_step3.html.haml b/app/views/communities/_form_step3.html.haml index eb0eaa03..ba566e2d 100644 --- a/app/views/communities/_form_step3.html.haml +++ b/app/views/communities/_form_step3.html.haml @@ -122,7 +122,7 @@ %td{ style: 'width: 10%;' } %img{ alt: "", src: record.avatar_url, class: "rounded-circle mr-2", style: "width: 70px; height: 70px; border: 2px solid white;" } %td{ style: 'width: 80%;' } - = record.display_name + = render_custom_emojis(record.display_name) - domain = record&.domain.present? ? "https://#{record.domain}" : ENV.fetch('MASTODON_INSTANCE_URL', nil) %br %a{ href: "#{record.url}", target: "_blank" }= "@#{record.username}@#{record.domain || 'channel.org'}" diff --git a/app/views/communities/follower_list.html.haml b/app/views/communities/follower_list.html.haml index c64a3808..36677f4e 100644 --- a/app/views/communities/follower_list.html.haml +++ b/app/views/communities/follower_list.html.haml @@ -29,7 +29,7 @@ %p.text-muted.small There's no follower. - @records.each do |account| %tr - %td= account&.display_name.present? ? account&.display_name : " - " + %td= account&.display_name.present? ? render_custom_emojis(account.display_name) : " - " %td - if account.present? %span.copyable-text{title: "Click to copy", style: "cursor: pointer;", onclick: "copyToClipboard('#{account_url(account)}')"}= "@#{username(account)}@#{domain(account)}" diff --git a/config/initializers/mastodon_emojis.rb b/config/initializers/mastodon_emojis.rb new file mode 100644 index 00000000..816da3f5 --- /dev/null +++ b/config/initializers/mastodon_emojis.rb @@ -0,0 +1,24 @@ +# config/initializers/mastodon_emojis.rb + +require 'net/http' +require 'json' + +module MastodonEmoji + INSTANCE_URL = ENV.fetch("MASTODON_INSTANCE_URL", "https://channel.org") + CACHE_KEY = "mastodon_custom_emojis" + CACHE_EXPIRY = 24.hours + + def self.fetch_and_cache_emojis + Rails.cache.fetch(CACHE_KEY, expires_in: CACHE_EXPIRY) do + url = URI("#{INSTANCE_URL}/api/v1/custom_emojis") + response = Net::HTTP.get(url) + JSON.parse(response).each_with_object({}) do |emoji, hash| + shortcode = ":#{emoji['shortcode']}:" + hash[shortcode] = emoji['url'] + end + rescue => e + Rails.logger.error("Failed to fetch Mastodon emojis: #{e.message}") + {} + end + end +end