diff --git a/app/soapbox/actions/moderation.js b/app/soapbox/actions/moderation.js index 645589c51..7edc3e775 100644 --- a/app/soapbox/actions/moderation.js +++ b/app/soapbox/actions/moderation.js @@ -1,6 +1,6 @@ import { defineMessages } from 'react-intl'; import { openModal } from 'soapbox/actions/modal'; -import { deactivateUsers, deleteUsers } from 'soapbox/actions/admin'; +import { deactivateUsers, deleteUsers, deleteStatus } from 'soapbox/actions/admin'; import snackbar from 'soapbox/actions/snackbar'; const messages = defineMessages({ @@ -10,6 +10,9 @@ const messages = defineMessages({ deleteUserPrompt: { id: 'confirmations.admin.delete_user.message', defaultMessage: 'You are about to delete {acct}. THIS IS A DESTRUCTIVE ACTION THAT CANNOT BE UNDONE.' }, deleteUserConfirm: { id: 'confirmations.admin.delete_user.confirm', defaultMessage: 'Delete {acct}' }, userDeleted: { id: 'admin.users.user_deleted_message', defaultMessage: '{acct} was deleted' }, + deleteStatusPrompt: { id: 'confirmations.admin.delete_status.message', defaultMessage: 'You are about to delete a post by {acct}. This action cannot be undone.' }, + deleteStatusConfirm: { id: 'confirmations.admin.delete_status.confirm', defaultMessage: 'Delete post' }, + statusDeleted: { id: 'admin.statuses.status_deleted_message', defaultMessage: 'Post by {acct} was deleted' }, }); export function deactivateUserModal(intl, accountId, afterConfirm = () => {}) { @@ -47,3 +50,23 @@ export function deleteUserModal(intl, accountId, afterConfirm = () => {}) { })); }; } + +export function deleteStatusModal(intl, statusId, afterConfirm = () => {}) { + return function(dispatch, getState) { + const state = getState(); + const accountId = state.getIn(['statuses', statusId, 'account']); + const acct = state.getIn(['accounts', accountId, 'acct']); + + dispatch(openModal('CONFIRM', { + message: intl.formatMessage(messages.deleteStatusPrompt, { acct: `@${acct}` }), + confirm: intl.formatMessage(messages.deleteStatusConfirm), + onConfirm: () => { + dispatch(deleteStatus(statusId)).then(() => { + const message = intl.formatMessage(messages.statusDeleted, { acct: `@${acct}` }); + dispatch(snackbar.success(message)); + }).catch(() => {}); + afterConfirm(); + }, + })); + }; +} diff --git a/app/soapbox/components/status_action_bar.js b/app/soapbox/components/status_action_bar.js index aae4d3f86..5895dde9e 100644 --- a/app/soapbox/components/status_action_bar.js +++ b/app/soapbox/components/status_action_bar.js @@ -47,6 +47,7 @@ const messages = defineMessages({ group_remove_post: { id: 'status.remove_post_from_group', defaultMessage: 'Remove post from group' }, deactivateUser: { id: 'admin.users.actions.deactivate_user', defaultMessage: 'Deactivate {acct}' }, deleteUser: { id: 'admin.users.actions.delete_user', defaultMessage: 'Delete {acct}' }, + deleteStatus: { id: 'admin.statuses.actions.delete_status', defaultMessage: 'Delete post' }, }); class StatusActionBar extends ImmutablePureComponent { @@ -71,6 +72,7 @@ class StatusActionBar extends ImmutablePureComponent { onEmbed: PropTypes.func, onDeactivateUser: PropTypes.func, onDeleteUser: PropTypes.func, + onDeleteStatus: PropTypes.func, onMuteConversation: PropTypes.func, onPin: PropTypes.func, withDismiss: PropTypes.bool, @@ -254,6 +256,10 @@ class StatusActionBar extends ImmutablePureComponent { this.props.onDeleteUser(this.props.status); } + handleDeleteStatus = () => { + this.props.onDeleteStatus(this.props.status); + } + _makeMenu = (publicStatus) => { const { status, intl, withDismiss, withGroupAdmin, me, isStaff } = this.props; const mutingConversation = status.get('muted'); @@ -305,6 +311,7 @@ class StatusActionBar extends ImmutablePureComponent { // menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` }); menu.push({ text: intl.formatMessage(messages.deactivateUser, { acct: `@${status.getIn(['account', 'acct'])}` }), action: this.handleDeactivateUser }); menu.push({ text: intl.formatMessage(messages.deleteUser, { acct: `@${status.getIn(['account', 'acct'])}` }), action: this.handleDeleteUser }); + menu.push({ text: intl.formatMessage(messages.deleteStatus), action: this.handleDeleteStatus }); } if (withGroupAdmin) { diff --git a/app/soapbox/containers/status_container.js b/app/soapbox/containers/status_container.js index f3de638ff..abb0da6c4 100644 --- a/app/soapbox/containers/status_container.js +++ b/app/soapbox/containers/status_container.js @@ -36,7 +36,7 @@ import { } from '../actions/groups'; import { getSettings } from '../actions/settings'; import { getSoapboxConfig } from 'soapbox/actions/soapbox'; -import { deactivateUserModal, deleteUserModal } from 'soapbox/actions/moderation'; +import { deactivateUserModal, deleteUserModal, deleteStatusModal } from 'soapbox/actions/moderation'; const messages = defineMessages({ deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, @@ -217,6 +217,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ dispatch(deleteUserModal(intl, status.getIn(['account', 'id']))); }, + onDeleteStatus(status) { + dispatch(deleteStatusModal(intl, status.get('id'))); + }, + }); export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Status)); diff --git a/app/soapbox/features/admin/components/report_status.js b/app/soapbox/features/admin/components/report_status.js index d98e9c755..63c9a3a66 100644 --- a/app/soapbox/features/admin/components/report_status.js +++ b/app/soapbox/features/admin/components/report_status.js @@ -5,19 +5,15 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import { injectIntl, defineMessages } from 'react-intl'; import StatusContent from 'soapbox/components/status_content'; import DropdownMenu from 'soapbox/containers/dropdown_menu_container'; -import { deleteStatus } from 'soapbox/actions/admin'; -import snackbar from 'soapbox/actions/snackbar'; import { openModal } from 'soapbox/actions/modal'; import noop from 'lodash/noop'; import { MediaGallery, Video, Audio } from 'soapbox/features/ui/util/async-components'; import Bundle from 'soapbox/features/ui/components/bundle'; +import { deleteStatusModal } from 'soapbox/actions/moderation'; const messages = defineMessages({ viewStatus: { id: 'admin.reports.actions.view_status', defaultMessage: 'View post' }, - deleteStatus: { id: 'admin.reports.actions.delete_status', defaultMessage: 'Delete post' }, - deleteStatusPrompt: { id: 'confirmations.admin.delete_status.message', defaultMessage: 'You are about to delete a post by {acct}. This action cannot be undone.' }, - deleteStatusConfirm: { id: 'confirmations.admin.delete_status.confirm', defaultMessage: 'Delete post' }, - statusDeleted: { id: 'admin.reports.status_deleted_message', defaultMessage: 'Post by {acct} was deleted' }, + deleteStatus: { id: 'admin.statuses.actions.delete_status', defaultMessage: 'Delete post' }, }); export default @connect() @@ -104,19 +100,8 @@ class ReportStatus extends ImmutablePureComponent { handleDeleteStatus = () => { const { intl, dispatch, status } = this.props; - const nickname = status.getIn(['account', 'acct']); const statusId = status.get('id'); - dispatch(openModal('CONFIRM', { - message: intl.formatMessage(messages.deleteStatusPrompt, { acct: `@${nickname}` }), - confirm: intl.formatMessage(messages.deleteStatusConfirm), - onConfirm: () => { - dispatch(deleteStatus(statusId)).then(() => { - const message = intl.formatMessage(messages.statusDeleted, { acct: `@${nickname}` }); - dispatch(snackbar.success(message)); - }).catch(() => {}); - this.handleCloseReport(); - }, - })); + dispatch(deleteStatusModal(intl, statusId)); } render() { diff --git a/app/soapbox/features/status/components/action_bar.js b/app/soapbox/features/status/components/action_bar.js index e54e918cf..2e8e6f375 100644 --- a/app/soapbox/features/status/components/action_bar.js +++ b/app/soapbox/features/status/components/action_bar.js @@ -38,6 +38,7 @@ const messages = defineMessages({ unbookmark: { id: 'status.unbookmark', defaultMessage: 'Remove bookmark' }, deactivateUser: { id: 'admin.users.actions.deactivate_user', defaultMessage: 'Deactivate {acct}' }, deleteUser: { id: 'admin.users.actions.delete_user', defaultMessage: 'Delete {acct}' }, + deleteStatus: { id: 'admin.statuses.actions.delete_status', defaultMessage: 'Delete post' }, }); const mapStateToProps = state => { @@ -78,6 +79,7 @@ class ActionBar extends React.PureComponent { onEmbed: PropTypes.func, onDeactivateUser: PropTypes.func, onDeleteUser: PropTypes.func, + onDeleteStatus: PropTypes.func, intl: PropTypes.object.isRequired, onOpenUnauthorizedModal: PropTypes.func.isRequired, me: SoapboxPropTypes.me, @@ -233,6 +235,10 @@ class ActionBar extends React.PureComponent { this.props.onDeleteUser(this.props.status); } + handleDeleteStatus = () => { + this.props.onDeleteStatus(this.props.status); + } + setRef = c => { this.node = c; } @@ -290,6 +296,7 @@ class ActionBar extends React.PureComponent { // menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` }); menu.push({ text: intl.formatMessage(messages.deactivateUser, { acct: `@${status.getIn(['account', 'acct'])}` }), action: this.handleDeactivateUser }); menu.push({ text: intl.formatMessage(messages.deleteUser, { acct: `@${status.getIn(['account', 'acct'])}` }), action: this.handleDeleteUser }); + menu.push({ text: intl.formatMessage(messages.deleteStatus), action: this.handleDeleteStatus }); } } diff --git a/app/soapbox/features/status/containers/detailed_status_container.js b/app/soapbox/features/status/containers/detailed_status_container.js index 247960ac3..e9c64e384 100644 --- a/app/soapbox/features/status/containers/detailed_status_container.js +++ b/app/soapbox/features/status/containers/detailed_status_container.js @@ -31,7 +31,7 @@ import { openModal } from '../../../actions/modal'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { showAlertForError } from '../../../actions/alerts'; import { getSettings } from 'soapbox/actions/settings'; -import { deactivateUserModal, deleteUserModal } from 'soapbox/actions/moderation'; +import { deactivateUserModal, deleteUserModal, deleteStatusModal } from 'soapbox/actions/moderation'; const messages = defineMessages({ deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, @@ -199,6 +199,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ dispatch(deleteUserModal(intl, status.getIn(['account', 'id']))); }, + onDeleteStatus(status) { + dispatch(deleteStatusModal(intl, status.get('id'))); + }, + }); export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(DetailedStatus)); diff --git a/app/soapbox/features/status/index.js b/app/soapbox/features/status/index.js index 96157d4f8..a48cedaa4 100644 --- a/app/soapbox/features/status/index.js +++ b/app/soapbox/features/status/index.js @@ -47,7 +47,7 @@ import { textForScreenReader, defaultMediaVisibility } from '../../components/st import Icon from 'soapbox/components/icon'; import { getSettings } from 'soapbox/actions/settings'; import { getSoapboxConfig } from 'soapbox/actions/soapbox'; -import { deactivateUserModal, deleteUserModal } from 'soapbox/actions/moderation'; +import { deactivateUserModal, deleteUserModal, deleteStatusModal } from 'soapbox/actions/moderation'; const messages = defineMessages({ deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, @@ -313,6 +313,11 @@ class Status extends ImmutablePureComponent { dispatch(deleteUserModal(intl, status.getIn(['account', 'id']))); } + handleDeleteStatus = (status) => { + const { dispatch, intl } = this.props; + dispatch(deleteStatusModal(intl, status.get('id'))); + } + handleHotkeyMoveUp = () => { this.handleMoveUp(this.props.status.get('id')); } @@ -536,6 +541,7 @@ class Status extends ImmutablePureComponent { onEmbed={this.handleEmbed} onDeactivateUser={this.handleDeactivateUser} onDeleteUser={this.handleDeleteUser} + onDeleteStatus={this.handleDeleteStatus} allowedEmoji={this.props.allowedEmoji} />