fix: chart design fixes & style updates (#4341)
* removed ticks outside of hover * simplifying copyhelper * finished implementing fred's feedback * addressed PR comments * fixed more of fred's feedback
This commit is contained in:
parent
8efc5af2bc
commit
d6d0a98afe
3
src/assets/svg/share.svg
Normal file
3
src/assets/svg/share.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="#fff">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4m14-7-5-5-5 5m5-5v12"/>
|
||||
</svg>
|
After Width: | Height: | Size: 257 B |
@ -1,64 +0,0 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import useCopyClipboard from 'hooks/useCopyClipboard'
|
||||
import React, { useCallback } from 'react'
|
||||
import { CheckCircle, Copy } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { LinkStyledButton } from 'theme'
|
||||
|
||||
const CopyIcon = styled(LinkStyledButton)`
|
||||
color: ${({ color, theme }) => color || theme.accentAction};
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
text-decoration: none;
|
||||
:hover,
|
||||
:active,
|
||||
:focus {
|
||||
text-decoration: none;
|
||||
color: ${({ color, theme }) => color || theme.accentAction};
|
||||
}
|
||||
`
|
||||
const StyledText = styled.span`
|
||||
margin-left: 0.25rem;
|
||||
${({ theme }) => theme.flexRowNoWrap};
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
const Copied = ({ iconSize }: { iconSize?: number }) => (
|
||||
<StyledText>
|
||||
<CheckCircle size={iconSize ?? '16'} />
|
||||
<StyledText>
|
||||
<Trans>Copied</Trans>
|
||||
</StyledText>
|
||||
</StyledText>
|
||||
)
|
||||
|
||||
const Icon = ({ iconSize }: { iconSize?: number }) => (
|
||||
<StyledText>
|
||||
<Copy size={iconSize ?? '16'} />
|
||||
</StyledText>
|
||||
)
|
||||
|
||||
interface BaseProps {
|
||||
toCopy: string
|
||||
color?: string
|
||||
iconSize?: number
|
||||
iconPosition?: 'left' | 'right'
|
||||
}
|
||||
export type CopyHelperProps = BaseProps & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, keyof BaseProps>
|
||||
|
||||
export default function CopyHelper({ color, toCopy, children, iconSize, iconPosition }: CopyHelperProps) {
|
||||
const [isCopied, setCopied] = useCopyClipboard()
|
||||
const copy = useCallback(() => {
|
||||
setCopied(toCopy)
|
||||
}, [toCopy, setCopied])
|
||||
|
||||
return (
|
||||
<CopyIcon onClick={copy} color={color}>
|
||||
{iconPosition === 'left' ? isCopied ? <Copied iconSize={iconSize} /> : <Icon iconSize={iconSize} /> : null}
|
||||
{iconPosition === 'left' && <> </>}
|
||||
{isCopied ? '' : children}
|
||||
{iconPosition === 'right' && <> </>}
|
||||
{iconPosition === 'right' ? isCopied ? <Copied iconSize={iconSize} /> : <Icon iconSize={iconSize} /> : null}
|
||||
</CopyIcon>
|
||||
)
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import CopyHelper from 'components/AccountDetails/Copy'
|
||||
import { getConnection, getConnectionName, getIsCoinbaseWallet, getIsMetaMask } from 'connection/utils'
|
||||
import { Context, useCallback, useContext } from 'react'
|
||||
import { ExternalLink as LinkIcon } from 'react-feather'
|
||||
@ -13,7 +12,7 @@ import { isMobile } from 'utils/userAgent'
|
||||
|
||||
import { ReactComponent as Close } from '../../assets/images/x.svg'
|
||||
import { clearAllTransactions } from '../../state/transactions/reducer'
|
||||
import { ExternalLink, LinkStyledButton, ThemedText } from '../../theme'
|
||||
import { CopyHelper, ExternalLink, LinkStyledButton, ThemedText } from '../../theme'
|
||||
import { shortenAddress } from '../../utils'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { ButtonSecondary } from '../Button'
|
||||
@ -130,11 +129,12 @@ const AccountControl = styled.div`
|
||||
`
|
||||
|
||||
const AddressLink = styled(ExternalLink)`
|
||||
font-size: 0.825rem;
|
||||
color: ${({ theme }) => theme.deprecated_text3};
|
||||
margin-left: 1rem;
|
||||
font-size: 0.825rem;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
text-decoration: none !important;
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
}
|
||||
@ -284,7 +284,7 @@ export default function AccountDetails({
|
||||
<AccountControl>
|
||||
<div>
|
||||
{account && (
|
||||
<CopyHelper toCopy={account} iconPosition="left">
|
||||
<CopyHelper toCopy={account} gap={6} iconSize={16} fontSize={14}>
|
||||
<Trans>Copy Address</Trans>
|
||||
</CopyHelper>
|
||||
)}
|
||||
|
@ -11,7 +11,7 @@ interface LineChartProps<T> {
|
||||
data: T[]
|
||||
getX: (t: T) => number
|
||||
getY: (t: T) => number
|
||||
marginTop: number
|
||||
marginTop?: number
|
||||
curve?: CurveFactory
|
||||
color?: Color
|
||||
strokeWidth: number
|
||||
|
@ -12,6 +12,7 @@ import { useAtom } from 'jotai'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { ArrowDownRight, ArrowUpRight } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { OPACITY_HOVER } from 'theme'
|
||||
import {
|
||||
dayHourFormatter,
|
||||
hourFormatter,
|
||||
@ -32,7 +33,7 @@ const TIME_DISPLAYS: [TimePeriod, string][] = [
|
||||
[TimePeriod.week, '1W'],
|
||||
[TimePeriod.month, '1M'],
|
||||
[TimePeriod.year, '1Y'],
|
||||
[TimePeriod.all, 'ALL'],
|
||||
[TimePeriod.all, 'All'],
|
||||
]
|
||||
|
||||
type PricePoint = { value: number; timestamp: number }
|
||||
@ -64,11 +65,6 @@ function getDelta(start: number, current: number) {
|
||||
return [formattedDelta, <StyledDownArrow size={16} key="arrow-down" />]
|
||||
}
|
||||
|
||||
export const ChartWrapper = styled.div`
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
`
|
||||
|
||||
export const ChartHeader = styled.div`
|
||||
position: absolute;
|
||||
`
|
||||
@ -111,6 +107,9 @@ const TimeButton = styled.button<{ active: boolean }>`
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: ${({ theme, active }) => (active ? theme.textPrimary : theme.textSecondary)};
|
||||
:hover {
|
||||
${({ active }) => !active && `opacity: ${OPACITY_HOVER};`}
|
||||
}
|
||||
`
|
||||
|
||||
function getTicks(startTimestamp: number, endTimestamp: number, numTicks = 5) {
|
||||
@ -142,7 +141,7 @@ function tickFormat(
|
||||
}
|
||||
}
|
||||
|
||||
const margin = { top: 86, bottom: 32, crosshair: 72 }
|
||||
const margin = { top: 86, bottom: 48, crosshair: 72 }
|
||||
const timeOptionsHeight = 44
|
||||
const crosshairDateOverhang = 80
|
||||
|
||||
@ -208,7 +207,7 @@ export function PriceChart({ width, height }: PriceChartProps) {
|
||||
const crosshairAtEdge = !!selected.xCoordinate && selected.xCoordinate > crosshairEdgeMax
|
||||
|
||||
return (
|
||||
<ChartWrapper>
|
||||
<>
|
||||
<ChartHeader>
|
||||
<TokenPrice>${selected.pricePoint.value.toFixed(2)}</TokenPrice>
|
||||
<DeltaContainer>
|
||||
@ -227,24 +226,24 @@ export function PriceChart({ width, height }: PriceChartProps) {
|
||||
width={graphWidth}
|
||||
height={graphHeight}
|
||||
>
|
||||
<AxisBottom
|
||||
scale={timeScale}
|
||||
stroke={theme.backgroundOutline}
|
||||
tickFormat={tickFormatter}
|
||||
tickStroke={theme.backgroundOutline}
|
||||
tickLength={4}
|
||||
tickTransform={'translate(0 -5)'}
|
||||
tickValues={ticks}
|
||||
top={graphHeight - 1}
|
||||
tickLabelProps={() => ({
|
||||
fill: theme.textSecondary,
|
||||
fontSize: 12,
|
||||
textAnchor: 'middle',
|
||||
transform: 'translate(0 -24)',
|
||||
})}
|
||||
/>
|
||||
{selected.xCoordinate !== null && (
|
||||
{selected.xCoordinate !== null ? (
|
||||
<g>
|
||||
<AxisBottom
|
||||
scale={timeScale}
|
||||
stroke={theme.backgroundOutline}
|
||||
tickFormat={tickFormatter}
|
||||
tickStroke={theme.backgroundOutline}
|
||||
tickLength={4}
|
||||
tickTransform={'translate(0 -5)'}
|
||||
tickValues={ticks}
|
||||
top={graphHeight - 1}
|
||||
tickLabelProps={() => ({
|
||||
fill: theme.textSecondary,
|
||||
fontSize: 12,
|
||||
textAnchor: 'middle',
|
||||
transform: 'translate(0 -24)',
|
||||
})}
|
||||
/>
|
||||
<text
|
||||
x={selected.xCoordinate + (crosshairAtEdge ? -4 : 4)}
|
||||
y={margin.crosshair + 10}
|
||||
@ -271,6 +270,8 @@ export function PriceChart({ width, height }: PriceChartProps) {
|
||||
strokeWidth={2}
|
||||
/>
|
||||
</g>
|
||||
) : (
|
||||
<AxisBottom scale={timeScale} stroke={theme.backgroundOutline} top={graphHeight - 1} hideTicks />
|
||||
)}
|
||||
<rect
|
||||
x={0}
|
||||
@ -293,7 +294,7 @@ export function PriceChart({ width, height }: PriceChartProps) {
|
||||
))}
|
||||
</TimeOptionsContainer>
|
||||
</TimeOptionsWrapper>
|
||||
</ChartWrapper>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,18 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import CopyHelper from 'components/AccountDetails/Copy'
|
||||
import Column from 'components/Column'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
import { AlertOctagon } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink, ThemedText } from 'theme'
|
||||
|
||||
import { CopyHelper } from '../../theme'
|
||||
import Modal from '../Modal'
|
||||
|
||||
const ContentWrapper = styled(Column)`
|
||||
align-items: center;
|
||||
margin: 32px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
`
|
||||
const WarningIcon = styled(AlertOctagon)`
|
||||
min-height: 22px;
|
||||
@ -49,7 +50,14 @@ export default function ConnectedAccountBlocked(props: ConnectedAccountBlockedPr
|
||||
<ThemedText.DeprecatedMain fontSize={12}>
|
||||
<Trans>If you believe this is an error, please send an email including your address to </Trans>{' '}
|
||||
</ThemedText.DeprecatedMain>
|
||||
<Copy iconSize={12} toCopy="compliance@uniswap.org" color={theme.deprecated_primary1} iconPosition="right">
|
||||
<Copy
|
||||
toCopy="compliance@uniswap.org"
|
||||
fontSize={14}
|
||||
iconSize={16}
|
||||
gap={6}
|
||||
color={theme.deprecated_primary1}
|
||||
iconPosition="right"
|
||||
>
|
||||
compliance@uniswap.org
|
||||
</Copy>
|
||||
</ContentWrapper>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import useTheme from 'hooks/useTheme'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { ChartWrapper, DeltaContainer, TokenPrice } from '../../Charts/PriceChart'
|
||||
import { DeltaContainer, TokenPrice } from '../../Charts/PriceChart'
|
||||
import { LoadingBubble } from '../loading'
|
||||
import {
|
||||
AboutHeader,
|
||||
@ -18,6 +19,11 @@ import {
|
||||
TopArea,
|
||||
} from './TokenDetail'
|
||||
|
||||
const LoadingChartContainer = styled(ChartContainer)`
|
||||
height: 336px;
|
||||
overflow: hidden;
|
||||
`
|
||||
|
||||
/* Loading state bubbles */
|
||||
const LoadingDetailBubble = styled(LoadingBubble)`
|
||||
height: 16px;
|
||||
@ -71,6 +77,15 @@ const Space = styled.div<{ heightSize: number }>`
|
||||
height: ${({ heightSize }) => `${heightSize}px`};
|
||||
`
|
||||
|
||||
function Wave() {
|
||||
const theme = useTheme()
|
||||
return (
|
||||
<svg width="416" height="160" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 0 80 Q 104 10, 208 80 T 416 80" stroke={theme.backgroundOutline} fill="transparent" strokeWidth="2" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
/* Loading State: row component with loading bubbles */
|
||||
export default function LoadingTokenDetail() {
|
||||
return (
|
||||
@ -85,35 +100,23 @@ export default function LoadingTokenDetail() {
|
||||
<TitleLoadingBubble />
|
||||
</TokenNameCell>
|
||||
</TokenInfoContainer>
|
||||
<ChartContainer>
|
||||
<ChartWrapper>
|
||||
<ChartHeader>
|
||||
<TokenPrice>
|
||||
<PriceLoadingBubble />
|
||||
</TokenPrice>
|
||||
<DeltaContainer>
|
||||
<Space heightSize={20} />
|
||||
</DeltaContainer>
|
||||
</ChartHeader>
|
||||
<TokenPrice>
|
||||
<PriceLoadingBubble />
|
||||
</TokenPrice>
|
||||
<DeltaContainer>
|
||||
<Space heightSize={20} />
|
||||
</DeltaContainer>
|
||||
<LoadingChartContainer>
|
||||
<div>
|
||||
<ChartAnimation>
|
||||
<svg width="416" height="160" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 0 80 Q 104 10, 208 80 T 416 80" stroke="#2e3138" fill="transparent" strokeWidth="2" />
|
||||
</svg>
|
||||
<svg width="416" height="160" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 0 80 Q 104 10, 208 80 T 416 80" stroke="#2e3138" fill="transparent" strokeWidth="2" />
|
||||
</svg>
|
||||
<svg width="416" height="160" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 0 80 Q 104 10, 208 80 T 416 80" stroke="#2e3138" fill="transparent" strokeWidth="2" />
|
||||
</svg>
|
||||
<svg width="416" height="160" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 0 80 Q 104 10, 208 80 T 416 80" stroke="#2e3138" fill="transparent" strokeWidth="2" />
|
||||
</svg>
|
||||
<svg width="416" height="160" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 0 80 Q 104 10, 208 80 T 416 80" stroke="#2e3138" fill="transparent" strokeWidth="2" />
|
||||
</svg>
|
||||
<Wave />
|
||||
<Wave />
|
||||
<Wave />
|
||||
<Wave />
|
||||
<Wave />
|
||||
</ChartAnimation>
|
||||
</ChartWrapper>
|
||||
</ChartContainer>
|
||||
</div>
|
||||
</LoadingChartContainer>
|
||||
<Space heightSize={32} />
|
||||
</ChartHeader>
|
||||
<AboutSection>
|
||||
|
@ -1,83 +1,64 @@
|
||||
import { useOnClickOutside } from 'hooks/useOnClickOutside'
|
||||
import { darken } from 'polished'
|
||||
import { useRef, useState } from 'react'
|
||||
import { Check, Link, Share, Twitter } from 'react-feather'
|
||||
import { useRef } from 'react'
|
||||
import { Twitter } from 'react-feather'
|
||||
import { useModalIsOpen, useToggleModal } from 'state/application/hooks'
|
||||
import { ApplicationModal } from 'state/application/reducer'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
import { Z_INDEX } from 'theme'
|
||||
import { ClickableStyle, CopyHelperRefType, OPACITY_CLICK, Z_INDEX } from 'theme'
|
||||
import { colors } from 'theme/colors'
|
||||
import { opacify } from 'theme/utils'
|
||||
|
||||
import { ReactComponent as ShareIcon } from '../../../assets/svg/share.svg'
|
||||
import { CopyHelper } from '../../../theme'
|
||||
|
||||
const TWITTER_WIDTH = 560
|
||||
const TWITTER_HEIGHT = 480
|
||||
|
||||
const ShareButtonDisplay = styled.div`
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: ${Z_INDEX.dropdown};
|
||||
|
||||
&:hover {
|
||||
color: ${({ theme }) => darken(0.1, theme.textSecondary)};
|
||||
}
|
||||
`
|
||||
|
||||
const Share = styled(ShareIcon)<{ open: boolean }>`
|
||||
stroke: ${({ theme }) => theme.textSecondary};
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
${ClickableStyle}
|
||||
${({ open }) => open && `opacity: ${OPACITY_CLICK} !important`};
|
||||
`
|
||||
|
||||
const ShareActions = styled.div`
|
||||
position: absolute;
|
||||
top: 28px;
|
||||
z-index: ${Z_INDEX.dropdown};
|
||||
width: 240px;
|
||||
top: 36px;
|
||||
right: 0px;
|
||||
padding: 8px 0px;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: fit-content;
|
||||
overflow: auto;
|
||||
padding: 8px;
|
||||
background-color: ${({ theme }) => theme.backgroundSurface};
|
||||
border: 1px solid ${({ theme }) => theme.backgroundOutline};
|
||||
border: 0.5px solid ${({ theme }) => theme.backgroundOutline};
|
||||
box-shadow: ${({ theme }) => theme.flyoutDropShadow};
|
||||
border-radius: 12px;
|
||||
`
|
||||
const ShareAction = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
gap: 8px;
|
||||
width: 200px;
|
||||
height: 48px;
|
||||
font-weight: 400;
|
||||
gap: 12px;
|
||||
height: 40px;
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => theme.backgroundModule};
|
||||
:hover {
|
||||
background-color: ${({ theme }) => opacify(10, theme.darkMode ? colors.gray200 : colors.gray300)};
|
||||
}
|
||||
`
|
||||
|
||||
const LinkCopied = styled.div<{ show: boolean }>`
|
||||
display: ${({ show }) => (show ? 'flex' : 'none')};
|
||||
width: 328px;
|
||||
height: 72px;
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
background-color: ${({ theme }) => theme.backgroundBackdrop};
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding: 24px 16px;
|
||||
position: absolute;
|
||||
right: 32px;
|
||||
bottom: 32px;
|
||||
font-size: 14px;
|
||||
gap: 8px;
|
||||
border: 1px solid rgba(153, 161, 189, 0.08);
|
||||
box-shadow: ${({ theme }) => theme.flyoutDropShadow};
|
||||
border-radius: 20px;
|
||||
animation: floatIn 0s ease-in 3s forwards;
|
||||
|
||||
@keyframes floatIn {
|
||||
to {
|
||||
width: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`
|
||||
interface TokenInfo {
|
||||
tokenName: string
|
||||
tokenSymbol: string
|
||||
@ -89,7 +70,6 @@ export default function ShareButton(tokenInfo: TokenInfo) {
|
||||
const open = useModalIsOpen(ApplicationModal.SHARE)
|
||||
const toggleShare = useToggleModal(ApplicationModal.SHARE)
|
||||
useOnClickOutside(node, open ? toggleShare : undefined)
|
||||
const [showCopied, setShowCopied] = useState(false)
|
||||
const positionX = (window.screen.width - TWITTER_WIDTH) / 2
|
||||
const positionY = (window.screen.height - TWITTER_HEIGHT) / 2
|
||||
|
||||
@ -101,41 +81,32 @@ export default function ShareButton(tokenInfo: TokenInfo) {
|
||||
`left=${positionX}, top=${positionY}, width=${TWITTER_WIDTH}, height=${TWITTER_HEIGHT}`
|
||||
)
|
||||
}
|
||||
const copyLink = () => {
|
||||
navigator.clipboard.writeText(window.location.href).then(
|
||||
function handleClipboardWriteSuccess() {
|
||||
setShowCopied(true)
|
||||
toggleShare()
|
||||
setTimeout(() => setShowCopied(false), 3000)
|
||||
},
|
||||
function error() {
|
||||
console.error('Clipboard copy failed.')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const copyHelperRef = useRef<CopyHelperRefType>(null)
|
||||
|
||||
return (
|
||||
<>
|
||||
<ShareButtonDisplay ref={node}>
|
||||
<Share size={18} onClick={toggleShare} aria-label={`ShareOptions`} />
|
||||
{open && (
|
||||
<ShareActions>
|
||||
<ShareAction onClick={copyLink}>
|
||||
<Link color={theme.textSecondary} size={18} />
|
||||
Copy link
|
||||
</ShareAction>
|
||||
<ShareButtonDisplay ref={node}>
|
||||
<Share onClick={toggleShare} aria-label={`ShareOptions`} open={open} />
|
||||
{open && (
|
||||
<ShareActions>
|
||||
<ShareAction onClick={() => copyHelperRef.current?.forceCopy()}>
|
||||
<CopyHelper
|
||||
link
|
||||
color={theme.textPrimary}
|
||||
iconPosition="left"
|
||||
toCopy={window.location.href}
|
||||
ref={copyHelperRef}
|
||||
>
|
||||
Copy Link
|
||||
</CopyHelper>
|
||||
</ShareAction>
|
||||
|
||||
<ShareAction onClick={shareTweet}>
|
||||
<Twitter color={theme.textSecondary} size={18} />
|
||||
Share to Twitter
|
||||
</ShareAction>
|
||||
</ShareActions>
|
||||
)}
|
||||
</ShareButtonDisplay>
|
||||
<LinkCopied show={showCopied}>
|
||||
<Check color={theme.accentSuccess} />
|
||||
Link Copied
|
||||
</LinkCopied>
|
||||
</>
|
||||
<ShareAction onClick={shareTweet}>
|
||||
<Twitter color={theme.textPrimary} size={20} strokeWidth={1.5} />
|
||||
Share to Twitter
|
||||
</ShareAction>
|
||||
</ShareActions>
|
||||
)}
|
||||
</ShareButtonDisplay>
|
||||
)
|
||||
}
|
||||
|
@ -8,14 +8,13 @@ import { getChainInfo } from 'constants/chainInfo'
|
||||
import { checkWarning } from 'constants/tokenSafety'
|
||||
import { useCurrency, useIsUserAddedToken, useToken } from 'hooks/Tokens'
|
||||
import { useAtomValue } from 'jotai/utils'
|
||||
import { darken } from 'polished'
|
||||
import { useCallback } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { ArrowLeft, Copy, Heart } from 'react-feather'
|
||||
import { ArrowLeft, Heart } from 'react-feather'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ClickableStyle, CopyContractAddress } from 'theme'
|
||||
|
||||
import { MOBILE_MEDIA_BREAKPOINT } from '../constants'
|
||||
import { favoritesAtom, useToggleFavorite } from '../state'
|
||||
import { ClickFavorited } from '../TokenTable/TokenRow'
|
||||
import Resource from './Resource'
|
||||
@ -62,10 +61,6 @@ const ContractAddress = styled.button`
|
||||
border: none;
|
||||
padding: 0px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: ${({ theme }) => darken(0.1, theme.textPrimary)};
|
||||
}
|
||||
`
|
||||
export const ContractAddressSection = styled.div`
|
||||
padding: 24px 0px;
|
||||
@ -114,7 +109,7 @@ export const TokenNameCell = styled.div`
|
||||
`
|
||||
const TokenActions = styled.div`
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
gap: 16px;
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
`
|
||||
export const TokenInfoContainer = styled.div`
|
||||
@ -132,17 +127,6 @@ export const ResourcesContainer = styled.div`
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
`
|
||||
const FullAddress = styled.span`
|
||||
@media only screen and (max-width: ${MOBILE_MEDIA_BREAKPOINT}) {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
const TruncatedAddress = styled.span`
|
||||
display: none;
|
||||
@media only screen and (max-width: ${MOBILE_MEDIA_BREAKPOINT}) {
|
||||
display: flex;
|
||||
}
|
||||
`
|
||||
const NetworkBadge = styled.div<{ networkColor?: string; backgroundColor?: string }>`
|
||||
border-radius: 5px;
|
||||
padding: 4px 8px;
|
||||
@ -152,9 +136,15 @@ const NetworkBadge = styled.div<{ networkColor?: string; backgroundColor?: strin
|
||||
color: ${({ theme, networkColor }) => networkColor ?? theme.textPrimary};
|
||||
background-color: ${({ theme, backgroundColor }) => backgroundColor ?? theme.backgroundSurface};
|
||||
`
|
||||
const FavoriteIcon = styled(Heart)<{ isFavorited: boolean }>`
|
||||
${ClickableStyle}
|
||||
height: 22px;
|
||||
width: 24px;
|
||||
color: ${({ isFavorited, theme }) => (isFavorited ? theme.accentAction : theme.textSecondary)};
|
||||
fill: ${({ isFavorited, theme }) => (isFavorited ? theme.accentAction : 'transparent')};
|
||||
`
|
||||
|
||||
export default function LoadedTokenDetail({ address }: { address: string }) {
|
||||
const theme = useTheme()
|
||||
const token = useToken(address)
|
||||
const currency = useCurrency(address)
|
||||
const favoriteTokens = useAtomValue<string[]>(favoritesAtom)
|
||||
@ -184,7 +174,6 @@ export default function LoadedTokenDetail({ address }: { address: string }) {
|
||||
'Ethereum is a decentralized computing platform that uses ETH (Ether) to pay transaction fees (gas). Developers can use Ethereum to run decentralized applications (dApps) and issue new crypto assets, known as Ethereum tokens.'
|
||||
const tokenMarketCap = '23.02B'
|
||||
const tokenVolume = '1.6B'
|
||||
const truncatedTokenAddress = `${address.slice(0, 4)}...${address.slice(-3)}`
|
||||
|
||||
return (
|
||||
<TopArea>
|
||||
@ -196,7 +185,7 @@ export default function LoadedTokenDetail({ address }: { address: string }) {
|
||||
<TokenNameCell>
|
||||
<CurrencyLogo currency={currency} size={'32px'} />
|
||||
{tokenName} <TokenSymbol>{tokenSymbol}</TokenSymbol>
|
||||
{!warning && <VerifiedIcon size="24px" />}
|
||||
{!warning && <VerifiedIcon size="20px" />}
|
||||
{networkBadgebackgroundColor && (
|
||||
<NetworkBadge networkColor={chainInfo?.color} backgroundColor={networkBadgebackgroundColor}>
|
||||
{networkLabel}
|
||||
@ -206,11 +195,7 @@ export default function LoadedTokenDetail({ address }: { address: string }) {
|
||||
<TokenActions>
|
||||
<ShareButton tokenName={tokenName} tokenSymbol={tokenSymbol} />
|
||||
<ClickFavorited onClick={toggleFavorite}>
|
||||
<Heart
|
||||
size={15}
|
||||
color={isFavorited ? theme.accentAction : theme.textSecondary}
|
||||
fill={isFavorited ? theme.accentAction : 'transparent'}
|
||||
/>
|
||||
<FavoriteIcon isFavorited={isFavorited} />
|
||||
</ClickFavorited>
|
||||
</TokenActions>
|
||||
</TokenInfoContainer>
|
||||
@ -235,7 +220,7 @@ export default function LoadedTokenDetail({ address }: { address: string }) {
|
||||
</Stat>
|
||||
<Stat>
|
||||
{/* TODO: connect to chart's selected time */}
|
||||
1h volume
|
||||
24H volume
|
||||
<StatPrice>${tokenVolume}</StatPrice>
|
||||
</Stat>
|
||||
</StatPair>
|
||||
@ -253,10 +238,8 @@ export default function LoadedTokenDetail({ address }: { address: string }) {
|
||||
<ContractAddressSection>
|
||||
<Contract>
|
||||
Contract Address
|
||||
<ContractAddress onClick={() => navigator.clipboard.writeText(address)}>
|
||||
<FullAddress>{address}</FullAddress>
|
||||
<TruncatedAddress>{truncatedTokenAddress}</TruncatedAddress>
|
||||
<Copy size={13} color={theme.textSecondary} />
|
||||
<ContractAddress>
|
||||
<CopyContractAddress address={address} />
|
||||
</ContractAddress>
|
||||
</Contract>
|
||||
</ContractAddressSection>
|
||||
|
167
src/graphql/__generated__/AllV3TicksQuery.graphql.ts
generated
Normal file
167
src/graphql/__generated__/AllV3TicksQuery.graphql.ts
generated
Normal file
@ -0,0 +1,167 @@
|
||||
/**
|
||||
* @generated SignedSource<<0becdf63598262462f6fa0cabb891ad0>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest, Query } from 'relay-runtime';
|
||||
export type AllV3TicksQuery$variables = {
|
||||
poolAddress: string;
|
||||
skip: number;
|
||||
};
|
||||
export type AllV3TicksQuery$data = {
|
||||
readonly ticks: ReadonlyArray<{
|
||||
readonly liquidityNet: any;
|
||||
readonly price0: any;
|
||||
readonly price1: any;
|
||||
readonly tick: any;
|
||||
}>;
|
||||
};
|
||||
export type AllV3TicksQuery = {
|
||||
response: AllV3TicksQuery$data;
|
||||
variables: AllV3TicksQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "poolAddress"
|
||||
},
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "skip"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 1000
|
||||
},
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "orderBy",
|
||||
"value": "tickIdx"
|
||||
},
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "skip",
|
||||
"variableName": "skip"
|
||||
},
|
||||
{
|
||||
"fields": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "poolAddress",
|
||||
"variableName": "poolAddress"
|
||||
}
|
||||
],
|
||||
"kind": "ObjectValue",
|
||||
"name": "where"
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
"alias": "tick",
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "tickIdx",
|
||||
"storageKey": null
|
||||
},
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "liquidityNet",
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "price0",
|
||||
"storageKey": null
|
||||
},
|
||||
v5 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "price1",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "AllV3TicksQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": "Tick",
|
||||
"kind": "LinkedField",
|
||||
"name": "ticks",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "AllV3TicksQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": "Tick",
|
||||
"kind": "LinkedField",
|
||||
"name": "ticks",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "9f2d65b1e565e3d0ecbe7b1f908ebc83",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "AllV3TicksQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query AllV3TicksQuery(\n $poolAddress: String!\n $skip: Int!\n) {\n ticks(first: 1000, skip: $skip, where: {poolAddress: $poolAddress}, orderBy: tickIdx) {\n tick: tickIdx\n liquidityNet\n price0\n price1\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "82709c11c929a8eb6caf2ab1df2b99cc";
|
||||
|
||||
export default node;
|
242
src/graphql/__generated__/FeeTierDistributionQuery.graphql.ts
generated
Normal file
242
src/graphql/__generated__/FeeTierDistributionQuery.graphql.ts
generated
Normal file
@ -0,0 +1,242 @@
|
||||
/**
|
||||
* @generated SignedSource<<5761481cf3bba524864626a7f965c0d7>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest, Query } from 'relay-runtime';
|
||||
export type FeeTierDistributionQuery$variables = {
|
||||
token0: string;
|
||||
token1: string;
|
||||
};
|
||||
export type FeeTierDistributionQuery$data = {
|
||||
readonly _meta: {
|
||||
readonly block: {
|
||||
readonly number: number;
|
||||
};
|
||||
} | null;
|
||||
readonly asToken0: ReadonlyArray<{
|
||||
readonly feeTier: any;
|
||||
readonly totalValueLockedToken0: any;
|
||||
readonly totalValueLockedToken1: any;
|
||||
}>;
|
||||
readonly asToken1: ReadonlyArray<{
|
||||
readonly feeTier: any;
|
||||
readonly totalValueLockedToken0: any;
|
||||
readonly totalValueLockedToken1: any;
|
||||
}>;
|
||||
};
|
||||
export type FeeTierDistributionQuery = {
|
||||
response: FeeTierDistributionQuery$data;
|
||||
variables: FeeTierDistributionQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "token0"
|
||||
},
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "token1"
|
||||
}
|
||||
],
|
||||
v1 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "_Meta_",
|
||||
"kind": "LinkedField",
|
||||
"name": "_meta",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "_Block_",
|
||||
"kind": "LinkedField",
|
||||
"name": "block",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "number",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
v2 = {
|
||||
"kind": "Literal",
|
||||
"name": "orderBy",
|
||||
"value": "totalValueLockedToken0"
|
||||
},
|
||||
v3 = {
|
||||
"kind": "Literal",
|
||||
"name": "orderDirection",
|
||||
"value": "desc"
|
||||
},
|
||||
v4 = [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"fields": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "token0",
|
||||
"variableName": "token0"
|
||||
},
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "token1",
|
||||
"variableName": "token1"
|
||||
}
|
||||
],
|
||||
"kind": "ObjectValue",
|
||||
"name": "where"
|
||||
}
|
||||
],
|
||||
v5 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "feeTier",
|
||||
"storageKey": null
|
||||
},
|
||||
v6 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "totalValueLockedToken0",
|
||||
"storageKey": null
|
||||
},
|
||||
v7 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "totalValueLockedToken1",
|
||||
"storageKey": null
|
||||
},
|
||||
v8 = [
|
||||
(v5/*: any*/),
|
||||
(v6/*: any*/),
|
||||
(v7/*: any*/)
|
||||
],
|
||||
v9 = [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"fields": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "token0",
|
||||
"variableName": "token1"
|
||||
},
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "token1",
|
||||
"variableName": "token0"
|
||||
}
|
||||
],
|
||||
"kind": "ObjectValue",
|
||||
"name": "where"
|
||||
}
|
||||
],
|
||||
v10 = [
|
||||
(v5/*: any*/),
|
||||
(v6/*: any*/),
|
||||
(v7/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "FeeTierDistributionQuery",
|
||||
"selections": [
|
||||
(v1/*: any*/),
|
||||
{
|
||||
"alias": "asToken0",
|
||||
"args": (v4/*: any*/),
|
||||
"concreteType": "Pool",
|
||||
"kind": "LinkedField",
|
||||
"name": "pools",
|
||||
"plural": true,
|
||||
"selections": (v8/*: any*/),
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": "asToken1",
|
||||
"args": (v9/*: any*/),
|
||||
"concreteType": "Pool",
|
||||
"kind": "LinkedField",
|
||||
"name": "pools",
|
||||
"plural": true,
|
||||
"selections": (v8/*: any*/),
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "FeeTierDistributionQuery",
|
||||
"selections": [
|
||||
(v1/*: any*/),
|
||||
{
|
||||
"alias": "asToken0",
|
||||
"args": (v4/*: any*/),
|
||||
"concreteType": "Pool",
|
||||
"kind": "LinkedField",
|
||||
"name": "pools",
|
||||
"plural": true,
|
||||
"selections": (v10/*: any*/),
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": "asToken1",
|
||||
"args": (v9/*: any*/),
|
||||
"concreteType": "Pool",
|
||||
"kind": "LinkedField",
|
||||
"name": "pools",
|
||||
"plural": true,
|
||||
"selections": (v10/*: any*/),
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "d989fcfb8fc9ef13bdc6de811e574284",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "FeeTierDistributionQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query FeeTierDistributionQuery(\n $token0: String!\n $token1: String!\n) {\n _meta {\n block {\n number\n }\n }\n asToken0: pools(orderBy: totalValueLockedToken0, orderDirection: desc, where: {token0: $token0, token1: $token1}) {\n feeTier\n totalValueLockedToken0\n totalValueLockedToken1\n id\n }\n asToken1: pools(orderBy: totalValueLockedToken0, orderDirection: desc, where: {token0: $token1, token1: $token0}) {\n feeTier\n totalValueLockedToken0\n totalValueLockedToken1\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "ac9cd4bfdc24db90dbb2bf8a7508009f";
|
||||
|
||||
export default node;
|
4892
src/graphql/schema/schema.graphql
Normal file
4892
src/graphql/schema/schema.graphql
Normal file
File diff suppressed because it is too large
Load Diff
167
src/hooks/graphql/__generated__/AllV3TicksQuery.graphql.ts
generated
Normal file
167
src/hooks/graphql/__generated__/AllV3TicksQuery.graphql.ts
generated
Normal file
@ -0,0 +1,167 @@
|
||||
/**
|
||||
* @generated SignedSource<<0becdf63598262462f6fa0cabb891ad0>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest, Query } from 'relay-runtime';
|
||||
export type AllV3TicksQuery$variables = {
|
||||
poolAddress: string;
|
||||
skip: number;
|
||||
};
|
||||
export type AllV3TicksQuery$data = {
|
||||
readonly ticks: ReadonlyArray<{
|
||||
readonly liquidityNet: any;
|
||||
readonly price0: any;
|
||||
readonly price1: any;
|
||||
readonly tick: any;
|
||||
}>;
|
||||
};
|
||||
export type AllV3TicksQuery = {
|
||||
response: AllV3TicksQuery$data;
|
||||
variables: AllV3TicksQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "poolAddress"
|
||||
},
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "skip"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 1000
|
||||
},
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "orderBy",
|
||||
"value": "tickIdx"
|
||||
},
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "skip",
|
||||
"variableName": "skip"
|
||||
},
|
||||
{
|
||||
"fields": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "poolAddress",
|
||||
"variableName": "poolAddress"
|
||||
}
|
||||
],
|
||||
"kind": "ObjectValue",
|
||||
"name": "where"
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
"alias": "tick",
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "tickIdx",
|
||||
"storageKey": null
|
||||
},
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "liquidityNet",
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "price0",
|
||||
"storageKey": null
|
||||
},
|
||||
v5 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "price1",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "AllV3TicksQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": "Tick",
|
||||
"kind": "LinkedField",
|
||||
"name": "ticks",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "AllV3TicksQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": "Tick",
|
||||
"kind": "LinkedField",
|
||||
"name": "ticks",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "9f2d65b1e565e3d0ecbe7b1f908ebc83",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "AllV3TicksQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query AllV3TicksQuery(\n $poolAddress: String!\n $skip: Int!\n) {\n ticks(first: 1000, skip: $skip, where: {poolAddress: $poolAddress}, orderBy: tickIdx) {\n tick: tickIdx\n liquidityNet\n price0\n price1\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "82709c11c929a8eb6caf2ab1df2b99cc";
|
||||
|
||||
export default node;
|
242
src/hooks/graphql/__generated__/FeeTierDistributionQuery.graphql.ts
generated
Normal file
242
src/hooks/graphql/__generated__/FeeTierDistributionQuery.graphql.ts
generated
Normal file
@ -0,0 +1,242 @@
|
||||
/**
|
||||
* @generated SignedSource<<5761481cf3bba524864626a7f965c0d7>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest, Query } from 'relay-runtime';
|
||||
export type FeeTierDistributionQuery$variables = {
|
||||
token0: string;
|
||||
token1: string;
|
||||
};
|
||||
export type FeeTierDistributionQuery$data = {
|
||||
readonly _meta: {
|
||||
readonly block: {
|
||||
readonly number: number;
|
||||
};
|
||||
} | null;
|
||||
readonly asToken0: ReadonlyArray<{
|
||||
readonly feeTier: any;
|
||||
readonly totalValueLockedToken0: any;
|
||||
readonly totalValueLockedToken1: any;
|
||||
}>;
|
||||
readonly asToken1: ReadonlyArray<{
|
||||
readonly feeTier: any;
|
||||
readonly totalValueLockedToken0: any;
|
||||
readonly totalValueLockedToken1: any;
|
||||
}>;
|
||||
};
|
||||
export type FeeTierDistributionQuery = {
|
||||
response: FeeTierDistributionQuery$data;
|
||||
variables: FeeTierDistributionQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "token0"
|
||||
},
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "token1"
|
||||
}
|
||||
],
|
||||
v1 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "_Meta_",
|
||||
"kind": "LinkedField",
|
||||
"name": "_meta",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "_Block_",
|
||||
"kind": "LinkedField",
|
||||
"name": "block",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "number",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
v2 = {
|
||||
"kind": "Literal",
|
||||
"name": "orderBy",
|
||||
"value": "totalValueLockedToken0"
|
||||
},
|
||||
v3 = {
|
||||
"kind": "Literal",
|
||||
"name": "orderDirection",
|
||||
"value": "desc"
|
||||
},
|
||||
v4 = [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"fields": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "token0",
|
||||
"variableName": "token0"
|
||||
},
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "token1",
|
||||
"variableName": "token1"
|
||||
}
|
||||
],
|
||||
"kind": "ObjectValue",
|
||||
"name": "where"
|
||||
}
|
||||
],
|
||||
v5 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "feeTier",
|
||||
"storageKey": null
|
||||
},
|
||||
v6 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "totalValueLockedToken0",
|
||||
"storageKey": null
|
||||
},
|
||||
v7 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "totalValueLockedToken1",
|
||||
"storageKey": null
|
||||
},
|
||||
v8 = [
|
||||
(v5/*: any*/),
|
||||
(v6/*: any*/),
|
||||
(v7/*: any*/)
|
||||
],
|
||||
v9 = [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"fields": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "token0",
|
||||
"variableName": "token1"
|
||||
},
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "token1",
|
||||
"variableName": "token0"
|
||||
}
|
||||
],
|
||||
"kind": "ObjectValue",
|
||||
"name": "where"
|
||||
}
|
||||
],
|
||||
v10 = [
|
||||
(v5/*: any*/),
|
||||
(v6/*: any*/),
|
||||
(v7/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "FeeTierDistributionQuery",
|
||||
"selections": [
|
||||
(v1/*: any*/),
|
||||
{
|
||||
"alias": "asToken0",
|
||||
"args": (v4/*: any*/),
|
||||
"concreteType": "Pool",
|
||||
"kind": "LinkedField",
|
||||
"name": "pools",
|
||||
"plural": true,
|
||||
"selections": (v8/*: any*/),
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": "asToken1",
|
||||
"args": (v9/*: any*/),
|
||||
"concreteType": "Pool",
|
||||
"kind": "LinkedField",
|
||||
"name": "pools",
|
||||
"plural": true,
|
||||
"selections": (v8/*: any*/),
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "FeeTierDistributionQuery",
|
||||
"selections": [
|
||||
(v1/*: any*/),
|
||||
{
|
||||
"alias": "asToken0",
|
||||
"args": (v4/*: any*/),
|
||||
"concreteType": "Pool",
|
||||
"kind": "LinkedField",
|
||||
"name": "pools",
|
||||
"plural": true,
|
||||
"selections": (v10/*: any*/),
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": "asToken1",
|
||||
"args": (v9/*: any*/),
|
||||
"concreteType": "Pool",
|
||||
"kind": "LinkedField",
|
||||
"name": "pools",
|
||||
"plural": true,
|
||||
"selections": (v10/*: any*/),
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "d989fcfb8fc9ef13bdc6de811e574284",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "FeeTierDistributionQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query FeeTierDistributionQuery(\n $token0: String!\n $token1: String!\n) {\n _meta {\n block {\n number\n }\n }\n asToken0: pools(orderBy: totalValueLockedToken0, orderDirection: desc, where: {token0: $token0, token1: $token1}) {\n feeTier\n totalValueLockedToken0\n totalValueLockedToken1\n id\n }\n asToken1: pools(orderBy: totalValueLockedToken0, orderDirection: desc, where: {token0: $token1, token1: $token0}) {\n feeTier\n totalValueLockedToken0\n totalValueLockedToken1\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "ac9cd4bfdc24db90dbb2bf8a7508009f";
|
||||
|
||||
export default node;
|
@ -156,14 +156,7 @@ export default function App() {
|
||||
{exploreFlag === ExploreVariant.Enabled && (
|
||||
<>
|
||||
<Route path="/explore" element={<Explore />} />
|
||||
<Route
|
||||
path="/tokens/:tokenAddress"
|
||||
element={
|
||||
<Suspense fallback={<LazyLoadSpinner />}>
|
||||
<TokenDetails />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route path="/tokens/:tokenAddress" element={<TokenDetails />} />
|
||||
</>
|
||||
)}
|
||||
<Route
|
||||
|
4892
src/schema/schema.graphql
Normal file
4892
src/schema/schema.graphql
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,30 +1,25 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { outboundLink } from 'components/analytics'
|
||||
import { MOBILE_MEDIA_BREAKPOINT } from 'components/Explore/constants'
|
||||
import useCopyClipboard from 'hooks/useCopyClipboard'
|
||||
import React, { HTMLProps, useCallback } from 'react'
|
||||
import { ArrowLeft, Copy, ExternalLink as LinkIconFeather, Trash, X } from 'react-feather'
|
||||
import React, { forwardRef, HTMLProps, ReactNode, useCallback, useImperativeHandle } from 'react'
|
||||
import {
|
||||
ArrowLeft,
|
||||
CheckCircle,
|
||||
Copy,
|
||||
ExternalLink as ExternalLinkIconFeather,
|
||||
Link as LinkIconFeather,
|
||||
Trash,
|
||||
X,
|
||||
} from 'react-feather'
|
||||
import { Link } from 'react-router-dom'
|
||||
import styled, { css, keyframes } from 'styled-components/macro'
|
||||
|
||||
import { ReactComponent as TooltipTriangle } from '../assets/svg/tooltip_triangle.svg'
|
||||
import { anonymizeLink } from '../utils/anonymizeLink'
|
||||
import { Color } from './styled'
|
||||
|
||||
export const ButtonText = styled.button`
|
||||
outline: none;
|
||||
border: none;
|
||||
font-size: inherit;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
|
||||
:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
`
|
||||
// TODO: Break this file into a components folder
|
||||
|
||||
export const CloseIcon = styled(X)<{ onClick: () => void }>`
|
||||
cursor: pointer;
|
||||
@ -68,23 +63,48 @@ export const LinkStyledButton = styled.button<{ disabled?: boolean }>`
|
||||
}
|
||||
`
|
||||
|
||||
export const LinkStyle = css`
|
||||
text-decoration: none;
|
||||
color: ${({ theme }) => theme.accentAction};
|
||||
stroke: ${({ theme }) => theme.accentAction};
|
||||
export const OPACITY_HOVER = 0.6
|
||||
export const OPACITY_CLICK = 0.4
|
||||
|
||||
export const ButtonText = styled.button`
|
||||
outline: none;
|
||||
border: none;
|
||||
font-size: inherit;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
|
||||
:hover {
|
||||
opacity: 0.6;
|
||||
opacity: ${OPACITY_HOVER};
|
||||
}
|
||||
|
||||
:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
`
|
||||
|
||||
export const ClickableStyle = css`
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
|
||||
:hover {
|
||||
opacity: ${OPACITY_HOVER};
|
||||
}
|
||||
:active {
|
||||
opacity: 0.4;
|
||||
opacity: ${OPACITY_CLICK};
|
||||
}
|
||||
`
|
||||
|
||||
export const LinkStyle = css`
|
||||
color: ${({ theme }) => theme.accentAction};
|
||||
stroke: ${({ theme }) => theme.accentAction};
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
// An internal link from the react-router-dom library that is correctly styled
|
||||
export const StyledInternalLink = styled(Link)`
|
||||
${ClickableStyle}
|
||||
${LinkStyle}
|
||||
`
|
||||
|
||||
@ -94,27 +114,21 @@ const LinkIconWrapper = styled.a`
|
||||
display: flex;
|
||||
`
|
||||
|
||||
const CopyIconWrapper = styled.div`
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
`
|
||||
|
||||
const IconStyle = css`
|
||||
height: 16px;
|
||||
width: 18px;
|
||||
margin-left: 10px;
|
||||
`
|
||||
|
||||
const LinkIcon = styled(LinkIconFeather)`
|
||||
const LinkIcon = styled(ExternalLinkIconFeather)`
|
||||
${IconStyle}
|
||||
${ClickableStyle}
|
||||
${LinkStyle}
|
||||
`
|
||||
|
||||
const CopyIcon = styled(Copy)`
|
||||
${IconStyle}
|
||||
${ClickableStyle}
|
||||
${LinkStyle}
|
||||
stroke: ${({ theme }) => theme.accentActive};
|
||||
`
|
||||
@ -129,7 +143,7 @@ export const TrashIcon = styled(Trash)`
|
||||
display: flex;
|
||||
|
||||
:hover {
|
||||
opacity: 0.7;
|
||||
opacity: ${OPACITY_HOVER};
|
||||
}
|
||||
`
|
||||
|
||||
@ -169,6 +183,7 @@ function handleClickExternalLink(event: React.MouseEvent<HTMLAnchorElement>) {
|
||||
}
|
||||
|
||||
const StyledLink = styled.a`
|
||||
${ClickableStyle}
|
||||
${LinkStyle}
|
||||
`
|
||||
/**
|
||||
@ -227,6 +242,14 @@ function ToolTip() {
|
||||
)
|
||||
}
|
||||
|
||||
const CopyIconWrapper = styled.div`
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
`
|
||||
|
||||
export function CopyLinkIcon({ toCopy }: { toCopy: string }) {
|
||||
const [isCopied, setCopied] = useCopyClipboard()
|
||||
const copy = useCallback(() => {
|
||||
@ -240,6 +263,134 @@ export function CopyLinkIcon({ toCopy }: { toCopy: string }) {
|
||||
)
|
||||
}
|
||||
|
||||
const FullAddress = styled.span`
|
||||
@media only screen and (max-width: ${MOBILE_MEDIA_BREAKPOINT}) {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
const TruncatedAddress = styled.span`
|
||||
display: none;
|
||||
@media only screen and (max-width: ${MOBILE_MEDIA_BREAKPOINT}) {
|
||||
display: flex;
|
||||
}
|
||||
`
|
||||
|
||||
const CopyAddressRow = styled.div<{ isClicked: boolean }>`
|
||||
${ClickableStyle}
|
||||
color: inherit;
|
||||
stroke: inherit;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
${({ isClicked }) => isClicked && `opacity: ` + OPACITY_CLICK + ` !important`}
|
||||
`
|
||||
|
||||
const CopyContractAddressWrapper = styled.div`
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
`
|
||||
|
||||
export function CopyContractAddress({ address }: { address: string }) {
|
||||
const [isCopied, setCopied] = useCopyClipboard()
|
||||
const copy = useCallback(() => {
|
||||
setCopied(address)
|
||||
}, [address, setCopied])
|
||||
|
||||
const truncated = `${address.slice(0, 4)}...${address.slice(-3)}`
|
||||
return (
|
||||
<CopyContractAddressWrapper onClick={copy}>
|
||||
<CopyAddressRow isClicked={isCopied}>
|
||||
<FullAddress>{address}</FullAddress>
|
||||
<TruncatedAddress>{truncated}</TruncatedAddress>
|
||||
<Copy size={14} />
|
||||
</CopyAddressRow>
|
||||
{isCopied && <ToolTip />}
|
||||
</CopyContractAddressWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
const CopyHelperContainer = styled(LinkStyledButton)<{ clicked: boolean }>`
|
||||
${({ clicked }) => !clicked && ClickableStyle};
|
||||
color: ${({ color, theme }) => color || theme.accentAction};
|
||||
padding: 0;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
text-decoration: none;
|
||||
:hover,
|
||||
:active,
|
||||
:focus {
|
||||
text-decoration: none;
|
||||
color: ${({ color, theme }) => color || theme.accentAction};
|
||||
}
|
||||
`
|
||||
const CopyHelperText = styled.span<{ fontSize: number }>`
|
||||
${({ theme }) => theme.flexRowNoWrap};
|
||||
font-size: ${({ fontSize }) => fontSize + 'px'};
|
||||
font-weight: 400;
|
||||
align-items: center;
|
||||
`
|
||||
const CopiedIcon = styled(CheckCircle)`
|
||||
color: ${({ theme }) => theme.accentSuccess};
|
||||
stroke-width: 1.5px;
|
||||
`
|
||||
interface CopyHelperProps {
|
||||
link?: boolean
|
||||
toCopy: string
|
||||
color?: Color
|
||||
fontSize?: number
|
||||
iconSize?: number
|
||||
gap?: number
|
||||
iconPosition?: 'left' | 'right'
|
||||
iconColor?: Color
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export type CopyHelperRefType = { forceCopy: () => void }
|
||||
export const CopyHelper = forwardRef<CopyHelperRefType, CopyHelperProps>(
|
||||
(
|
||||
{
|
||||
link,
|
||||
toCopy,
|
||||
color,
|
||||
fontSize = 16,
|
||||
iconSize = 20,
|
||||
gap = 12,
|
||||
iconPosition = 'left',
|
||||
iconColor,
|
||||
children,
|
||||
}: CopyHelperProps,
|
||||
ref
|
||||
) => {
|
||||
const [isCopied, setCopied] = useCopyClipboard()
|
||||
const copy = useCallback(() => {
|
||||
setCopied(toCopy)
|
||||
}, [toCopy, setCopied])
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
forceCopy() {
|
||||
copy()
|
||||
},
|
||||
}))
|
||||
|
||||
const BaseIcon = isCopied ? CopiedIcon : link ? LinkIconFeather : Copy
|
||||
|
||||
return (
|
||||
<CopyHelperContainer onClick={copy} color={color} clicked={isCopied}>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', gap }}>
|
||||
{iconPosition === 'left' && <BaseIcon size={iconSize} strokeWidth={1.5} color={iconColor} />}
|
||||
<CopyHelperText fontSize={fontSize}>{isCopied ? <Trans>Copied!</Trans> : children}</CopyHelperText>
|
||||
{iconPosition === 'right' && <BaseIcon size={iconSize} strokeWidth={1.5} color={iconColor} />}
|
||||
</div>
|
||||
</CopyHelperContainer>
|
||||
)
|
||||
}
|
||||
)
|
||||
CopyHelper.displayName = 'CopyHelper'
|
||||
|
||||
const rotate = keyframes`
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
|
Loading…
Reference in New Issue
Block a user