feat: [info] add new tdp nav (#7531)

* feat: [info] add new tdp nav

* tidy up code

* pr review

* nit remove unused component style

* pr review

* lol extraneous extra
This commit is contained in:
Kristie Huang 2023-11-09 14:51:23 -05:00 committed by GitHub
parent 712f82cb1a
commit 876d3a1cc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 96 additions and 31 deletions

@ -1,6 +1,7 @@
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
import styled from 'styled-components' import styled from 'styled-components'
import { CopyContractAddress, ThemedText } from 'theme/components' import { CopyContractAddress, ThemedText } from 'theme/components'
import { shortenAddress } from 'utils/addresses'
const ContractAddressSection = styled.div` const ContractAddressSection = styled.div`
display: flex; display: flex;
@ -29,7 +30,7 @@ export default function AddressSection({ address }: { address: string }) {
<Trans>Contract address</Trans> <Trans>Contract address</Trans>
</ThemedText.SubHeaderSmall> </ThemedText.SubHeaderSmall>
<ContractAddress> <ContractAddress>
<CopyContractAddress address={address} /> <CopyContractAddress address={address} truncatedAddress={shortenAddress(address, 2, 3)} />
</ContractAddress> </ContractAddress>
</ContractAddressSection> </ContractAddressSection>
) )

@ -1,17 +1,31 @@
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import styled from 'styled-components' import styled, { css } from 'styled-components'
export const BreadcrumbNav = styled.div<{ isInfoTDPEnabled?: boolean }>`
display: flex;
color: ${({ theme }) => theme.neutral1};
${({ isInfoTDPEnabled }) =>
isInfoTDPEnabled
? css`
font-size: 16px;
line-height: 24px;
`
: css`
font-size: 14px;
line-height: 20px;
`}
align-items: center;
gap: 4px;
margin-bottom: 16px;
width: fit-content;
`
export const BreadcrumbNavLink = styled(Link)` export const BreadcrumbNavLink = styled(Link)`
display: flex; display: flex;
color: ${({ theme }) => theme.neutral2};
font-size: 14px;
line-height: 20px;
align-items: center; align-items: center;
gap: 4px; color: ${({ theme }) => theme.neutral2};
text-decoration: none;
margin-bottom: 16px;
transition-duration: ${({ theme }) => theme.transition.duration.fast}; transition-duration: ${({ theme }) => theme.transition.duration.fast};
width: fit-content; text-decoration: none;
&:hover { &:hover {
color: ${({ theme }) => theme.neutral3}; color: ${({ theme }) => theme.neutral3};

@ -1,6 +1,8 @@
import { Trans } from '@lingui/macro'
import { SwapSkeleton } from 'components/swap/SwapSkeleton' import { SwapSkeleton } from 'components/swap/SwapSkeleton'
import { useInfoExplorePageEnabled } from 'featureFlags/flags/infoExplore' import { useInfoExplorePageEnabled } from 'featureFlags/flags/infoExplore'
import { ArrowLeft } from 'react-feather' import { useInfoTDPEnabled } from 'featureFlags/flags/infoTDP'
import { ArrowLeft, ChevronRight } from 'react-feather'
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import styled, { useTheme } from 'styled-components' import styled, { useTheme } from 'styled-components'
import { ThemedText } from 'theme/components' import { ThemedText } from 'theme/components'
@ -8,7 +10,7 @@ import { textFadeIn } from 'theme/styles'
import { LoadingBubble } from '../loading' import { LoadingBubble } from '../loading'
import { AboutContainer, AboutHeader } from './About' import { AboutContainer, AboutHeader } from './About'
import { BreadcrumbNavLink } from './BreadcrumbNavLink' import { BreadcrumbNav, BreadcrumbNavLink } from './BreadcrumbNavLink'
import { StatPair, StatsWrapper, StatWrapper } from './StatsSection' import { StatPair, StatsWrapper, StatWrapper } from './StatsSection'
const SWAP_COMPONENT_WIDTH = 360 const SWAP_COMPONENT_WIDTH = 360
@ -92,6 +94,9 @@ const SquaredBubble = styled(DetailBubble)`
height: 32px; height: 32px;
border-radius: 8px; border-radius: 8px;
` `
const NavBubble = styled(DetailBubble)`
width: 169px;
`
const TokenLogoBubble = styled(DetailBubble)` const TokenLogoBubble = styled(DetailBubble)`
width: 32px; width: 32px;
height: 32px; height: 32px;
@ -222,13 +227,26 @@ function LoadingStats() {
export default function TokenDetailsSkeleton() { export default function TokenDetailsSkeleton() {
const { chainName } = useParams<{ chainName?: string }>() const { chainName } = useParams<{ chainName?: string }>()
const isInfoExplorePageEnabled = useInfoExplorePageEnabled() const isInfoExplorePageEnabled = useInfoExplorePageEnabled()
const isInfoTDPEnabled = useInfoTDPEnabled()
return ( return (
<LeftPanel> <LeftPanel>
<BreadcrumbNavLink {isInfoTDPEnabled ? (
to={(isInfoExplorePageEnabled ? '/explore' : '') + (chainName ? `/tokens/${chainName}` : `/tokens`)} <BreadcrumbNav isInfoTDPEnabled>
> <BreadcrumbNavLink to={`${isInfoExplorePageEnabled ? '/explore' : ''}/tokens/${chainName}`}>
<ArrowLeft size={14} /> Tokens <Trans>Explore</Trans> <ChevronRight size={14} /> <Trans>Tokens</Trans> <ChevronRight size={14} />
</BreadcrumbNavLink> </BreadcrumbNavLink>{' '}
<NavBubble />
</BreadcrumbNav>
) : (
<BreadcrumbNav>
<BreadcrumbNavLink
to={(isInfoExplorePageEnabled ? '/explore' : '') + (chainName ? `/tokens/${chainName}` : `/tokens`)}
>
<ArrowLeft size={14} /> Tokens
</BreadcrumbNavLink>
</BreadcrumbNav>
)}
<TokenInfoContainer> <TokenInfoContainer>
<TokenNameCell> <TokenNameCell>
<TokenLogoBubble /> <TokenLogoBubble />

@ -6,7 +6,7 @@ import { PortfolioLogo } from 'components/AccountDrawer/MiniPortfolio/PortfolioL
import { AboutSection } from 'components/Tokens/TokenDetails/About' import { AboutSection } from 'components/Tokens/TokenDetails/About'
import AddressSection from 'components/Tokens/TokenDetails/AddressSection' import AddressSection from 'components/Tokens/TokenDetails/AddressSection'
import BalanceSummary from 'components/Tokens/TokenDetails/BalanceSummary' import BalanceSummary from 'components/Tokens/TokenDetails/BalanceSummary'
import { BreadcrumbNavLink } from 'components/Tokens/TokenDetails/BreadcrumbNavLink' import { BreadcrumbNav, BreadcrumbNavLink } from 'components/Tokens/TokenDetails/BreadcrumbNavLink'
import ChartSection from 'components/Tokens/TokenDetails/ChartSection' import ChartSection from 'components/Tokens/TokenDetails/ChartSection'
import MobileBalanceSummaryFooter from 'components/Tokens/TokenDetails/MobileBalanceSummaryFooter' import MobileBalanceSummaryFooter from 'components/Tokens/TokenDetails/MobileBalanceSummaryFooter'
import ShareButton from 'components/Tokens/TokenDetails/ShareButton' import ShareButton from 'components/Tokens/TokenDetails/ShareButton'
@ -32,12 +32,13 @@ import { useOnGlobalChainSwitch } from 'hooks/useGlobalChainSwitch'
import { UNKNOWN_TOKEN_SYMBOL, useTokenFromActiveNetwork } from 'lib/hooks/useCurrency' import { UNKNOWN_TOKEN_SYMBOL, useTokenFromActiveNetwork } from 'lib/hooks/useCurrency'
import { Swap } from 'pages/Swap' import { Swap } from 'pages/Swap'
import { useCallback, useMemo, useState, useTransition } from 'react' import { useCallback, useMemo, useState, useTransition } from 'react'
import { ArrowLeft } from 'react-feather' import { ArrowLeft, ChevronRight } from 'react-feather'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { Field } from 'state/swap/actions' import { Field } from 'state/swap/actions'
import { SwapState } from 'state/swap/reducer' import { SwapState } from 'state/swap/reducer'
import styled from 'styled-components' import styled from 'styled-components'
import { isAddress } from 'utils' import { CopyContractAddress } from 'theme/components'
import { isAddress, shortenAddress } from 'utils'
import { addressesAreEquivalent } from 'utils/addressesAreEquivalent' import { addressesAreEquivalent } from 'utils/addressesAreEquivalent'
import { OnChangeTimePeriod } from './ChartSection' import { OnChangeTimePeriod } from './ChartSection'
@ -130,7 +131,6 @@ export default function TokenDetails({
}, {} as { [key: string]: string | undefined }) ?? {}, }, {} as { [key: string]: string | undefined }) ?? {},
[tokenQueryData] [tokenQueryData]
) )
const isInfoTDPEnabled = useInfoTDPEnabled()
const { token: detailedToken, didFetchFromChain } = useRelevantToken(address, pageChainId, tokenQueryData) const { token: detailedToken, didFetchFromChain } = useRelevantToken(address, pageChainId, tokenQueryData)
@ -139,6 +139,7 @@ export default function TokenDetails({
const navigate = useNavigate() const navigate = useNavigate()
const isInfoExplorePageEnabled = useInfoExplorePageEnabled() const isInfoExplorePageEnabled = useInfoExplorePageEnabled()
const isInfoTDPEnabled = useInfoTDPEnabled()
// Wrapping navigate in a transition prevents Suspense from unnecessarily showing fallbacks again. // Wrapping navigate in a transition prevents Suspense from unnecessarily showing fallbacks again.
const [isPending, startTokenTransition] = useTransition() const [isPending, startTokenTransition] = useTransition()
@ -210,6 +211,7 @@ export default function TokenDetails({
if (detailedToken === undefined || !address) { if (detailedToken === undefined || !address) {
return <InvalidTokenDetails pageChainId={pageChainId} isInvalidAddress={!address} /> return <InvalidTokenDetails pageChainId={pageChainId} isInvalidAddress={!address} />
} }
const tokenSymbolName = detailedToken && (detailedToken.symbol ?? <Trans>Symbol not found</Trans>)
return ( return (
<Trace <Trace
page={InterfacePageName.TOKEN_DETAILS_PAGE} page={InterfacePageName.TOKEN_DETAILS_PAGE}
@ -219,15 +221,37 @@ export default function TokenDetails({
<TokenDetailsLayout> <TokenDetailsLayout>
{detailedToken && !isPending ? ( {detailedToken && !isPending ? (
<LeftPanel> <LeftPanel>
<BreadcrumbNavLink to={`${isInfoExplorePageEnabled ? '/explore' : ''}/tokens/${chain.toLowerCase()}`}> {isInfoTDPEnabled ? (
<ArrowLeft data-testid="token-details-return-button" size={14} /> Tokens <BreadcrumbNav isInfoTDPEnabled>
</BreadcrumbNavLink> <BreadcrumbNavLink to={`${isInfoExplorePageEnabled ? '/explore' : ''}/tokens/${chain.toLowerCase()}`}>
<Trans>Explore</Trans> <ChevronRight size={14} /> <Trans>Tokens</Trans> <ChevronRight size={14} />
</BreadcrumbNavLink>{' '}
{tokenSymbolName}{' '}
{!detailedToken.isNative && (
<>
(
<CopyContractAddress
address={address}
showTruncatedOnly
truncatedAddress={shortenAddress(address)}
/>
)
</>
)}
</BreadcrumbNav>
) : (
<BreadcrumbNav>
<BreadcrumbNavLink to={`${isInfoExplorePageEnabled ? '/explore' : ''}/tokens/${chain.toLowerCase()}`}>
<ArrowLeft data-testid="token-details-return-button" size={14} /> Tokens
</BreadcrumbNavLink>
</BreadcrumbNav>
)}
<TokenInfoContainer data-testid="token-info-container"> <TokenInfoContainer data-testid="token-info-container">
<TokenNameCell> <TokenNameCell>
<PortfolioLogo currencies={[detailedToken]} chainId={detailedToken.chainId} size="32px" /> <PortfolioLogo currencies={[detailedToken]} chainId={detailedToken.chainId} size="32px" />
<TokenTitle> <TokenTitle>
{detailedToken.name ?? <Trans>Name not found</Trans>} {detailedToken.name ?? <Trans>Name not found</Trans>}
<TokenSymbol>{detailedToken.symbol ?? <Trans>Symbol not found</Trans>}</TokenSymbol> <TokenSymbol>{tokenSymbolName}</TokenSymbol>
</TokenTitle> </TokenTitle>
</TokenNameCell> </TokenNameCell>
<TokenActions> <TokenActions>

@ -243,13 +243,14 @@ export function CopyLinkIcon({ toCopy }: { toCopy: string }) {
) )
} }
const FullAddress = styled.span` const FullAddress = styled.span<{ showTruncated?: boolean }>`
${({ showTruncated }) => showTruncated && 'display: none;'}
@media only screen and (max-width: ${MOBILE_MEDIA_BREAKPOINT}) { @media only screen and (max-width: ${MOBILE_MEDIA_BREAKPOINT}) {
display: none; display: none;
} }
` `
const TruncatedAddress = styled.span` const TruncatedAddress = styled.span<{ showTruncated?: boolean }>`
display: none; display: ${({ showTruncated }) => (showTruncated ? 'flex' : 'none')};
@media only screen and (max-width: ${MOBILE_MEDIA_BREAKPOINT}) { @media only screen and (max-width: ${MOBILE_MEDIA_BREAKPOINT}) {
display: flex; display: flex;
} }
@ -273,7 +274,15 @@ const CopyContractAddressWrapper = styled.div`
display: flex; display: flex;
` `
export function CopyContractAddress({ address }: { address: string }) { export function CopyContractAddress({
address,
showTruncatedOnly,
truncatedAddress,
}: {
address: string
showTruncatedOnly?: boolean
truncatedAddress?: string
}) {
const [isCopied, setCopied] = useCopyClipboard() const [isCopied, setCopied] = useCopyClipboard()
const [tooltipX, setTooltipX] = useState<number | undefined>() const [tooltipX, setTooltipX] = useState<number | undefined>()
const copy = useCallback( const copy = useCallback(
@ -284,12 +293,11 @@ export function CopyContractAddress({ address }: { address: string }) {
[address, setCopied] [address, setCopied]
) )
const truncated = `${address.slice(0, 4)}...${address.slice(-3)}`
return ( return (
<CopyContractAddressWrapper onClick={copy}> <CopyContractAddressWrapper onClick={copy}>
<CopyAddressRow isClicked={isCopied}> <CopyAddressRow isClicked={isCopied}>
<FullAddress>{address}</FullAddress> <FullAddress showTruncated={showTruncatedOnly}>{address}</FullAddress>
<TruncatedAddress>{truncated}</TruncatedAddress> <TruncatedAddress showTruncated={showTruncatedOnly}>{truncatedAddress}</TruncatedAddress>
<Copy size={14} /> <Copy size={14} />
</CopyAddressRow> </CopyAddressRow>
{isCopied && <Tooltip isCopyContractTooltip tooltipX={tooltipX} />} {isCopied && <Tooltip isCopyContractTooltip tooltipX={tooltipX} />}