From 453290c6d7ccfd6980c417c228536a07be7aa80b Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 15 Mar 2021 19:29:42 -0500 Subject: [PATCH] Make it harder to accidentally delete a local user --- app/soapbox/actions/moderation.js | 6 ++++ .../ui/components/confirmation_modal.js | 34 +++++++++++++++++-- app/soapbox/utils/accounts.js | 5 +++ app/styles/components/modal.scss | 8 +++++ 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/app/soapbox/actions/moderation.js b/app/soapbox/actions/moderation.js index d2489658f..cc05bd905 100644 --- a/app/soapbox/actions/moderation.js +++ b/app/soapbox/actions/moderation.js @@ -4,6 +4,7 @@ import { openModal } from 'soapbox/actions/modal'; import { deactivateUsers, deleteUsers, deleteStatus, toggleStatusSensitivity } from 'soapbox/actions/admin'; import snackbar from 'soapbox/actions/snackbar'; import AccountContainer from 'soapbox/containers/account_container'; +import { isLocal } from 'soapbox/utils/accounts'; const messages = defineMessages({ deactivateUserPrompt: { id: 'confirmations.admin.deactivate_user.message', defaultMessage: 'You are about to deactivate @{acct}. Deactivating a user is a reversible action.' }, @@ -11,6 +12,7 @@ const messages = defineMessages({ userDeactivated: { id: 'admin.users.user_deactivated_message', defaultMessage: '@{acct} was deactivated' }, 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 @{name}' }, + deleteLocalUserCheckbox: { id: 'confirmations.admin.delete_local_user.checkbox', defaultMessage: 'I understand that I am about to delete a local user.' }, 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' }, @@ -49,6 +51,7 @@ export function deleteUserModal(intl, accountId, afterConfirm = () => {}) { const acct = state.getIn(['accounts', accountId, 'acct']); const name = state.getIn(['accounts', accountId, 'username']); const favicon = state.getIn(['accounts', accountId, 'pleroma', 'favicon']); + const local = isLocal(state.getIn(['accounts', accountId])); const message = (<> @@ -63,9 +66,12 @@ export function deleteUserModal(intl, accountId, afterConfirm = () => {}) { {intl.formatMessage(messages.deleteUserConfirm, { name })} ); + const checkbox = local ? intl.formatMessage(messages.deleteLocalUserCheckbox) : false; + dispatch(openModal('CONFIRM', { message, confirm, + checkbox, onConfirm: () => { dispatch(deleteUsers([acct])).then(() => { const message = intl.formatMessage(messages.userDeleted, { acct }); diff --git a/app/soapbox/features/ui/components/confirmation_modal.js b/app/soapbox/features/ui/components/confirmation_modal.js index d4caac3cd..4c7a91145 100644 --- a/app/soapbox/features/ui/components/confirmation_modal.js +++ b/app/soapbox/features/ui/components/confirmation_modal.js @@ -2,21 +2,27 @@ import React from 'react'; import PropTypes from 'prop-types'; import { injectIntl, FormattedMessage } from 'react-intl'; import Button from '../../../components/button'; +import { SimpleForm, FieldsGroup, Checkbox } from 'soapbox/features/forms'; export default @injectIntl class ConfirmationModal extends React.PureComponent { static propTypes = { message: PropTypes.node.isRequired, - confirm: PropTypes.string.isRequired, + confirm: PropTypes.node.isRequired, onClose: PropTypes.func.isRequired, onConfirm: PropTypes.func.isRequired, secondary: PropTypes.string, onSecondary: PropTypes.func, intl: PropTypes.object.isRequired, onCancel: PropTypes.func, + checkbox: PropTypes.node, }; + state = { + checked: false, + } + componentDidMount() { this.button.focus(); } @@ -37,12 +43,17 @@ class ConfirmationModal extends React.PureComponent { if (onCancel) onCancel(); } + handleCheckboxChange = e => { + this.setState({ checked: e.target.checked }); + } + setRef = (c) => { this.button = c; } render() { - const { message, confirm, secondary } = this.props; + const { message, confirm, secondary, checkbox } = this.props; + const { checked } = this.state; return (
@@ -50,6 +61,18 @@ class ConfirmationModal extends React.PureComponent { {message}
+ {checkbox &&
+ + + + + +
} +
); diff --git a/app/soapbox/utils/accounts.js b/app/soapbox/utils/accounts.js index 929429b24..acc4cde65 100644 --- a/app/soapbox/utils/accounts.js +++ b/app/soapbox/utils/accounts.js @@ -40,3 +40,8 @@ export const getFollowDifference = (state, accountId, type) => { const counter = state.getIn(['accounts_counters', accountId, `${type}_count`], 0); return Math.max(counter - listSize, 0); }; + +export const isLocal = account => { + let domain = account.get('acct').split('@')[1]; + return domain === undefined ? true : false; +}; diff --git a/app/styles/components/modal.scss b/app/styles/components/modal.scss index 4327ac152..e64965da4 100644 --- a/app/styles/components/modal.scss +++ b/app/styles/components/modal.scss @@ -630,6 +630,14 @@ } } +.confirmation-modal__checkbox { + padding: 0 30px; + + .simple_form { + margin-top: -14px; + } +} + .report-modal__target { padding: 20px;