Fix to hide mute and blocked quotes
This commit is contained in:
parent
e9ee34d3f4
commit
6fd2947b92
22 changed files with 219 additions and 105 deletions
|
@ -8,7 +8,8 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@statuses = load_statuses
|
@statuses = load_statuses
|
||||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -7,7 +7,8 @@ class Api::V1::BookmarksController < Api::BaseController
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@statuses = load_statuses
|
@statuses = load_statuses
|
||||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -7,7 +7,8 @@ class Api::V1::FavouritesController < Api::BaseController
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@statuses = load_statuses
|
@statuses = load_statuses
|
||||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -30,8 +30,9 @@ class Api::V1::StatusesController < Api::BaseController
|
||||||
|
|
||||||
@context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
|
@context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
|
||||||
statuses = [@status] + @context.ancestors + @context.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
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
|
|
@ -7,10 +7,12 @@ class Api::V1::Timelines::HomeController < Api::BaseController
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@statuses = load_statuses
|
@statuses = load_statuses
|
||||||
|
accountIds = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
|
||||||
|
|
||||||
render json: @statuses,
|
render json: @statuses,
|
||||||
each_serializer: REST::StatusSerializer,
|
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),
|
||||||
status: account_home_feed.regenerating? ? 206 : 200
|
status: account_home_feed.regenerating? ? 206 : 200
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,12 @@ class Api::V1::Timelines::ListController < Api::BaseController
|
||||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
accountIds = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
|
||||||
|
|
||||||
render json: @statuses,
|
render json: @statuses,
|
||||||
each_serializer: REST::StatusSerializer,
|
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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -6,7 +6,9 @@ class Api::V1::Timelines::PublicController < Api::BaseController
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@statuses = load_statuses
|
@statuses = load_statuses
|
||||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -6,7 +6,8 @@ class Api::V1::Timelines::TagController < Api::BaseController
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@statuses = load_statuses
|
@statuses = load_statuses
|
||||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
import { fetchRelationships } from './accounts';
|
||||||
import api, { getLinks } from '../api';
|
import api, { getLinks } from '../api';
|
||||||
import { importFetchedStatuses } from './importer';
|
import { importFetchedStatuses } from './importer';
|
||||||
|
import { uniq } from '../utils/uniq';
|
||||||
|
|
||||||
export const BOOKMARKED_STATUSES_FETCH_REQUEST = 'BOOKMARKED_STATUSES_FETCH_REQUEST';
|
export const BOOKMARKED_STATUSES_FETCH_REQUEST = 'BOOKMARKED_STATUSES_FETCH_REQUEST';
|
||||||
export const BOOKMARKED_STATUSES_FETCH_SUCCESS = 'BOOKMARKED_STATUSES_FETCH_SUCCESS';
|
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 => {
|
api(getState).get('/api/v1/bookmarks').then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedStatuses(response.data));
|
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));
|
dispatch(fetchBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(fetchBookmarkedStatusesFail(error));
|
dispatch(fetchBookmarkedStatusesFail(error));
|
||||||
|
@ -61,6 +64,7 @@ export function expandBookmarkedStatuses() {
|
||||||
api(getState).get(url).then(response => {
|
api(getState).get(url).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedStatuses(response.data));
|
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));
|
dispatch(expandBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(expandBookmarkedStatusesFail(error));
|
dispatch(expandBookmarkedStatusesFail(error));
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
import { fetchRelationships } from './accounts';
|
||||||
import api, { getLinks } from '../api';
|
import api, { getLinks } from '../api';
|
||||||
import { importFetchedStatuses } from './importer';
|
import { importFetchedStatuses } from './importer';
|
||||||
|
import { uniq } from '../utils/uniq';
|
||||||
|
|
||||||
export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST';
|
export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST';
|
||||||
export const FAVOURITED_STATUSES_FETCH_SUCCESS = 'FAVOURITED_STATUSES_FETCH_SUCCESS';
|
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 => {
|
api(getState).get('/api/v1/favourites').then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedStatuses(response.data));
|
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));
|
dispatch(fetchFavouritedStatusesSuccess(response.data, next ? next.uri : null));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(fetchFavouritedStatusesFail(error));
|
dispatch(fetchFavouritedStatusesFail(error));
|
||||||
|
@ -64,6 +67,7 @@ export function expandFavouritedStatuses() {
|
||||||
api(getState).get(url).then(response => {
|
api(getState).get(url).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedStatuses(response.data));
|
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));
|
dispatch(expandFavouritedStatusesSuccess(response.data, next ? next.uri : null));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(expandFavouritedStatusesFail(error));
|
dispatch(expandFavouritedStatusesFail(error));
|
||||||
|
|
|
@ -70,6 +70,10 @@ export function importFetchedStatuses(statuses) {
|
||||||
processStatus(status.reblog);
|
processStatus(status.reblog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (status.quote && status.quote.id) {
|
||||||
|
processStatus(status.quote);
|
||||||
|
}
|
||||||
|
|
||||||
if (status.poll && status.poll.id) {
|
if (status.poll && status.poll.id) {
|
||||||
pushUnique(polls, normalizePoll(status.poll));
|
pushUnique(polls, normalizePoll(status.poll));
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ defineMessages({
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchRelatedRelationships = (dispatch, notifications) => {
|
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) {
|
if (accountIds.length > 0) {
|
||||||
dispatch(fetchRelationships(accountIds));
|
dispatch(fetchRelationships(accountIds));
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
import { fetchRelationships } from './accounts';
|
||||||
import { importFetchedStatus, importFetchedStatuses } from './importer';
|
import { importFetchedStatus, importFetchedStatuses } from './importer';
|
||||||
import { submitMarkers } from './markers';
|
import { submitMarkers } from './markers';
|
||||||
import api, { getLinks } from 'mastodon/api';
|
import api, { getLinks } from 'mastodon/api';
|
||||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||||
import compareId from 'mastodon/compare_id';
|
import compareId from 'mastodon/compare_id';
|
||||||
import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
|
import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
|
||||||
|
import { uniq } from '../utils/uniq';
|
||||||
|
|
||||||
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
|
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
|
||||||
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
|
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
|
||||||
|
@ -39,6 +41,7 @@ export function updateTimeline(timeline, status, accept) {
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(importFetchedStatus(status));
|
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({
|
dispatch({
|
||||||
type: TIMELINE_UPDATE,
|
type: TIMELINE_UPDATE,
|
||||||
|
@ -111,6 +114,7 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
|
||||||
api(getState).get(path, { params }).then(response => {
|
api(getState).get(path, { params }).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedStatuses(response.data));
|
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));
|
dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.status === 206, isLoadingRecent, isLoadingMore, isLoadingRecent && preferPendingItems));
|
||||||
|
|
||||||
if (timelineId === 'home') {
|
if (timelineId === 'home') {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Avatar from './avatar';
|
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
|
// to use the progress bar to show download progress
|
||||||
import Bundle from '../features/ui/components/bundle';
|
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) => {
|
export const textForScreenReader = (intl, status, rebloggedByText = false) => {
|
||||||
const displayName = status.getIn(['account', 'display_name']);
|
const displayName = status.getIn(['account', 'display_name']);
|
||||||
|
|
||||||
|
@ -59,7 +83,8 @@ const messages = defineMessages({
|
||||||
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
|
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
|
||||||
});
|
});
|
||||||
|
|
||||||
export default @injectIntl
|
export default @connect(mapStateToProps)
|
||||||
|
@injectIntl
|
||||||
class Status extends ImmutablePureComponent {
|
class Status extends ImmutablePureComponent {
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
|
@ -70,6 +95,7 @@ class Status extends ImmutablePureComponent {
|
||||||
status: ImmutablePropTypes.map,
|
status: ImmutablePropTypes.map,
|
||||||
account: ImmutablePropTypes.map,
|
account: ImmutablePropTypes.map,
|
||||||
otherAccounts: ImmutablePropTypes.list,
|
otherAccounts: ImmutablePropTypes.list,
|
||||||
|
quote_muted: PropTypes.bool,
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
onReply: PropTypes.func,
|
onReply: PropTypes.func,
|
||||||
onFavourite: PropTypes.func,
|
onFavourite: PropTypes.func,
|
||||||
|
@ -114,6 +140,7 @@ class Status extends ImmutablePureComponent {
|
||||||
'hidden',
|
'hidden',
|
||||||
'unread',
|
'unread',
|
||||||
'pictureInPicture',
|
'pictureInPicture',
|
||||||
|
'quote_muted',
|
||||||
];
|
];
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -308,9 +335,9 @@ class Status extends ImmutablePureComponent {
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let media = null;
|
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;
|
let { status, account, ...other } = this.props;
|
||||||
|
|
||||||
|
@ -569,12 +596,21 @@ class Status extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quote_status.get('visibility') === 'unlisted' && contextType !== 'home') {
|
if (quote_muted) {
|
||||||
unlistedQuoteText = intl.formatMessage({ id: 'status.unlisted_quote', defaultMessage: 'Unlisted quote' });
|
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 = (
|
quote = (
|
||||||
<div className={classNames('quote-status', `status-${quote_status.get('visibility')}`, { muted: this.props.muted })} data-id={quote_status.get('id')}>
|
<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 })}>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -592,6 +628,14 @@ class Status extends ImmutablePureComponent {
|
||||||
</div>
|
</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 (
|
return (
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import Avatar from '../../../components/avatar';
|
import Avatar from '../../../components/avatar';
|
||||||
|
@ -6,7 +7,7 @@ import DisplayName from '../../../components/display_name';
|
||||||
import StatusContent from '../../../components/status_content';
|
import StatusContent from '../../../components/status_content';
|
||||||
import MediaGallery from '../../../components/media_gallery';
|
import MediaGallery from '../../../components/media_gallery';
|
||||||
import { Link } from 'react-router-dom';
|
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 Card from './card';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import Video from '../../video';
|
import Video from '../../video';
|
||||||
|
@ -24,7 +25,31 @@ const messages = defineMessages({
|
||||||
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
|
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 {
|
class DetailedStatus extends ImmutablePureComponent {
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
|
@ -33,6 +58,7 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
status: ImmutablePropTypes.map,
|
status: ImmutablePropTypes.map,
|
||||||
|
quote_muted: PropTypes.bool,
|
||||||
onOpenMedia: PropTypes.func.isRequired,
|
onOpenMedia: PropTypes.func.isRequired,
|
||||||
onOpenVideo: PropTypes.func.isRequired,
|
onOpenVideo: PropTypes.func.isRequired,
|
||||||
onToggleHidden: PropTypes.func.isRequired,
|
onToggleHidden: PropTypes.func.isRequired,
|
||||||
|
@ -120,6 +146,7 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
|
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 outerStyle = { boxSizing: 'border-box' };
|
||||||
const { intl, compact, pictureInPicture } = this.props;
|
const { intl, compact, pictureInPicture } = this.props;
|
||||||
|
|
||||||
|
@ -194,6 +221,15 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 = (
|
quote = (
|
||||||
<div className='quote-status'>
|
<div className='quote-status'>
|
||||||
<a href={quote_status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
|
<a href={quote_status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
|
||||||
|
@ -206,6 +242,15 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||||
</div>
|
</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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (pictureInPicture.get('inUse')) {
|
if (pictureInPicture.get('inUse')) {
|
||||||
media = <PictureInPicturePlaceholder />;
|
media = <PictureInPicturePlaceholder />;
|
||||||
|
|
|
@ -457,6 +457,10 @@
|
||||||
"defaultMessage": "Unlisted quote",
|
"defaultMessage": "Unlisted quote",
|
||||||
"id": "status.unlisted_quote"
|
"id": "status.unlisted_quote"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"defaultMessage": "Muted quote",
|
||||||
|
"id": "status.muted_quote"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"defaultMessage": "Favourite",
|
"defaultMessage": "Favourite",
|
||||||
"id": "status.favourite"
|
"id": "status.favourite"
|
||||||
|
|
|
@ -401,11 +401,12 @@
|
||||||
"status.more": "More",
|
"status.more": "More",
|
||||||
"status.mute": "Mute @{name}",
|
"status.mute": "Mute @{name}",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
|
"status.muted_quote": "Muted quote",
|
||||||
"status.open": "Expand this post",
|
"status.open": "Expand this post",
|
||||||
"status.pin": "Pin on profile",
|
"status.pin": "Pin on profile",
|
||||||
"status.pinned": "Pinned post",
|
"status.pinned": "Pinned post",
|
||||||
"status.read_more": "Read more",
|
|
||||||
"status.quote": "Quote",
|
"status.quote": "Quote",
|
||||||
|
"status.read_more": "Read more",
|
||||||
"status.reblog": "Boost",
|
"status.reblog": "Boost",
|
||||||
"status.reblog_private": "Boost with original visibility",
|
"status.reblog_private": "Boost with original visibility",
|
||||||
"status.reblogged_by": "{name} boosted",
|
"status.reblogged_by": "{name} boosted",
|
||||||
|
@ -424,6 +425,7 @@
|
||||||
"status.show_poll": "Show poll",
|
"status.show_poll": "Show poll",
|
||||||
"status.show_thread": "Show thread",
|
"status.show_thread": "Show thread",
|
||||||
"status.uncached_media_warning": "Not available",
|
"status.uncached_media_warning": "Not available",
|
||||||
|
"status.unlisted_quote": "Unlisted quote",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
"status.unpin": "Unpin from profile",
|
"status.unpin": "Unpin from profile",
|
||||||
"suggestions.dismiss": "Dismiss suggestion",
|
"suggestions.dismiss": "Dismiss suggestion",
|
||||||
|
|
|
@ -401,12 +401,12 @@
|
||||||
"status.more": "もっと見る",
|
"status.more": "もっと見る",
|
||||||
"status.mute": "@{name}さんをミュート",
|
"status.mute": "@{name}さんをミュート",
|
||||||
"status.mute_conversation": "会話をミュート",
|
"status.mute_conversation": "会話をミュート",
|
||||||
|
"status.muted_quote": "ミュートされた引用",
|
||||||
"status.open": "詳細を表示",
|
"status.open": "詳細を表示",
|
||||||
"status.pin": "プロフィールに固定表示",
|
"status.pin": "プロフィールに固定表示",
|
||||||
"status.pinned": "固定された投稿",
|
"status.pinned": "固定された投稿",
|
||||||
"status.read_more": "もっと見る",
|
|
||||||
"status.quote": "引用",
|
"status.quote": "引用",
|
||||||
"status.unlisted_quote": "未収載の引用",
|
"status.read_more": "もっと見る",
|
||||||
"status.reblog": "ブースト",
|
"status.reblog": "ブースト",
|
||||||
"status.reblog_private": "ブースト",
|
"status.reblog_private": "ブースト",
|
||||||
"status.reblogged_by": "{name}さんがブースト",
|
"status.reblogged_by": "{name}さんがブースト",
|
||||||
|
@ -425,6 +425,7 @@
|
||||||
"status.show_poll": "アンケートを表示",
|
"status.show_poll": "アンケートを表示",
|
||||||
"status.show_thread": "スレッドを表示",
|
"status.show_thread": "スレッドを表示",
|
||||||
"status.uncached_media_warning": "利用できません",
|
"status.uncached_media_warning": "利用できません",
|
||||||
|
"status.unlisted_quote": "未収載の引用",
|
||||||
"status.unmute_conversation": "会話のミュートを解除",
|
"status.unmute_conversation": "会話のミュートを解除",
|
||||||
"status.unpin": "プロフィールへの固定を解除",
|
"status.unpin": "プロフィールへの固定を解除",
|
||||||
"suggestions.dismiss": "隠す",
|
"suggestions.dismiss": "隠す",
|
||||||
|
|
|
@ -88,22 +88,49 @@ export const makeGetStatus = () => {
|
||||||
[
|
[
|
||||||
(state, { id }) => state.getIn(['statuses', id]),
|
(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, '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', 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, '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,
|
getFiltersRegex,
|
||||||
],
|
],
|
||||||
|
|
||||||
(statusBase, statusReblog, accountBase, accountReblog, filtersRegex) => {
|
(statusBase, statusReblog, statusQuote, accountBase, accountReblog, accountQuote, relationship, reblogRelationship, quoteRelationship, moved, reblogMoved, quoteMoved, filtersRegex) => {
|
||||||
if (!statusBase) {
|
if (!statusBase) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
accountBase = accountBase.withMutations(map => {
|
||||||
|
map.set('relationship', relationship);
|
||||||
|
map.set('moved', moved);
|
||||||
|
});
|
||||||
|
|
||||||
if (statusReblog) {
|
if (statusReblog) {
|
||||||
|
accountReblog = accountReblog.withMutations(map => {
|
||||||
|
map.set('relationship', reblogRelationship);
|
||||||
|
map.set('moved', reblogMoved);
|
||||||
|
});
|
||||||
statusReblog = statusReblog.set('account', accountReblog);
|
statusReblog = statusReblog.set('account', accountReblog);
|
||||||
} else {
|
} else {
|
||||||
statusReblog = null;
|
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];
|
const dropRegex = (accountReblog || accountBase).get('id') !== me && filtersRegex[0];
|
||||||
if (dropRegex && dropRegex.test(statusBase.get('reblog') ? statusReblog.get('search_index') : statusBase.get('search_index'))) {
|
if (dropRegex && dropRegex.test(statusBase.get('reblog') ? statusReblog.get('search_index') : statusBase.get('search_index'))) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -114,6 +141,7 @@ export const makeGetStatus = () => {
|
||||||
|
|
||||||
return statusBase.withMutations(map => {
|
return statusBase.withMutations(map => {
|
||||||
map.set('reblog', statusReblog);
|
map.set('reblog', statusReblog);
|
||||||
|
map.set('quote', statusQuote);
|
||||||
map.set('account', accountBase);
|
map.set('account', accountBase);
|
||||||
map.set('filtered', filtered);
|
map.set('filtered', filtered);
|
||||||
});
|
});
|
||||||
|
|
3
app/javascript/mastodon/utils/uniq.js
Normal file
3
app/javascript/mastodon/utils/uniq.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export const uniq = array => {
|
||||||
|
return array.filter((x, i, self) => self.indexOf(x) === i)
|
||||||
|
};
|
|
@ -1036,6 +1036,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& > .muted-quote {
|
||||||
|
color: $dark-text-color;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.status__avatar,
|
.status__avatar,
|
||||||
.detailed-status__display-avatar {
|
.detailed-status__display-avatar {
|
||||||
height: 18px;
|
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 {
|
.account-authorize {
|
||||||
padding: 14px 10px;
|
padding: 14px 10px;
|
||||||
|
|
||||||
|
@ -1688,6 +1623,15 @@ a.account__display-name {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.account__action-bar {
|
||||||
|
position: absolute;
|
||||||
|
height: 24px;
|
||||||
|
width: 48px;
|
||||||
|
top: 60px;
|
||||||
|
left: 10px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.status__avatar {
|
.status__avatar {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
left: 10px;
|
left: 10px;
|
||||||
|
@ -2456,6 +2400,11 @@ a.account__display-name {
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.account__action-bar {
|
||||||
|
top: 67px;
|
||||||
|
left: 15px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -178,6 +178,16 @@ class REST::NestedQuoteSerializer < REST::StatusSerializer
|
||||||
attribute :quote do
|
attribute :quote do
|
||||||
nil
|
nil
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
class REST::StatusSerializer < ActiveModel::Serializer
|
class REST::StatusSerializer < ActiveModel::Serializer
|
||||||
|
|
Loading…
Reference in a new issue