synapsecns/sanguine

View on GitHub
packages/synapse-interface/components/Activity/Transaction/TransactionOptions.tsx

Summary

Maintainability
D
2 days
Test Coverage
import { Fragment, useCallback } from 'react'
import Image from 'next/image'
import { Address } from 'viem'
import { Popover, Transition } from '@headlessui/react'
import { Chain } from '@/utils/types'
import { TransactionStatus } from './Transaction'
import {
  getTransactionExplorerLink,
  getTransactionHashExplorerLink,
} from './components/TransactionExplorerLink'
import { getExplorerAddressUrl, getExplorerTxUrl } from '@/constants/urls'
import { DISCORD_URL } from '@/constants/urls'
import Button from '@/components/ui/tailwind/Button'
import SynapseLogo from '@assets/icons/syn.svg'
import DiscordIcon from '@/components/icons/DiscordIcon'
import { DownArrow } from '@/components/icons/DownArrow'

export const TransactionOptions = ({
  connectedAddress,
  originChain,
  destinationChain,
  kappa,
  transactionHash,
  transactionStatus,
  isDelayed,
}: {
  connectedAddress: Address
  originChain: Chain
  destinationChain: Chain
  kappa?: string
  transactionHash?: string
  transactionStatus: TransactionStatus
  isDelayed: boolean
}) => {
  const handleOriginExplorerLink: () => void = useCallback(() => {
    if (originChain && connectedAddress) {
      const explorerLink: string = getExplorerAddressUrl({
        chainId: originChain.id,
        address: connectedAddress,
      })
      window.open(explorerLink, '_blank', 'noopener,noreferrer')
    }
  }, [originChain, connectedAddress])

  const handleDestinationExplorerLink: () => void = useCallback(() => {
    if (destinationChain && connectedAddress) {
      const explorerLink: string = getExplorerAddressUrl({
        chainId: destinationChain.id,
        address: connectedAddress,
      })
      window.open(explorerLink, '_blank', 'noopener,noreferrer')
    }
  }, [destinationChain, connectedAddress])

  const handleSynapseExplorerLink: () => void = useCallback(() => {
    if (kappa) {
      const explorerLink: string = getTransactionExplorerLink({
        kappa,
        fromChainId: originChain?.id,
        toChainId: destinationChain?.id,
      })
      window.open(explorerLink, '_blank', 'noopener,noreferrer')
    } else if (transactionHash) {
      const explorerLink: string = getTransactionHashExplorerLink({
        transactionHash: transactionHash,
      })
      window.open(explorerLink, '_blank', 'noopener,noreferrer')
    }
  }, [kappa, originChain, transactionStatus, isDelayed, transactionHash])

  const handleSupportClick: () => void = () => {
    window.open(DISCORD_URL, '_blank', 'noopener,noreferrer')
  }

  return (
    <Popover className="relative inline-block pt-1 mb-auto">
      {({ open }) => (
        <>
          <Popover.Button
            as="div"
            onMouseEnter={() => {}}
            className={`
              group rounded-md inline-flex items-center
              hover:text-gray-900 focus:outline-none
              ${open ? 'text-gray-900' : 'text-purple-800'}
            `}
          >
            <DropdownButton open={open} isDelayed={isDelayed} />
          </Popover.Button>
          <TransactionPopoverContainer>
            <OptionButton
              icon={
                <Image
                  className="rounded-full"
                  height={20}
                  src={originChain.explorerImg}
                  alt={`${originChain.explorerName} logo`}
                />
              }
              text={`${originChain.explorerName}`}
              onClick={handleOriginExplorerLink}
            />
            <OptionButton
              icon={
                <Image
                  className="rounded-full"
                  height={20}
                  src={destinationChain.explorerImg}
                  alt={`${destinationChain.explorerName} logo`}
                />
              }
              text={`${destinationChain.explorerName}`}
              onClick={handleDestinationExplorerLink}
            />
            <OptionButton
              icon={
                <Image
                  className="rounded-full"
                  height={20}
                  src={SynapseLogo}
                  alt="Synapse Logo"
                />
              }
              text={`Synapse Explorer`}
              onClick={handleSynapseExplorerLink}
            />
            <OptionButton
              icon={<DiscordIcon height={20} />}
              text={'Contact Support'}
              onClick={handleSupportClick}
            />
          </TransactionPopoverContainer>
        </>
      )}
    </Popover>
  )
}

export function TransactionPopoverContainer({
  children,
  className,
}: {
  children: any
  className?: string
}) {
  return (
    <Transition
      as={Fragment as any}
      enter="transition ease-out duration-200"
      enterFrom="opacity-0 translate-y-1"
      enterTo="opacity-100 translate-y-0"
      leave="transition ease-in duration-150"
      leaveFrom="opacity-100 translate-y-0"
      leaveTo="opacity-0 translate-y-1"
    >
      <Popover.Panel
        style={{ boxShadow: '0px 4px 4px 0px rgba(0, 0, 0, 0.25)' }}
        className={`
          absolute z-10 top-[25px] left-[20px] transform-gpu
          -translate-x-full border border-separator bg-surface
          w-screen max-w-xs rounded-md overflow-hidden shadow-md
        `}
      >
        <div className="shadow-xl">
          <div className="relative grid gap-1 p-1">{children}</div>
        </div>
      </Popover.Panel>
    </Transition>
  )
}

export function DropdownButton({
  open,
  onClick,
  isDelayed,
  className,
  ...props
}: {
  open: boolean
  onClick?: () => void
  isDelayed?: boolean
  className?: string
  props?: any
}) {
  return (
    <Button
      data-test-id="dropdown-button"
      onClick={onClick ? onClick : () => {}}
      className={`
        w-full
        group rounded-sm p-1
        border border-[#2F2F2F] hover:border-[#101018]
        bg-surface hover:bg-[#101018]
        focus:bg-transparent
        ${className}
      `}
      {...props}
    >
      <div className="space-x-2">
        <div className="flex items-center space-x-1 rounded-md">
          <DownArrow className="!w-3 !h-3 text-secondary" />
        </div>
      </div>
    </Button>
  )
}

export const OptionButton = ({
  icon,
  text,
  onClick,
}: {
  icon: any
  text: string
  onClick: () => void
}) => {
  return (
    <div
      data-test-id="option-button"
      onClick={onClick}
      className="flex hover:cursor-pointer hover:bg-[#0A415C] rounded-sm p-1"
    >
      <div className="my-auto mr-1">{icon}</div>
      <div>{text}</div>
    </div>
  )
}