glitch-soc/mastodon

View on GitHub
app/javascript/flavours/glitch/features/ui/components/domain_block_modal.tsx

Summary

Maintainability
F
1 wk
Test Coverage
import { useCallback, useEffect, useState } from 'react';

import { FormattedMessage } from 'react-intl';

import CampaignIcon from '@/material-icons/400-24px/campaign.svg?react';
import DomainDisabledIcon from '@/material-icons/400-24px/domain_disabled.svg?react';
import HistoryIcon from '@/material-icons/400-24px/history.svg?react';
import PersonRemoveIcon from '@/material-icons/400-24px/person_remove.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
import { blockAccount } from 'flavours/glitch/actions/accounts';
import { blockDomain } from 'flavours/glitch/actions/domain_blocks';
import { closeModal } from 'flavours/glitch/actions/modal';
import { apiRequest } from 'flavours/glitch/api';
import { Button } from 'flavours/glitch/components/button';
import { Icon } from 'flavours/glitch/components/icon';
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
import { ShortNumber } from 'flavours/glitch/components/short_number';
import { useAppDispatch } from 'flavours/glitch/store';

interface DomainBlockPreviewResponse {
  following_count: number;
  followers_count: number;
}

export const DomainBlockModal: React.FC<{
  domain: string;
  accountId: string;
  acct: string;
}> = ({ domain, accountId, acct }) => {
  const dispatch = useAppDispatch();
  const [loading, setLoading] = useState(true);
  const [preview, setPreview] = useState<
    DomainBlockPreviewResponse | 'error' | null
  >(null);

  const handleClick = useCallback(() => {
    if (loading) {
      return; // Prevent destructive action before the preview finishes loading or times out
    }

    dispatch(closeModal({ modalType: undefined, ignoreFocus: false }));
    dispatch(blockDomain(domain));
  }, [dispatch, loading, domain]);

  const handleSecondaryClick = useCallback(() => {
    dispatch(closeModal({ modalType: undefined, ignoreFocus: false }));
    dispatch(blockAccount(accountId));
  }, [dispatch, accountId]);

  const handleCancel = useCallback(() => {
    dispatch(closeModal({ modalType: undefined, ignoreFocus: false }));
  }, [dispatch]);

  useEffect(() => {
    setLoading(true);

    apiRequest<DomainBlockPreviewResponse>('GET', 'v1/domain_blocks/preview', {
      params: { domain },
      timeout: 5000,
    })
      .then((data) => {
        setPreview(data);
        setLoading(false);
        return '';
      })
      .catch(() => {
        setPreview('error');
        setLoading(false);
      });
  }, [setPreview, setLoading, domain]);

  return (
    <div className='modal-root__modal safety-action-modal' aria-live='polite'>
      <div className='safety-action-modal__top'>
        <div className='safety-action-modal__header'>
          <div className='safety-action-modal__header__icon'>
            <Icon id='' icon={DomainDisabledIcon} />
          </div>

          <div>
            <h1>
              <FormattedMessage
                id='domain_block_modal.title'
                defaultMessage='Block domain?'
              />
            </h1>
            <div>{domain}</div>
          </div>
        </div>

        <div className='safety-action-modal__bullet-points'>
          {preview &&
            preview !== 'error' &&
            preview.followers_count + preview.following_count > 0 && (
              <div>
                <div className='safety-action-modal__bullet-points__icon'>
                  <Icon id='' icon={PersonRemoveIcon} />
                </div>
                <div>
                  <strong>
                    <FormattedMessage
                      id='domain_block_modal.you_will_lose_num_followers'
                      defaultMessage='You will lose {followersCount, plural, one {{followersCountDisplay} follower} other {{followersCountDisplay} followers}} and {followingCount, plural, one {{followingCountDisplay} person you follow} other {{followingCountDisplay} people you follow}}.'
                      values={{
                        followersCount: preview.followers_count,
                        followersCountDisplay: (
                          <ShortNumber value={preview.followers_count} />
                        ),
                        followingCount: preview.following_count,
                        followingCountDisplay: (
                          <ShortNumber value={preview.following_count} />
                        ),
                      }}
                    />
                  </strong>
                </div>
              </div>
            )}

          {preview === 'error' && (
            <div>
              <div className='safety-action-modal__bullet-points__icon'>
                <Icon id='' icon={PersonRemoveIcon} />
              </div>
              <div>
                <strong>
                  <FormattedMessage
                    id='domain_block_modal.you_will_lose_relationships'
                    defaultMessage='You will lose all followers and people you follow from this server.'
                  />
                </strong>
              </div>
            </div>
          )}

          <div className='safety-action-modal__bullet-points--deemphasized'>
            <div className='safety-action-modal__bullet-points__icon'>
              <Icon id='' icon={CampaignIcon} />
            </div>
            <div>
              <FormattedMessage
                id='domain_block_modal.they_wont_know'
                defaultMessage="They won't know they've been blocked."
              />
            </div>
          </div>

          <div className='safety-action-modal__bullet-points--deemphasized'>
            <div className='safety-action-modal__bullet-points__icon'>
              <Icon id='' icon={VisibilityOffIcon} />
            </div>
            <div>
              <FormattedMessage
                id='domain_block_modal.you_wont_see_posts'
                defaultMessage="You won't see posts or notifications from users on this server."
              />
            </div>
          </div>

          <div className='safety-action-modal__bullet-points--deemphasized'>
            <div className='safety-action-modal__bullet-points__icon'>
              <Icon id='' icon={ReplyIcon} />
            </div>
            <div>
              <FormattedMessage
                id='domain_block_modal.they_cant_follow'
                defaultMessage='Nobody from this server can follow you.'
              />
            </div>
          </div>

          <div className='safety-action-modal__bullet-points--deemphasized'>
            <div className='safety-action-modal__bullet-points__icon'>
              <Icon id='' icon={HistoryIcon} />
            </div>
            <div>
              <FormattedMessage
                id='domain_block_modal.they_can_interact_with_old_posts'
                defaultMessage='People from this server can interact with your old posts.'
              />
            </div>
          </div>
        </div>
      </div>

      <div className='safety-action-modal__bottom'>
        <div className='safety-action-modal__actions'>
          <Button onClick={handleSecondaryClick} secondary>
            <FormattedMessage
              id='domain_block_modal.block_account_instead'
              defaultMessage='Block @{name} instead'
              values={{ name: acct.split('@')[0] }}
            />
          </Button>

          <div className='spacer' />

          <button onClick={handleCancel} className='link-button'>
            <FormattedMessage
              id='confirmation_modal.cancel'
              defaultMessage='Cancel'
            />
          </button>

          <Button onClick={handleClick} dangerous aria-busy={loading}>
            {loading ? (
              <LoadingIndicator />
            ) : (
              <FormattedMessage
                id='domain_block_modal.block'
                defaultMessage='Block server'
              />
            )}
          </Button>
        </div>
      </div>
    </div>
  );
};

// eslint-disable-next-line import/no-default-export
export default DomainBlockModal;