Merge branch 'main' into glitch-soc/merge-upstream

Conflicts:
- `app/controllers/accounts_controller.rb`:
  Upstream introduced support for private pinned toots, but glitch-soc's query
  was a bit different as it filtered out local-only toots.
  Used upstream's query, while adding local-only filtering back.
- `app/controllers/activitypub/collections_controller.rb`:
  Same thing with regards to local-only posts.
- `app/validators/status_pin_validator.rb`:
  Not a real conflict, but the line below was different in glitch-soc due to
  the configurable pinned toots limit.
This commit is contained in:
Claire
2022-01-17 08:28:17 +01:00
18 changed files with 164 additions and 28 deletions

View File

@ -29,7 +29,7 @@ class AccountsController < ApplicationController
return
end
@pinned_statuses = cache_collection(@account.pinned_statuses.not_local_only, Status) if show_pinned_statuses?
@pinned_statuses = cached_filtered_status_pins if show_pinned_statuses?
@statuses = cached_filtered_status_page
@rss_url = rss_url
@ -65,6 +65,10 @@ class AccountsController < ApplicationController
[replies_requested?, media_requested?, tag_requested?, params[:max_id].present?, params[:min_id].present?].none?
end
def filtered_pinned_statuses
@account.pinned_statuses.not_local_only.where(visibility: [:public, :unlisted])
end
def filtered_statuses
default_statuses.tap do |statuses|
statuses.merge!(hashtag_scope) if tag_requested?
@ -143,6 +147,13 @@ class AccountsController < ApplicationController
request.path.split('.').first.end_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize)
end
def cached_filtered_status_pins
cache_collection(
filtered_pinned_statuses,
Status
)
end
def cached_filtered_status_page
cache_collection_paginated_by_id(
filtered_statuses,

View File

@ -21,6 +21,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
case params[:id]
when 'featured'
@items = for_signed_account { cache_collection(@account.pinned_statuses.not_local_only, Status) }
@items = @items.map { |item| item.distributable? ? item : ActivityPub::TagManager.instance.uri_for(item) }
when 'tags'
@items = for_signed_account { @account.featured_tags }
when 'devices'

View File

@ -46,9 +46,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
end
def pinned_scope
return Status.none if @account.blocking?(current_account)
@account.pinned_statuses
@account.pinned_statuses.permitted_for(@account, current_account)
end
def no_replies_scope

View File

@ -225,6 +225,7 @@ class StatusActionBar extends ImmutablePureComponent {
const anonymousAccess = !me;
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
const pinnableStatus = ['public', 'unlisted', 'private'].includes(status.get('visibility'));
const mutingConversation = status.get('muted');
const account = status.get('account');
const writtenByMe = status.getIn(['account', 'id']) === me;
@ -242,7 +243,7 @@ class StatusActionBar extends ImmutablePureComponent {
menu.push({ text: intl.formatMessage(status.get('bookmarked') ? messages.removeBookmark : messages.bookmark), action: this.handleBookmarkClick });
if (writtenByMe && publicStatus) {
if (writtenByMe && pinnableStatus) {
menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
}

View File

@ -188,6 +188,7 @@ class ActionBar extends React.PureComponent {
const { status, relationship, intl } = this.props;
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
const pinnableStatus = ['public', 'unlisted', 'private'].includes(status.get('visibility'));
const mutingConversation = status.get('muted');
const account = status.get('account');
const writtenByMe = status.getIn(['account', 'id']) === me;
@ -201,7 +202,7 @@ class ActionBar extends React.PureComponent {
}
if (writtenByMe) {
if (publicStatus) {
if (pinnableStatus) {
menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
menu.push(null);
}

View File

@ -3,7 +3,7 @@
class ActivityPub::Activity::Accept < ActivityPub::Activity
def perform
return accept_follow_for_relay if relay_follow?
return follow_request_from_object.authorize! unless follow_request_from_object.nil?
return accept_follow!(follow_request_from_object) unless follow_request_from_object.nil?
case @object['type']
when 'Follow'
@ -19,7 +19,16 @@ class ActivityPub::Activity::Accept < ActivityPub::Activity
return if target_account.nil? || !target_account.local?
follow_request = FollowRequest.find_by(account: target_account, target_account: @account)
follow_request&.authorize!
accept_follow!(follow_request)
end
def accept_follow!(request)
return if request.nil?
is_first_follow = !request.target_account.followers.local.exists?
request.authorize!
RemoteAccountRefreshWorker.perform_async(request.target_account_id) if is_first_follow
end
def accept_follow_for_relay

View File

@ -4,8 +4,7 @@ class ActivityPub::Activity::Add < ActivityPub::Activity
def perform
return unless @json['target'].present? && value_or_id(@json['target']) == @account.featured_collection_url
status = status_from_uri(object_uri)
status ||= fetch_remote_original_status
status = status_from_object
return unless !status.nil? && status.account_id == @account.id && !@account.pinned?(status)

View File

@ -23,7 +23,7 @@ class ActivityPub::FetchFeaturedCollectionService < BaseService
def process_items(items)
status_ids = items.map { |item| value_or_id(item) }
.filter_map { |uri| ActivityPub::FetchRemoteStatusService.new.call(uri) unless ActivityPub::TagManager.instance.local_uri?(uri) }
.filter_map { |uri| ActivityPub::FetchRemoteStatusService.new.call(uri, on_behalf_of: local_follower) unless ActivityPub::TagManager.instance.local_uri?(uri) }
.filter_map { |status| status.id if status.account_id == @account.id }
to_remove = []
to_add = status_ids
@ -46,4 +46,8 @@ class ActivityPub::FetchFeaturedCollectionService < BaseService
def supported_context?
super(@json)
end
def local_follower
@local_follower ||= account.followers.local.without_suspended.first
end
end

View File

@ -6,7 +6,7 @@ class StatusPinValidator < ActiveModel::Validator
def validate(pin)
pin.errors.add(:base, I18n.t('statuses.pin_errors.reblog')) if pin.status.reblog?
pin.errors.add(:base, I18n.t('statuses.pin_errors.ownership')) if pin.account_id != pin.status.account_id
pin.errors.add(:base, I18n.t('statuses.pin_errors.private')) unless %w(public unlisted).include?(pin.status.visibility)
pin.errors.add(:base, I18n.t('statuses.pin_errors.direct')) if pin.status.direct_visibility?
pin.errors.add(:base, I18n.t('statuses.pin_errors.limit')) if pin.account.status_pins.count >= MAX_PINNED && pin.account.local?
end
end

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
class RemoteAccountRefreshWorker
include Sidekiq::Worker
include ExponentialBackoff
include JsonLdHelper
sidekiq_options queue: 'pull', retry: 3
def perform(id)
account = Account.find_by(id: id)
return if account.nil? || account.local?
ActivityPub::FetchRemoteAccountService.new.call(account.uri)
rescue Mastodon::UnexpectedResponseError => e
response = e.response
if response_error_unsalvageable?(response)
# Give up
else
raise e
end
end
end