Skip to content

Commit

Permalink
Improve blocked view of profiles (mastodon#10491)
Browse files Browse the repository at this point in the history
* Revert "Fix filtering of favourited_by, reblogged_by, followers and following (mastodon#10447)"

This reverts commit 1205440.

* Revert "Hide blocking accounts from blocked users (mastodon#10442)"

This reverts commit 62bafa2.

* Improve blocked view of profiles

- Change "You are blocked" to "Profile unavailable"
- Hide following/followers in API when blocked
- Disable follow button and show "Profile unavailable" on public profile as well
  • Loading branch information
Gargron authored Apr 7, 2019
1 parent 0b0d3d2 commit 7c1ea77
Show file tree
Hide file tree
Showing 26 changed files with 97 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,17 @@ def set_account
end

def load_accounts
return [] if @account.user_hides_network? && current_account.id != @account.id
return [] if hide_results?

default_accounts.merge(paginated_follows).to_a
end

def hide_results?
(@account.user_hides_network? && current_account.id != @account.id) || (current_account && @account.blocking?(current_account))
end

def default_accounts
Account.without_blocking(current_account).includes(:active_relationships, :account_stat).references(:active_relationships)
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 @@ -19,13 +19,17 @@ def set_account
end

def load_accounts
return [] if @account.user_hides_network? && current_account.id != @account.id
return [] if hide_results?

default_accounts.merge(paginated_follows).to_a
end

def hide_results?
(@account.user_hides_network? && current_account.id != @account.id) || (current_account && @account.blocking?(current_account))
end

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

def paginated_follows
Expand Down
10 changes: 0 additions & 10 deletions app/controllers/api/v1/accounts/statuses_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
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 @@ -20,14 +18,6 @@ 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: 0 additions & 5 deletions app/controllers/api/v1/accounts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ 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 @@ -76,10 +75,6 @@ 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,7 +22,6 @@ 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.without_blocking(current_account).includes(:statuses, :account_stat).references(:statuses)
Account.includes(:statuses, :account_stat).references(:statuses)
end

def paginated_statuses
Expand Down
2 changes: 1 addition & 1 deletion app/helpers/stream_entries_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def account_action_button(account)
safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.unfollow')])
end
elsif !(account.memorial? || account.moved?)
link_to account_follow_path(account), class: 'button logo-button', data: { method: :post } do
link_to account_follow_path(account), class: "button logo-button#{account.blocking?(current_account) ? ' disabled' : ''}", data: { method: :post } do
safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.follow')])
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,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 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 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} />;
} 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: 16 additions & 6 deletions app/javascript/mastodon/features/account_timeline/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@ 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'], ImmutableList()),
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], ImmutableList()),
statusIds: state.getIn(['timelines', `account:${path}`, 'items'], emptyList),
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], emptyList),
isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']),
hasMore: state.getIn(['timelines', `account:${path}`, 'hasMore']),
hasMore: state.getIn(['timelines', `account:${path}`, 'hasMore']),
blockedBy: state.getIn(['relationships', accountId, 'blocked_by'], false),
};
};

Expand All @@ -37,26 +40,31 @@ 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 @@ -66,7 +74,7 @@ class AccountTimeline extends ImmutablePureComponent {
}

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

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

const emptyMessage = blockedBy ? <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' /> : <FormattedMessage id='empty_column.account_timeline' defaultMessage='No toots here!' />;

return (
<Column>
<ColumnBackButton />
Expand All @@ -84,13 +94,13 @@ class AccountTimeline extends ImmutablePureComponent {
prepend={<HeaderContainer accountId={this.props.params.accountId} />}
alwaysPrepend
scrollKey='account_timeline'
statusIds={statusIds}
statusIds={blockedBy ? emptyList : statusIds}
featuredStatusIds={featuredStatusIds}
isLoading={isLoading}
hasMore={hasMore}
onLoadMore={this.handleLoadMore}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={<FormattedMessage id='empty_column.account_timeline' defaultMessage='No toots here!' />}
emptyMessage={emptyMessage}
/>
</Column>
);
Expand Down
8 changes: 5 additions & 3 deletions app/javascript/mastodon/features/followers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ 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 @@ -31,6 +32,7 @@ class Followers extends ImmutablePureComponent {
shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list,
hasMore: PropTypes.bool,
blockedBy: PropTypes.bool,
};

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

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

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

const emptyMessage = <FormattedMessage id='account.followers.empty' defaultMessage='No one follows this user yet.' />;
const emptyMessage = blockedBy ? <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' /> : <FormattedMessage id='account.followers.empty' defaultMessage='No one follows this user yet.' />;

return (
<Column>
Expand All @@ -75,7 +77,7 @@ class Followers extends ImmutablePureComponent {
alwaysPrepend
emptyMessage={emptyMessage}
>
{accountIds.map(id =>
{blockedBy ? [] : accountIds.map(id =>
<AccountContainer key={id} id={id} withNote={false} />
)}
</ScrollableList>
Expand Down
8 changes: 5 additions & 3 deletions app/javascript/mastodon/features/following/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ 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 @@ -31,6 +32,7 @@ class Following extends ImmutablePureComponent {
shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list,
hasMore: PropTypes.bool,
blockedBy: PropTypes.bool,
};

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

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

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

const emptyMessage = <FormattedMessage id='account.follows.empty' defaultMessage="This user doesn't follow anyone yet." />;
const emptyMessage = blockedBy ? <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' /> : <FormattedMessage id='account.follows.empty' defaultMessage="This user doesn't follow anyone yet." />;

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

&:disabled {
&:disabled,
&.disabled {
background-color: $ui-primary-color;
cursor: default;
}
Expand Down
17 changes: 17 additions & 0 deletions app/javascript/styles/mastodon/stream_entries.scss
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,23 @@
}
}

&:disabled,
&.disabled {
svg path:last-child {
fill: $ui-primary-color;
}

&:active,
&:focus,
&:hover {
background: $ui-primary-color;

svg path:last-child {
fill: $ui-primary-color;
}
}
}

&.button--destructive {
&:active,
&:focus,
Expand Down
1 change: 0 additions & 1 deletion app/models/account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ 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: 5 additions & 1 deletion 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,
attr_reader :following, :followed_by, :blocking, :blocked_by,
:muting, :requested, :domain_blocking,
:endorsed

Expand All @@ -12,6 +12,7 @@ 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 @@ -22,6 +23,7 @@ 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 @@ -37,6 +39,7 @@ def cached
following: {},
followed_by: {},
blocking: {},
blocked_by: {},
muting: {},
requested: {},
domain_blocking: {},
Expand Down Expand Up @@ -64,6 +67,7 @@ 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: 5 additions & 1 deletion 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,
attributes :id, :following, :showing_reblogs, :followed_by, :blocking, :blocked_by,
:muting, :muting_notifications, :requested, :domain_blocking,
:endorsed

Expand All @@ -27,6 +27,10 @@ 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
Loading

0 comments on commit 7c1ea77

Please sign in to comment.