diff --git a/src/custom/assets/cow-swap/arrow.svg b/src/custom/assets/cow-swap/arrow.svg new file mode 100644 index 0000000000..511d2a92a9 --- /dev/null +++ b/src/custom/assets/cow-swap/arrow.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/custom/assets/cow-swap/cow_v2.svg b/src/custom/assets/cow-swap/cow_v2.svg new file mode 100644 index 0000000000..2fd9cdbc69 --- /dev/null +++ b/src/custom/assets/cow-swap/cow_v2.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/custom/assets/cow-swap/question.ts b/src/custom/assets/cow-swap/question.ts new file mode 100644 index 0000000000..83e560e424 --- /dev/null +++ b/src/custom/assets/cow-swap/question.ts @@ -0,0 +1,10 @@ +export function questionIcon(darkMode: boolean): string { + return ` + + + ` +} diff --git a/src/custom/assets/cow-swap/vCOW.png b/src/custom/assets/cow-swap/vCOW.png new file mode 100644 index 0000000000..4feb81be05 Binary files /dev/null and b/src/custom/assets/cow-swap/vCOW.png differ diff --git a/src/custom/components/Page/index.tsx b/src/custom/components/Page/index.tsx index a12ac3a913..9b5a4cc599 100644 --- a/src/custom/components/Page/index.tsx +++ b/src/custom/components/Page/index.tsx @@ -3,6 +3,29 @@ import { PropsWithChildren } from 'react' import styled, { css } from 'styled-components/macro' import AppBody from 'pages/AppBody' import { WithClassName } from 'types' +import { useIsDarkMode } from 'state/user/hooks' +import SVG from 'react-inlinesvg' +import { questionIcon } from 'assets/cow-swap/question' + +const HelpCircleWrapper = styled.div` + > svg { + opacity: 0.5; + transition: opacity 0.2s ease-in-out; + + &:hover { + opacity: 1; + } + } +` + +export function HelpCircle({ size }: { size: number }) { + const darkMode = useIsDarkMode() + return ( + + + + ) +} export const PageWrapper = styled(AppBody)` padding: 0 24px 24px; @@ -14,11 +37,17 @@ export const Title = styled.h1` font-size: 32px; margin: 24px 0 16px; color: ${({ theme }) => theme.text1}; + ${({ theme }) => theme.mediaWidth.upToVerySmall` font-size: 24px; `} ` +export const SectionTitle = styled(Title)` + font-size: 21px; + margin: 12px 0 16px; +` + export const Content = styled.div` font-size: 15px; margin: 0 0 28px; diff --git a/src/custom/pages/Profile/index.tsx b/src/custom/pages/Profile/index.tsx index 2b471cfa50..dd1362595d 100644 --- a/src/custom/pages/Profile/index.tsx +++ b/src/custom/pages/Profile/index.tsx @@ -12,16 +12,22 @@ import { ChildWrapper, Loader, ExtLink, - ProfileWrapper, - ProfileGridWrap, + CardsWrapper, + Card, + BannerCard, + BalanceDisplay, + ConvertWrapper, } from 'pages/Profile/styled' import { useActiveWeb3React } from 'hooks/web3' import Copy from 'components/Copy/CopyMod' -import { HelpCircle, RefreshCcw } from 'react-feather' +import { RefreshCcw } from 'react-feather' import Web3Status from 'components/Web3Status' import useReferralLink from 'hooks/useReferralLink' import useFetchProfile from 'hooks/useFetchProfile' -import { formatSmartLocaleAware, numberFormatter } from 'utils/format' +import { + // formatSmartLocaleAware, + numberFormatter, +} from 'utils/format' import { getExplorerAddressLink } from 'utils/explorer' import useTimeAgo from 'hooks/useTimeAgo' import { MouseoverTooltipContent } from 'components/Tooltip' @@ -29,13 +35,17 @@ import NotificationBanner from 'components/NotificationBanner' import { SupportedChainId, SupportedChainId as ChainId } from 'constants/chains' import AffiliateStatusCheck from 'components/AffiliateStatusCheck' import { useHasOrders } from 'api/gnosisProtocol/hooks' -import { Title } from 'components/Page' -import { useTokenBalance } from 'state/wallet/hooks' -import { useVCowData } from 'state/claim/hooks' -import { V_COW, COW } from 'constants/tokens' -import VCOWDropdown from './VCOWDropdown' -import { isPr, isLocal } from 'utils/environments' -import { IS_CLAIMING_ENABLED } from 'pages/Claim/const' +import { Title, SectionTitle, HelpCircle } from 'components/Page' +import { ButtonPrimary } from 'custom/components/Button' +import vCOWImage from 'assets/cow-swap/vCOW.png' +import SVG from 'react-inlinesvg' +import ArrowIcon from 'assets/cow-swap/arrow.svg' +import CowImage from 'assets/cow-swap/cow_v2.svg' +import CowProtocolImage from 'assets/cow-swap/cowprotocol.svg' +// import { useTokenBalance } from 'state/wallet/hooks' +// import { useVCowData } from 'state/claim/hooks' +// import { V_COW, COW } from 'constants/tokens' +// import { isPr, isLocal } from 'utils/environments' export default function Profile() { const referralLink = useReferralLink() @@ -45,17 +55,41 @@ export default function Profile() { const isTradesTooltipVisible = account && chainId === SupportedChainId.MAINNET && !!profileData?.totalTrades const hasOrders = useHasOrders(account) - const vCowBalance = useTokenBalance(account || undefined, chainId ? V_COW[chainId] : undefined) - const cowBalance = useTokenBalance(account || undefined, chainId ? COW[chainId] : undefined) + // const vCowBalance = useTokenBalance(account || undefined, chainId ? V_COW[chainId] : undefined) + const vCowBalance = '10,240,800.32' + // const cowBalance = useTokenBalance(account || undefined, chainId ? COW[chainId] : undefined) + const cowBalance = '0' - const { vested, total, unvested } = useVCowData() + // const { vested, total, unvested } = useVCowData() + const unvested = '9,240,800.32' + const vested = '1,240,800.32' // TODO: remove once this is not needed anymore - if (isPr || isLocal) { - console.force.log('vested', formatSmartLocaleAware(vested, vested?.currency.decimals)) - console.force.log('total', formatSmartLocaleAware(total, total?.currency.decimals)) - console.force.log('unvested', formatSmartLocaleAware(unvested, unvested?.currency.decimals)) - console.force.log('cowBalance', formatSmartLocaleAware(cowBalance, cowBalance?.currency.decimals)) + // if (isPr || isLocal) { + // console.force.log('vested', formatSmartLocaleAware(vested, vested?.currency.decimals)) + // console.force.log('total', formatSmartLocaleAware(total, total?.currency.decimals)) + // console.force.log('unvested', formatSmartLocaleAware(unvested, unvested?.currency.decimals)) + // console.force.log('cowBalance', formatSmartLocaleAware(cowBalance, cowBalance?.currency.decimals)) + // } + + const tooltipText = { + balanceBreakdown: ( +
+ Unvested {unvested} vCOW Vested {vested} vCOW +
+ ), + vested: ( +
+

+ Vested vCOW is the portion of your vCOW token balance, which is fully available to convert to + COW token. +

+

+ This includes any vCOW received through an airdrop. +

+

When converting your vested vCOW balance to COW, your entire vested balance will be converted.

+
+ ), } const renderNotificationMessages = ( @@ -76,18 +110,70 @@ export default function Profile() { return ( {chainId && chainId === ChainId.MAINNET && } - - - - Profile - - {IS_CLAIMING_ENABLED && vCowBalance && } - - + Profile + + + {vCowBalance && ( + + + vCOW token + + Total vCOW balance + + {vCowBalance} vCOW{' '} + + + + + + + + + + Vested{' '} + + + + + {vested} + + + Convert to COW + + + + )} + + {cowBalance && ( + + + Cow Balance + + Available COW balance + {cowBalance} COW + + + + )} + + + + CoW DAO Governance + Use your (v)COW balance to vote on important proposals or participate in forum discussions. + + {' '} + View proposals ↗ + CoW Forum ↗ + + + + + + - Affiliate Program + Affiliate Program {account && ( diff --git a/src/custom/pages/Profile/styled.tsx b/src/custom/pages/Profile/styled.tsx index b5ca6911be..984895b4a3 100644 --- a/src/custom/pages/Profile/styled.tsx +++ b/src/custom/pages/Profile/styled.tsx @@ -1,5 +1,7 @@ import styled, { css } from 'styled-components/macro' import Page, { GdocsListStyle } from 'components/Page' +import { ButtonPrimary } from 'custom/components/Button' +import { BannerExplainer } from 'pages/Claim/styled' import * as CSS from 'csstype' import { transparentize } from 'polished' import { ExternalLink } from 'theme' @@ -8,6 +10,7 @@ export const Container = styled.div` max-width: 910px; width: 100%; ` + export const Wrapper = styled(Page)` ${GdocsListStyle} @@ -19,14 +22,17 @@ export const Wrapper = styled(Page)` justify-content: flex-end; flex-direction: column; margin: 0; - background: ${({ theme }) => transparentize(0.5, theme.bg1)}; + background: ${({ theme }) => transparentize(0.3, theme.bg1)}; box-shadow: none; border: 1px solid ${({ theme }) => theme.cardBorder}; + ${({ theme }) => theme.mediaWidth.upToSmall` padding: 16px; `} + span[role='img'] { font-size: 55px; + ${({ theme }) => theme.mediaWidth.upToSmall` font-size: 30px; `} @@ -96,6 +102,7 @@ export const ItemTitle = styled.h3` font-size: 18px; line-height: 1.21; color: ${({ theme }) => theme.text1}; + ${({ theme }) => theme.mediaWidth.upToSmall` margin: 0 0 10px 0; font-size: 16px; @@ -126,11 +133,12 @@ export const FlexWrap = styled.div` ` export const StyledContainer = styled.div` - display: flex; - flex:1; - align-items:center; - justify-content: space-between; + display: flex; + flex:1; + align-items:center; + justify-content: space-between; } + ${({ theme }) => theme.mediaWidth.upToSmall` flex-wrap: wrap; flex-direction: column; @@ -196,15 +204,6 @@ export const Loader = styled.div<{ isLoading: boolean }>` `} ` -export const ProfileWrapper = styled(Wrapper)` - margin: 16px 0 16px 0; - padding: 16px 24px; - z-index: 2; - ${({ theme }) => theme.mediaWidth.upToVerySmall` - padding: 0 16px 16px; - `}; -` - export const ProfileGridWrap = styled(GridWrap)` grid-template-columns: 1fr auto; justify-content: space-between; @@ -225,3 +224,232 @@ export const ProfileGridWrap = styled(GridWrap)` grid-row-gap: 0px; `}; ` + +export const CardsWrapper = styled.div` + display: flex; + flex-flow: row wrap; + gap: 16px; + margin: 16px 0 16px 0; + padding: 0; + z-index: 2; + + > div:nth-of-type(3n) { + flex: 1 1 100%; + } + + ${({ theme }) => theme.mediaWidth.upToSmall` + display: flex; + flex-flow: column wrap; + `}; +` + +export const Card = styled.div` + display: flex; + flex-flow: row wrap; + flex: 1; + min-height: 192px; + margin: 0; + background: ${({ theme }) => transparentize(0.3, theme.bg1)}; + box-shadow: none; + padding: 24px; + gap: 24px 0; + border-radius: 16px; + border: 1px solid ${({ theme }) => theme.cardBorder}; + + ${({ theme }) => theme.mediaWidth.upToSmall` + min-height: 130px; + padding: 24px 16px; + `}; + + ${ButtonPrimary} { + height: 52px; + + > svg { + height: 100%; + width: auto; + object-fit: contain; + margin: 0 0 0 6px; + transform: translateX(0); + transition: transform 0.2s ease-in-out; + } + + &:hover > svg { + transform: translateX(2px); + } + } +` + +export const BannerCard = styled(BannerExplainer)` + min-height: 192px; + border-radius: 16px; + border: 0; + padding: 0 100px 0 24px; + flex: 1; + overflow: hidden; + + ${({ theme }) => theme.mediaWidth.upToSmall` + text-align: center; + padding: 24px 16px; + `} + + &:hover { + border: 0; + } + + > span { + align-items: flex-start; + justify-content: space-between; + height: 100%; + padding: 24px 0; + + ${({ theme }) => theme.mediaWidth.upToSmall` + padding: 0; + `} + + > b { + font-size: 24px; + + ${({ theme }) => theme.mediaWidth.upToSmall` + text-align: center; + margin: 0 auto; + `}; + } + + > small { + font-size: 14px; + line-height: 1.5; + text-align: left; + padding: 0; + margin: 8px 0 auto; + + ${({ theme }) => theme.mediaWidth.upToSmall` + text-align: center; + margin: 16px auto; + `} + } + + > span { + display: flex; + margin: 8px 0 0; + gap: 0 16px; + width: 100%; + + ${({ theme }) => theme.mediaWidth.upToSmall` + flex-flow: column wrap; + gap: 16px 0; + justify-content: center; + margin: 24px 0 12px; + `} + } + + > span > a, + > span > a:link { + font-size: 15px; + color: ${({ theme }) => theme.white}; + + &:hover { + color: ${({ theme }) => theme.primary1}; + } + } + } + + > svg { + left: initial; + right: -190px; + transform: scale(-1, 1); // flip mirror + opacity: 0.25; + mix-blend-mode: initial; + } +` + +export const BalanceDisplay = styled.div<{ titleSize?: number; altColor?: boolean; hAlign?: string }>` + display: flex; + flex-flow: row wrap; + align-items: center; + align-content: center; + justify-content: ${({ hAlign }) => (hAlign === 'left' ? 'flex-start' : 'center')}; + gap: 3px 12px; + width: 100%; + font-size: 14px; + color: ${({ theme }) => transparentize(0.3, theme.text1)}; + + ${({ theme }) => theme.mediaWidth.upToMedium` + gap: 12px; + flex-flow: column wrap; + `}; + + ${({ theme }) => theme.mediaWidth.upToSmall` + justify-content: center; + `}; + + > img { + ${({ theme }) => theme.mediaWidth.upToSmall` + height: 56px; + width: 56px; + object-fit: contain; + `}; + } + + > span { + display: flex; + flex-flow: column wrap; + gap: 3px 0; + } + + i { + display: flex; + align-items: center; + gap: 0 3px; + width: 100%; + font-style: normal; + + ${({ theme }) => theme.mediaWidth.upToMedium` + justify-content: center; + `}; + } + + b { + width: 100%; + display: flex; + align-items: center; + gap: 0 6px; + color: ${({ theme, altColor }) => (altColor ? theme.primary1 : theme.text1)}; + font-size: ${({ titleSize }) => (titleSize ? `${titleSize}px` : '21px')}; + + ${({ theme }) => theme.mediaWidth.upToMedium` + justify-content: center; + `}; + + ${({ theme }) => theme.mediaWidth.upToSmall` + font-size: 18px; + `}; + + > div { + cursor: pointer; + } + + // Todo: Prevent requiring overriding tooltip padding with important! + > div > div { + padding: 0 !important; + } + } +` + +export const ConvertWrapper = styled.div` + display: grid; + grid-template-columns: 1fr 200px; + align-items: center; + ${({ theme }) => theme.neumorphism.boxShadow}; + background: ${({ theme }) => theme.bg7}; + border-radius: 16px; + padding: 16px; + width: 100%; + + ${({ theme }) => theme.mediaWidth.upToMedium` + display: flex; + flex-flow: column wrap; + gap: 16px 0; + + > div { gap: 6px 12px; } + `}; +`