Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
I
interface
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
LuckySwap
interface
Commits
7930709b
Unverified
Commit
7930709b
authored
Sep 14, 2022
by
cartcrom
Committed by
GitHub
Sep 14, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor: unnesting token details into sectioned components (#4621)
finished refactoring
parent
6fe2c92c
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
411 additions
and
408 deletions
+411
-408
index.tsx
src/components/TokenSafety/index.tsx
+7
-1
About.tsx
src/components/Tokens/TokenDetails/About.tsx
+103
-0
AddressSection.tsx
src/components/Tokens/TokenDetails/AddressSection.tsx
+36
-0
BreadcrumbNavLink.tsx
src/components/Tokens/TokenDetails/BreadcrumbNavLink.tsx
+17
-0
ChartSection.tsx
src/components/Tokens/TokenDetails/ChartSection.tsx
+117
-0
LoadingTokenDetails.tsx
src/components/Tokens/TokenDetails/LoadingTokenDetails.tsx
+28
-27
StatsSection.tsx
src/components/Tokens/TokenDetails/StatsSection.tsx
+67
-0
TokenDetail.tsx
src/components/Tokens/TokenDetails/TokenDetail.tsx
+0
-281
TokenDetailContainers.tsx
src/components/Tokens/TokenDetails/TokenDetailContainers.tsx
+0
-81
App.tsx
src/pages/App.tsx
+1
-1
index.tsx
src/pages/TokenDetails/index.tsx
+35
-17
No files found.
src/components/TokenSafety/index.tsx
View file @
7930709b
...
...
@@ -265,7 +265,13 @@ export default function TokenSafety({
</
InfoText
>
</
ShortColumn
>
<
LinkColumn
>
{
urls
}
</
LinkColumn
>
<
Buttons
warning=
{
displayWarning
}
onContinue=
{
acknowledge
}
onCancel=
{
onCancel
}
showCancel=
{
showCancel
}
/>
<
Buttons
warning=
{
displayWarning
}
onContinue=
{
acknowledge
}
onCancel=
{
onCancel
}
onBlocked=
{
onBlocked
}
showCancel=
{
showCancel
}
/>
</
Container
>
</
Wrapper
>
)
...
...
src/components/Tokens/TokenDetails/About.tsx
0 → 100644
View file @
7930709b
import
{
Trans
}
from
'
@lingui/macro
'
import
{
darken
}
from
'
polished
'
import
{
useState
}
from
'
react
'
import
styled
from
'
styled-components/macro
'
import
Resource
from
'
./Resource
'
const
NoInfoAvailable
=
styled
.
span
`
color:
${({
theme
})
=>
theme
.
textTertiary
}
;
font-weight: 400;
font-size: 16px;
`
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-top: 14px;
&:hover,
&:focus {
color:
${({
theme
})
=>
darken
(
0.1
,
theme
.
textSecondary
)}
;
cursor: pointer;
}
`
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
}
const
TRUNCATE_CHARACTER_COUNT
=
400
export
const
AboutContainer
=
styled
.
div
`
gap: 16px;
padding: 24px 0px;
`
export
const
AboutHeader
=
styled
.
span
`
font-size: 28px;
line-height: 36px;
`
export
const
ResourcesContainer
=
styled
.
div
`
display: flex;
padding-top: 12px;
gap: 14px;
`
type
AboutSectionProps
=
{
address
:
string
description
?:
string
|
null
|
undefined
homepageUrl
?:
string
|
null
|
undefined
twitterName
?:
string
|
null
|
undefined
}
export
function
AboutSection
({
address
,
description
,
homepageUrl
,
twitterName
}:
AboutSectionProps
)
{
const
[
isDescriptionTruncated
,
setIsDescriptionTruncated
]
=
useState
(
true
)
const
shouldTruncate
=
!!
description
&&
description
.
length
>
TRUNCATE_CHARACTER_COUNT
const
tokenDescription
=
shouldTruncate
&&
isDescriptionTruncated
?
truncateDescription
(
description
)
:
description
return
(
<
AboutContainer
>
<
AboutHeader
>
<
Trans
>
About
</
Trans
>
</
AboutHeader
>
<
TokenDescriptionContainer
>
{
!
description
&&
(
<
NoInfoAvailable
>
<
Trans
>
No token information available
</
Trans
>
</
NoInfoAvailable
>
)
}
{
tokenDescription
}
{
shouldTruncate
&&
(
<
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}`
}
/>
{
homepageUrl
&&
<
Resource
name=
{
'
Website
'
}
link=
{
homepageUrl
}
/>
}
{
twitterName
&&
<
Resource
name=
{
'
Twitter
'
}
link=
{
`https://twitter.com/${twitterName}`
}
/>
}
</
ResourcesContainer
>
</
AboutContainer
>
)
}
src/components/Tokens/TokenDetails/AddressSection.tsx
0 → 100644
View file @
7930709b
import
{
Trans
}
from
'
@lingui/macro
'
import
styled
from
'
styled-components/macro
'
import
{
CopyContractAddress
}
from
'
theme
'
export
const
ContractAddressSection
=
styled
.
div
`
display: flex;
flex-direction: column;
color:
${({
theme
})
=>
theme
.
textSecondary
}
;
font-weight: 600;
font-size: 14px;
gap: 4px;
padding: 36px 0px;
`
const
ContractAddress
=
styled
.
button
`
display: flex;
color:
${({
theme
})
=>
theme
.
textPrimary
}
;
gap: 10px;
align-items: center;
background: transparent;
border: none;
min-height: 38px;
padding: 0px;
cursor: pointer;
`
export
default
function
AddressSection
({
address
}:
{
address
:
string
})
{
return
(
<
ContractAddressSection
>
<
Trans
>
Contract address
</
Trans
>
<
ContractAddress
>
<
CopyContractAddress
address=
{
address
}
/>
</
ContractAddress
>
</
ContractAddressSection
>
)
}
src/components/Tokens/TokenDetails/BreadcrumbNavLink.tsx
0 → 100644
View file @
7930709b
import
{
Link
}
from
'
react-router-dom
'
import
styled
from
'
styled-components/macro
'
export
const
BreadcrumbNavLink
=
styled
(
Link
)
`
display: flex;
color:
${({
theme
})
=>
theme
.
textSecondary
}
;
font-size: 14px;
line-height: 20px;
align-items: center;
gap: 4px;
text-decoration: none;
margin-bottom: 16px;
&:hover {
color:
${({
theme
})
=>
theme
.
textTertiary
}
;
}
`
src/components/Tokens/TokenDetails/ChartSection.tsx
0 → 100644
View file @
7930709b
import
{
Trans
}
from
'
@lingui/macro
'
import
{
Token
}
from
'
@uniswap/sdk-core
'
import
{
ParentSize
}
from
'
@visx/responsive
'
import
{
useWeb3React
}
from
'
@web3-react/core
'
import
CurrencyLogo
from
'
components/CurrencyLogo
'
import
{
VerifiedIcon
}
from
'
components/TokenSafety/TokenSafetyIcon
'
import
{
getChainInfo
}
from
'
constants/chainInfo
'
import
{
nativeOnChain
,
WRAPPED_NATIVE_CURRENCY
}
from
'
constants/tokens
'
import
{
checkWarning
}
from
'
constants/tokenSafety
'
import
{
SingleTokenData
}
from
'
graphql/data/Token
'
import
{
useCurrency
}
from
'
hooks/Tokens
'
import
styled
from
'
styled-components/macro
'
import
{
useIsFavorited
,
useToggleFavorite
}
from
'
../state
'
import
{
ClickFavorited
,
FavoriteIcon
}
from
'
../TokenTable/TokenRow
'
import
PriceChart
from
'
./PriceChart
'
import
ShareButton
from
'
./ShareButton
'
export
const
ChartHeader
=
styled
.
div
`
width: 100%;
display: flex;
flex-direction: column;
color:
${({
theme
})
=>
theme
.
textPrimary
}
;
gap: 4px;
margin-bottom: 24px;
`
export
const
TokenInfoContainer
=
styled
.
div
`
display: flex;
justify-content: space-between;
align-items: center;
`
export
const
ChartContainer
=
styled
.
div
`
display: flex;
height: 436px;
align-items: center;
`
export
const
TokenNameCell
=
styled
.
div
`
display: flex;
gap: 8px;
font-size: 20px;
line-height: 28px;
align-items: center;
`
const
TokenSymbol
=
styled
.
span
`
text-transform: uppercase;
color:
${({
theme
})
=>
theme
.
textSecondary
}
;
`
const
TokenActions
=
styled
.
div
`
display: flex;
gap: 16px;
color:
${({
theme
})
=>
theme
.
textSecondary
}
;
`
const
NetworkBadge
=
styled
.
div
<
{
networkColor
?:
string
;
backgroundColor
?:
string
}
>
`
border-radius: 5px;
padding: 4px 8px;
font-weight: 600;
font-size: 12px;
line-height: 12px;
color:
${({
theme
,
networkColor
})
=>
networkColor
??
theme
.
textPrimary
};
background
-
color
:
$
{({
theme
,
backgroundColor
})
=>
backgroundColor
??
theme
.
backgroundSurface
};
`
export default function ChartSection({ token, tokenData }: { token: Token; tokenData: SingleTokenData | undefined }) {
const { chainId: connectedChainId } = useWeb3React()
const isFavorited = useIsFavorited(token.address)
const toggleFavorite = useToggleFavorite(token.address)
const chainInfo = getChainInfo(token?.chainId)
const networkLabel = chainInfo?.label
const networkBadgebackgroundColor = chainInfo?.backgroundColor
const warning = checkWarning(token.address)
let currency = useCurrency(token.address)
if (connectedChainId) {
const wrappedNativeCurrency = WRAPPED_NATIVE_CURRENCY[connectedChainId]
const isWrappedNativeToken = wrappedNativeCurrency?.address === token?.address
if (isWrappedNativeToken) {
currency = nativeOnChain(connectedChainId)
}
}
const tokenName = tokenData?.name ?? token?.name
const tokenSymbol = tokenData?.tokens?.[0]?.symbol ?? token?.symbol
return (
<ChartHeader>
<TokenInfoContainer>
<TokenNameCell>
<CurrencyLogo currency={currency} size={'32px'} symbol={tokenSymbol} />
{tokenName ?? <Trans>Name not found</Trans>}
<TokenSymbol>{tokenSymbol ?? <Trans>Symbol not found</Trans>}</TokenSymbol>
{!warning && <VerifiedIcon size="20px" />}
{networkBadgebackgroundColor && (
<NetworkBadge networkColor={chainInfo?.color} backgroundColor={networkBadgebackgroundColor}>
{networkLabel}
</NetworkBadge>
)}
</TokenNameCell>
<TokenActions>
{tokenName && tokenSymbol && (
<ShareButton tokenName={tokenName} tokenSymbol={tokenSymbol} tokenAddress={token.address} />
)}
<ClickFavorited onClick={toggleFavorite}>
<FavoriteIcon isFavorited={isFavorited} />
</ClickFavorited>
</TokenActions>
</TokenInfoContainer>
<ChartContainer>
<ParentSize>
{({ width, height }) => (
<PriceChart tokenAddress={token.address} width={width} height={height} priceData={tokenData?.prices?.[0]} />
)}
</ParentSize>
</ChartContainer>
</ChartHeader>
)
}
src/components/Tokens/TokenDetails/LoadingTokenDetail.tsx
→
src/components/Tokens/TokenDetails/LoadingTokenDetail
s
.tsx
View file @
7930709b
import
{
Footer
,
LeftPanel
,
RightPanel
,
TokenDetailsLayout
}
from
'
pages/TokenDetails
'
import
styled
,
{
useTheme
}
from
'
styled-components/macro
'
import
{
LoadingBubble
}
from
'
../loading
'
import
{
AboutContainer
,
AboutHeader
,
ResourcesContainer
}
from
'
./About
'
import
{
ContractAddressSection
}
from
'
./AddressSection
'
import
{
BreadcrumbNavLink
}
from
'
./BreadcrumbNavLink
'
import
{
ChartContainer
,
ChartHeader
,
TokenInfoContainer
,
TokenNameCell
}
from
'
./ChartSection
'
import
{
DeltaContainer
,
TokenPrice
}
from
'
./PriceChart
'
import
{
AboutContainer
,
AboutHeader
,
BreadcrumbNavLink
,
ChartContainer
,
ChartHeader
,
ContractAddressSection
,
ResourcesContainer
,
Stat
,
StatPair
,
StatsSection
,
TokenInfoContainer
,
TokenNameCell
,
TopArea
,
}
from
'
./TokenDetailContainers
'
import
{
StatPair
,
StatWrapper
,
TokenStatsSection
}
from
'
./StatsSection
'
const
LoadingChartContainer
=
styled
(
ChartContainer
)
`
height: 336px;
...
...
@@ -90,7 +81,7 @@ export function Wave() {
/* Loading State: row component with loading bubbles */
export
default
function
LoadingTokenDetail
()
{
return
(
<
TopArea
>
<
LeftPanel
>
<
BreadcrumbNavLink
to=
"/explore"
>
<
Space
heightSize=
{
20
}
/>
</
BreadcrumbNavLink
>
...
...
@@ -120,30 +111,30 @@ export default function LoadingTokenDetail() {
</
LoadingChartContainer
>
<
Space
heightSize=
{
32
}
/>
</
ChartHeader
>
<
StatsSection
>
<
Token
StatsSection
>
<
StatsLoadingContainer
>
<
StatPair
>
<
Stat
>
<
Stat
Wrapper
>
<
HalfLoadingBubble
/>
<
StatLoadingBubble
/>
</
Stat
>
<
Stat
>
</
Stat
Wrapper
>
<
Stat
Wrapper
>
<
HalfLoadingBubble
/>
<
StatLoadingBubble
/>
</
Stat
>
</
Stat
Wrapper
>
</
StatPair
>
<
StatPair
>
<
Stat
>
<
Stat
Wrapper
>
<
HalfLoadingBubble
/>
<
StatLoadingBubble
/>
</
Stat
>
<
Stat
>
</
Stat
Wrapper
>
<
Stat
Wrapper
>
<
HalfLoadingBubble
/>
<
StatLoadingBubble
/>
</
Stat
>
</
Stat
Wrapper
>
</
StatPair
>
</
StatsLoadingContainer
>
</
StatsSection
>
</
Token
StatsSection
>
<
AboutContainer
>
<
AboutHeader
>
<
SquareLoadingBubble
/>
...
...
@@ -155,6 +146,16 @@ export default function LoadingTokenDetail() {
<
ResourcesContainer
>
{
null
}
</
ResourcesContainer
>
</
AboutContainer
>
<
ContractAddressSection
>
{
null
}
</
ContractAddressSection
>
</
TopArea
>
</
LeftPanel
>
)
}
export
function
LoadingTokenDetails
()
{
return
(
<
TokenDetailsLayout
>
<
LoadingTokenDetail
/>
<
RightPanel
/>
<
Footer
/>
</
TokenDetailsLayout
>
)
}
src/components/Tokens/TokenDetails/StatsSection.tsx
0 → 100644
View file @
7930709b
import
{
Trans
}
from
'
@lingui/macro
'
import
{
ReactNode
}
from
'
react
'
import
styled
from
'
styled-components/macro
'
import
{
formatDollarAmount
}
from
'
utils/formatDollarAmt
'
export
const
StatWrapper
=
styled
.
div
`
display: flex;
flex-direction: column;
color:
${({
theme
})
=>
theme
.
textSecondary
}
;
font-size: 14px;
min-width: 168px;
flex: 1;
gap: 4px;
padding: 24px 0px;
`
export
const
TokenStatsSection
=
styled
.
div
`
display: flex;
flex-wrap: wrap;
`
export
const
StatPair
=
styled
.
div
`
display: flex;
flex: 1;
flex-wrap: wrap;
`
const
StatPrice
=
styled
.
span
`
font-size: 28px;
color:
${({
theme
})
=>
theme
.
textPrimary
}
;
`
const
NoData
=
styled
.
div
`
color:
${({
theme
})
=>
theme
.
textTertiary
}
;
`
type
NumericStat
=
number
|
undefined
|
null
function
Stat
({
value
,
title
}:
{
value
:
NumericStat
;
title
:
ReactNode
})
{
return
(
<
StatWrapper
>
{
title
}
<
StatPrice
>
{
value
?
formatDollarAmount
(
value
)
:
'
-
'
}
</
StatPrice
>
</
StatWrapper
>
)
}
type
StatsSectionProps
=
{
marketCap
?:
NumericStat
volume24H
?:
NumericStat
priceLow52W
?:
NumericStat
priceHigh52W
?:
NumericStat
}
export
default
function
StatsSection
({
marketCap
,
volume24H
,
priceLow52W
,
priceHigh52W
}:
StatsSectionProps
)
{
if
(
marketCap
||
volume24H
||
priceLow52W
||
priceHigh52W
)
{
return
(
<
TokenStatsSection
>
<
StatPair
>
<
Stat
value=
{
marketCap
}
title=
{
<
Trans
>
Market Cap
</
Trans
>
}
/>
<
Stat
value=
{
volume24H
}
title=
{
<
Trans
>
24H volume
</
Trans
>
}
/>
</
StatPair
>
<
StatPair
>
<
Stat
value=
{
priceLow52W
}
title=
{
<
Trans
>
52W low
</
Trans
>
}
/>
<
Stat
value=
{
priceHigh52W
}
title=
{
<
Trans
>
52W high
</
Trans
>
}
/>
</
StatPair
>
</
TokenStatsSection
>
)
}
else
{
return
<
NoData
>
No stats available
</
NoData
>
}
}
src/components/Tokens/TokenDetails/TokenDetail.tsx
deleted
100644 → 0
View file @
6fe2c92c
import
{
Trans
}
from
'
@lingui/macro
'
import
{
ParentSize
}
from
'
@visx/responsive
'
import
{
useWeb3React
}
from
'
@web3-react/core
'
import
CurrencyLogo
from
'
components/CurrencyLogo
'
import
PriceChart
from
'
components/Tokens/TokenDetails/PriceChart
'
import
{
VerifiedIcon
}
from
'
components/TokenSafety/TokenSafetyIcon
'
import
{
getChainInfo
}
from
'
constants/chainInfo
'
import
{
nativeOnChain
,
WRAPPED_NATIVE_CURRENCY
}
from
'
constants/tokens
'
import
{
checkWarning
}
from
'
constants/tokenSafety
'
import
{
TokenQuery$data
}
from
'
graphql/data/__generated__/TokenQuery.graphql
'
import
{
useCurrency
,
useToken
}
from
'
hooks/Tokens
'
import
{
darken
}
from
'
polished
'
import
{
Suspense
}
from
'
react
'
import
{
useState
}
from
'
react
'
import
{
ArrowLeft
}
from
'
react-feather
'
import
styled
from
'
styled-components/macro
'
import
{
CopyContractAddress
}
from
'
theme
'
import
{
formatDollarAmount
}
from
'
utils/formatDollarAmt
'
import
{
useIsFavorited
,
useToggleFavorite
}
from
'
../state
'
import
{
ClickFavorited
,
FavoriteIcon
}
from
'
../TokenTable/TokenRow
'
import
LoadingTokenDetail
from
'
./LoadingTokenDetail
'
import
Resource
from
'
./Resource
'
import
ShareButton
from
'
./ShareButton
'
import
{
AboutContainer
,
AboutHeader
,
BreadcrumbNavLink
,
ChartContainer
,
ChartHeader
,
ContractAddressSection
,
ResourcesContainer
,
Stat
,
StatPair
,
StatsSection
,
TokenInfoContainer
,
TokenNameCell
,
TopArea
,
}
from
'
./TokenDetailContainers
'
const
ContractAddress
=
styled
.
button
`
display: flex;
color:
${({
theme
})
=>
theme
.
textPrimary
}
;
gap: 10px;
align-items: center;
background: transparent;
border: none;
min-height: 38px;
padding: 0px;
cursor: pointer;
`
const
Contract
=
styled
.
div
`
display: flex;
flex-direction: column;
color:
${({
theme
})
=>
theme
.
textSecondary
}
;
font-size: 14px;
gap: 4px;
`
const
StatPrice
=
styled
.
span
`
font-size: 28px;
color:
${({
theme
})
=>
theme
.
textPrimary
}
;
`
const
TokenActions
=
styled
.
div
`
display: flex;
gap: 16px;
color:
${({
theme
})
=>
theme
.
textSecondary
}
;
`
const
TokenSymbol
=
styled
.
span
`
text-transform: uppercase;
color:
${({
theme
})
=>
theme
.
textSecondary
}
;
`
const
NetworkBadge
=
styled
.
div
<
{
networkColor
?:
string
;
backgroundColor
?:
string
}
>
`
border-radius: 5px;
padding: 4px 8px;
font-weight: 600;
font-size: 12px;
line-height: 12px;
color:
${({
theme
,
networkColor
})
=>
networkColor
??
theme
.
textPrimary
};
background
-
color
:
$
{({
theme
,
backgroundColor
})
=>
backgroundColor
??
theme
.
backgroundSurface
};
`
const NoInfoAvailable = styled.span`
color
:
$
{({
theme
})
=>
theme
.
textTertiary
};
font
-
weight
:
400
;
font
-
size
:
16
px
;
`
const TokenDescriptionContainer = styled.div`
overflow
:
hidden
;
text
-
overflow
:
ellipsis
;
max
-
width
:
100
%
;
max
-
height
:
fit
-
content
;
padding
-
top
:
16
px
;
line
-
height
:
24
px
;
white
-
space
:
pre
-
wrap
;
`
const TruncateDescriptionButton = styled.div`
color
:
$
{({
theme
})
=>
theme
.
textSecondary
}
;
font-weight: 400;
font-size: 14px;
padding-top: 14px;
&: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
>
<
TokenDescriptionContainer
>
{
(
!
tokenDetailData
||
!
tokenDetailData
.
description
)
&&
(
<
NoInfoAvailable
>
<
Trans
>
No token information available
</
Trans
>
</
NoInfoAvailable
>
)
}
{
tokenDescription
}
{
shouldTruncate
&&
(
<
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
,
query
}:
{
address
:
string
;
query
:
TokenQuery$data
})
{
const
{
chainId
:
connectedChainId
}
=
useWeb3React
()
const
token
=
useToken
(
address
)
let
currency
=
useCurrency
(
address
)
const
isFavorited
=
useIsFavorited
(
address
)
const
toggleFavorite
=
useToggleFavorite
(
address
)
const
warning
=
checkWarning
(
address
)
const
chainInfo
=
getChainInfo
(
token
?.
chainId
)
const
networkLabel
=
chainInfo
?.
label
const
networkBadgebackgroundColor
=
chainInfo
?.
backgroundColor
const
tokenData
=
query
.
tokenProjects
?.[
0
]
const
tokenDetails
=
tokenData
?.
markets
?.[
0
]
const
relevantTokenDetailData
=
{
description
:
tokenData
?.
description
,
homepageUrl
:
tokenData
?.
homepageUrl
,
twitterName
:
tokenData
?.
twitterName
,
}
if
(
!
token
||
!
token
.
name
||
!
token
.
symbol
||
!
connectedChainId
)
{
return
<
LoadingTokenDetail
/>
}
const
wrappedNativeCurrency
=
WRAPPED_NATIVE_CURRENCY
[
connectedChainId
]
const
isWrappedNativeToken
=
wrappedNativeCurrency
?.
address
===
token
.
address
if
(
isWrappedNativeToken
)
{
currency
=
nativeOnChain
(
connectedChainId
)
}
const
tokenName
=
tokenData
?.
name
??
token
.
name
const
tokenSymbol
=
tokenData
?.
tokens
?.[
0
]?.
symbol
??
token
.
symbol
return
(
<
Suspense
fallback=
{
<
LoadingTokenDetail
/>
}
>
<
TopArea
>
<
BreadcrumbNavLink
to=
"/tokens"
>
<
ArrowLeft
size=
{
14
}
/>
Tokens
</
BreadcrumbNavLink
>
<
ChartHeader
>
<
TokenInfoContainer
>
<
TokenNameCell
>
<
CurrencyLogo
currency=
{
currency
}
size=
{
'
32px
'
}
symbol=
{
tokenSymbol
}
/>
{
tokenName
??
<
Trans
>
Name not found
</
Trans
>
}
<
TokenSymbol
>
{
tokenSymbol
??
<
Trans
>
Symbol not found
</
Trans
>
}
</
TokenSymbol
>
{
!
warning
&&
<
VerifiedIcon
size=
"20px"
/>
}
{
networkBadgebackgroundColor
&&
(
<
NetworkBadge
networkColor=
{
chainInfo
?.
color
}
backgroundColor=
{
networkBadgebackgroundColor
}
>
{
networkLabel
}
</
NetworkBadge
>
)
}
</
TokenNameCell
>
<
TokenActions
>
{
tokenName
&&
tokenSymbol
&&
(
<
ShareButton
tokenName=
{
tokenName
}
tokenSymbol=
{
tokenSymbol
}
tokenAddress=
{
address
}
/>
)
}
<
ClickFavorited
onClick=
{
toggleFavorite
}
>
<
FavoriteIcon
isFavorited=
{
isFavorited
}
/>
</
ClickFavorited
>
</
TokenActions
>
</
TokenInfoContainer
>
<
ChartContainer
>
<
ParentSize
>
{
({
width
,
height
})
=>
(
<
PriceChart
tokenAddress=
{
address
}
width=
{
width
}
height=
{
height
}
priceData=
{
tokenData
?.
prices
?.[
0
]
}
/>
)
}
</
ParentSize
>
</
ChartContainer
>
</
ChartHeader
>
<
StatsSection
>
<
StatPair
>
<
Stat
>
<
Trans
>
Market cap
</
Trans
>
<
StatPrice
>
{
tokenDetails
?.
marketCap
?.
value
?
formatDollarAmount
(
tokenDetails
.
marketCap
?.
value
)
:
'
-
'
}
</
StatPrice
>
</
Stat
>
<
Stat
>
24H volume
<
StatPrice
>
{
tokenDetails
?.
volume1D
?.
value
?
formatDollarAmount
(
tokenDetails
.
volume1D
.
value
)
:
'
-
'
}
</
StatPrice
>
</
Stat
>
</
StatPair
>
<
StatPair
>
<
Stat
>
52W low
<
StatPrice
>
{
tokenDetails
?.
priceLow52W
?.
value
?
formatDollarAmount
(
tokenDetails
.
priceLow52W
?.
value
)
:
'
-
'
}
</
StatPrice
>
</
Stat
>
<
Stat
>
52W high
<
StatPrice
>
{
tokenDetails
?.
priceHigh52W
?.
value
?
formatDollarAmount
(
tokenDetails
.
priceHigh52W
?.
value
)
:
'
-
'
}
</
StatPrice
>
</
Stat
>
</
StatPair
>
</
StatsSection
>
<
AboutSection
address=
{
address
}
tokenDetailData=
{
relevantTokenDetailData
}
/>
<
ContractAddressSection
>
<
Contract
>
<
Trans
>
Contract address
</
Trans
>
<
ContractAddress
>
<
CopyContractAddress
address=
{
address
}
/>
</
ContractAddress
>
</
Contract
>
</
ContractAddressSection
>
</
TopArea
>
</
Suspense
>
)
}
src/components/Tokens/TokenDetails/TokenDetailContainers.tsx
deleted
100644 → 0
View file @
6fe2c92c
import
{
Link
}
from
'
react-router-dom
'
import
styled
from
'
styled-components/macro
'
export
const
AboutContainer
=
styled
.
div
`
gap: 16px;
padding: 24px 0px;
`
export
const
AboutHeader
=
styled
.
span
`
font-size: 28px;
line-height: 36px;
`
export
const
BreadcrumbNavLink
=
styled
(
Link
)
`
display: flex;
color:
${({
theme
})
=>
theme
.
textSecondary
}
;
font-size: 14px;
line-height: 20px;
align-items: center;
gap: 4px;
text-decoration: none;
margin-bottom: 16px;
&:hover {
color:
${({
theme
})
=>
theme
.
textTertiary
}
;
}
`
export
const
ChartHeader
=
styled
.
div
`
width: 100%;
display: flex;
flex-direction: column;
color:
${({
theme
})
=>
theme
.
textPrimary
}
;
gap: 4px;
margin-bottom: 24px;
`
export
const
ContractAddressSection
=
styled
.
div
`
padding: 36px 0px;
`
export
const
ChartContainer
=
styled
.
div
`
display: flex;
height: 436px;
align-items: center;
`
export
const
Stat
=
styled
.
div
`
display: flex;
flex-direction: column;
color:
${({
theme
})
=>
theme
.
textSecondary
}
;
font-size: 14px;
min-width: 168px;
flex: 1;
gap: 4px;
padding: 24px 0px;
`
export
const
StatsSection
=
styled
.
div
`
display: flex;
flex-wrap: wrap;
`
export
const
StatPair
=
styled
.
div
`
display: flex;
flex: 1;
flex-wrap: wrap;
`
export
const
TokenNameCell
=
styled
.
div
`
display: flex;
gap: 8px;
font-size: 20px;
line-height: 28px;
align-items: center;
`
export
const
TokenInfoContainer
=
styled
.
div
`
display: flex;
justify-content: space-between;
align-items: center;
`
export
const
TopArea
=
styled
.
div
`
max-width: 832px;
overflow: hidden;
`
export
const
ResourcesContainer
=
styled
.
div
`
display: flex;
padding-top: 12px;
gap: 14px;
`
src/pages/App.tsx
View file @
7930709b
...
...
@@ -23,6 +23,7 @@ import Header from '../components/Header'
import
Polling
from
'
../components/Header/Polling
'
import
Navbar
from
'
../components/NavBar
'
import
Popups
from
'
../components/Popups
'
import
{
LoadingTokenDetails
}
from
'
../components/Tokens/TokenDetails/LoadingTokenDetails
'
import
{
useIsExpertMode
}
from
'
../state/user/hooks
'
import
DarkModeQueryParamReader
from
'
../theme/DarkModeQueryParamReader
'
import
AddLiquidity
from
'
./AddLiquidity
'
...
...
@@ -40,7 +41,6 @@ import RemoveLiquidity from './RemoveLiquidity'
import
RemoveLiquidityV3
from
'
./RemoveLiquidity/V3
'
import
Swap
from
'
./Swap
'
import
{
OpenClaimAddressModalAndRedirectToSwap
,
RedirectPathToSwapOnly
,
RedirectToSwap
}
from
'
./Swap/redirects
'
import
{
LoadingTokenDetails
}
from
'
./TokenDetails
'
import
Tokens
,
{
LoadingTokens
}
from
'
./Tokens
'
const
TokenDetails
=
lazy
(()
=>
import
(
'
./TokenDetails
'
))
...
...
src/pages/TokenDetails/index.tsx
View file @
7930709b
...
...
@@ -6,11 +6,14 @@ import {
SMALL_MEDIA_BREAKPOINT
,
}
from
'
components/Tokens/constants
'
import
{
filterTimeAtom
}
from
'
components/Tokens/state
'
import
{
AboutSection
}
from
'
components/Tokens/TokenDetails/About
'
import
AddressSection
from
'
components/Tokens/TokenDetails/AddressSection
'
import
BalanceSummary
from
'
components/Tokens/TokenDetails/BalanceSummary
'
import
{
BreadcrumbNavLink
}
from
'
components/Tokens/TokenDetails/BreadcrumbNavLink
'
import
ChartSection
from
'
components/Tokens/TokenDetails/ChartSection
'
import
FooterBalanceSummary
from
'
components/Tokens/TokenDetails/FooterBalanceSummary
'
import
LoadingTokenDetail
from
'
components/Tokens/TokenDetails/LoadingTokenDetail
'
import
NetworkBalance
from
'
components/Tokens/TokenDetails/NetworkBalance
'
import
TokenDetail
from
'
components/Tokens/TokenDetails/TokenDetail
'
import
StatsSection
from
'
components/Tokens/TokenDetails/StatsSection
'
import
TokenSafetyMessage
from
'
components/TokenSafety/TokenSafetyMessage
'
import
TokenSafetyModal
from
'
components/TokenSafety/TokenSafetyModal
'
import
Widget
,
{
WIDGET_WIDTH
}
from
'
components/Widget
'
...
...
@@ -22,16 +25,17 @@ import { useIsUserAddedToken, useToken } from 'hooks/Tokens'
import
{
useNetworkTokenBalances
}
from
'
hooks/useNetworkTokenBalances
'
import
{
useAtomValue
}
from
'
jotai/utils
'
import
{
useCallback
,
useMemo
,
useState
}
from
'
react
'
import
{
ArrowLeft
}
from
'
react-feather
'
import
{
useNavigate
,
useParams
}
from
'
react-router-dom
'
import
styled
from
'
styled-components/macro
'
const
Footer
=
styled
.
div
`
export
const
Footer
=
styled
.
div
`
display: none;
@media only screen and (max-width:
${
LARGE_MEDIA_BREAKPOINT
}
) {
display: flex;
}
`
const
TokenDetailsLayout
=
styled
.
div
`
export
const
TokenDetailsLayout
=
styled
.
div
`
display: flex;
gap: 80px;
padding: 68px 20px;
...
...
@@ -64,7 +68,11 @@ const TokenDetailsLayout = styled.div`
padding-right: 8px;
}
`
const
RightPanel
=
styled
.
div
`
export
const
LeftPanel
=
styled
.
div
`
max-width: 832px;
overflow: hidden;
`
export
const
RightPanel
=
styled
.
div
`
display: flex;
flex-direction: column;
gap: 20px;
...
...
@@ -74,7 +82,6 @@ const RightPanel = styled.div`
display: none;
}
`
function
NetworkBalances
(
tokenAddress
:
string
|
undefined
)
{
return
useNetworkTokenBalances
({
address
:
tokenAddress
})
}
...
...
@@ -142,6 +149,9 @@ export default function TokenDetails() {
})
:
null
const
tokenData
=
query
.
tokenProjects
?.[
0
]
const
tokenDetails
=
tokenData
?.
markets
?.[
0
]
// TODO: Fix this logic to not automatically redirect on refresh, yet still catch invalid addresses
//const location = useLocation()
//if (token === undefined) {
...
...
@@ -152,7 +162,25 @@ export default function TokenDetails() {
<
TokenDetailsLayout
>
{
token
&&
(
<>
<
TokenDetail
address=
{
token
.
address
}
query=
{
query
}
/>
<
LeftPanel
>
<
BreadcrumbNavLink
to=
"/tokens"
>
<
ArrowLeft
size=
{
14
}
/>
Tokens
</
BreadcrumbNavLink
>
<
ChartSection
token=
{
token
}
tokenData=
{
tokenData
}
/>
<
StatsSection
marketCap=
{
tokenDetails
?.
marketCap
?.
value
}
volume24H=
{
tokenDetails
?.
volume1D
?.
value
}
priceHigh52W=
{
tokenDetails
?.
priceHigh52W
?.
value
}
priceLow52W=
{
tokenDetails
?.
priceLow52W
?.
value
}
/>
<
AboutSection
address=
{
token
.
address
}
description=
{
tokenData
?.
description
}
homepageUrl=
{
tokenData
?.
homepageUrl
}
twitterName=
{
tokenData
?.
twitterName
}
/>
<
AddressSection
address=
{
token
.
address
}
/>
</
LeftPanel
>
<
RightPanel
>
<
Widget
defaultToken=
{
token
??
undefined
}
onReviewSwapClick=
{
onReviewSwap
}
/>
{
tokenWarning
&&
<
TokenSafetyMessage
tokenAddress=
{
token
.
address
}
warning=
{
tokenWarning
}
/>
}
...
...
@@ -178,13 +206,3 @@ export default function TokenDetails() {
</
TokenDetailsLayout
>
)
}
export
function
LoadingTokenDetails
()
{
return
(
<
TokenDetailsLayout
>
<
LoadingTokenDetail
/>
<
RightPanel
/>
<
Footer
/>
</
TokenDetailsLayout
>
)
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment