diff --git a/app/controllers/api/v1/reports_controller.rb b/app/controllers/api/v1/reports_controller.rb
index 8ff6c8fe5c..300c9faa3f 100644
--- a/app/controllers/api/v1/reports_controller.rb
+++ b/app/controllers/api/v1/reports_controller.rb
@@ -23,6 +23,6 @@ class Api::V1::ReportsController < Api::BaseController
end
def report_params
- params.permit(:account_id, :comment, :category, :forward, status_ids: [], rule_ids: [])
+ params.permit(:account_id, :comment, :category, :forward, forward_to_domains: [], status_ids: [], rule_ids: [])
end
end
diff --git a/app/javascript/mastodon/features/report/comment.jsx b/app/javascript/mastodon/features/report/comment.jsx
index 4888b76bcb..98ac4caa0a 100644
--- a/app/javascript/mastodon/features/report/comment.jsx
+++ b/app/javascript/mastodon/features/report/comment.jsx
@@ -1,87 +1,121 @@
import PropTypes from 'prop-types';
-import { PureComponent } from 'react';
+import { useCallback, useEffect, useRef } from 'react';
-import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
+import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
+
+import { OrderedSet, List as ImmutableList } from 'immutable';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { shallowEqual } from 'react-redux';
+import { createSelector } from 'reselect';
import Toggle from 'react-toggle';
+import { fetchAccount } from 'mastodon/actions/accounts';
import Button from 'mastodon/components/button';
+import { useAppDispatch, useAppSelector } from 'mastodon/store';
const messages = defineMessages({
placeholder: { id: 'report.placeholder', defaultMessage: 'Type or paste additional comments' },
});
-class Comment extends PureComponent {
+const selectRepliedToAccountIds = createSelector(
+ [
+ (state) => state.get('statuses'),
+ (_, statusIds) => statusIds,
+ ],
+ (statusesMap, statusIds) => statusIds.map((statusId) => statusesMap.getIn([statusId, 'in_reply_to_account_id'])),
+ {
+ resultEqualityCheck: shallowEqual,
+ }
+);
- static propTypes = {
- onSubmit: PropTypes.func.isRequired,
- comment: PropTypes.string.isRequired,
- onChangeComment: PropTypes.func.isRequired,
- intl: PropTypes.object.isRequired,
- isSubmitting: PropTypes.bool,
- forward: PropTypes.bool,
- isRemote: PropTypes.bool,
- domain: PropTypes.string,
- onChangeForward: PropTypes.func.isRequired,
- };
+const Comment = ({ comment, domain, statusIds, isRemote, isSubmitting, selectedDomains, onSubmit, onChangeComment, onToggleDomain }) => {
+ const intl = useIntl();
- handleClick = () => {
- const { onSubmit } = this.props;
- onSubmit();
- };
+ const dispatch = useAppDispatch();
+ const loadedRef = useRef(false);
- handleChange = e => {
- const { onChangeComment } = this.props;
- onChangeComment(e.target.value);
- };
+ const handleClick = useCallback(() => onSubmit(), [onSubmit]);
+ const handleChange = useCallback((e) => onChangeComment(e.target.value), [onChangeComment]);
+ const handleToggleDomain = useCallback(e => onToggleDomain(e.target.value, e.target.checked), [onToggleDomain]);
- handleKeyDown = e => {
+ const handleKeyDown = useCallback((e) => {
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
- this.handleClick();
+ handleClick();
}
- };
+ }, [handleClick]);
- handleForwardChange = e => {
- const { onChangeForward } = this.props;
- onChangeForward(e.target.checked);
- };
+ // Memoize accountIds since we don't want it to trigger `useEffect` on each render
+ const accountIds = useAppSelector((state) => domain ? selectRepliedToAccountIds(state, statusIds) : ImmutableList());
- render () {
- const { comment, isRemote, forward, domain, isSubmitting, intl } = this.props;
+ // While we could memoize `availableDomains`, it is pretty inexpensive to recompute
+ const accountsMap = useAppSelector((state) => state.get('accounts'));
+ const availableDomains = domain ? OrderedSet([domain]).union(accountIds.map((accountId) => accountsMap.getIn([accountId, 'acct'], '').split('@')[1]).filter(domain => !!domain)) : OrderedSet();
- return (
- <>
-
+ useEffect(() => {
+ if (loadedRef.current) {
+ return;
+ }
-
+ loadedRef.current = true;
- {isRemote && (
- <>
-
+ // First, pre-select known domains
+ availableDomains.forEach((domain) => {
+ onToggleDomain(domain, true);
+ });
-