Fix to hide mute and blocked quotes

This commit is contained in:
noellabo 2020-06-06 12:59:45 +09:00
parent e9ee34d3f4
commit 6fd2947b92
22 changed files with 219 additions and 105 deletions

View file

@ -7,8 +7,9 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
after_action :insert_pagination_headers, unless: -> { truthy_param?(:pinned) }
def index
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
@statuses = load_statuses
accountIds = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id), account_relationships: AccountRelationshipsPresenter.new(accountIds, current_user&.account_id)
end
private

View file

@ -6,8 +6,9 @@ class Api::V1::BookmarksController < Api::BaseController
after_action :insert_pagination_headers
def index
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
@statuses = load_statuses
accountIds = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id), account_relationships: AccountRelationshipsPresenter.new(accountIds, current_user&.account_id)
end
private

View file

@ -6,8 +6,9 @@ class Api::V1::FavouritesController < Api::BaseController
after_action :insert_pagination_headers
def index
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
@statuses = load_statuses
accountIds = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id), account_relationships: AccountRelationshipsPresenter.new(accountIds, current_user&.account_id)
end
private

View file

@ -30,8 +30,9 @@ class Api::V1::StatusesController < Api::BaseController
@context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
statuses = [@status] + @context.ancestors + @context.descendants
accountIds = statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
render json: @context, serializer: REST::ContextSerializer, relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id)
render json: @context, serializer: REST::ContextSerializer, relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id), account_relationships: AccountRelationshipsPresenter.new(accountIds, current_user&.account_id)
end
def create

View file

@ -6,11 +6,13 @@ class Api::V1::Timelines::HomeController < Api::BaseController
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
def show
@statuses = load_statuses
@statuses = load_statuses
accountIds = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
render json: @statuses,
each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
account_relationships: AccountRelationshipsPresenter.new(accountIds, current_user&.account_id),
status: account_home_feed.regenerating? ? 206 : 200
end

View file

@ -9,9 +9,12 @@ class Api::V1::Timelines::ListController < Api::BaseController
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
def show
accountIds = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
render json: @statuses,
each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user.account_id)
relationships: StatusRelationshipsPresenter.new(@statuses, current_user.account_id),
account_relationships: AccountRelationshipsPresenter.new(accountIds, current_user&.account_id)
end
private

View file

@ -5,8 +5,10 @@ class Api::V1::Timelines::PublicController < Api::BaseController
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
def show
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
@statuses = load_statuses
accountIds = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id), account_relationships: AccountRelationshipsPresenter.new(accountIds, current_user&.account_id)
end
private

View file

@ -5,8 +5,9 @@ class Api::V1::Timelines::TagController < Api::BaseController
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
def show
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
@statuses = load_statuses
accountIds = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id), account_relationships: AccountRelationshipsPresenter.new(accountIds, current_user&.account_id)
end
private

View file

@ -1,5 +1,7 @@
import { fetchRelationships } from './accounts';
import api, { getLinks } from '../api';
import { importFetchedStatuses } from './importer';
import { uniq } from '../utils/uniq';
export const BOOKMARKED_STATUSES_FETCH_REQUEST = 'BOOKMARKED_STATUSES_FETCH_REQUEST';
export const BOOKMARKED_STATUSES_FETCH_SUCCESS = 'BOOKMARKED_STATUSES_FETCH_SUCCESS';
@ -20,6 +22,7 @@ export function fetchBookmarkedStatuses() {
api(getState).get('/api/v1/bookmarks').then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedStatuses(response.data));
dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id))));
dispatch(fetchBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
}).catch(error => {
dispatch(fetchBookmarkedStatusesFail(error));
@ -61,6 +64,7 @@ export function expandBookmarkedStatuses() {
api(getState).get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedStatuses(response.data));
dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id))));
dispatch(expandBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
}).catch(error => {
dispatch(expandBookmarkedStatusesFail(error));

View file

@ -1,5 +1,7 @@
import { fetchRelationships } from './accounts';
import api, { getLinks } from '../api';
import { importFetchedStatuses } from './importer';
import { uniq } from '../utils/uniq';
export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST';
export const FAVOURITED_STATUSES_FETCH_SUCCESS = 'FAVOURITED_STATUSES_FETCH_SUCCESS';
@ -20,6 +22,7 @@ export function fetchFavouritedStatuses() {
api(getState).get('/api/v1/favourites').then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedStatuses(response.data));
dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id))));
dispatch(fetchFavouritedStatusesSuccess(response.data, next ? next.uri : null));
}).catch(error => {
dispatch(fetchFavouritedStatusesFail(error));
@ -64,6 +67,7 @@ export function expandFavouritedStatuses() {
api(getState).get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedStatuses(response.data));
dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id))));
dispatch(expandFavouritedStatusesSuccess(response.data, next ? next.uri : null));
}).catch(error => {
dispatch(expandFavouritedStatusesFail(error));

View file

@ -70,6 +70,10 @@ export function importFetchedStatuses(statuses) {
processStatus(status.reblog);
}
if (status.quote && status.quote.id) {
processStatus(status.quote);
}
if (status.poll && status.poll.id) {
pushUnique(polls, normalizePoll(status.poll));
}

View file

@ -46,7 +46,7 @@ defineMessages({
});
const fetchRelatedRelationships = (dispatch, notifications) => {
const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.account.id);
const accountIds = notifications.map(item => item.account.id);
if (accountIds.length > 0) {
dispatch(fetchRelationships(accountIds));

View file

@ -1,9 +1,11 @@
import { fetchRelationships } from './accounts';
import { importFetchedStatus, importFetchedStatuses } from './importer';
import { submitMarkers } from './markers';
import api, { getLinks } from 'mastodon/api';
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import compareId from 'mastodon/compare_id';
import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
import { uniq } from '../utils/uniq';
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
@ -39,6 +41,7 @@ export function updateTimeline(timeline, status, accept) {
}
dispatch(importFetchedStatus(status));
dispatch(fetchRelationships([status.reblog ? status.reblog.account.id : status.account.id, status.quote ? status.quote.account.id : null].filter(function(e){return e})));
dispatch({
type: TIMELINE_UPDATE,
@ -111,6 +114,7 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
api(getState).get(path, { params }).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedStatuses(response.data));
dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id).concat(response.data.map(item => item.quote ? item.quote.account.id : null)).filter(function(e){return e}))));
dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.status === 206, isLoadingRecent, isLoadingMore, isLoadingRecent && preferPendingItems));
if (timelineId === 'home') {

View file

@ -1,4 +1,5 @@
import React from 'react';
import { connect } from 'react-redux';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import Avatar from './avatar';
@ -23,6 +24,29 @@ import PictureInPicturePlaceholder from 'mastodon/components/picture_in_picture_
// to use the progress bar to show download progress
import Bundle from '../features/ui/components/bundle';
const mapStateToProps = (state, props) => {
let status = props.status;
if (status === null) {
return null;
}
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
status = status.get('reblog');
}
if (status.get('quote', null) === null) {
return {
quote_muted: status.get('quote_id', null) ? true : false,
};
}
const id = status.getIn(['quote', 'account', 'id'], null);
return {
quote_muted: id !== null && (state.getIn(['relationships', id, 'muting']) || state.getIn(['relationships', id, 'blocking']) || state.getIn(['relationships', id, 'blocked_by']) || state.getIn(['relationships', id, 'domain_blocking'])) || status.getIn(['quote', 'quote_muted']),
};
};
export const textForScreenReader = (intl, status, rebloggedByText = false) => {
const displayName = status.getIn(['account', 'display_name']);
@ -59,7 +83,8 @@ const messages = defineMessages({
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
});
export default @injectIntl
export default @connect(mapStateToProps)
@injectIntl
class Status extends ImmutablePureComponent {
static contextTypes = {
@ -70,6 +95,7 @@ class Status extends ImmutablePureComponent {
status: ImmutablePropTypes.map,
account: ImmutablePropTypes.map,
otherAccounts: ImmutablePropTypes.list,
quote_muted: PropTypes.bool,
onClick: PropTypes.func,
onReply: PropTypes.func,
onFavourite: PropTypes.func,
@ -114,6 +140,7 @@ class Status extends ImmutablePureComponent {
'hidden',
'unread',
'pictureInPicture',
'quote_muted',
];
state = {
@ -308,9 +335,9 @@ class Status extends ImmutablePureComponent {
render () {
let media = null;
let statusAvatar, prepend, rebloggedByText, unlistedQuoteText;
let statusAvatar, prepend, rebloggedByText;
const { intl, hidden, featured, otherAccounts, unread, showThread, scrollKey, pictureInPicture, contextType } = this.props;
const { intl, hidden, featured, otherAccounts, unread, showThread, scrollKey, pictureInPicture, contextType, quote_muted } = this.props;
let { status, account, ...other } = this.props;
@ -569,12 +596,21 @@ class Status extends ImmutablePureComponent {
}
}
if (quote_status.get('visibility') === 'unlisted' && contextType !== 'home') {
unlistedQuoteText = intl.formatMessage({ id: 'status.unlisted_quote', defaultMessage: 'Unlisted quote' });
if (quote_muted) {
quote = (
<div className={classNames('quote-status', `status-${quote_status.get('visibility')}`, { muted: this.props.muted })} data-id={quote_status.get('id')}>
<div className={classNames('status__content muted-quote', { 'status__content--with-action': this.context.router })}>
<FormattedMessage id='status.muted_quote' defaultMessage='Muted quote' />
</div>
</div>
);
} else if (quote_status.get('visibility') === 'unlisted' && contextType !== 'home') {
quote = (
<div className={classNames('quote-status', `status-${quote_status.get('visibility')}`, { muted: this.props.muted })} data-id={quote_status.get('id')}>
<div className={classNames('status__content unlisted-quote', { 'status__content--with-action': this.context.router })}>
<button onClick={this.handleQuoteClick}>{unlistedQuoteText}</button>
<button onClick={this.handleQuoteClick}>
<FormattedMessage id='status.unlisted_quote' defaultMessage='Unlisted quote' />
</button>
</div>
</div>
);
@ -592,6 +628,14 @@ class Status extends ImmutablePureComponent {
</div>
);
}
} else if (quote_muted) {
quote = (
<div className={classNames('quote-status', { muted: this.props.muted })}>
<div className={classNames('status__content muted-quote', { 'status__content--with-action': this.context.router })}>
<FormattedMessage id='status.muted_quote' defaultMessage='Muted quote' />
</div>
</div>
);
}
return (

View file

@ -1,4 +1,5 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import Avatar from '../../../components/avatar';
@ -6,7 +7,7 @@ import DisplayName from '../../../components/display_name';
import StatusContent from '../../../components/status_content';
import MediaGallery from '../../../components/media_gallery';
import { Link } from 'react-router-dom';
import { injectIntl, defineMessages, FormattedDate } from 'react-intl';
import { injectIntl, defineMessages, FormattedDate, FormattedMessage } from 'react-intl';
import Card from './card';
import ImmutablePureComponent from 'react-immutable-pure-component';
import Video from '../../video';
@ -24,7 +25,31 @@ const messages = defineMessages({
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
});
export default @injectIntl
const mapStateToProps = (state, props) => {
let status = props.status;
if (status === null) {
return null;
}
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
status = status.get('reblog');
}
if (status.get('quote', null) === null) {
return {
quote_muted: status.get('quote_id', null) ? true : false,
};
}
const id = status.getIn(['quote', 'account', 'id'], null);
return {
quote_muted: id !== null && (state.getIn(['relationships', id, 'muting']) || state.getIn(['relationships', id, 'blocking']) || state.getIn(['relationships', id, 'blocked_by']) || state.getIn(['relationships', id, 'domain_blocking'])) || status.getIn(['quote', 'quote_muted']),
};
};
export default @connect(mapStateToProps)
@injectIntl
class DetailedStatus extends ImmutablePureComponent {
static contextTypes = {
@ -33,6 +58,7 @@ class DetailedStatus extends ImmutablePureComponent {
static propTypes = {
status: ImmutablePropTypes.map,
quote_muted: PropTypes.bool,
onOpenMedia: PropTypes.func.isRequired,
onOpenVideo: PropTypes.func.isRequired,
onToggleHidden: PropTypes.func.isRequired,
@ -120,6 +146,7 @@ class DetailedStatus extends ImmutablePureComponent {
render () {
const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
const quote_muted = this.props.quote_muted
const outerStyle = { boxSizing: 'border-box' };
const { intl, compact, pictureInPicture } = this.props;
@ -194,15 +221,33 @@ class DetailedStatus extends ImmutablePureComponent {
}
}
quote = (
<div className='quote-status'>
<a href={quote_status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
<div className='detailed-status__display-avatar'><Avatar account={quote_status.get('account')} size={18} /></div>
<DisplayName account={quote_status.get('account')} localDomain={this.props.domain} />
</a>
if (quote_muted) {
quote = (
<div className='quote-status'>
<div className='status__content muted-quote'>
<FormattedMessage id='status.muted_quote' defaultMessage='Muted quote' />
</div>
</div>
);
} else {
quote = (
<div className='quote-status'>
<a href={quote_status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
<div className='detailed-status__display-avatar'><Avatar account={quote_status.get('account')} size={18} /></div>
<DisplayName account={quote_status.get('account')} localDomain={this.props.domain} />
</a>
<StatusContent status={quote_status} onClick={this.handleQuoteClick} expanded={!status.get('quote_hidden')} onExpandedToggle={this.handleExpandedQuoteToggle} quote />
{quote_media}
<StatusContent status={quote_status} onClick={this.handleQuoteClick} expanded={!status.get('quote_hidden')} onExpandedToggle={this.handleExpandedQuoteToggle} quote />
{quote_media}
</div>
);
}
} else if (quote_muted) {
quote = (
<div className={classNames('quote-status', { muted: this.props.muted })}>
<div className={classNames('status__content muted-quote', { 'status__content--with-action': this.context.router })}>
<FormattedMessage id='status.muted_quote' defaultMessage='Muted quote' />
</div>
</div>
);
}

View file

@ -457,6 +457,10 @@
"defaultMessage": "Unlisted quote",
"id": "status.unlisted_quote"
},
{
"defaultMessage": "Muted quote",
"id": "status.muted_quote"
},
{
"defaultMessage": "Favourite",
"id": "status.favourite"

View file

@ -401,11 +401,12 @@
"status.more": "More",
"status.mute": "Mute @{name}",
"status.mute_conversation": "Mute conversation",
"status.muted_quote": "Muted quote",
"status.open": "Expand this post",
"status.pin": "Pin on profile",
"status.pinned": "Pinned post",
"status.read_more": "Read more",
"status.quote": "Quote",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
@ -424,6 +425,7 @@
"status.show_poll": "Show poll",
"status.show_thread": "Show thread",
"status.uncached_media_warning": "Not available",
"status.unlisted_quote": "Unlisted quote",
"status.unmute_conversation": "Unmute conversation",
"status.unpin": "Unpin from profile",
"suggestions.dismiss": "Dismiss suggestion",

View file

@ -401,12 +401,12 @@
"status.more": "もっと見る",
"status.mute": "@{name}さんをミュート",
"status.mute_conversation": "会話をミュート",
"status.muted_quote": "ミュートされた引用",
"status.open": "詳細を表示",
"status.pin": "プロフィールに固定表示",
"status.pinned": "固定された投稿",
"status.read_more": "もっと見る",
"status.quote": "引用",
"status.unlisted_quote": "未収載の引用",
"status.read_more": "もっと見る",
"status.reblog": "ブースト",
"status.reblog_private": "ブースト",
"status.reblogged_by": "{name}さんがブースト",
@ -425,6 +425,7 @@
"status.show_poll": "アンケートを表示",
"status.show_thread": "スレッドを表示",
"status.uncached_media_warning": "利用できません",
"status.unlisted_quote": "未収載の引用",
"status.unmute_conversation": "会話のミュートを解除",
"status.unpin": "プロフィールへの固定を解除",
"suggestions.dismiss": "隠す",

View file

@ -88,22 +88,49 @@ export const makeGetStatus = () => {
[
(state, { id }) => state.getIn(['statuses', id]),
(state, { id }) => state.getIn(['statuses', state.getIn(['statuses', id, 'reblog'])]),
(state, { id }) => state.getIn(['statuses', state.getIn(['statuses', id, 'quote_id'])]),
(state, { id }) => state.getIn(['accounts', state.getIn(['statuses', id, 'account'])]),
(state, { id }) => state.getIn(['accounts', state.getIn(['statuses', state.getIn(['statuses', id, 'reblog']), 'account'])]),
(state, { id }) => state.getIn(['accounts', state.getIn(['statuses', state.getIn(['statuses', id, 'quote_id']), 'account'])]),
(state, { id }) => state.getIn(['relationships', state.getIn(['statuses', id, 'account'])]),
(state, { id }) => state.getIn(['relationships', state.getIn(['statuses', state.getIn(['statuses', id, 'reblog']), 'account'])]),
(state, { id }) => state.getIn(['relationships', state.getIn(['statuses', state.getIn(['statuses', id, 'quote_id']), 'account'])]),
(state, { id }) => state.getIn(['accounts', state.getIn(['accounts', state.getIn(['statuses', id, 'account']), 'moved'])]),
(state, { id }) => state.getIn(['accounts', state.getIn(['accounts', state.getIn(['statuses', state.getIn(['statuses', id, 'reblog']), 'account']), 'moved'])]),
(state, { id }) => state.getIn(['accounts', state.getIn(['accounts', state.getIn(['statuses', state.getIn(['statuses', id, 'quote_id']), 'account']), 'moved'])]),
getFiltersRegex,
],
(statusBase, statusReblog, accountBase, accountReblog, filtersRegex) => {
(statusBase, statusReblog, statusQuote, accountBase, accountReblog, accountQuote, relationship, reblogRelationship, quoteRelationship, moved, reblogMoved, quoteMoved, filtersRegex) => {
if (!statusBase) {
return null;
}
accountBase = accountBase.withMutations(map => {
map.set('relationship', relationship);
map.set('moved', moved);
});
if (statusReblog) {
accountReblog = accountReblog.withMutations(map => {
map.set('relationship', reblogRelationship);
map.set('moved', reblogMoved);
});
statusReblog = statusReblog.set('account', accountReblog);
} else {
statusReblog = null;
}
if (statusQuote) {
accountQuote = accountQuote.withMutations(map => {
map.set('relationship', quoteRelationship);
map.set('moved', quoteMoved);
});
statusQuote = statusQuote.set('account', accountQuote);
} else {
statusQuote = null;
}
const dropRegex = (accountReblog || accountBase).get('id') !== me && filtersRegex[0];
if (dropRegex && dropRegex.test(statusBase.get('reblog') ? statusReblog.get('search_index') : statusBase.get('search_index'))) {
return null;
@ -114,6 +141,7 @@ export const makeGetStatus = () => {
return statusBase.withMutations(map => {
map.set('reblog', statusReblog);
map.set('quote', statusQuote);
map.set('account', accountBase);
map.set('filtered', filtered);
});

View file

@ -0,0 +1,3 @@
export const uniq = array => {
return array.filter((x, i, self) => self.indexOf(x) === i)
};

View file

@ -1036,6 +1036,12 @@
}
}
& > .muted-quote {
color: $dark-text-color;
font-weight: 500;
font-size: 100%;
}
.status__avatar,
.detailed-status__display-avatar {
height: 18px;
@ -1531,77 +1537,6 @@ a .account__avatar {
}
}
.account__action-bar {
border-top: 1px solid lighten($ui-base-color, 8%);
border-bottom: 1px solid lighten($ui-base-color, 8%);
line-height: 36px;
overflow: hidden;
flex: 0 0 auto;
display: flex;
}
.account__action-bar-dropdown {
padding: 10px;
.icon-button {
vertical-align: middle;
}
.dropdown--active {
.dropdown__content.dropdown__right {
left: 6px;
right: initial;
}
&::after {
bottom: initial;
margin-left: 11px;
margin-top: -7px;
right: initial;
}
}
}
.account__action-bar-links {
display: flex;
flex: 1 1 auto;
line-height: 18px;
text-align: center;
}
.account__action-bar__tab {
text-decoration: none;
overflow: hidden;
flex: 0 1 100%;
border-right: 1px solid lighten($ui-base-color, 8%);
padding: 10px 0;
border-bottom: 4px solid transparent;
&.active {
border-bottom: 4px solid $ui-highlight-color;
}
& > span {
display: block;
text-transform: uppercase;
font-size: 11px;
color: $darker-text-color;
}
strong {
display: block;
font-size: 15px;
font-weight: 500;
color: $primary-text-color;
@each $lang in $cjk-langs {
&:lang(#{$lang}) {
font-weight: 700;
}
}
}
}
.account-authorize {
padding: 14px 10px;
@ -1688,6 +1623,15 @@ a.account__display-name {
margin-right: 10px;
}
.account__action-bar {
position: absolute;
height: 24px;
width: 48px;
top: 60px;
left: 10px;
z-index: 1;
}
.status__avatar {
height: 48px;
left: 10px;
@ -2456,6 +2400,11 @@ a.account__display-name {
margin-right: 15px;
}
}
.account__action-bar {
top: 67px;
left: 15px;
}
}
}

View file

@ -178,6 +178,16 @@ class REST::NestedQuoteSerializer < REST::StatusSerializer
attribute :quote do
nil
end
attribute :quote_muted, if: :current_user?
def quote_muted
if instance_options && instance_options[:account_relationships]
instance_options[:account_relationships].muting[object.account_id] ? true : false || instance_options[:account_relationships].blocking[object.account_id] || instance_options[:account_relationships].blocked_by[object.account_id] || instance_options[:account_relationships].domain_blocking[object.account_id] || false
else
current_user.account.muting?(object.account) || object.account.blocking?(current_user.account) || current_user.account.blocking?(object.account) || current_user.account.domain_blocking?(object.account.domain)
end
end
end
class REST::StatusSerializer < ActiveModel::Serializer