import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
+import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import IconButton from './icon_button';
import DropdownMenuContainer from '../containers/dropdown_menu_container';
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
+ removeBookmark: { id: 'status.remove_bookmark', defaultMessage: 'Remove bookmark' },
open: { id: 'status.open', defaultMessage: 'Expand this status' },
report: { id: 'status.report', defaultMessage: 'Report @{name}' },
muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
copy: { id: 'status.copy', defaultMessage: 'Copy link to status' },
+ blockDomain: { id: 'account.block_domain', defaultMessage: 'Hide everything from {domain}' },
+ unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
+ unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
+ unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
});
const obfuscatedCount = count => {
}
};
-export default @injectIntl
+const mapStateToProps = (state, { status }) => ({
+ relationship: state.getIn(['relationships', status.getIn(['account', 'id'])]),
+});
+
+export default @connect(mapStateToProps)
+@injectIntl
class StatusActionBar extends ImmutablePureComponent {
static contextTypes = {
static propTypes = {
status: ImmutablePropTypes.map.isRequired,
+ relationship: ImmutablePropTypes.map,
onReply: PropTypes.func,
onFavourite: PropTypes.func,
onReblog: PropTypes.func,
onDirect: PropTypes.func,
onMention: PropTypes.func,
onMute: PropTypes.func,
+ onUnmute: PropTypes.func,
onBlock: PropTypes.func,
+ onUnblock: PropTypes.func,
+ onBlockDomain: PropTypes.func,
+ onUnblockDomain: PropTypes.func,
onReport: PropTypes.func,
onEmbed: PropTypes.func,
onMuteConversation: PropTypes.func,
// evaluate to false. See react-immutable-pure-component for usage.
updateOnProps = [
'status',
+ 'relationship',
'withDismiss',
]
}
handleMuteClick = () => {
- this.props.onMute(this.props.status.get('account'));
+ const { status, relationship, onMute, onUnmute } = this.props;
+ const account = status.get('account');
+
+ if (relationship && relationship.get('muting')) {
+ onUnmute(account);
+ } else {
+ onMute(account);
+ }
}
handleBlockClick = () => {
- this.props.onBlock(this.props.status);
+ const { status, relationship, onBlock, onUnblock } = this.props;
+ const account = status.get('account');
+
+ if (relationship && relationship.get('blocking')) {
+ onBlock(status);
+ } else {
+ onUnblock(account);
+ }
+ }
+
+ handleBlockDomain = () => {
+ const { status, onBlockDomain } = this.props;
+ const account = status.get('account');
+
+ onBlockDomain(account.get('acct').split('@')[1]);
+ }
+
+ handleUnblockDomain = () => {
+ const { status, onUnblockDomain } = this.props;
+ const account = status.get('account');
+
+ onUnblockDomain(account.get('acct').split('@')[1]);
}
handleOpen = () => {
}
render () {
- const { status, intl, withDismiss } = this.props;
+ const { status, relationship, intl, withDismiss } = this.props;
const mutingConversation = status.get('muted');
const anonymousAccess = !me;
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
+ const account = status.get('account');
let menu = [];
let reblogIcon = 'retweet';
menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
}
+ menu.push({ text: intl.formatMessage(status.get('bookmarked') ? messages.removeBookmark : messages.bookmark), action: this.handleBookmarkClick });
menu.push(null);
if (status.getIn(['account', 'id']) === me || withDismiss) {
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick });
} else {
- menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
- menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick });
+ menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.handleMentionClick });
+ menu.push({ text: intl.formatMessage(messages.direct, { name: account.get('username') }), action: this.handleDirectClick });
menu.push(null);
- menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
- menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
- menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
+
+ if (relationship && relationship.get('muting')) {
+ menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.handleMuteClick });
+ } else {
+ menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.handleMuteClick });
+ }
+
+ if (relationship && relationship.get('blocking')) {
+ menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.handleBlockClick });
+ } else {
+ menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.handleBlockClick });
+ }
+
+ menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.handleReport });
+
+ if (account.get('acct') !== account.get('username')) {
+ const domain = account.get('acct').split('@')[1];
+
+ menu.push(null);
+
+ if (relationship && relationship.get('domain_blocking')) {
+ menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.handleUnblockDomain });
+ } else {
+ menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.handleBlockDomain });
+ }
+ }
if (isStaff) {
menu.push(null);
- menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
+ menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
}
}
<IconButton className='status__action-bar-button' disabled={!publicStatus} active={status.get('reblogged')} pressed={status.get('reblogged')} title={!publicStatus ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(messages.reblog)} icon={reblogIcon} onClick={this.handleReblogClick} />
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} pressed={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} />
{shareButton}
- <IconButton className='status__action-bar-button bookmark-icon' disabled={anonymousAccess} active={status.get('bookmarked')} pressed={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} />
<div className='status__action-bar-dropdown'>
<DropdownMenuContainer disabled={anonymousAccess} status={status} items={menu} icon='ellipsis-h' size={18} direction='right' title={intl.formatMessage(messages.more)} />
import { openDropdownMenu, closeDropdownMenu } from '../actions/dropdown_menu';
+import { fetchRelationships } from 'mastodon/actions/accounts';
import { openModal, closeModal } from '../actions/modal';
import { connect } from 'react-redux';
import DropdownMenu from '../components/dropdown_menu';
const mapDispatchToProps = (dispatch, { status, items }) => ({
onOpen(id, onItemClick, dropdownPlacement, keyboard) {
+ dispatch(fetchRelationships([status.getIn(['account', 'id'])]));
+
dispatch(isUserTouching() ? openModal('ACTIONS', {
status,
actions: items,
onClick: onItemClick,
}) : openDropdownMenu(id, dropdownPlacement, keyboard));
},
+
onClose(id) {
dispatch(closeModal('ACTIONS'));
dispatch(closeDropdownMenu(id));
+import React from 'react';
import { connect } from 'react-redux';
import Status from '../components/status';
import { makeGetStatus } from '../selectors';
hideStatus,
revealStatus,
} from '../actions/statuses';
+import {
+ unmuteAccount,
+ unblockAccount,
+} from '../actions/accounts';
+import {
+ blockDomain,
+ unblockDomain,
+} from '../actions/domain_blocks';
import { initMuteModal } from '../actions/mutes';
import { initBlockModal } from '../actions/blocks';
import { initReport } from '../actions/reports';
import { openModal } from '../actions/modal';
-import { defineMessages, injectIntl } from 'react-intl';
+import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { boostModal, deleteModal } from '../initial_state';
import { showAlertForError } from '../actions/alerts';
redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
+ blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
});
const makeMapStateToProps = () => {
dispatch(initBlockModal(account));
},
+ onUnblock (account) {
+ dispatch(unblockAccount(account.get('id')));
+ },
+
onReport (status) {
dispatch(initReport(status.get('account'), status));
},
dispatch(initMuteModal(account));
},
+ onUnmute (account) {
+ dispatch(unmuteAccount(account.get('id')));
+ },
+
onMuteConversation (status) {
if (status.get('muted')) {
dispatch(unmuteStatus(status.get('id')));
}
},
+ onBlockDomain (domain) {
+ dispatch(openModal('CONFIRM', {
+ message: <FormattedMessage id='confirmations.domain_block.message' defaultMessage='Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.' values={{ domain: <strong>{domain}</strong> }} />,
+ confirm: intl.formatMessage(messages.blockDomainConfirm),
+ onConfirm: () => dispatch(blockDomain(domain)),
+ }));
+ },
+
+ onUnblockDomain (domain) {
+ dispatch(unblockDomain(domain));
+ },
+
});
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Status));
import React from 'react';
import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
import IconButton from '../../../components/icon_button';
import ImmutablePropTypes from 'react-immutable-proptypes';
import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
copy: { id: 'status.copy', defaultMessage: 'Copy link to status' },
+ blockDomain: { id: 'account.block_domain', defaultMessage: 'Hide everything from {domain}' },
+ unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
+ unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
+ unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
});
-export default @injectIntl
+const mapStateToProps = (state, { status }) => ({
+ relationship: state.getIn(['relationships', status.getIn(['account', 'id'])]),
+});
+
+export default @connect(mapStateToProps)
+@injectIntl
class ActionBar extends React.PureComponent {
static contextTypes = {
static propTypes = {
status: ImmutablePropTypes.map.isRequired,
+ relationship: ImmutablePropTypes.map,
onReply: PropTypes.func.isRequired,
onReblog: PropTypes.func.isRequired,
onFavourite: PropTypes.func.isRequired,
onDirect: PropTypes.func.isRequired,
onMention: PropTypes.func.isRequired,
onMute: PropTypes.func,
- onMuteConversation: PropTypes.func,
+ onUnmute: PropTypes.func,
onBlock: PropTypes.func,
+ onUnblock: PropTypes.func,
+ onBlockDomain: PropTypes.func,
+ onUnblockDomain: PropTypes.func,
+ onMuteConversation: PropTypes.func,
onReport: PropTypes.func,
onPin: PropTypes.func,
onEmbed: PropTypes.func,
}
handleMuteClick = () => {
- this.props.onMute(this.props.status.get('account'));
- }
+ const { status, relationship, onMute, onUnmute } = this.props;
+ const account = status.get('account');
- handleConversationMuteClick = () => {
- this.props.onMuteConversation(this.props.status);
+ if (relationship && relationship.get('muting')) {
+ onUnmute(account);
+ } else {
+ onMute(account);
+ }
}
handleBlockClick = () => {
- this.props.onBlock(this.props.status);
+ const { status, relationship, onBlock, onUnblock } = this.props;
+ const account = status.get('account');
+
+ if (relationship && relationship.get('blocking')) {
+ onBlock(status);
+ } else {
+ onUnblock(account);
+ }
+ }
+
+ handleBlockDomain = () => {
+ const { status, onBlockDomain } = this.props;
+ const account = status.get('account');
+
+ onBlockDomain(account.get('acct').split('@')[1]);
+ }
+
+ handleUnblockDomain = () => {
+ const { status, onUnblockDomain } = this.props;
+ const account = status.get('account');
+
+ onUnblockDomain(account.get('acct').split('@')[1]);
+ }
+
+ handleConversationMuteClick = () => {
+ this.props.onMuteConversation(this.props.status);
}
handleReport = () => {
}
render () {
- const { status, intl } = this.props;
+ const { status, relationship, intl } = this.props;
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
const mutingConversation = status.get('muted');
+ const account = status.get('account');
let menu = [];
menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick });
menu.push(null);
- menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
- menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
+
+ if (relationship && relationship.get('muting')) {
+ menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.handleMuteClick });
+ } else {
+ menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.handleMuteClick });
+ }
+
+ if (relationship && relationship.get('blocking')) {
+ menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.handleBlockClick });
+ } else {
+ menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.handleBlockClick });
+ }
+
menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
+
+ if (account.get('acct') !== account.get('username')) {
+ const domain = account.get('acct').split('@')[1];
+
+ menu.push(null);
+
+ if (relationship && relationship.get('domain_blocking')) {
+ menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.handleUnblockDomain });
+ } else {
+ menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.handleBlockDomain });
+ }
+ }
+
if (isStaff) {
menu.push(null);
menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
<div className='detailed-status__button'><IconButton className='bookmark-icon' active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} /></div>
<div className='detailed-status__action-bar-dropdown'>
- <DropdownMenuContainer size={18} icon='ellipsis-h' items={menu} direction='left' title='More' />
+ <DropdownMenuContainer size={18} icon='ellipsis-h' status={status} items={menu} direction='left' title='More' />
</div>
</div>
);
hideStatus,
revealStatus,
} from '../../actions/statuses';
+import {
+ unblockAccount,
+ unmuteAccount,
+} from '../../actions/accounts';
+import {
+ blockDomain,
+ unblockDomain,
+} from '../../actions/domain_blocks';
import { initMuteModal } from '../../actions/mutes';
import { initBlockModal } from '../../actions/blocks';
import { initReport } from '../../actions/reports';
import ColumnHeader from '../../components/column_header';
import StatusContainer from '../../containers/status_container';
import { openModal } from '../../actions/modal';
-import { defineMessages, injectIntl } from 'react-intl';
+import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { HotKeys } from 'react-hotkeys';
import { boostModal, deleteModal } from '../../initial_state';
detailedStatus: { id: 'status.detailed_status', defaultMessage: 'Detailed conversation view' },
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
+ blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
});
const makeMapStateToProps = () => {
this.props.dispatch(openModal('EMBED', { url: status.get('url') }));
}
+ handleUnmuteClick = account => {
+ this.props.dispatch(unmuteAccount(account.get('id')));
+ }
+
+ handleUnblockClick = account => {
+ this.props.dispatch(unblockAccount(account.get('id')));
+ }
+
+ handleBlockDomainClick = domain => {
+ this.props.dispatch(openModal('CONFIRM', {
+ message: <FormattedMessage id='confirmations.domain_block.message' defaultMessage='Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.' values={{ domain: <strong>{domain}</strong> }} />,
+ confirm: this.props.intl.formatMessage(messages.blockDomainConfirm),
+ onConfirm: () => this.props.dispatch(blockDomain(domain)),
+ }));
+ }
+
+ handleUnblockDomainClick = domain => {
+ this.props.dispatch(unblockDomain(domain));
+ }
+
+
handleHotkeyMoveUp = () => {
this.handleMoveUp(this.props.status.get('id'));
}
onDirect={this.handleDirectClick}
onMention={this.handleMentionClick}
onMute={this.handleMuteClick}
+ onUnmute={this.handleUnmuteClick}
onMuteConversation={this.handleConversationMuteClick}
onBlock={this.handleBlockClick}
+ onUnblock={this.handleUnblockClick}
+ onBlockDomain={this.handleBlockDomainClick}
+ onUnblockDomain={this.handleUnblockDomainClick}
onReport={this.handleReport}
onPin={this.handlePin}
onEmbed={this.handleEmbed}