about summary refs log tree commit diff
path: root/app/javascript/mastodon/features/ui/components/report_modal.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'app/javascript/mastodon/features/ui/components/report_modal.jsx')
-rw-r--r--app/javascript/mastodon/features/ui/components/report_modal.jsx219
1 files changed, 219 insertions, 0 deletions
diff --git a/app/javascript/mastodon/features/ui/components/report_modal.jsx b/app/javascript/mastodon/features/ui/components/report_modal.jsx
new file mode 100644
index 000000000..8b505b8bd
--- /dev/null
+++ b/app/javascript/mastodon/features/ui/components/report_modal.jsx
@@ -0,0 +1,219 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { submitReport } from 'mastodon/actions/reports';
+import { expandAccountTimeline } from 'mastodon/actions/timelines';
+import { fetchServer } from 'mastodon/actions/server';
+import PropTypes from 'prop-types';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { makeGetAccount } from 'mastodon/selectors';
+import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
+import { OrderedSet } from 'immutable';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import IconButton from 'mastodon/components/icon_button';
+import Category from 'mastodon/features/report/category';
+import Statuses from 'mastodon/features/report/statuses';
+import Rules from 'mastodon/features/report/rules';
+import Comment from 'mastodon/features/report/comment';
+import Thanks from 'mastodon/features/report/thanks';
+
+const messages = defineMessages({
+  close: { id: 'lightbox.close', defaultMessage: 'Close' },
+});
+
+const makeMapStateToProps = () => {
+  const getAccount = makeGetAccount();
+
+  const mapStateToProps = (state, { accountId }) => ({
+    account: getAccount(state, accountId),
+  });
+
+  return mapStateToProps;
+};
+
+class ReportModal extends ImmutablePureComponent {
+
+  static propTypes = {
+    accountId: PropTypes.string.isRequired,
+    statusId: PropTypes.string,
+    dispatch: PropTypes.func.isRequired,
+    intl: PropTypes.object.isRequired,
+    account: ImmutablePropTypes.map.isRequired,
+  };
+
+  state = {
+    step: 'category',
+    selectedStatusIds: OrderedSet(this.props.statusId ? [this.props.statusId] : []),
+    comment: '',
+    category: null,
+    selectedRuleIds: OrderedSet(),
+    forward: true,
+    isSubmitting: false,
+    isSubmitted: false,
+  };
+
+  handleSubmit = () => {
+    const { dispatch, accountId } = this.props;
+    const { selectedStatusIds, comment, category, selectedRuleIds, forward } = this.state;
+
+    this.setState({ isSubmitting: true });
+
+    dispatch(submitReport({
+      account_id: accountId,
+      status_ids: selectedStatusIds.toArray(),
+      comment,
+      forward,
+      category,
+      rule_ids: selectedRuleIds.toArray(),
+    }, this.handleSuccess, this.handleFail));
+  };
+
+  handleSuccess = () => {
+    this.setState({ isSubmitting: false, isSubmitted: true, step: 'thanks' });
+  };
+
+  handleFail = () => {
+    this.setState({ isSubmitting: false });
+  };
+
+  handleStatusToggle = (statusId, checked) => {
+    const { selectedStatusIds } = this.state;
+
+    if (checked) {
+      this.setState({ selectedStatusIds: selectedStatusIds.add(statusId) });
+    } else {
+      this.setState({ selectedStatusIds: selectedStatusIds.remove(statusId) });
+    }
+  };
+
+  handleRuleToggle = (ruleId, checked) => {
+    const { selectedRuleIds } = this.state;
+
+    if (checked) {
+      this.setState({ selectedRuleIds: selectedRuleIds.add(ruleId) });
+    } else {
+      this.setState({ selectedRuleIds: selectedRuleIds.remove(ruleId) });
+    }
+  };
+
+  handleChangeCategory = category => {
+    this.setState({ category });
+  };
+
+  handleChangeComment = comment => {
+    this.setState({ comment });
+  };
+
+  handleChangeForward = forward => {
+    this.setState({ forward });
+  };
+
+  handleNextStep = step => {
+    this.setState({ step });
+  };
+
+  componentDidMount () {
+    const { dispatch, accountId } = this.props;
+
+    dispatch(expandAccountTimeline(accountId, { withReplies: true }));
+    dispatch(fetchServer());
+  }
+
+  render () {
+    const {
+      accountId,
+      account,
+      intl,
+      onClose,
+    } = this.props;
+
+    if (!account) {
+      return null;
+    }
+
+    const {
+      step,
+      selectedStatusIds,
+      selectedRuleIds,
+      comment,
+      forward,
+      category,
+      isSubmitting,
+      isSubmitted,
+    } = this.state;
+
+    const domain   = account.get('acct').split('@')[1];
+    const isRemote = !!domain;
+
+    let stepComponent;
+
+    switch(step) {
+    case 'category':
+      stepComponent = (
+        <Category
+          onNextStep={this.handleNextStep}
+          startedFrom={this.props.statusId ? 'status' : 'account'}
+          category={category}
+          onChangeCategory={this.handleChangeCategory}
+        />
+      );
+      break;
+    case 'rules':
+      stepComponent = (
+        <Rules
+          onNextStep={this.handleNextStep}
+          selectedRuleIds={selectedRuleIds}
+          onToggle={this.handleRuleToggle}
+        />
+      );
+      break;
+    case 'statuses':
+      stepComponent = (
+        <Statuses
+          onNextStep={this.handleNextStep}
+          accountId={accountId}
+          selectedStatusIds={selectedStatusIds}
+          onToggle={this.handleStatusToggle}
+        />
+      );
+      break;
+    case 'comment':
+      stepComponent = (
+        <Comment
+          onSubmit={this.handleSubmit}
+          isSubmitting={isSubmitting}
+          isRemote={isRemote}
+          comment={comment}
+          forward={forward}
+          domain={domain}
+          onChangeComment={this.handleChangeComment}
+          onChangeForward={this.handleChangeForward}
+        />
+      );
+      break;
+    case 'thanks':
+      stepComponent = (
+        <Thanks
+          submitted={isSubmitted}
+          account={account}
+          onClose={onClose}
+        />
+      );
+    }
+
+    return (
+      <div className='modal-root__modal report-dialog-modal'>
+        <div className='report-modal__target'>
+          <IconButton className='report-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={20} />
+          <FormattedMessage id='report.target' defaultMessage='Report {target}' values={{ target: <strong>{account.get('acct')}</strong> }} />
+        </div>
+
+        <div className='report-dialog-modal__container'>
+          {stepComponent}
+        </div>
+      </div>
+    );
+  }
+
+}
+
+export default connect(makeMapStateToProps)(injectIntl(ReportModal));