import PropTypes from 'prop-types'; import { useCallback, useEffect, useRef } from 'react'; 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' }, }); const selectRepliedToAccountIds = createSelector( [ (state) => state.get('statuses'), (_, statusIds) => statusIds, ], (statusesMap, statusIds) => statusIds.map((statusId) => statusesMap.getIn([statusId, 'in_reply_to_account_id'])), { resultEqualityCheck: shallowEqual, } ); const Comment = ({ comment, domain, statusIds, isRemote, isSubmitting, selectedDomains, onSubmit, onChangeComment, onToggleDomain }) => { const intl = useIntl(); const dispatch = useAppDispatch(); const loadedRef = useRef(false); 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]); const handleKeyDown = useCallback((e) => { if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { handleClick(); } }, [handleClick]); // Memoize accountIds since we don't want it to trigger `useEffect` on each render const accountIds = useAppSelector((state) => domain ? selectRepliedToAccountIds(state, statusIds) : ImmutableList()); // 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(); useEffect(() => { if (loadedRef.current) { return; } loadedRef.current = true; // First, pre-select known domains availableDomains.forEach((domain) => { onToggleDomain(domain, true); }); // Then, fetch missing replied-to accounts const unknownAccounts = OrderedSet(accountIds.filter(accountId => accountId && !accountsMap.has(accountId))); unknownAccounts.forEach((accountId) => { dispatch(fetchAccount(accountId)); }); }); return ( <>