Improve blocked view of profiles (#10491)

* Revert "Fix filtering of favourited_by, reblogged_by, followers and following (#10447)"

This reverts commit 0317f37c6f.

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

This reverts commit 4cd944d364.

* 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
th-downstream
Eugen Rochko 6 years ago committed by GitHub
parent 1ed2c9c16e
commit c84c30c542

@ -19,13 +19,17 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
end end
def load_accounts 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 default_accounts.merge(paginated_follows).to_a
end end
def hide_results?
(@account.user_hides_network? && current_account.id != @account.id) || (current_account && @account.blocking?(current_account))
end
def default_accounts 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 end
def paginated_follows def paginated_follows

@ -19,13 +19,17 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
end end
def load_accounts 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 default_accounts.merge(paginated_follows).to_a
end end
def hide_results?
(@account.user_hides_network? && current_account.id != @account.id) || (current_account && @account.blocking?(current_account))
end
def default_accounts 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 end
def paginated_follows def paginated_follows

@ -3,8 +3,6 @@
class Api::V1::Accounts::StatusesController < Api::BaseController class Api::V1::Accounts::StatusesController < Api::BaseController
before_action -> { authorize_if_got_token! :read, :'read:statuses' } before_action -> { authorize_if_got_token! :read, :'read:statuses' }
before_action :set_account before_action :set_account
before_action :check_account_suspension
before_action :check_account_block
after_action :insert_pagination_headers after_action :insert_pagination_headers
respond_to :json respond_to :json
@ -20,14 +18,6 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
@account = Account.find(params[:account_id]) @account = Account.find(params[:account_id])
end 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 def load_statuses
cached_account_statuses cached_account_statuses
end end

@ -10,7 +10,6 @@ class Api::V1::AccountsController < Api::BaseController
before_action :require_user!, except: [:show, :create] before_action :require_user!, except: [:show, :create]
before_action :set_account, except: [:create] before_action :set_account, except: [:create]
before_action :check_account_suspension, only: [:show] before_action :check_account_suspension, only: [:show]
before_action :check_account_block, only: [:show]
before_action :check_enabled_registrations, only: [:create] before_action :check_enabled_registrations, only: [:create]
respond_to :json respond_to :json
@ -76,10 +75,6 @@ class Api::V1::AccountsController < Api::BaseController
gone if @account.suspended? gone if @account.suspended?
end end
def check_account_block
gone if current_account.present? && @account.blocking?(current_account)
end
def account_params def account_params
params.permit(:username, :email, :password, :agreement, :locale) params.permit(:username, :email, :password, :agreement, :locale)
end end

@ -22,7 +22,6 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
def default_accounts def default_accounts
Account Account
.without_blocking(current_account)
.includes(:favourites, :account_stat) .includes(:favourites, :account_stat)
.references(:favourites) .references(:favourites)
.where(favourites: { status_id: @status.id }) .where(favourites: { status_id: @status.id })

@ -21,7 +21,7 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
end end
def default_accounts def default_accounts
Account.without_blocking(current_account).includes(:statuses, :account_stat).references(:statuses) Account.includes(:statuses, :account_stat).references(:statuses)
end end
def paginated_statuses def paginated_statuses

@ -23,7 +23,7 @@ module StreamEntriesHelper
safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.unfollow')]) safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.unfollow')])
end end
elsif !(account.memorial? || account.moved?) 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')]) safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.follow')])
end end
end end

@ -109,7 +109,7 @@ class Header extends ImmutablePureComponent {
} else if (account.getIn(['relationship', 'requested'])) { } else if (account.getIn(['relationship', 'requested'])) {
actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />; actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />;
} else if (!account.getIn(['relationship', 'blocking'])) { } 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'])) { } else if (account.getIn(['relationship', 'blocking'])) {
actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />; actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />;
} }

@ -14,14 +14,17 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { fetchAccountIdentityProofs } from '../../actions/identity_proofs'; import { fetchAccountIdentityProofs } from '../../actions/identity_proofs';
const emptyList = ImmutableList();
const mapStateToProps = (state, { params: { accountId }, withReplies = false }) => { const mapStateToProps = (state, { params: { accountId }, withReplies = false }) => {
const path = withReplies ? `${accountId}:with_replies` : accountId; const path = withReplies ? `${accountId}:with_replies` : accountId;
return { return {
statusIds: state.getIn(['timelines', `account:${path}`, 'items'], ImmutableList()), statusIds: state.getIn(['timelines', `account:${path}`, 'items'], emptyList),
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], ImmutableList()), featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], emptyList),
isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']), 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),
}; };
}; };
@ -37,6 +40,7 @@ class AccountTimeline extends ImmutablePureComponent {
isLoading: PropTypes.bool, isLoading: PropTypes.bool,
hasMore: PropTypes.bool, hasMore: PropTypes.bool,
withReplies: PropTypes.bool, withReplies: PropTypes.bool,
blockedBy: PropTypes.bool,
}; };
componentWillMount () { componentWillMount () {
@ -44,9 +48,11 @@ class AccountTimeline extends ImmutablePureComponent {
this.props.dispatch(fetchAccount(accountId)); this.props.dispatch(fetchAccount(accountId));
this.props.dispatch(fetchAccountIdentityProofs(accountId)); this.props.dispatch(fetchAccountIdentityProofs(accountId));
if (!withReplies) { if (!withReplies) {
this.props.dispatch(expandAccountFeaturedTimeline(accountId)); this.props.dispatch(expandAccountFeaturedTimeline(accountId));
} }
this.props.dispatch(expandAccountTimeline(accountId, { withReplies })); this.props.dispatch(expandAccountTimeline(accountId, { withReplies }));
} }
@ -54,9 +60,11 @@ class AccountTimeline extends ImmutablePureComponent {
if ((nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) || nextProps.withReplies !== this.props.withReplies) { 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(fetchAccount(nextProps.params.accountId));
this.props.dispatch(fetchAccountIdentityProofs(nextProps.params.accountId)); this.props.dispatch(fetchAccountIdentityProofs(nextProps.params.accountId));
if (!nextProps.withReplies) { if (!nextProps.withReplies) {
this.props.dispatch(expandAccountFeaturedTimeline(nextProps.params.accountId)); this.props.dispatch(expandAccountFeaturedTimeline(nextProps.params.accountId));
} }
this.props.dispatch(expandAccountTimeline(nextProps.params.accountId, { withReplies: nextProps.params.withReplies })); this.props.dispatch(expandAccountTimeline(nextProps.params.accountId, { withReplies: nextProps.params.withReplies }));
} }
} }
@ -66,7 +74,7 @@ class AccountTimeline extends ImmutablePureComponent {
} }
render () { render () {
const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore } = this.props; const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy } = this.props;
if (!statusIds && isLoading) { if (!statusIds && isLoading) {
return ( return (
@ -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 ( return (
<Column> <Column>
<ColumnBackButton /> <ColumnBackButton />
@ -84,13 +94,13 @@ class AccountTimeline extends ImmutablePureComponent {
prepend={<HeaderContainer accountId={this.props.params.accountId} />} prepend={<HeaderContainer accountId={this.props.params.accountId} />}
alwaysPrepend alwaysPrepend
scrollKey='account_timeline' scrollKey='account_timeline'
statusIds={statusIds} statusIds={blockedBy ? emptyList : statusIds}
featuredStatusIds={featuredStatusIds} featuredStatusIds={featuredStatusIds}
isLoading={isLoading} isLoading={isLoading}
hasMore={hasMore} hasMore={hasMore}
onLoadMore={this.handleLoadMore} onLoadMore={this.handleLoadMore}
shouldUpdateScroll={shouldUpdateScroll} shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={<FormattedMessage id='empty_column.account_timeline' defaultMessage='No toots here!' />} emptyMessage={emptyMessage}
/> />
</Column> </Column>
); );

@ -20,6 +20,7 @@ import ScrollableList from '../../components/scrollable_list';
const mapStateToProps = (state, props) => ({ const mapStateToProps = (state, props) => ({
accountIds: state.getIn(['user_lists', 'followers', props.params.accountId, 'items']), accountIds: state.getIn(['user_lists', 'followers', props.params.accountId, 'items']),
hasMore: !!state.getIn(['user_lists', 'followers', props.params.accountId, 'next']), hasMore: !!state.getIn(['user_lists', 'followers', props.params.accountId, 'next']),
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
}); });
export default @connect(mapStateToProps) export default @connect(mapStateToProps)
@ -31,6 +32,7 @@ class Followers extends ImmutablePureComponent {
shouldUpdateScroll: PropTypes.func, shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list, accountIds: ImmutablePropTypes.list,
hasMore: PropTypes.bool, hasMore: PropTypes.bool,
blockedBy: PropTypes.bool,
}; };
componentWillMount () { componentWillMount () {
@ -50,7 +52,7 @@ class Followers extends ImmutablePureComponent {
}, 300, { leading: true }); }, 300, { leading: true });
render () { render () {
const { shouldUpdateScroll, accountIds, hasMore } = this.props; const { shouldUpdateScroll, accountIds, hasMore, blockedBy } = this.props;
if (!accountIds) { if (!accountIds) {
return ( return (
@ -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 ( return (
<Column> <Column>
@ -75,7 +77,7 @@ class Followers extends ImmutablePureComponent {
alwaysPrepend alwaysPrepend
emptyMessage={emptyMessage} emptyMessage={emptyMessage}
> >
{accountIds.map(id => {blockedBy ? [] : accountIds.map(id =>
<AccountContainer key={id} id={id} withNote={false} /> <AccountContainer key={id} id={id} withNote={false} />
)} )}
</ScrollableList> </ScrollableList>

@ -20,6 +20,7 @@ import ScrollableList from '../../components/scrollable_list';
const mapStateToProps = (state, props) => ({ const mapStateToProps = (state, props) => ({
accountIds: state.getIn(['user_lists', 'following', props.params.accountId, 'items']), accountIds: state.getIn(['user_lists', 'following', props.params.accountId, 'items']),
hasMore: !!state.getIn(['user_lists', 'following', props.params.accountId, 'next']), hasMore: !!state.getIn(['user_lists', 'following', props.params.accountId, 'next']),
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
}); });
export default @connect(mapStateToProps) export default @connect(mapStateToProps)
@ -31,6 +32,7 @@ class Following extends ImmutablePureComponent {
shouldUpdateScroll: PropTypes.func, shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list, accountIds: ImmutablePropTypes.list,
hasMore: PropTypes.bool, hasMore: PropTypes.bool,
blockedBy: PropTypes.bool,
}; };
componentWillMount () { componentWillMount () {
@ -50,7 +52,7 @@ class Following extends ImmutablePureComponent {
}, 300, { leading: true }); }, 300, { leading: true });
render () { render () {
const { shouldUpdateScroll, accountIds, hasMore } = this.props; const { shouldUpdateScroll, accountIds, hasMore, blockedBy } = this.props;
if (!accountIds) { if (!accountIds) {
return ( return (
@ -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 ( return (
<Column> <Column>
@ -75,7 +77,7 @@ class Following extends ImmutablePureComponent {
alwaysPrepend alwaysPrepend
emptyMessage={emptyMessage} emptyMessage={emptyMessage}
> >
{accountIds.map(id => {blockedBy ? [] : accountIds.map(id =>
<AccountContainer key={id} id={id} withNote={false} /> <AccountContainer key={id} id={id} withNote={false} />
)} )}
</ScrollableList> </ScrollableList>

@ -46,7 +46,8 @@
} }
} }
&:disabled { &:disabled,
&.disabled {
background-color: $ui-primary-color; background-color: $ui-primary-color;
cursor: default; cursor: default;
} }

@ -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 { &.button--destructive {
&:active, &:active,
&:focus, &:focus,

@ -98,7 +98,6 @@ class Account < ApplicationRecord
scope :tagged_with, ->(tag) { joins(:accounts_tags).where(accounts_tags: { tag_id: tag }) } 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 :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 :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, delegate :email,
:unconfirmed_email, :unconfirmed_email,

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class AccountRelationshipsPresenter class AccountRelationshipsPresenter
attr_reader :following, :followed_by, :blocking, attr_reader :following, :followed_by, :blocking, :blocked_by,
:muting, :requested, :domain_blocking, :muting, :requested, :domain_blocking,
:endorsed :endorsed
@ -12,6 +12,7 @@ class AccountRelationshipsPresenter
@following = cached[:following].merge(Account.following_map(@uncached_account_ids, @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)) @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)) @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)) @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)) @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)) @domain_blocking = cached[:domain_blocking].merge(Account.domain_blocking_map(@uncached_account_ids, @current_account_id))
@ -22,6 +23,7 @@ class AccountRelationshipsPresenter
@following.merge!(options[:following_map] || {}) @following.merge!(options[:following_map] || {})
@followed_by.merge!(options[:followed_by_map] || {}) @followed_by.merge!(options[:followed_by_map] || {})
@blocking.merge!(options[:blocking_map] || {}) @blocking.merge!(options[:blocking_map] || {})
@blocked_by.merge!(options[:blocked_by_map] || {})
@muting.merge!(options[:muting_map] || {}) @muting.merge!(options[:muting_map] || {})
@requested.merge!(options[:requested_map] || {}) @requested.merge!(options[:requested_map] || {})
@domain_blocking.merge!(options[:domain_blocking_map] || {}) @domain_blocking.merge!(options[:domain_blocking_map] || {})
@ -37,6 +39,7 @@ class AccountRelationshipsPresenter
following: {}, following: {},
followed_by: {}, followed_by: {},
blocking: {}, blocking: {},
blocked_by: {},
muting: {}, muting: {},
requested: {}, requested: {},
domain_blocking: {}, domain_blocking: {},
@ -64,6 +67,7 @@ class AccountRelationshipsPresenter
following: { account_id => following[account_id] }, following: { account_id => following[account_id] },
followed_by: { account_id => followed_by[account_id] }, followed_by: { account_id => followed_by[account_id] },
blocking: { account_id => blocking[account_id] }, blocking: { account_id => blocking[account_id] },
blocked_by: { account_id => blocked_by[account_id] },
muting: { account_id => muting[account_id] }, muting: { account_id => muting[account_id] },
requested: { account_id => requested[account_id] }, requested: { account_id => requested[account_id] },
domain_blocking: { account_id => domain_blocking[account_id] }, domain_blocking: { account_id => domain_blocking[account_id] },

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class REST::RelationshipSerializer < ActiveModel::Serializer 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, :muting, :muting_notifications, :requested, :domain_blocking,
:endorsed :endorsed
@ -27,6 +27,10 @@ class REST::RelationshipSerializer < ActiveModel::Serializer
instance_options[:relationships].blocking[object.id] || false instance_options[:relationships].blocking[object.id] || false
end end
def blocked_by
instance_options[:relationships].blocked_by[object.id] || false
end
def muting def muting
instance_options[:relationships].muting[object.id] ? true : false instance_options[:relationships].muting[object.id] ? true : false
end end

@ -10,15 +10,7 @@ class AccountSearchService < BaseService
@options = options @options = options
@account = account @account = account
results = search_service_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 end
private private

@ -12,8 +12,6 @@ class SearchService < BaseService
default_results.tap do |results| default_results.tap do |results|
if url_query? if url_query?
results.merge!(url_resource_results) unless url_resource.nil? 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? elsif @query.present?
results[:accounts] = perform_accounts_search! if account_searchable? results[:accounts] = perform_accounts_search! if account_searchable?
results[:statuses] = perform_statuses_search! if full_text_searchable? results[:statuses] = perform_statuses_search! if full_text_searchable?

@ -33,7 +33,9 @@
= active_link_to t('accounts.posts_with_replies'), short_account_with_replies_url(@account) = active_link_to t('accounts.posts_with_replies'), short_account_with_replies_url(@account)
= active_link_to t('accounts.media'), short_account_media_url(@account) = active_link_to t('accounts.media'), short_account_media_url(@account)
- if @statuses.empty? - if user_signed_in? && @account.blocking?(current_account)
.nothing-here.nothing-here--under-tabs= t('accounts.unavailable')
- elsif @statuses.empty?
= nothing_here 'nothing-here--under-tabs' = nothing_here 'nothing-here--under-tabs'
- else - else
.activity-stream .activity-stream

@ -9,6 +9,8 @@
- if @account.user_hides_network? - if @account.user_hides_network?
.nothing-here= t('accounts.network_hidden') .nothing-here= t('accounts.network_hidden')
- elsif user_signed_in? && @account.blocking?(current_account)
.nothing-here= t('accounts.unavailable')
- elsif @follows.empty? - elsif @follows.empty?
= nothing_here = nothing_here
- else - else

@ -9,6 +9,8 @@
- if @account.user_hides_network? - if @account.user_hides_network?
.nothing-here= t('accounts.network_hidden') .nothing-here= t('accounts.network_hidden')
- elsif user_signed_in? && @account.blocking?(current_account)
.nothing-here= t('accounts.unavailable')
- elsif @follows.empty? - elsif @follows.empty?
= nothing_here = nothing_here
- else - else

@ -68,6 +68,7 @@ en:
admin: Admin admin: Admin
bot: Bot bot: Bot
moderator: Mod moderator: Mod
unavailable: Profile unavailable
unfollow: Unfollow unfollow: Unfollow
admin: admin:
account_actions: account_actions:

@ -7,40 +7,15 @@ describe Api::V1::Accounts::FollowerAccountsController do
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') } let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
before do before do
Fabricate(:follow, target_account: user.account)
allow(controller).to receive(:doorkeeper_token) { token } allow(controller).to receive(:doorkeeper_token) { token }
end end
describe 'GET #index' do describe 'GET #index' do
let(:simon) { Fabricate(:account, username: 'simon') }
let(:lewis) { Fabricate(:account, username: 'lewis') }
before do
simon.follow!(lewis)
end
it 'returns http success' do it 'returns http success' do
get :index, params: { account_id: lewis.id, limit: 1 } get :index, params: { account_id: user.account.id, limit: 1 }
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end end
it 'returns JSON with correct data' do
get :index, params: { account_id: lewis.id, limit: 1 }
json = body_as_json
expect(json).to be_a Enumerable
expect(json.first[:username]).to eq 'simon'
end
it 'does not return accounts blocking you' do
simon.block!(user.account)
get :index, params: { account_id: lewis.id, limit: 1 }
json = body_as_json
expect(json).to be_a Enumerable
expect(json.size).to eq 0
end
end end
end end

@ -7,40 +7,15 @@ describe Api::V1::Accounts::FollowingAccountsController do
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') } let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
before do before do
Fabricate(:follow, account: user.account)
allow(controller).to receive(:doorkeeper_token) { token } allow(controller).to receive(:doorkeeper_token) { token }
end end
describe 'GET #index' do describe 'GET #index' do
let(:simon) { Fabricate(:account, username: 'simon') }
let(:lewis) { Fabricate(:account, username: 'lewis') }
before do
lewis.follow!(simon)
end
it 'returns http success' do it 'returns http success' do
get :index, params: { account_id: lewis.id, limit: 1 } get :index, params: { account_id: user.account.id, limit: 1 }
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end end
it 'returns JSON with correct data' do
get :index, params: { account_id: lewis.id, limit: 1 }
json = body_as_json
expect(json).to be_a Enumerable
expect(json.first[:username]).to eq 'simon'
end
it 'does not return accounts blocking you' do
simon.block!(user.account)
get :index, params: { account_id: lewis.id, limit: 1 }
json = body_as_json
expect(json).to be_a Enumerable
expect(json.size).to eq 0
end
end end
end end

@ -156,22 +156,5 @@ describe AccountSearchService, type: :service do
expect(results).to eq [] expect(results).to eq []
end end
end end
describe 'should not include accounts blocking the requester' do
let!(:blocked) { Fabricate(:account) }
let!(:blocker) { Fabricate(:account, username: 'exact') }
before do
blocker.block!(blocked)
end
it 'returns the fuzzy match first, and does not return suspended exacts' do
partial = Fabricate(:account, username: 'exactness')
results = subject.call('exact', blocked, limit: 10)
expect(results.size).to eq 1
expect(results).to eq [partial]
end
end
end end
end end

@ -3,8 +3,6 @@
require 'rails_helper' require 'rails_helper'
describe SearchService, type: :service do describe SearchService, type: :service do
let(:current_account) { Fabricate(:user).account }
subject { described_class.new } subject { described_class.new }
describe '#call' do describe '#call' do
@ -12,7 +10,7 @@ describe SearchService, type: :service do
it 'returns empty results without searching' do it 'returns empty results without searching' do
allow(AccountSearchService).to receive(:new) allow(AccountSearchService).to receive(:new)
allow(Tag).to receive(:search_for) allow(Tag).to receive(:search_for)
results = subject.call('', current_account, 10) results = subject.call('', nil, 10)
expect(results).to eq(empty_results) expect(results).to eq(empty_results)
expect(AccountSearchService).not_to have_received(:new) expect(AccountSearchService).not_to have_received(:new)
@ -29,33 +27,33 @@ describe SearchService, type: :service do
it 'returns the empty results' do it 'returns the empty results' do
service = double(call: nil) service = double(call: nil)
allow(ResolveURLService).to receive(:new).and_return(service) allow(ResolveURLService).to receive(:new).and_return(service)
results = subject.call(@query, current_account, 10) results = subject.call(@query, nil, 10)
expect(service).to have_received(:call).with(@query, on_behalf_of: current_account) expect(service).to have_received(:call).with(@query, on_behalf_of: nil)
expect(results).to eq empty_results expect(results).to eq empty_results
end end
end end
context 'that finds an account' do context 'that finds an account' do
it 'includes the account in the results' do it 'includes the account in the results' do
account = Fabricate(:account) account = Account.new
service = double(call: account) service = double(call: account)
allow(ResolveURLService).to receive(:new).and_return(service) allow(ResolveURLService).to receive(:new).and_return(service)
results = subject.call(@query, current_account, 10) results = subject.call(@query, nil, 10)
expect(service).to have_received(:call).with(@query, on_behalf_of: current_account) expect(service).to have_received(:call).with(@query, on_behalf_of: nil)
expect(results).to eq empty_results.merge(accounts: [account]) expect(results).to eq empty_results.merge(accounts: [account])
end end
end end
context 'that finds a status' do context 'that finds a status' do
it 'includes the status in the results' do it 'includes the status in the results' do
status = Fabricate(:status) status = Status.new
service = double(call: status) service = double(call: status)
allow(ResolveURLService).to receive(:new).and_return(service) allow(ResolveURLService).to receive(:new).and_return(service)
results = subject.call(@query, current_account, 10) results = subject.call(@query, nil, 10)
expect(service).to have_received(:call).with(@query, on_behalf_of: current_account) expect(service).to have_received(:call).with(@query, on_behalf_of: nil)
expect(results).to eq empty_results.merge(statuses: [status]) expect(results).to eq empty_results.merge(statuses: [status])
end end
end end
@ -65,12 +63,12 @@ describe SearchService, type: :service do
context 'that matches an account' do context 'that matches an account' do
it 'includes the account in the results' do it 'includes the account in the results' do
query = 'username' query = 'username'
account = Fabricate(:account) account = Account.new
service = double(call: [account]) service = double(call: [account])
allow(AccountSearchService).to receive(:new).and_return(service) allow(AccountSearchService).to receive(:new).and_return(service)
results = subject.call(query, current_account, 10) results = subject.call(query, nil, 10)
expect(service).to have_received(:call).with(query, current_account, limit: 10, offset: 0, resolve: false) expect(service).to have_received(:call).with(query, nil, limit: 10, offset: 0, resolve: false)
expect(results).to eq empty_results.merge(accounts: [account]) expect(results).to eq empty_results.merge(accounts: [account])
end end
end end
@ -81,7 +79,7 @@ describe SearchService, type: :service do
tag = Tag.new tag = Tag.new
allow(Tag).to receive(:search_for).with('tag', 10, 0).and_return([tag]) allow(Tag).to receive(:search_for).with('tag', 10, 0).and_return([tag])
results = subject.call(query, current_account, 10) results = subject.call(query, nil, 10)
expect(Tag).to have_received(:search_for).with('tag', 10, 0) expect(Tag).to have_received(:search_for).with('tag', 10, 0)
expect(results).to eq empty_results.merge(hashtags: [tag]) expect(results).to eq empty_results.merge(hashtags: [tag])
end end
@ -89,7 +87,7 @@ describe SearchService, type: :service do
query = '@username' query = '@username'
allow(Tag).to receive(:search_for) allow(Tag).to receive(:search_for)
results = subject.call(query, current_account, 10) results = subject.call(query, nil, 10)
expect(Tag).not_to have_received(:search_for) expect(Tag).not_to have_received(:search_for)
expect(results).to eq empty_results expect(results).to eq empty_results
end end

Loading…
Cancel
Save