Commit 1e692491 authored by lynn's avatar lynn Committed by GitHub

fix: truncates to token details descriptions (#4450)

* inti

* respond to vm comments

* add paragraphs as per fred, respond vm comments

* respond last vm comment

* simplify

* move fn out of component

* fix messed up styling

* simplify

* simplify

* fred nits

* remove xtra token detail props

* fix sentence casing
parent b3639b34
...@@ -3,8 +3,8 @@ import styled, { useTheme } from 'styled-components/macro' ...@@ -3,8 +3,8 @@ import styled, { useTheme } from 'styled-components/macro'
import { LoadingBubble } from '../loading' import { LoadingBubble } from '../loading'
import { DeltaContainer, TokenPrice } from './PriceChart' import { DeltaContainer, TokenPrice } from './PriceChart'
import { import {
AboutContainer,
AboutHeader, AboutHeader,
AboutSection,
BreadcrumbNavLink, BreadcrumbNavLink,
ChartContainer, ChartContainer,
ChartHeader, ChartHeader,
...@@ -118,7 +118,7 @@ export default function LoadingTokenDetail() { ...@@ -118,7 +118,7 @@ export default function LoadingTokenDetail() {
</LoadingChartContainer> </LoadingChartContainer>
<Space heightSize={32} /> <Space heightSize={32} />
</ChartHeader> </ChartHeader>
<AboutSection> <AboutContainer>
<AboutHeader> <AboutHeader>
<SquareLoadingBubble /> <SquareLoadingBubble />
</AboutHeader> </AboutHeader>
...@@ -127,7 +127,7 @@ export default function LoadingTokenDetail() { ...@@ -127,7 +127,7 @@ export default function LoadingTokenDetail() {
<HalfLoadingBubble /> <HalfLoadingBubble />
<ResourcesContainer>{null}</ResourcesContainer> <ResourcesContainer>{null}</ResourcesContainer>
</AboutSection> </AboutContainer>
<StatsSection> <StatsSection>
<StatsLoadingContainer> <StatsLoadingContainer>
<StatPair> <StatPair>
......
...@@ -9,6 +9,7 @@ import { checkWarning } from 'constants/tokenSafety' ...@@ -9,6 +9,7 @@ import { checkWarning } from 'constants/tokenSafety'
import { chainIdToChainName, useTokenDetailQuery } from 'graphql/data/TokenDetailQuery' import { chainIdToChainName, useTokenDetailQuery } from 'graphql/data/TokenDetailQuery'
import { useCurrency, useIsUserAddedToken, useToken } from 'hooks/Tokens' import { useCurrency, useIsUserAddedToken, useToken } from 'hooks/Tokens'
import { useAtomValue } from 'jotai/utils' import { useAtomValue } from 'jotai/utils'
import { darken } from 'polished'
import { useCallback } from 'react' import { useCallback } from 'react'
import { useState } from 'react' import { useState } from 'react'
import { ArrowLeft, Heart, TrendingUp } from 'react-feather' import { ArrowLeft, Heart, TrendingUp } from 'react-feather'
...@@ -23,12 +24,6 @@ import { Wave } from './LoadingTokenDetail' ...@@ -23,12 +24,6 @@ import { Wave } from './LoadingTokenDetail'
import Resource from './Resource' import Resource from './Resource'
import ShareButton from './ShareButton' import ShareButton from './ShareButton'
export const AboutSection = styled.div`
display: flex;
flex-direction: column;
gap: 16px;
padding: 24px 0px;
`
export const AboutHeader = styled.span` export const AboutHeader = styled.span`
font-size: 28px; font-size: 28px;
line-height: 36px; line-height: 36px;
...@@ -174,6 +169,94 @@ const MissingData = styled.div` ...@@ -174,6 +169,94 @@ const MissingData = styled.div`
gap: 20px; gap: 20px;
` `
export const AboutContainer = styled.div`
gap: 16px;
padding: 24px 0px;
`
const TokenDescriptionContainer = styled.div`
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
max-height: fit-content;
padding-top: 16px;
line-height: 24px;
white-space: pre-wrap;
`
const TruncateDescriptionButton = styled.div`
color: ${({ theme }) => theme.textSecondary};
font-weight: 400;
font-size: 14px;
padding: 14px 0px;
&:hover,
&:focus {
color: ${({ theme }) => darken(0.1, theme.textSecondary)};
cursor: pointer;
}
`
const TRUNCATE_CHARACTER_COUNT = 400
type TokenDetailData = {
description: string | null | undefined
homepageUrl: string | null | undefined
twitterName: string | null | undefined
}
const truncateDescription = (desc: string) => {
//trim the string to the maximum length
let tokenDescriptionTruncated = desc.slice(0, TRUNCATE_CHARACTER_COUNT)
//re-trim if we are in the middle of a word
tokenDescriptionTruncated = `${tokenDescriptionTruncated.slice(
0,
Math.min(tokenDescriptionTruncated.length, tokenDescriptionTruncated.lastIndexOf(' '))
)}...`
return tokenDescriptionTruncated
}
export function AboutSection({ address, tokenDetailData }: { address: string; tokenDetailData: TokenDetailData }) {
const [isDescriptionTruncated, setIsDescriptionTruncated] = useState(true)
const shouldTruncate =
tokenDetailData && tokenDetailData.description
? tokenDetailData.description.length > TRUNCATE_CHARACTER_COUNT
: false
const tokenDescription =
tokenDetailData && tokenDetailData.description && shouldTruncate && isDescriptionTruncated
? truncateDescription(tokenDetailData.description)
: tokenDetailData.description
return (
<AboutContainer>
<AboutHeader>
<Trans>About</Trans>
</AboutHeader>
{(!tokenDetailData || !tokenDetailData.description) && (
<NoInfoAvailable>
<Trans>No token information available</Trans>
</NoInfoAvailable>
)}
<TokenDescriptionContainer>
{tokenDescription}
<TruncateDescriptionButton onClick={() => setIsDescriptionTruncated(!isDescriptionTruncated)}>
{isDescriptionTruncated ? <Trans>Read more</Trans> : <Trans>Hide</Trans>}
</TruncateDescriptionButton>
</TokenDescriptionContainer>
<ResourcesContainer>
<Resource name={'Etherscan'} link={`https://etherscan.io/address/${address}`} />
<Resource name={'Protocol info'} link={`https://info.uniswap.org/#/tokens/${address}`} />
{tokenDetailData?.homepageUrl && <Resource name={'Website'} link={tokenDetailData.homepageUrl} />}
{tokenDetailData?.twitterName && (
<Resource name={'Twitter'} link={`https://twitter.com/${tokenDetailData.twitterName}`} />
)}
</ResourcesContainer>
</AboutContainer>
)
}
export default function LoadedTokenDetail({ address }: { address: string }) { export default function LoadedTokenDetail({ address }: { address: string }) {
const token = useToken(address) const token = useToken(address)
const currency = useCurrency(address) const currency = useCurrency(address)
...@@ -193,6 +276,11 @@ export default function LoadedTokenDetail({ address }: { address: string }) { ...@@ -193,6 +276,11 @@ export default function LoadedTokenDetail({ address }: { address: string }) {
const networkBadgebackgroundColor = chainInfo?.backgroundColor const networkBadgebackgroundColor = chainInfo?.backgroundColor
const filterNetwork = useAtomValue(filterNetworkAtom) const filterNetwork = useAtomValue(filterNetworkAtom)
const tokenDetailData = useTokenDetailQuery(address, chainIdToChainName(filterNetwork)) const tokenDetailData = useTokenDetailQuery(address, chainIdToChainName(filterNetwork))
const relevantTokenDetailData = (({ description, homepageUrl, twitterName }) => ({
description,
homepageUrl,
twitterName,
}))(tokenDetailData)
// catch token error and loading state // catch token error and loading state
if (!token || !token.name || !token.symbol) { if (!token || !token.name || !token.symbol) {
...@@ -225,18 +313,7 @@ export default function LoadedTokenDetail({ address }: { address: string }) { ...@@ -225,18 +313,7 @@ export default function LoadedTokenDetail({ address }: { address: string }) {
</MissingChartData> </MissingChartData>
</ChartHeader> </ChartHeader>
<MissingData> <MissingData>
<AboutSection> <AboutSection address={address} tokenDetailData={relevantTokenDetailData} />
<AboutHeader>
<Trans>About</Trans>
</AboutHeader>
<NoInfoAvailable>
<Trans>No token information available</Trans>
</NoInfoAvailable>
<ResourcesContainer>
<Resource name={'Etherscan'} link={`https://etherscan.io/address/${address}`} />
<Resource name={'Protocol Info'} link={`https://info.uniswap.org/#/tokens/${address}`} />
</ResourcesContainer>
</AboutSection>
<StatsSection> <StatsSection>
<NoInfoAvailable> <NoInfoAvailable>
<Trans>No stats available</Trans> <Trans>No stats available</Trans>
...@@ -244,7 +321,7 @@ export default function LoadedTokenDetail({ address }: { address: string }) { ...@@ -244,7 +321,7 @@ export default function LoadedTokenDetail({ address }: { address: string }) {
</StatsSection> </StatsSection>
<ContractAddressSection> <ContractAddressSection>
<Contract> <Contract>
Contract Address Contract address
<ContractAddress> <ContractAddress>
<CopyContractAddress address={address} /> <CopyContractAddress address={address} />
</ContractAddress> </ContractAddress>
...@@ -293,20 +370,7 @@ export default function LoadedTokenDetail({ address }: { address: string }) { ...@@ -293,20 +370,7 @@ export default function LoadedTokenDetail({ address }: { address: string }) {
<ParentSize>{({ width, height }) => <PriceChart token={token} width={width} height={height} />}</ParentSize> <ParentSize>{({ width, height }) => <PriceChart token={token} width={width} height={height} />}</ParentSize>
</ChartContainer> </ChartContainer>
</ChartHeader> </ChartHeader>
<AboutSection> <AboutSection address={address} tokenDetailData={relevantTokenDetailData} />
<AboutHeader>
<Trans>About</Trans>
</AboutHeader>
{tokenDetailData.description}
<ResourcesContainer>
<Resource name={'Etherscan'} link={`https://etherscan.io/address/${address}`} />
<Resource name={'Protocol Info'} link={`https://info.uniswap.org/#/tokens/${address}`} />
{tokenDetailData.homepageUrl && <Resource name={'Website'} link={tokenDetailData.homepageUrl} />}
{tokenDetailData.twitterName && (
<Resource name={'Twitter'} link={`https://twitter.com/${tokenDetailData.twitterName}`} />
)}
</ResourcesContainer>
</AboutSection>
<StatsSection> <StatsSection>
<StatPair> <StatPair>
<Stat> <Stat>
...@@ -339,7 +403,7 @@ export default function LoadedTokenDetail({ address }: { address: string }) { ...@@ -339,7 +403,7 @@ export default function LoadedTokenDetail({ address }: { address: string }) {
</StatsSection> </StatsSection>
<ContractAddressSection> <ContractAddressSection>
<Contract> <Contract>
Contract Address Contract address
<ContractAddress> <ContractAddress>
<CopyContractAddress address={address} /> <CopyContractAddress address={address} />
</ContractAddress> </ContractAddress>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment