Skip to content

Commit

Permalink
Hide blocking accounts from blocked users (mastodon#10442)
Browse files Browse the repository at this point in the history
* Revert "Add indication that you have been blocked in web UI (mastodon#10420)"

This reverts commit bd02ec6.

* Revert "Add `blocked_by` relationship to the REST API (mastodon#10373)"

This reverts commit 9745de8.

* Hide blocking accounts from search results

* Filter blocking accouts from account followers

* Filter blocking accouts from account's following accounts

* Filter blocking accounts from “reblogged by” and “favourited by” lists

* Remove blocking account from URL search

* Return 410 on trying to fetch user data from a user who blocked us

* Return 410 in /api/v1/account/statuses for suspended or blocking accounts

* Fix status filtering when performing URL search

* Restore some React improvements

Restore some cleanup from bd02ec6

* Refactor by adding `without_blocking` scope
  • Loading branch information
ClearlyClaire authored and Gargron committed Apr 1, 2019
1 parent b08dff9 commit 86e2bc4
Show file tree
Hide file tree
Showing 20 changed files with 136 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def load_accounts
end

def default_accounts
Account.includes(:active_relationships, :account_stat).references(:active_relationships)
Account.without_blocking(current_account).includes(:active_relationships, :account_stat).references(:active_relationships)
end

def paginated_follows
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def load_accounts
end

def default_accounts
Account.includes(:passive_relationships, :account_stat).references(:passive_relationships)
Account.without_blocking(current_account).includes(:passive_relationships, :account_stat).references(:passive_relationships)
end

def paginated_follows
Expand Down
10 changes: 10 additions & 0 deletions app/controllers/api/v1/accounts/statuses_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
class Api::V1::Accounts::StatusesController < Api::BaseController
before_action -> { authorize_if_got_token! :read, :'read:statuses' }
before_action :set_account
before_action :check_account_suspension
before_action :check_account_block
after_action :insert_pagination_headers

respond_to :json
Expand All @@ -18,6 +20,14 @@ def set_account
@account = Account.find(params[:account_id])
end

def check_account_suspension
gone if @account.suspended?
end

def check_account_block
gone if current_account.present? && @account.blocking?(current_account)
end

def load_statuses
cached_account_statuses
end
Expand Down
5 changes: 5 additions & 0 deletions app/controllers/api/v1/accounts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class Api::V1::AccountsController < Api::BaseController
before_action :require_user!, except: [:show, :create]
before_action :set_account, except: [:create]
before_action :check_account_suspension, only: [:show]
before_action :check_account_block, only: [:show]
before_action :check_enabled_registrations, only: [:create]

respond_to :json
Expand Down Expand Up @@ -75,6 +76,10 @@ def check_account_suspension
gone if @account.suspended?
end

def check_account_block
gone if current_account.present? && @account.blocking?(current_account)
end

def account_params
params.permit(:username, :email, :password, :agreement, :locale)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def load_accounts

def default_accounts
Account
.without_blocking(current_account)
.includes(:favourites, :account_stat)
.references(:favourites)
.where(favourites: { status_id: @status.id })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def load_accounts
end

def default_accounts
Account.includes(:statuses, :account_stat).references(:statuses)
Account.without_blocking(current_account).includes(:statuses, :account_stat).references(:statuses)
end

def paginated_statuses
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class Header extends ImmutablePureComponent {
} else if (account.getIn(['relationship', 'requested'])) {
actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />;
} else if (!account.getIn(['relationship', 'blocking'])) {
actionBtn = <Button disabled={account.getIn(['relationship', 'blocked_by'])} className={classNames('logo-button', { 'button--destructive': account.getIn(['relationship', 'following']) })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} />;
actionBtn = <Button className={classNames('logo-button', { 'button--destructive': account.getIn(['relationship', 'following']) })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} />;
} else if (account.getIn(['relationship', 'blocking'])) {
actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />;
}
Expand Down
22 changes: 6 additions & 16 deletions app/javascript/mastodon/features/account_timeline/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,14 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { FormattedMessage } from 'react-intl';
import { fetchAccountIdentityProofs } from '../../actions/identity_proofs';

const emptyList = ImmutableList();

const mapStateToProps = (state, { params: { accountId }, withReplies = false }) => {
const path = withReplies ? `${accountId}:with_replies` : accountId;

return {
statusIds: state.getIn(['timelines', `account:${path}`, 'items'], emptyList),
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], emptyList),
statusIds: state.getIn(['timelines', `account:${path}`, 'items'], ImmutableList()),
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], ImmutableList()),
isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']),
hasMore: state.getIn(['timelines', `account:${path}`, 'hasMore']),
blockedBy: state.getIn(['relationships', accountId, 'blocked_by'], false),
hasMore: state.getIn(['timelines', `account:${path}`, 'hasMore']),
};
};

Expand All @@ -40,31 +37,26 @@ class AccountTimeline extends ImmutablePureComponent {
isLoading: PropTypes.bool,
hasMore: PropTypes.bool,
withReplies: PropTypes.bool,
blockedBy: PropTypes.bool,
};

componentWillMount () {
const { params: { accountId }, withReplies } = this.props;

this.props.dispatch(fetchAccount(accountId));
this.props.dispatch(fetchAccountIdentityProofs(accountId));

if (!withReplies) {
this.props.dispatch(expandAccountFeaturedTimeline(accountId));
}

this.props.dispatch(expandAccountTimeline(accountId, { withReplies }));
}

componentWillReceiveProps (nextProps) {
if ((nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) || nextProps.withReplies !== this.props.withReplies) {
this.props.dispatch(fetchAccount(nextProps.params.accountId));
this.props.dispatch(fetchAccountIdentityProofs(nextProps.params.accountId));

if (!nextProps.withReplies) {
this.props.dispatch(expandAccountFeaturedTimeline(nextProps.params.accountId));
}

this.props.dispatch(expandAccountTimeline(nextProps.params.accountId, { withReplies: nextProps.params.withReplies }));
}
}
Expand All @@ -74,7 +66,7 @@ class AccountTimeline extends ImmutablePureComponent {
}

render () {
const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy } = this.props;
const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore } = this.props;

if (!statusIds && isLoading) {
return (
Expand All @@ -84,8 +76,6 @@ class AccountTimeline extends ImmutablePureComponent {
);
}

const emptyMessage = blockedBy ? <FormattedMessage id='empty_column.account_timeline_blocked' defaultMessage='You are blocked' /> : <FormattedMessage id='empty_column.account_timeline' defaultMessage='No toots here!' />;

return (
<Column>
<ColumnBackButton />
Expand All @@ -94,13 +84,13 @@ class AccountTimeline extends ImmutablePureComponent {
prepend={<HeaderContainer accountId={this.props.params.accountId} />}
alwaysPrepend
scrollKey='account_timeline'
statusIds={blockedBy ? emptyList : statusIds}
statusIds={statusIds}
featuredStatusIds={featuredStatusIds}
isLoading={isLoading}
hasMore={hasMore}
onLoadMore={this.handleLoadMore}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
emptyMessage={<FormattedMessage id='empty_column.account_timeline' defaultMessage='No toots here!' />}
/>
</Column>
);
Expand Down
8 changes: 3 additions & 5 deletions app/javascript/mastodon/features/followers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import ScrollableList from '../../components/scrollable_list';
const mapStateToProps = (state, props) => ({
accountIds: state.getIn(['user_lists', 'followers', props.params.accountId, 'items']),
hasMore: !!state.getIn(['user_lists', 'followers', props.params.accountId, 'next']),
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
});

export default @connect(mapStateToProps)
Expand All @@ -32,7 +31,6 @@ class Followers extends ImmutablePureComponent {
shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list,
hasMore: PropTypes.bool,
blockedBy: PropTypes.bool,
};

componentWillMount () {
Expand All @@ -52,7 +50,7 @@ class Followers extends ImmutablePureComponent {
}, 300, { leading: true });

render () {
const { shouldUpdateScroll, accountIds, hasMore, blockedBy } = this.props;
const { shouldUpdateScroll, accountIds, hasMore } = this.props;

if (!accountIds) {
return (
Expand All @@ -62,7 +60,7 @@ class Followers extends ImmutablePureComponent {
);
}

const emptyMessage = blockedBy ? <FormattedMessage id='empty_column.account_timeline_blocked' defaultMessage='You are blocked' /> : <FormattedMessage id='account.followers.empty' defaultMessage='No one follows this user yet.' />;
const emptyMessage = <FormattedMessage id='account.followers.empty' defaultMessage='No one follows this user yet.' />;

return (
<Column>
Expand All @@ -77,7 +75,7 @@ class Followers extends ImmutablePureComponent {
alwaysPrepend
emptyMessage={emptyMessage}
>
{blockedBy ? [] : accountIds.map(id =>
{accountIds.map(id =>
<AccountContainer key={id} id={id} withNote={false} />
)}
</ScrollableList>
Expand Down
8 changes: 3 additions & 5 deletions app/javascript/mastodon/features/following/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import ScrollableList from '../../components/scrollable_list';
const mapStateToProps = (state, props) => ({
accountIds: state.getIn(['user_lists', 'following', props.params.accountId, 'items']),
hasMore: !!state.getIn(['user_lists', 'following', props.params.accountId, 'next']),
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
});

export default @connect(mapStateToProps)
Expand All @@ -32,7 +31,6 @@ class Following extends ImmutablePureComponent {
shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list,
hasMore: PropTypes.bool,
blockedBy: PropTypes.bool,
};

componentWillMount () {
Expand All @@ -52,7 +50,7 @@ class Following extends ImmutablePureComponent {
}, 300, { leading: true });

render () {
const { shouldUpdateScroll, accountIds, hasMore, blockedBy } = this.props;
const { shouldUpdateScroll, accountIds, hasMore } = this.props;

if (!accountIds) {
return (
Expand All @@ -62,7 +60,7 @@ class Following extends ImmutablePureComponent {
);
}

const emptyMessage = blockedBy ? <FormattedMessage id='empty_column.account_timeline_blocked' defaultMessage='You are blocked' /> : <FormattedMessage id='account.follows.empty' defaultMessage="This user doesn't follow anyone yet." />;
const emptyMessage = <FormattedMessage id='account.follows.empty' defaultMessage="This user doesn't follow anyone yet." />;

return (
<Column>
Expand All @@ -77,7 +75,7 @@ class Following extends ImmutablePureComponent {
alwaysPrepend
emptyMessage={emptyMessage}
>
{blockedBy ? [] : accountIds.map(id =>
{accountIds.map(id =>
<AccountContainer key={id} id={id} withNote={false} />
)}
</ScrollableList>
Expand Down
6 changes: 3 additions & 3 deletions app/javascript/styles/mastodon/stream_entries.scss
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@
}
}

&:active:not(:disabled),
&:focus:not(:disabled),
&:hover:not(:disabled) {
&:active,
&:focus,
&:hover {
background: lighten($ui-highlight-color, 10%);

svg path:last-child {
Expand Down
1 change: 1 addition & 0 deletions app/models/account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class Account < ApplicationRecord
scope :tagged_with, ->(tag) { joins(:accounts_tags).where(accounts_tags: { tag_id: tag }) }
scope :by_recent_status, -> { order(Arel.sql('(case when account_stats.last_status_at is null then 1 else 0 end) asc, account_stats.last_status_at desc')) }
scope :popular, -> { order('account_stats.followers_count desc') }
scope :without_blocking, ->(account) { account.nil? ? all : where.not(id: Block.where(target_account_id: account.id).pluck(&:account_id)) }

delegate :email,
:unconfirmed_email,
Expand Down
6 changes: 1 addition & 5 deletions app/presenters/account_relationships_presenter.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

class AccountRelationshipsPresenter
attr_reader :following, :followed_by, :blocking, :blocked_by,
attr_reader :following, :followed_by, :blocking,
:muting, :requested, :domain_blocking,
:endorsed

Expand All @@ -12,7 +12,6 @@ def initialize(account_ids, current_account_id, **options)
@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))
@blocked_by = cached[:blocked_by].merge(Account.blocked_by_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))
Expand All @@ -23,7 +22,6 @@ def initialize(account_ids, current_account_id, **options)
@following.merge!(options[:following_map] || {})
@followed_by.merge!(options[:followed_by_map] || {})
@blocking.merge!(options[:blocking_map] || {})
@blocked_by.merge!(options[:blocked_by_map] || {})
@muting.merge!(options[:muting_map] || {})
@requested.merge!(options[:requested_map] || {})
@domain_blocking.merge!(options[:domain_blocking_map] || {})
Expand All @@ -39,7 +37,6 @@ def cached
following: {},
followed_by: {},
blocking: {},
blocked_by: {},
muting: {},
requested: {},
domain_blocking: {},
Expand Down Expand Up @@ -67,7 +64,6 @@ def cache_uncached!
following: { account_id => following[account_id] },
followed_by: { account_id => followed_by[account_id] },
blocking: { account_id => blocking[account_id] },
blocked_by: { account_id => blocked_by[account_id] },
muting: { account_id => muting[account_id] },
requested: { account_id => requested[account_id] },
domain_blocking: { account_id => domain_blocking[account_id] },
Expand Down
6 changes: 1 addition & 5 deletions app/serializers/rest/relationship_serializer.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

class REST::RelationshipSerializer < ActiveModel::Serializer
attributes :id, :following, :showing_reblogs, :followed_by, :blocking, :blocked_by,
attributes :id, :following, :showing_reblogs, :followed_by, :blocking,
:muting, :muting_notifications, :requested, :domain_blocking,
:endorsed

Expand All @@ -27,10 +27,6 @@ def blocking
instance_options[:relationships].blocking[object.id] || false
end

def blocked_by
instance_options[:relationships].blocked_by[object.id] || false
end

def muting
instance_options[:relationships].muting[object.id] ? true : false
end
Expand Down
10 changes: 9 additions & 1 deletion app/services/account_search_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ def call(query, account = nil, options = {})
@options = options
@account = account

search_service_results
results = search_service_results

unless account.nil?
account_ids = results.map(&:id)
blocked_by_map = Account.blocked_by_map(account_ids, account.id)
results.reject! { |item| blocked_by_map[item.id] }
end

results
end

private
Expand Down
2 changes: 2 additions & 0 deletions app/services/search_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ def call(query, account, limit, options = {})
default_results.tap do |results|
if url_query?
results.merge!(url_resource_results) unless url_resource.nil?
results[:accounts].reject! { |item| item.blocking?(@account) }
results[:statuses].reject! { |status| StatusFilter.new(status, @account).filtered? }
elsif @query.present?
results[:accounts] = perform_accounts_search! if account_searchable?
results[:statuses] = perform_statuses_search! if full_text_searchable?
Expand Down
Loading

0 comments on commit 86e2bc4

Please sign in to comment.