diff --git a/app/assets/javascripts/components/features/status/components/detailed_status.jsx b/app/assets/javascripts/components/features/status/components/detailed_status.jsx
index caa46ff3c..2da57252e 100644
--- a/app/assets/javascripts/components/features/status/components/detailed_status.jsx
+++ b/app/assets/javascripts/components/features/status/components/detailed_status.jsx
@@ -54,7 +54,7 @@ const DetailedStatus = React.createClass({
return (
-
+
diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss
index 95e432cb6..8c76ddf99 100644
--- a/app/assets/stylesheets/components.scss
+++ b/app/assets/stylesheets/components.scss
@@ -1,7 +1,7 @@
@import 'variables';
.app-body{
- -ms-overflow-style: -ms-autohiding-scrollbar;
+ -ms-overflow-style: -ms-autohiding-scrollbar;
}
.button {
@@ -165,6 +165,14 @@
}
}
+.avatar {
+ border-radius: 4px;
+ background: transparent no-repeat;
+ background-position: 50%;
+ background-clip: padding-box;
+ position: relative;
+}
+
.lightbox .icon-button {
color: $color1;
}
diff --git a/app/models/account.rb b/app/models/account.rb
index a482fc8e6..8ceda7f97 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -12,12 +12,12 @@ class Account < ApplicationRecord
validates :username, presence: true, uniqueness: { scope: :domain, case_sensitive: true }, unless: 'local?'
# Avatar upload
- has_attached_file :avatar, styles: { original: '120x120#' }, convert_options: { all: '-quality 80 -strip' }
+ has_attached_file :avatar, styles: ->(f) { avatar_styles(f) }, convert_options: { all: '-quality 80 -strip' }
validates_attachment_content_type :avatar, content_type: IMAGE_MIME_TYPES
validates_attachment_size :avatar, less_than: 2.megabytes
# Header upload
- has_attached_file :header, styles: { original: '700x335#' }, convert_options: { all: '-quality 80 -strip' }
+ has_attached_file :header, styles: ->(f) { header_styles(f) }, convert_options: { all: '-quality 80 -strip' }
validates_attachment_content_type :header, content_type: IMAGE_MIME_TYPES
validates_attachment_size :header, less_than: 2.megabytes
@@ -158,6 +158,22 @@ class Account < ApplicationRecord
save!
end
+ def avatar_original_url
+ avatar.url(:original)
+ end
+
+ def avatar_static_url
+ avatar_content_type == 'image/gif' ? avatar.url(:static) : avatar_original_url
+ end
+
+ def header_original_url
+ header.url(:original)
+ end
+
+ def header_static_url
+ header_content_type == 'image/gif' ? header.url(:static) : header_original_url
+ end
+
def avatar_remote_url=(url)
parsed_url = URI.parse(url)
@@ -292,6 +308,18 @@ class Account < ApplicationRecord
def follow_mapping(query, field)
query.pluck(field).inject({}) { |mapping, id| mapping[id] = true; mapping }
end
+
+ def avatar_styles(file)
+ styles = { original: '120x120#' }
+ styles[:static] = { format: 'png' } if file.content_type == 'image/gif'
+ styles
+ end
+
+ def header_styles(file)
+ styles = { original: '700x335#' }
+ styles[:static] = { format: 'png' } if file.content_type == 'image/gif'
+ styles
+ end
end
before_create do
diff --git a/app/views/api/v1/accounts/show.rabl b/app/views/api/v1/accounts/show.rabl
index 32df0457a..8826aa22d 100644
--- a/app/views/api/v1/accounts/show.rabl
+++ b/app/views/api/v1/accounts/show.rabl
@@ -4,8 +4,9 @@ attributes :id, :username, :acct, :display_name, :locked, :created_at
node(:note) { |account| Formatter.instance.simplified_format(account) }
node(:url) { |account| TagManager.instance.url_for(account) }
-node(:avatar) { |account| full_asset_url(account.avatar.url(:original)) }
-node(:header) { |account| full_asset_url(account.header.url(:original)) }
-node(:followers_count) { |account| defined?(@followers_counts_map) ? (@followers_counts_map[account.id] || 0) : account.followers_count }
-node(:following_count) { |account| defined?(@following_counts_map) ? (@following_counts_map[account.id] || 0) : account.following_count }
-node(:statuses_count) { |account| defined?(@statuses_counts_map) ? (@statuses_counts_map[account.id] || 0) : account.statuses_count }
+node(:avatar) { |account| full_asset_url(account.avatar_original_url) }
+node(:avatar_static) { |account| full_asset_url(account.avatar_static_url) }
+node(:header) { |account| full_asset_url(account.header_original_url) }
+node(:header_static) { |account| full_asset_url(account.header_static_url) }
+
+attributes :followers_count, :following_count, :statuses_count
diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake
index 037a13398..a8fb58b7f 100644
--- a/lib/tasks/mastodon.rake
+++ b/lib/tasks/mastodon.rake
@@ -92,5 +92,17 @@ namespace :mastodon do
Rails.logger.debug 'Done!'
end
+
+ desc 'Generate static versions of GIF avatars/headers'
+ task add_static_avatars: :environment do
+ Rails.logger.debug 'Generating static avatars/headers for GIF ones...'
+
+ Account.unscoped.where(avatar_content_type: 'image/gif').or(Account.unscoped.where(header_content_type: 'image/gif')).find_each do |account|
+ account.avatar.reprocess!
+ account.header.reprocess!
+ end
+
+ Rails.logger.debug 'Done!'
+ end
end
end
diff --git a/spec/fixtures/files/avatar.gif b/spec/fixtures/files/avatar.gif
new file mode 100644
index 000000000..d929801e5
Binary files /dev/null and b/spec/fixtures/files/avatar.gif differ
diff --git a/spec/javascript/components/avatar.test.jsx b/spec/javascript/components/avatar.test.jsx
index 852e13a89..7131bbec7 100644
--- a/spec/javascript/components/avatar.test.jsx
+++ b/spec/javascript/components/avatar.test.jsx
@@ -6,16 +6,10 @@ import Avatar from '../../../app/assets/javascripts/components/components/avatar
describe('
', () => {
const src = '/path/to/image.jpg';
const size = 100;
- const wrapper = render(
);
+ const wrapper = render(
);
- it('renders an img element with the given src', () => {
- expect(wrapper.find('img')).to.have.attr('src', `${src}`);
- });
-
- it('renders an img element of the given size', () => {
- ['width', 'height'].map((attr) => {
- expect(wrapper.find('img')).to.have.attr(attr, `${size}`);
- });
+ it('renders a div element with the given src as background', () => {
+ expect(wrapper.find('div')).to.have.style('background-image', `url(${src})`);
});
it('renders a div element of the given size', () => {
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index 0906bb0ae..fb367ab7a 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -421,4 +421,24 @@ RSpec.describe Account, type: :model do
end
end
end
+
+ describe 'static avatars' do
+ describe 'when GIF' do
+ it 'creates a png static style' do
+ subject.avatar = attachment_fixture('avatar.gif')
+ subject.save
+
+ expect(subject.avatar_static_url).to_not eq subject.avatar_original_url
+ end
+ end
+
+ describe 'when non-GIF' do
+ it 'does not create extra static style' do
+ subject.avatar = attachment_fixture('attachment.jpg')
+ subject.save
+
+ expect(subject.avatar_static_url).to eq subject.avatar_original_url
+ end
+ end
+ end
end