glitch-soc/mastodon

View on GitHub
app/javascript/flavours/glitch/features/notifications/components/notification_request.jsx

Summary

Maintainability
F
6 days
Test Coverage
import PropTypes from 'prop-types';
import { useCallback } from 'react';

import { defineMessages, useIntl } from 'react-intl';

import classNames from 'classnames';
import { Link, useHistory } from 'react-router-dom';

import { useSelector, useDispatch } from 'react-redux';

import DeleteIcon from '@/material-icons/400-24px/delete.svg?react';
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
import { initBlockModal } from 'flavours/glitch/actions/blocks';
import { initMuteModal } from 'flavours/glitch/actions/mutes';
import { acceptNotificationRequest, dismissNotificationRequest } from 'flavours/glitch/actions/notification_requests';
import { initReport } from 'flavours/glitch/actions/reports';
import { Avatar } from 'flavours/glitch/components/avatar';
import { CheckBox } from 'flavours/glitch/components/check_box';
import { IconButton } from 'flavours/glitch/components/icon_button';
import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
import { makeGetAccount } from 'flavours/glitch/selectors';
import { toCappedNumber } from 'flavours/glitch/utils/numbers';

const getAccount = makeGetAccount();

const messages = defineMessages({
  accept: { id: 'notification_requests.accept', defaultMessage: 'Accept' },
  dismiss: { id: 'notification_requests.dismiss', defaultMessage: 'Dismiss' },
  view: { id: 'notification_requests.view', defaultMessage: 'View notifications' },
  mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
  block: { id: 'account.block', defaultMessage: 'Block @{name}' },
  report: { id: 'status.report', defaultMessage: 'Report @{name}' },
  more: { id: 'status.more', defaultMessage: 'More' },
});

export const NotificationRequest = ({ id, accountId, notificationsCount, checked, showCheckbox, toggleCheck }) => {
  const dispatch = useDispatch();
  const account = useSelector(state => getAccount(state, accountId));
  const intl = useIntl();
  const { push: historyPush } = useHistory();

  const handleDismiss = useCallback(() => {
    dispatch(dismissNotificationRequest({ id }));
  }, [dispatch, id]);

  const handleAccept = useCallback(() => {
    dispatch(acceptNotificationRequest({ id }));
  }, [dispatch, id]);

  const handleMute = useCallback(() => {
    dispatch(initMuteModal(account));
  }, [dispatch, account]);

  const handleBlock = useCallback(() => {
    dispatch(initBlockModal(account));
  }, [dispatch, account]);

  const handleReport = useCallback(() => {
    dispatch(initReport(account));
  }, [dispatch, account]);

  const handleView = useCallback(() => {
    historyPush(`/notifications/requests/${id}`);
  }, [historyPush, id]);

  const menu = [
    { text: intl.formatMessage(messages.view), action: handleView },
    null,
    { text: intl.formatMessage(messages.accept), action: handleAccept },
    null,
    { text: intl.formatMessage(messages.mute, { name: account.username }), action: handleMute, dangerous: true },
    { text: intl.formatMessage(messages.block, { name: account.username }), action: handleBlock, dangerous: true },
    { text: intl.formatMessage(messages.report, { name: account.username }), action: handleReport, dangerous: true },
  ];

  const handleCheck = useCallback(() => {
    toggleCheck(id);
  }, [toggleCheck, id]);

  const handleClick = useCallback((e) => {
    if (showCheckbox) {
      toggleCheck(id);
      e.preventDefault();
      e.stopPropagation();
    }
  }, [toggleCheck, id, showCheckbox]);

  return (
    /* eslint-disable-next-line jsx-a11y/no-static-element-interactions -- this is just a minor affordance, but we will need a comprehensive accessibility pass */
    <div className={classNames('notification-request', showCheckbox && 'notification-request--forced-checkbox')} onClick={handleClick}>
      <div className='notification-request__checkbox' aria-hidden={!showCheckbox}>
        <CheckBox checked={checked} onChange={handleCheck} />
      </div>
      <Link to={`/notifications/requests/${id}`} className='notification-request__link' onClick={handleClick} title={account?.acct}>
        <Avatar account={account} size={40} counter={toCappedNumber(notificationsCount)} />

        <div className='notification-request__name'>
          <div className='notification-request__name__display-name'>
            <bdi><strong dangerouslySetInnerHTML={{ __html: account?.get('display_name_html') }} /></bdi>
          </div>

          <span>@{account?.get('acct')}</span>
        </div>
      </Link>

      <div className='notification-request__actions'>
        <IconButton iconComponent={DeleteIcon} onClick={handleDismiss} title={intl.formatMessage(messages.dismiss)} />
        <DropdownMenuContainer
          items={menu}
          icons='ellipsis-h'
          iconComponent={MoreHorizIcon}
          direction='right'
          title={intl.formatMessage(messages.more)}
        />
      </div>
    </div>
  );
};

NotificationRequest.propTypes = {
  id: PropTypes.string.isRequired,
  accountId: PropTypes.string.isRequired,
  notificationsCount: PropTypes.string.isRequired,
  checked: PropTypes.bool,
  showCheckbox: PropTypes.bool,
  toggleCheck: PropTypes.func,
};