Address.tsx 3.67 KiB
Newer Older
Andrei Eres's avatar
Andrei Eres committed
import { AccountJson } from '@polkadot/extension-base/background/types'
Andrei Eres's avatar
Andrei Eres committed
import Identicon from '@polkadot/react-identicon'
Andrei Eres's avatar
Andrei Eres committed
import { IconTheme } from '@polkadot/react-identicon/types'
Andrei Eres's avatar
Andrei Eres committed
import { useStore } from 'nanostores/react'
import React, { useEffect, useState } from 'react'
Andrei Eres's avatar
Andrei Eres committed
import { DEFAULT_TYPE, RELAY_CHAIN, UNKNOWN } from '../../utils/constants'
Andrei Eres's avatar
Andrei Eres committed
import styled from 'styled-components'
Andrei Eres's avatar
Andrei Eres committed
import copyIcon from '../assets/copy.svg'
Andrei Eres's avatar
Andrei Eres committed
import useMetadata from '../../hooks/useMetadata'
import { useTimedReset } from '../../hooks/useTimedReset'
import { accounts as accountsStore } from '../../stores/accounts'
Andrei Eres's avatar
Andrei Eres committed
import { BaseProps } from '../types'
Andrei Eres's avatar
Andrei Eres committed
import { recodeAddress, Recoded } from '../../utils/recodeAddress'
Andrei Eres's avatar
Andrei Eres committed
type Props = BaseProps & {
Andrei Eres's avatar
Andrei Eres committed
  address?: string
  genesisHash?: string | null
  name?: string
}

const defaultRecoded = {
  account: null,
  formatted: null,
  prefix: 42,
  type: DEFAULT_TYPE,
}

const Address: React.FC<Props> = ({
  address,
  className,
  genesisHash,
  name,
}) => {
Andrei Eres's avatar
Andrei Eres committed
  const [justCopied, setJustCopied] = useTimedReset<boolean>(false)
Andrei Eres's avatar
Andrei Eres committed
  const [recoded, setRecoded] = useState<Recoded>(defaultRecoded)
Andrei Eres's avatar
Andrei Eres committed
  const accounts = useStore(accountsStore) as AccountJson[]
Andrei Eres's avatar
Andrei Eres committed
  const chain = useMetadata(genesisHash || recoded.genesisHash, true)
  const iconTheme = (chain?.icon || 'polkadot') as IconTheme
  const nameLabel = name || recoded.account?.name || UNKNOWN
Andrei Eres's avatar
Andrei Eres committed
  const chainLabel = ` · ${chain?.definition.chain.replace(RELAY_CHAIN, '')}`
Andrei Eres's avatar
Andrei Eres committed
  const hashLabel =
    (justCopied && 'Copied') || recoded.formatted || address || UNKNOWN
  const onCopy = () => {
Andrei Eres's avatar
Andrei Eres committed
    if (justCopied) return

    navigator.clipboard
      .writeText(hashLabel)
      .then(() => setJustCopied(true))
      .catch(console.error)
Andrei Eres's avatar
Andrei Eres committed

  useEffect(() => {
    if (!address) return

    const recoded = recodeAddress(address, accounts, chain)
Andrei Eres's avatar
Andrei Eres committed
    recoded && setRecoded(recoded)
  }, [accounts, address, chain])
Andrei Eres's avatar
Andrei Eres committed

  return (
    <div className={className}>
Andrei Eres's avatar
Andrei Eres committed
      <div className='logo'>
        <Identicon
          prefix={recoded.prefix}
          theme={iconTheme}
          value={recoded.formatted || address}
          size={50}
        />
      </div>
      <div className='content'>
Andrei Eres's avatar
Andrei Eres committed
        <div className='name'>
          <span>{nameLabel}</span>
          {chain && <span className='chain'>{chainLabel}</span>}
        </div>
Andrei Eres's avatar
Andrei Eres committed
        <div
          className={`address highlighted ${justCopied && 'just-copied'}`}
          onClick={onCopy}
        >
          <div className='icon copy'>
            <img src={copyIcon} />
Andrei Eres's avatar
Andrei Eres committed
          </div>
Andrei Eres's avatar
Andrei Eres committed
          <div className='hash'>{hashLabel}</div>
        </div>
Andrei Eres's avatar
Andrei Eres committed
      </div>
    </div>
  )
}

export default styled(Address)`
Andrei Eres's avatar
Andrei Eres committed
  display: flex;
  position: relative;
  height: 3rem;
Andrei Eres's avatar
Andrei Eres committed
  background: ${({ theme }: Props) => theme.cardBgColor};
Andrei Eres's avatar
Andrei Eres committed
  border-radius: 0.2rem;
Andrei Eres's avatar
Andrei Eres committed

  .logo {
    padding: 0.25rem;
    padding-right: 0rem;
  }

  .logo svg {
    cursor: default;
  }

  .content {
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 0 0.5rem;
  }

  .name {
    margin-top: -0.1rem;
    margin-bottom: 0.1rem;
  }
Andrei Eres's avatar
Andrei Eres committed

  .address {
Andrei Eres's avatar
Andrei Eres committed
    display: flex;
    align-items: center;
    font-size: ${({ theme }: Props) => theme.smallFontSize};
Andrei Eres's avatar
Andrei Eres committed
    color: ${({ theme }: Props) => theme.fadedTextColor};
Andrei Eres's avatar
Andrei Eres committed
  .hash {
    padding: 0 0.2rem;
  }

  .icon {
    width: 1rem;
    height: 1rem;
  }

  .highlighted {
    border-radius: 0.2rem;
    transition: ${({ theme }: Props) => theme.transition};
    cursor: pointer;
  }

  .highlighted:hover {
    background: ${({ theme }: Props) => theme.hightlight};
Andrei Eres's avatar
Andrei Eres committed
  .highlighted.just-copied {
    background: none;
Andrei Eres's avatar
Andrei Eres committed
  }
Andrei Eres's avatar
Andrei Eres committed
  .chain {
    color: ${({ theme }: Props) => theme.fadedTextColor};
  }

Andrei Eres's avatar
Andrei Eres committed
  & + & {
    margin-top: 0.2rem;
  }
Andrei Eres's avatar
Andrei Eres committed
`