From a71af984011ff98df2fa1b7f6c983706a91d99bf Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 17 Feb 2018 14:28:48 +0100 Subject: Push discovered status through streaming API within a time window (#6484) Time window of 6 hours --- app/lib/activitypub/activity.rb | 2 +- app/lib/ostatus/activity/creation.rb | 2 +- app/models/status.rb | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/lib/activitypub/activity.rb b/app/lib/activitypub/activity.rb index 0f9e4f263..4617905c6 100644 --- a/app/lib/activitypub/activity.rb +++ b/app/lib/activitypub/activity.rb @@ -74,7 +74,7 @@ class ActivityPub::Activity # Only continue if the status is supposed to have # arrived in real-time - return unless @options[:override_timestamps] + return unless @options[:override_timestamps] || status.within_realtime_window? distribute_to_followers(status) end diff --git a/app/lib/ostatus/activity/creation.rb b/app/lib/ostatus/activity/creation.rb index b38407cd3..7cf2d90dc 100644 --- a/app/lib/ostatus/activity/creation.rb +++ b/app/lib/ostatus/activity/creation.rb @@ -61,7 +61,7 @@ class OStatus::Activity::Creation < OStatus::Activity::Base Rails.logger.debug "Queuing remote status #{status.id} (#{id}) for distribution" LinkCrawlWorker.perform_async(status.id) unless status.spoiler_text? - DistributionWorker.perform_async(status.id) if @options[:override_timestamps] + DistributionWorker.perform_async(status.id) if @options[:override_timestamps] || status.within_realtime_window? status end diff --git a/app/models/status.rb b/app/models/status.rb index 0de89ad4e..8186f4784 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -80,6 +80,8 @@ class Status < ApplicationRecord delegate :domain, to: :account, prefix: true + REAL_TIME_WINDOW = 6.hours + def searchable_by(preloaded = nil) ids = [account_id] @@ -108,6 +110,10 @@ class Status < ApplicationRecord !reblog_of_id.nil? end + def within_realtime_window? + created_at >= REAL_TIME_WINDOW.ago + end + def verb if destroyed? :delete -- cgit From cba2897108dffe69d5a16befe6c6220f6eaae59f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 18 Feb 2018 03:14:46 +0100 Subject: Cache relationships in API (#6482) * Cache relationships in API * Fetch relationships for search results in UI * Only save one account's maps in each cache item --- app/javascript/mastodon/actions/search.js | 2 + app/models/account_domain_block.rb | 8 ++- app/models/block.rb | 4 +- app/models/concerns/relationship_cacheable.rb | 16 ++++++ app/models/follow.rb | 1 + app/models/follow_request.rb | 1 + app/models/mute.rb | 4 +- app/presenters/account_relationships_presenter.rb | 68 +++++++++++++++++++++-- 8 files changed, 92 insertions(+), 12 deletions(-) create mode 100644 app/models/concerns/relationship_cacheable.rb (limited to 'app') diff --git a/app/javascript/mastodon/actions/search.js b/app/javascript/mastodon/actions/search.js index 78c6109f7..73cb106ec 100644 --- a/app/javascript/mastodon/actions/search.js +++ b/app/javascript/mastodon/actions/search.js @@ -1,4 +1,5 @@ import api from '../api'; +import { fetchRelationships } from './accounts'; export const SEARCH_CHANGE = 'SEARCH_CHANGE'; export const SEARCH_CLEAR = 'SEARCH_CLEAR'; @@ -38,6 +39,7 @@ export function submitSearch() { }, }).then(response => { dispatch(fetchSearchSuccess(response.data)); + dispatch(fetchRelationships(response.data.accounts.map(item => item.id))); }).catch(error => { dispatch(fetchSearchFail(error)); }); diff --git a/app/models/account_domain_block.rb b/app/models/account_domain_block.rb index abcc923b3..bc00b4f32 100644 --- a/app/models/account_domain_block.rb +++ b/app/models/account_domain_block.rb @@ -16,12 +16,16 @@ class AccountDomainBlock < ApplicationRecord belongs_to :account validates :domain, presence: true, uniqueness: { scope: :account_id } - after_create :remove_blocking_cache - after_destroy :remove_blocking_cache + after_commit :remove_blocking_cache + after_commit :remove_relationship_cache private def remove_blocking_cache Rails.cache.delete("exclude_domains_for:#{account_id}") end + + def remove_relationship_cache + Rails.cache.delete_matched("relationship:#{account_id}:*") + end end diff --git a/app/models/block.rb b/app/models/block.rb index 441e6bca3..d6ecabd3b 100644 --- a/app/models/block.rb +++ b/app/models/block.rb @@ -12,14 +12,14 @@ class Block < ApplicationRecord include Paginable + include RelationshipCacheable belongs_to :account belongs_to :target_account, class_name: 'Account' validates :account_id, uniqueness: { scope: :target_account_id } - after_create :remove_blocking_cache - after_destroy :remove_blocking_cache + after_commit :remove_blocking_cache private diff --git a/app/models/concerns/relationship_cacheable.rb b/app/models/concerns/relationship_cacheable.rb new file mode 100644 index 000000000..0d9359f7e --- /dev/null +++ b/app/models/concerns/relationship_cacheable.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module RelationshipCacheable + extend ActiveSupport::Concern + + included do + after_commit :remove_relationship_cache + end + + private + + def remove_relationship_cache + Rails.cache.delete("relationship:#{account_id}:#{target_account_id}") + Rails.cache.delete("relationship:#{target_account_id}:#{account_id}") + end +end diff --git a/app/models/follow.rb b/app/models/follow.rb index f953b8e3e..8e6fe537a 100644 --- a/app/models/follow.rb +++ b/app/models/follow.rb @@ -13,6 +13,7 @@ class Follow < ApplicationRecord include Paginable + include RelationshipCacheable belongs_to :account, counter_cache: :following_count diff --git a/app/models/follow_request.rb b/app/models/follow_request.rb index bd6c4a0b9..cde26ceed 100644 --- a/app/models/follow_request.rb +++ b/app/models/follow_request.rb @@ -13,6 +13,7 @@ class FollowRequest < ApplicationRecord include Paginable + include RelationshipCacheable belongs_to :account belongs_to :target_account, class_name: 'Account' diff --git a/app/models/mute.rb b/app/models/mute.rb index 948f22444..8efa27ac0 100644 --- a/app/models/mute.rb +++ b/app/models/mute.rb @@ -13,14 +13,14 @@ class Mute < ApplicationRecord include Paginable + include RelationshipCacheable belongs_to :account belongs_to :target_account, class_name: 'Account' validates :account_id, uniqueness: { scope: :target_account_id } - after_create :remove_blocking_cache - after_destroy :remove_blocking_cache + after_commit :remove_blocking_cache private diff --git a/app/presenters/account_relationships_presenter.rb b/app/presenters/account_relationships_presenter.rb index bf1ba3716..d27fb7b01 100644 --- a/app/presenters/account_relationships_presenter.rb +++ b/app/presenters/account_relationships_presenter.rb @@ -5,11 +5,67 @@ class AccountRelationshipsPresenter :muting, :requested, :domain_blocking def initialize(account_ids, current_account_id, **options) - @following = Account.following_map(account_ids, current_account_id).merge(options[:following_map] || {}) - @followed_by = Account.followed_by_map(account_ids, current_account_id).merge(options[:followed_by_map] || {}) - @blocking = Account.blocking_map(account_ids, current_account_id).merge(options[:blocking_map] || {}) - @muting = Account.muting_map(account_ids, current_account_id).merge(options[:muting_map] || {}) - @requested = Account.requested_map(account_ids, current_account_id).merge(options[:requested_map] || {}) - @domain_blocking = Account.domain_blocking_map(account_ids, current_account_id).merge(options[:domain_blocking_map] || {}) + @account_ids = account_ids.map { |a| a.is_a?(Account) ? a.id : a } + @current_account_id = current_account_id + + @following = cached[:following].merge(Account.following_map(@uncached_account_ids, @current_account_id)) + @followed_by = cached[:followed_by].merge(Account.followed_by_map(@uncached_account_ids, @current_account_id)) + @blocking = cached[:blocking].merge(Account.blocking_map(@uncached_account_ids, @current_account_id)) + @muting = cached[:muting].merge(Account.muting_map(@uncached_account_ids, @current_account_id)) + @requested = cached[:requested].merge(Account.requested_map(@uncached_account_ids, @current_account_id)) + @domain_blocking = cached[:domain_blocking].merge(Account.domain_blocking_map(@uncached_account_ids, @current_account_id)) + + cache_uncached! + + @following.merge!(options[:following_map] || {}) + @followed_by.merge!(options[:followed_by_map] || {}) + @blocking.merge!(options[:blocking_map] || {}) + @muting.merge!(options[:muting_map] || {}) + @requested.merge!(options[:requested_map] || {}) + @domain_blocking.merge!(options[:domain_blocking_map] || {}) + end + + private + + def cached + return @cached if defined?(@cached) + + @cached = { + following: {}, + followed_by: {}, + blocking: {}, + muting: {}, + requested: {}, + domain_blocking: {}, + } + + @uncached_account_ids = [] + + @account_ids.each do |account_id| + maps_for_account = Rails.cache.read("relationship:#{@current_account_id}:#{account_id}") + + if maps_for_account.is_a?(Hash) + @cached.merge!(maps_for_account) + else + @uncached_account_ids << account_id + end + end + + @cached + end + + def cache_uncached! + @uncached_account_ids.each do |account_id| + maps_for_account = { + following: { account_id => following[account_id] }, + followed_by: { account_id => followed_by[account_id] }, + blocking: { account_id => blocking[account_id] }, + muting: { account_id => muting[account_id] }, + requested: { account_id => requested[account_id] }, + domain_blocking: { account_id => domain_blocking[account_id] }, + } + + Rails.cache.write("relationship:#{@current_account_id}:#{account_id}", maps_for_account, expires_in: 1.day) + end end end -- cgit From bc6751ecce2ba81e074fa3b672a3fe5ee6004302 Mon Sep 17 00:00:00 2001 From: HellPie Date: Sun, 18 Feb 2018 16:32:58 +0100 Subject: Remove outline from body window (Fixes #6501) (#6502) --- app/javascript/styles/mastodon/basics.scss | 1 + 1 file changed, 1 insertion(+) (limited to 'app') diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss index 0e3b9ad6b..bec0d4d91 100644 --- a/app/javascript/styles/mastodon/basics.scss +++ b/app/javascript/styles/mastodon/basics.scss @@ -122,5 +122,6 @@ button { height: 100%; align-items: center; justify-content: center; + outline: 0 !important; } } -- cgit From bb26cdda242afff59f7718b3211732fc068980f1 Mon Sep 17 00:00:00 2001 From: Konrad Pozniak Date: Sun, 18 Feb 2018 22:57:53 +0100 Subject: add parameter locked to /api/v1/update_credentials (#6506) --- app/controllers/api/v1/accounts/credentials_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/controllers/api/v1/accounts/credentials_controller.rb b/app/controllers/api/v1/accounts/credentials_controller.rb index da534d960..68af22529 100644 --- a/app/controllers/api/v1/accounts/credentials_controller.rb +++ b/app/controllers/api/v1/accounts/credentials_controller.rb @@ -20,6 +20,6 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController private def account_params - params.permit(:display_name, :note, :avatar, :header) + params.permit(:display_name, :note, :avatar, :header, :locked) end end -- cgit From cbb69d41f6eda4d997f7bcb89e9aef8fd8bec2e4 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 19 Feb 2018 02:39:18 +0100 Subject: Fix media spoiler design (#6507) - 4px rounded corners on media attachments - Better colors/contrast for CW/media spoiler on public pages - Fix vertical alignment of "Show more" button - Fix layout jump when unhiding standalone media --- app/javascript/mastodon/components/media_gallery.js | 6 +----- app/javascript/styles/mastodon/components.scss | 20 +++++++++++++++++--- app/javascript/styles/mastodon/stream_entries.scss | 20 ++++++-------------- 3 files changed, 24 insertions(+), 22 deletions(-) (limited to 'app') diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js index 00943e205..a3ffc45ea 100644 --- a/app/javascript/mastodon/components/media_gallery.js +++ b/app/javascript/mastodon/components/media_gallery.js @@ -227,12 +227,8 @@ export default class MediaGallery extends React.PureComponent { const style = {}; if (this.isStandaloneEligible()) { - if (!visible && width) { - // only need to forcibly set the height in "sensitive" mode + if (width) { style.height = width / this.props.media.getIn([0, 'meta', 'small', 'aspect']); - } else { - // layout automatically, using image's natural aspect ratio - style.height = ''; } } else { // crop the image diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index d1fbabfc5..6a256c466 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -686,12 +686,13 @@ background: transparent; border: 0; color: lighten($ui-base-color, 8%); - font-weight: 500; + font-weight: 700; font-size: 11px; padding: 0 6px; text-transform: uppercase; - line-height: inherit; + line-height: 20px; cursor: pointer; + vertical-align: middle; } .status__prepend-icon-wrapper { @@ -899,6 +900,11 @@ height: 24px; margin: -1px 0 0; } + + .status__content__spoiler-link { + line-height: 24px; + margin: -1px 0 0; + } } .video-player { @@ -2667,12 +2673,16 @@ a.status-card { background: $base-overlay-background; color: $ui-primary-color; border: 0; + padding: 0; width: 100%; height: 100%; + border-radius: 4px; + appearance: none; &:hover, &:active, &:focus { + padding: 0; color: lighten($ui-primary-color, 8%); } } @@ -2685,7 +2695,7 @@ a.status-card { .media-spoiler__trigger { display: block; font-size: 11px; - font-weight: 500; + font-weight: 700; } .spoiler-button { @@ -4091,6 +4101,7 @@ a.status-card { box-sizing: border-box; margin-top: 8px; overflow: hidden; + border-radius: 4px; position: relative; width: 100%; } @@ -4101,6 +4112,8 @@ a.status-card { display: block; float: left; position: relative; + border-radius: 4px; + overflow: hidden; &.standalone { .media-gallery__item-gifv-thumbnail { @@ -4113,6 +4126,7 @@ a.status-card { cursor: zoom-in; display: block; text-decoration: none; + color: $ui-secondary-color; height: 100%; line-height: 0; diff --git a/app/javascript/styles/mastodon/stream_entries.scss b/app/javascript/styles/mastodon/stream_entries.scss index e11ca29d9..442b143a0 100644 --- a/app/javascript/styles/mastodon/stream_entries.scss +++ b/app/javascript/styles/mastodon/stream_entries.scss @@ -146,10 +146,10 @@ a.status__content__spoiler-link { color: $primary-text-color; - background: $ui-primary-color; + background: $ui-base-color; &:hover { - background: lighten($ui-primary-color, 8%); + background: lighten($ui-base-color, 8%); } } } @@ -214,10 +214,10 @@ a.status__content__spoiler-link { color: $primary-text-color; - background: $ui-primary-color; + background: $ui-base-color; &:hover { - background: lighten($ui-primary-color, 8%); + background: lighten($ui-base-color, 8%); } } } @@ -260,16 +260,8 @@ } .media-spoiler { - background: $ui-primary-color; - color: $white; - transition: all 40ms linear; - - &:hover, - &:active, - &:focus { - background: darken($ui-primary-color, 2%); - color: unset; - } + background: $ui-base-color; + color: $ui-primary-color; } .pre-header { -- cgit From 66105929e07fc7ddbdb8b66696b9ce1ed5d25957 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 19 Feb 2018 16:06:12 +0100 Subject: Don't resize avatars/headers if their dimensions are already right (#6508) Also don't apply "-quality 80" option which is probably the reason for slight color differences between original and remote image (because it would apply it twice, once on original instance, and again on the receiving instance) --- app/models/concerns/account_avatar.rb | 12 +++++++++--- app/models/concerns/account_header.rb | 12 +++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) (limited to 'app') diff --git a/app/models/concerns/account_avatar.rb b/app/models/concerns/account_avatar.rb index 8a5c9a22c..53d0d876f 100644 --- a/app/models/concerns/account_avatar.rb +++ b/app/models/concerns/account_avatar.rb @@ -7,9 +7,15 @@ module AccountAvatar class_methods do def avatar_styles(file) - styles = { original: '120x120#' } - styles[:static] = { format: 'png', convert_options: '-coalesce' } if file.content_type == 'image/gif' + styles = {} + geometry = Paperclip::Geometry.from_file(file) + + styles[:original] = '120x120#' if geometry.width != geometry.height || geometry.width > 120 || geometry.height > 120 + styles[:static] = { format: 'png', convert_options: '-coalesce' } if file.content_type == 'image/gif' + styles + rescue Paperclip::Errors::NotIdentifiedByImageMagickError + {} end private :avatar_styles @@ -17,7 +23,7 @@ module AccountAvatar included do # Avatar upload - has_attached_file :avatar, styles: ->(f) { avatar_styles(f) }, convert_options: { all: '-quality 80 -strip' } + has_attached_file :avatar, styles: ->(f) { avatar_styles(f) }, convert_options: { all: '-strip' } validates_attachment_content_type :avatar, content_type: IMAGE_MIME_TYPES validates_attachment_size :avatar, less_than: 2.megabytes end diff --git a/app/models/concerns/account_header.rb b/app/models/concerns/account_header.rb index aff2aa3f9..991473d8c 100644 --- a/app/models/concerns/account_header.rb +++ b/app/models/concerns/account_header.rb @@ -7,9 +7,15 @@ module AccountHeader class_methods do def header_styles(file) - styles = { original: '700x335#' } - styles[:static] = { format: 'png', convert_options: '-coalesce' } if file.content_type == 'image/gif' + styles = {} + geometry = Paperclip::Geometry.from_file(file) + + styles[:original] = '700x335#' unless geometry.width == 700 && geometry.height == 335 + styles[:static] = { format: 'png', convert_options: '-coalesce' } if file.content_type == 'image/gif' + styles + rescue Paperclip::Errors::NotIdentifiedByImageMagickError + {} end private :header_styles @@ -17,7 +23,7 @@ module AccountHeader included do # Header upload - has_attached_file :header, styles: ->(f) { header_styles(f) }, convert_options: { all: '-quality 80 -strip' } + has_attached_file :header, styles: ->(f) { header_styles(f) }, convert_options: { all: '-strip' } validates_attachment_content_type :header, content_type: IMAGE_MIME_TYPES validates_attachment_size :header, less_than: 2.megabytes end -- cgit