antoncoding/monarch

View on GitHub
src/hooks/useTransactionWithToast.tsx

Summary

Maintainability
A
40 mins
Test Coverage
import React, { useCallback, useEffect } from 'react';
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { useSendTransaction, useWaitForTransactionReceipt } from 'wagmi';
import { TxHashDisplay } from '../components/TxHashDisplay';
import { getExplorerTxURL } from '../utils/external';
import { SupportedNetworks } from '../utils/networks';

type UseTransactionWithToastProps = {
  toastId: string;
  pendingText: string;
  successText: string;
  errorText: string;
  chainId?: number;
  pendingDescription?: string;
  successDescription?: string;
  onSuccess?: () => void;
};

export function useTransactionWithToast({
  toastId,
  pendingText,
  successText,
  errorText,
  chainId,
  pendingDescription,
  successDescription,
  onSuccess,
}: UseTransactionWithToastProps) {
  const {
    data: hash,
    sendTransaction,
    error: txError,
    sendTransactionAsync,
  } = useSendTransaction();
  const { isLoading: isConfirming, isSuccess: isConfirmed } = useWaitForTransactionReceipt({
    hash,
  });

  const onClick = useCallback(() => {
    if (hash) {
      // if chainId is not supported, use 1
      const chainIdToUse = chainId ?? 1;
      window.open(getExplorerTxURL(hash, chainIdToUse as SupportedNetworks), '_blank');
    }
  }, [hash, chainId]);

  const renderToastContent = useCallback(
    (title: string, description?: string) => (
      <div className="p-2">
        <div>{title}</div>
        {description && <div className="mb-2 mt-1 text-sm">{description}</div>}
        <TxHashDisplay hash={hash} />
      </div>
    ),
    [hash],
  );

  useEffect(() => {
    if (isConfirming) {
      toast.loading(renderToastContent(pendingText, pendingDescription), {
        toastId,
        onClick,
      });
    }
  }, [isConfirming, pendingText, pendingDescription, toastId, onClick, renderToastContent]);

  useEffect(() => {
    if (isConfirmed) {
      toast.update(toastId, {
        render: renderToastContent(`${successText} 🎉`, successDescription),
        type: 'success',
        isLoading: false,
        autoClose: 5000,
        onClick,
      });
      if (onSuccess) {
        onSuccess();
      }
    }
    if (txError) {
      toast.update(toastId, {
        render: (
          <div className="p-2">
            <div>{errorText}</div>
            <div className="py-2 font-mono text-xs hover:underline">{txError.message}</div>
          </div>
        ),
        type: 'error',
        isLoading: false,
        autoClose: 5000,
        onClick,
      });
    }
  }, [
    isConfirmed,
    txError,
    successText,
    successDescription,
    errorText,
    toastId,
    onClick,
    renderToastContent,
    onSuccess,
  ]);

  return { sendTransactionAsync, sendTransaction, isConfirming, isConfirmed };
}