diff --git a/.eslintrc.js b/.eslintrc.js index b85fd109..4adda122 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -85,6 +85,7 @@ module.exports = { 'jsx-a11y/tabindex-no-positive': 'off', '@typescript-eslint/ban-ts-ignore': 'off', '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-empty-function': 'off', 'react/jsx-filename-extension': 'off', 'max-len': 'off', 'max-params': ['error', 3], @@ -97,13 +98,15 @@ module.exports = { 'no-extra-boolean-cast': 0, 'no-plusplus': 'off', 'no-prototype-builtins': 'off', + 'no-nested-ternary': 'off', 'no-restricted-properties': 'off', 'no-restricted-syntax': 'off', 'no-promise-executor-return': 'off', - 'no-unused-vars': ['error', { + 'no-unused-vars': ['off', { varsIgnorePattern: 'Fragment', ignoreRestSiblings: true, }], - '@typescript-eslint/no-unused-vars': 'error', + 'linebreak-style': 0, + '@typescript-eslint/no-unused-vars': 'off', 'no-use-before-define': 'off', 'object-curly-newline': ['error', { ObjectExpression: { consistent: true }, diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 137b23d2..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,12 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - package-ecosystem: "npm" - directory: "/" # Location of package manifests - schedule: - interval: "weekly" - day: "monday" diff --git a/.github/workflows/jediswap-mainnet.yml b/.github/workflows/jediswap-mainnet.yml index b2e180b8..62b5afe1 100644 --- a/.github/workflows/jediswap-mainnet.yml +++ b/.github/workflows/jediswap-mainnet.yml @@ -106,4 +106,8 @@ jobs: - name: Run Cache Invalidation run: aws cloudfront create-invalidation --distribution-id $CDN_DISTRIBUTION_ID --paths /\* env: +<<<<<<< HEAD CDN_DISTRIBUTION_ID: ${{ secrets.AWS_CDN_DISTRIBUTION_ID }} +======= + CDN_DISTRIBUTION_ID: ${{ secrets.AWS_CDN_DISTRIBUTION_ID }} +>>>>>>> d2a7f2f5a171ea0ae113d50e1bf5b40cdd8891df diff --git a/.github/workflows/jediswap-testnet.yml b/.github/workflows/jediswap-testnet.yml index 5338fdcf..ace39759 100644 --- a/.github/workflows/jediswap-testnet.yml +++ b/.github/workflows/jediswap-testnet.yml @@ -43,4 +43,8 @@ jobs: - name: Run Cache Invalidation run: aws cloudfront create-invalidation --distribution-id $CDN_DISTRIBUTION_ID --paths /\* env: +<<<<<<< HEAD CDN_DISTRIBUTION_ID: ${{ secrets.AWS_CDN_DISTRIBUTION_ID }} +======= + CDN_DISTRIBUTION_ID: ${{ secrets.AWS_CDN_DISTRIBUTION_ID }} +>>>>>>> d2a7f2f5a171ea0ae113d50e1bf5b40cdd8891df diff --git a/.vscode/settings.json b/.vscode/settings.json index 31bfa7ea..037e03e9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,13 +2,6 @@ "npm.packageManager": "yarn", "typescript.updateImportsOnFileMove.enabled": "always", "javascript.updateImportsOnFileMove.enabled": "always", - "editor.formatOnSaveMode": "file", - "editor.tabCompletion": "on", - "editor.tabSize": 2, - "editor.inlineSuggest.enabled": true, - "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.formatOnSave": true, - "editor.formatOnPaste": false, "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, @@ -18,5 +11,10 @@ "files.eol": "\n", "eslint.enable": true, "eslint.debug": true, - "prettier.trailingComma": "none", -} + "extensions.disabled": [ + "ms-vscode.jscs", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "rvest.vs-code-prettier-eslint" + ] +} \ No newline at end of file diff --git a/package.json b/package.json index c7b7e6e4..99721394 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "@cloudflare/workers-types": "^4.20230710.1", "@craco/craco": "^7.1.0", "@ethersproject/experimental": "^5.4.0", - "@lingui/cli": "^4.3.0", + "@lingui/cli": "^4.5.0", "@lingui/swc-plugin": "^4.0.4", "@swc/core": "^1.3.72", "@swc/jest": "^0.2.27", @@ -155,6 +155,7 @@ "yarn-deduplicate": "^6.0.0" }, "dependencies": { + "@argent/starknet-react-webwallet-connector": "^6.4.2", "@apollo/client": "^3.7.2", "@coinbase/wallet-sdk": "^3.6.4", "@fontsource/ibm-plex-mono": "^4.5.1", diff --git a/src/assets/images/ExternalLinkIcon.png b/src/assets/images/ExternalLinkIcon.png new file mode 100644 index 00000000..d3ba1bbe Binary files /dev/null and b/src/assets/images/ExternalLinkIcon.png differ diff --git a/src/assets/images/mail.png b/src/assets/images/mail.png new file mode 100644 index 00000000..ee220018 Binary files /dev/null and b/src/assets/images/mail.png differ diff --git a/src/assets/images/noPosition.png b/src/assets/images/noPosition.png new file mode 100644 index 00000000..583d3992 Binary files /dev/null and b/src/assets/images/noPosition.png differ diff --git a/src/assets/jedi/squareLogo.png b/src/assets/jedi/squareLogo.png new file mode 100644 index 00000000..e7cef76f Binary files /dev/null and b/src/assets/jedi/squareLogo.png differ diff --git a/src/assets/svg/dot_line.svg b/src/assets/svg/dot_line.svg index f8a0f9b5..efc90471 100644 --- a/src/assets/svg/dot_line.svg +++ b/src/assets/svg/dot_line.svg @@ -1,3 +1,3 @@ - - + + diff --git a/src/assets/svg/expando-icon-closed.svg b/src/assets/svg/expando-icon-closed.svg index 63311a80..45f87432 100644 --- a/src/assets/svg/expando-icon-closed.svg +++ b/src/assets/svg/expando-icon-closed.svg @@ -1,3 +1,3 @@ - + diff --git a/src/assets/svg/expando-icon-opened.svg b/src/assets/svg/expando-icon-opened.svg index 4ea0cc9b..fbd779e8 100644 --- a/src/assets/svg/expando-icon-opened.svg +++ b/src/assets/svg/expando-icon-opened.svg @@ -1,3 +1,3 @@ - + diff --git a/src/assets/wallets/Braavos.svg b/src/assets/wallets/Braavos.svg new file mode 100644 index 00000000..cb36a3d9 --- /dev/null +++ b/src/assets/wallets/Braavos.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/wallets/Wallet.png b/src/assets/wallets/Wallet.png new file mode 100644 index 00000000..47bec8d4 Binary files /dev/null and b/src/assets/wallets/Wallet.png differ diff --git a/src/assets/wallets/argentx.png b/src/assets/wallets/argentx.png new file mode 100644 index 00000000..3bc6fb0c Binary files /dev/null and b/src/assets/wallets/argentx.png differ diff --git a/src/components/About/AboutFooter.tsx b/src/components/About/AboutFooter.tsx index 3d1cdae2..fcd66bc8 100644 --- a/src/components/About/AboutFooter.tsx +++ b/src/components/About/AboutFooter.tsx @@ -1,14 +1,13 @@ -import { BrowserEvent, InterfaceElementName, SharedEventName } from '@uniswap/analytics-events' -import { TraceEvent } from 'analytics' -import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes' -import styled from 'styled-components' -import { BREAKPOINTS } from 'theme' -import { ExternalLink, StyledRouterLink } from 'theme/components' -import { useIsDarkMode } from 'theme/components/ThemeToggle' - -import { DiscordIcon, GithubIcon, TwitterIcon } from './Icons' -import darkUnicornImgSrc from './images/unicornEmbossDark.png' -import lightUnicornImgSrc from './images/unicornEmbossLight.png' +import { BrowserEvent, InterfaceElementName, SharedEventName } from '@uniswap/analytics-events'; +import styled from 'styled-components'; + +import { TraceEvent } from 'analytics'; +import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'; +import { BREAKPOINTS } from 'theme'; +import { ExternalLink, StyledRouterLink } from 'theme/components'; +import { DiscordIcon, GithubIcon, TwitterIcon } from './Icons'; +import darkUnicornImgSrc from './images/unicornEmbossDark.png'; +import lightUnicornImgSrc from './images/unicornEmbossLight.png'; const Footer = styled.div` display: flex; @@ -21,12 +20,12 @@ const Footer = styled.div` flex-direction: row; justify-content: space-between; } -` +`; const LogoSection = styled.div` display: flex; flex-direction: column; -` +`; const LogoSectionLeft = styled(LogoSection)` display: none; @@ -34,7 +33,7 @@ const LogoSectionLeft = styled(LogoSection)` @media screen and (min-width: ${BREAKPOINTS.lg}px) { display: flex; } -` +`; const LogoSectionBottom = styled(LogoSection)` display: flex; @@ -42,7 +41,7 @@ const LogoSectionBottom = styled(LogoSection)` @media screen and (min-width: ${BREAKPOINTS.lg}px) { display: none; } -` +`; const StyledLogo = styled.img` width: 72px; @@ -52,18 +51,18 @@ const StyledLogo = styled.img` @media screen and (min-width: ${BREAKPOINTS.lg}px) { display: block; } -` +`; const SocialLinks = styled.div` display: flex; align-items: center; gap: 20px; margin: 20px 0 0 0; -` +`; const SocialLink = styled.a` display: flex; -` +`; const FooterLinks = styled.div` display: grid; @@ -73,7 +72,7 @@ const FooterLinks = styled.div` grid-template-columns: 1fr 1fr 1fr 1fr; gap: 24px; } -` +`; const LinkGroup = styled.div` display: flex; @@ -84,62 +83,59 @@ const LinkGroup = styled.div` @media screen and (min-width: ${BREAKPOINTS.xl}px) { margin: 0; } -` +`; const LinkGroupTitle = styled.span` font-size: 16px; line-height: 20px; font-weight: 535; -` +`; const ExternalTextLink = styled(ExternalLink)` font-size: 16px; line-height: 20px; color: ${({ theme }) => theme.neutral2}; -` +`; const TextLink = styled(StyledRouterLink)` font-size: 16px; line-height: 20px; color: ${({ theme }) => theme.neutral2}; -` +`; const Copyright = styled.span` font-size: 16px; line-height: 20px; margin: 1rem 0 0 0; color: ${({ theme }) => theme.neutral3}; -` - -const LogoSectionContent = () => { - const isDarkMode = useIsDarkMode() - return ( - <> - - - - - - - - - - - - +`; + +const LogoSectionContent = () => ( + <> + + + + + + + + - - © {new Date().getFullYear()} Uniswap Labs - - ) -} + + + + + + © {new Date().getFullYear()} Uniswap Labs + +); export const AboutFooter = () => { - const shouldDisableNFTRoutes = useDisableNFTRoutes() + const shouldDisableNFTRoutes = useDisableNFTRoutes(); return ( - ) -} + ); +}; diff --git a/src/components/About/Card.tsx b/src/components/About/Card.tsx index 4cab78f6..9b80bc73 100644 --- a/src/components/About/Card.tsx +++ b/src/components/About/Card.tsx @@ -1,21 +1,18 @@ -import { BrowserEvent, SharedEventName } from '@uniswap/analytics-events' -import { TraceEvent } from 'analytics' -import { Link } from 'react-router-dom' -import styled, { DefaultTheme } from 'styled-components' -import { BREAKPOINTS } from 'theme' -import { useIsDarkMode } from 'theme/components/ThemeToggle' +import { BrowserEvent, SharedEventName } from '@uniswap/analytics-events'; +import { Link } from 'react-router-dom'; +import styled, { DefaultTheme } from 'styled-components'; + +import { TraceEvent } from 'analytics'; +import { BREAKPOINTS } from 'theme'; export enum CardType { Primary = 'Primary', Secondary = 'Secondary', } -const StyledCard = styled.div<{ $isDarkMode: boolean; $backgroundImgSrc?: string; $type: CardType }>` +const StyledCard = styled.div<{ $backgroundImgSrc?: string; $type: CardType }>` display: flex; - background: ${({ $isDarkMode, $backgroundImgSrc, $type, theme }) => - $isDarkMode - ? `${theme.surface2} ${$backgroundImgSrc ? ` url(${$backgroundImgSrc})` : ''}` - : `${$type === CardType.Primary ? 'white' : theme.surface2} url(${$backgroundImgSrc})`}; + background: ${({ $backgroundImgSrc, theme }) => `${theme.surface2} ${$backgroundImgSrc ? ` url(${$backgroundImgSrc})` : ''}`}; background-size: auto 100%; background-position: right; background-repeat: no-repeat; @@ -31,22 +28,19 @@ const StyledCard = styled.div<{ $isDarkMode: boolean; $backgroundImgSrc?: string border: 1px solid ${({ theme, $type }) => ($type === CardType.Primary ? 'transparent' : theme.surface3)}; transition: ${({ theme }) => `${theme.transition.duration.medium} ${theme.transition.timing.ease} border`}; - &:hover { - border: 1px solid ${({ theme, $isDarkMode }) => ($isDarkMode ? theme.surface3 : theme.neutral3)}; - } @media screen and (min-width: ${BREAKPOINTS.sm}px) { height: ${({ $backgroundImgSrc }) => ($backgroundImgSrc ? 360 : 260)}px; } @media screen and (min-width: ${BREAKPOINTS.xl}px) { padding: 32px; } -` +}`; const TitleRow = styled.div` display: flex; align-items: center; justify-content: space-between; -` +`; const CardTitle = styled.div` font-size: 20px; @@ -57,16 +51,16 @@ const CardTitle = styled.div` font-size: 28px; line-height: 36px; } -` +`; const getCardDescriptionColor = (type: CardType, theme: DefaultTheme) => { switch (type) { case CardType.Secondary: - return theme.neutral2 + return theme.neutral2; default: - return theme.neutral1 + return theme.neutral1; } -} +}; const CardDescription = styled.div<{ type: CardType }>` display: flex; @@ -82,7 +76,7 @@ const CardDescription = styled.div<{ type: CardType }>` line-height: 28px; max-width: 480px; } -` +`; const CardCTA = styled(CardDescription)` color: ${({ theme }) => theme.accent1}; @@ -95,10 +89,9 @@ const CardCTA = styled(CardDescription)` &:hover { opacity: 0.6; } -` +`; -const Card = ({ - type = CardType.Primary, +const Card = ({ type = CardType.Primary, title, description, cta, @@ -106,8 +99,7 @@ const Card = ({ external, backgroundImgSrc, icon, - elementName, -}: { + elementName }: { type?: CardType title: string description: string @@ -117,31 +109,27 @@ const Card = ({ backgroundImgSrc?: string icon?: React.ReactNode elementName?: string -}) => { - const isDarkMode = useIsDarkMode() - return ( - - - - {title} - {icon} - - - {description} - {cta} - - - - ) -} +}) => ( + + + + {title} + {icon} + + + {description} + {cta} + + + +); -export default Card +export default Card; diff --git a/src/components/About/ProtocolBanner.tsx b/src/components/About/ProtocolBanner.tsx index 9eb04a01..035ceb50 100644 --- a/src/components/About/ProtocolBanner.tsx +++ b/src/components/About/ProtocolBanner.tsx @@ -1,13 +1,12 @@ -import { ButtonEmpty } from 'components/Button' -import styled from 'styled-components' -import { BREAKPOINTS } from 'theme' -import { useIsDarkMode } from 'theme/components/ThemeToggle' +import styled from 'styled-components'; -import meshSrc from './images/Mesh.png' +import { ButtonEmpty } from 'components/Button'; +import { BREAKPOINTS } from 'theme'; +import meshSrc from './images/Mesh.png'; -const DARK_MODE_GRADIENT = 'radial-gradient(101.8% 4091.31% at 0% 0%, #4673FA 0%, #9646FA 100%)' +const DARK_MODE_GRADIENT = 'radial-gradient(101.8% 4091.31% at 0% 0%, #4673FA 0%, #9646FA 100%)'; -const Banner = styled.div<{ isDarkMode: boolean }>` +const Banner = styled.div` height: 340px; width: 100%; border-radius: 32px; @@ -22,23 +21,20 @@ const Banner = styled.div<{ isDarkMode: boolean }>` box-shadow: 0px 10px 24px rgba(51, 53, 72, 0.04); - background: ${({ isDarkMode }) => - isDarkMode - ? `url(${meshSrc}), ${DARK_MODE_GRADIENT}` - : `url(${meshSrc}), linear-gradient(93.06deg, #FF00C7 2.66%, #FF9FFB 98.99%);`}; + background: ${() => `url(${meshSrc}), ${DARK_MODE_GRADIENT}`}; @media screen and (min-width: ${BREAKPOINTS.lg}px) { height: 140px; flex-direction: row; } -` +`; const TextContainer = styled.div` color: white; display: flex; flex: 1; flex-direction: column; -` +`; const HeaderText = styled.div` font-weight: 535; @@ -49,7 +45,7 @@ const HeaderText = styled.div` font-size: 28px; line-height: 36px; } -` +`; const DescriptionText = styled.div` margin: 10px 10px 0 0; @@ -61,7 +57,7 @@ const DescriptionText = styled.div` font-size: 20px; line-height: 28px; } -` +`; const BannerButtonContainer = styled.div` width: 100%; @@ -77,30 +73,27 @@ const BannerButtonContainer = styled.div` @media screen and (min-width: ${BREAKPOINTS.lg}px) { width: auto; } -` +`; const BannerButton = styled(ButtonEmpty)` color: white; border: 1px solid white; -` - -const ProtocolBanner = () => { - const isDarkMode = useIsDarkMode() - return ( - - - Powered by the Uniswap Protocol - - The leading decentralized crypto trading protocol, governed by a global community. - - - - - Learn more - - - - ) -} - -export default ProtocolBanner +`; + +const ProtocolBanner = () => ( + + + Powered by the Uniswap Protocol + + The leading decentralized crypto trading protocol, governed by a global community. + + + + + Learn more + + + +); + +export default ProtocolBanner; diff --git a/src/components/About/constants.tsx b/src/components/About/constants.tsx deleted file mode 100644 index dd679ee2..00000000 --- a/src/components/About/constants.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { InterfaceElementName } from '@uniswap/analytics-events' -import { DollarSign, Terminal } from 'react-feather' -import styled from 'styled-components' -import { lightTheme } from 'theme/colors' - -import darkArrowImgSrc from './images/aboutArrowDark.png' -import lightArrowImgSrc from './images/aboutArrowLight.png' -import darkDollarImgSrc from './images/aboutDollarDark.png' -import darkTerminalImgSrc from './images/aboutTerminalDark.png' -import nftCardImgSrc from './images/nftCard.png' -import swapCardImgSrc from './images/swapCard.png' - -export const MAIN_CARDS = [ - { - to: '/swap', - title: 'Swap tokens', - description: 'Buy, sell, and explore tokens on Ethereum, Polygon, Optimism, and more.', - cta: 'Trade Tokens', - darkBackgroundImgSrc: swapCardImgSrc, - lightBackgroundImgSrc: swapCardImgSrc, - elementName: InterfaceElementName.ABOUT_PAGE_SWAP_CARD, - }, - { - to: '/nfts', - title: 'Trade NFTs', - description: 'Buy and sell NFTs across marketplaces to find more listings at better prices.', - cta: 'Explore NFTs', - darkBackgroundImgSrc: nftCardImgSrc, - lightBackgroundImgSrc: nftCardImgSrc, - elementName: InterfaceElementName.ABOUT_PAGE_NFTS_CARD, - }, -] - -const StyledCardLogo = styled.img` - min-width: 20px; - min-height: 20px; - max-height: 48px; - max-width: 48px; -` - -export const MORE_CARDS = [ - { - to: 'https://support.uniswap.org/hc/en-us/articles/11306574799117-How-to-use-Moon-Pay-on-the-Uniswap-web-app-', - external: true, - title: 'Buy crypto', - description: 'Buy crypto with your credit card or bank account at the best rates.', - lightIcon: , - darkIcon: , - cta: 'Buy now', - elementName: InterfaceElementName.ABOUT_PAGE_BUY_CRYPTO_CARD, - }, - { - to: '/pools', - title: 'Earn', - description: 'Provide liquidity to pools on Uniswap and earn fees on swaps.', - lightIcon: , - darkIcon: , - cta: 'Provide liquidity', - elementName: InterfaceElementName.ABOUT_PAGE_EARN_CARD, - }, - { - to: 'https://docs.uniswap.org', - external: true, - title: 'Build dApps', - description: 'Build apps and tools on the largest DeFi protocol on Ethereum.', - lightIcon: , - darkIcon: , - cta: 'Developer docs', - elementName: InterfaceElementName.ABOUT_PAGE_DEV_DOCS_CARD, - }, -] diff --git a/src/components/AccountDrawer/AnalyticsToggle.tsx b/src/components/AccountDrawer/AnalyticsToggle.tsx deleted file mode 100644 index de616d8e..00000000 --- a/src/components/AccountDrawer/AnalyticsToggle.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { t } from '@lingui/macro' -import { allowAnalyticsAtom } from 'analytics' -import { useAtom } from 'jotai' - -import { SettingsToggle } from './SettingsToggle' - -export function AnalyticsToggle() { - const [allowAnalytics, updateAllowAnalytics] = useAtom(allowAnalyticsAtom) - - return ( - void updateAllowAnalytics((value) => !value)} - /> - ) -} diff --git a/src/components/AccountDrawer/AuthenticatedHeader.tsx b/src/components/AccountDrawer/AuthenticatedHeader.tsx index d03a8c3a..de335090 100644 --- a/src/components/AccountDrawer/AuthenticatedHeader.tsx +++ b/src/components/AccountDrawer/AuthenticatedHeader.tsx @@ -1,77 +1,37 @@ -import { Trans } from '@lingui/macro' -import { BrowserEvent, InterfaceElementName, InterfaceEventName, SharedEventName } from '@uniswap/analytics-events' -import { CurrencyAmount, Token } from '@uniswap/sdk-core' -import { useWeb3React } from '@web3-react/core' -import { sendAnalyticsEvent, TraceEvent } from 'analytics' -import { ButtonEmphasis, ButtonSize, LoadingButtonSpinner, ThemeButton } from 'components/Button' -import Column from 'components/Column' -import { Power } from 'components/Icons/Power' -import { Settings } from 'components/Icons/Settings' -import { AutoRow } from 'components/Row' -import { LoadingBubble } from 'components/Tokens/loading' -import { DeltaArrow } from 'components/Tokens/TokenDetails/Delta' -import Tooltip from 'components/Tooltip' -import { getConnection } from 'connection' -import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes' -import useENSName from 'hooks/useENSName' -import { useIsNotOriginCountry } from 'hooks/useIsNotOriginCountry' -import { useProfilePageState, useSellAsset, useWalletCollections } from 'nft/hooks' -import { useIsNftClaimAvailable } from 'nft/hooks/useIsNftClaimAvailable' -import { ProfilePageStateType } from 'nft/types' -import { useCallback, useState } from 'react' -import { CreditCard, Info } from 'react-feather' -import { useNavigate } from 'react-router-dom' -import { useAppDispatch } from 'state/hooks' -import { updateSelectedWallet } from 'state/user/reducer' -import styled from 'styled-components' -import { CopyHelper, ExternalLink, ThemedText } from 'theme/components' -import { shortenAddress } from 'utils' -import { NumberType, useFormatter } from 'utils/formatNumbers' - -import { useCloseModal, useFiatOnrampAvailability, useOpenModal, useToggleModal } from '../../state/application/hooks' -import { ApplicationModal } from '../../state/application/reducer' -import { useUserHasAvailableClaim, useUserUnclaimedAmount } from '../../state/claim/hooks' -import StatusIcon from '../Identicon/StatusIcon' -import { useCachedPortfolioBalancesQuery } from '../PrefetchBalancesWrapper/PrefetchBalancesWrapper' -import { useToggleAccountDrawer } from '.' -import IconButton, { IconHoverText, IconWithConfirmTextButton } from './IconButton' -import MiniPortfolio from './MiniPortfolio' -import { portfolioFadeInAnimation } from './MiniPortfolio/PortfolioRow' +import { Trans } from '@lingui/macro'; +import { useWeb3React } from '@web3-react/core'; +import { useCallback, useState } from 'react'; +import styled from 'styled-components'; + +import { ThemeButton } from 'components/Button'; +import Column from 'components/Column'; +import { Power } from 'components/Icons/Power'; +import { Settings } from 'components/Icons/Settings'; +import { AutoRow } from 'components/Row'; +import { LoadingBubble } from 'components/Tokens/loading'; +import { DeltaArrow } from 'components/Tokens/TokenDetails/Delta'; +import { getConnection } from 'connection'; +import useENSName from 'hooks/useENSName'; +import { useAppDispatch } from 'state/hooks'; +import { updateSelectedWallet } from 'state/user/reducer'; +import { CopyHelper, ExternalLink, ThemedText } from 'theme/components'; +import { shortenAddress } from 'utils'; +import { NumberType, useFormatter } from 'utils/formatNumbers'; +import { useCloseModal, useFiatOnrampAvailability, useOpenModal, useToggleModal } from '../../state/application/hooks'; +import { ApplicationModal } from '../../state/application/reducer'; +import StatusIcon from '../Identicon/StatusIcon'; +import { useCachedPortfolioBalancesQuery } from '../PrefetchBalancesWrapper/PrefetchBalancesWrapper'; +import { useToggleAccountDrawer } from '.'; +import IconButton, { IconHoverText, IconWithConfirmTextButton } from './IconButton'; +import MiniPortfolio from './MiniPortfolio'; +import { portfolioFadeInAnimation } from './MiniPortfolio/PortfolioRow'; const AuthenticatedHeaderWrapper = styled.div` padding: 20px 16px; display: flex; flex-direction: column; flex: 1; -` - -const HeaderButton = styled(ThemeButton)` - border-color: transparent; - border-radius: 12px; - border-style: solid; - border-width: 1px; - height: 40px; - margin-top: 8px; -` - -const WalletButton = styled(ThemeButton)` - border-radius: 12px; - padding-top: 10px; - padding-bottom: 10px; - margin-top: 4px; - color: white; - border: none; -` - -const UNIButton = styled(WalletButton)` - border-radius: 12px; - padding-top: 10px; - padding-bottom: 10px; - margin-top: 4px; - color: white; - border: none; - background: linear-gradient(to right, #9139b0 0%, #4261d6 100%); -` +`; const IconContainer = styled.div` display: flex; @@ -87,29 +47,14 @@ const IconContainer = styled.div` left: 0px; } } -` -const FiatOnrampNotAvailableText = styled(ThemedText.BodySmall)` - align-items: center; - color: ${({ theme }) => theme.neutral2}; - display: flex; - justify-content: center; -` -const FiatOnrampAvailabilityExternalLink = styled(ExternalLink)` - align-items: center; - display: flex; - height: 14px; - justify-content: center; - margin-left: 6px; - width: 14px; -` +`; const StatusWrapper = styled.div` - display: inline-block; width: 70%; max-width: 70%; padding-right: 8px; display: inline-flex; -` +`; const AccountNamesWrapper = styled.div` overflow: hidden; @@ -119,113 +64,74 @@ const AccountNamesWrapper = styled.div` flex-direction: column; justify-content: center; margin-left: 8px; -` - -const StyledInfoIcon = styled(Info)` - height: 12px; - width: 12px; - flex: 1 1 auto; -` -const StyledLoadingButtonSpinner = styled(LoadingButtonSpinner)` - fill: ${({ theme }) => theme.accent1}; -` +`; const HeaderWrapper = styled.div` margin-bottom: 20px; display: flex; justify-content: space-between; - align-items: flex-start; -` + align-items: center; +`; const CopyText = styled(CopyHelper).attrs({ iconSize: 14, iconPosition: 'right', -})`` +})``; const FadeInColumn = styled(Column)` ${portfolioFadeInAnimation} -` +`; const PortfolioDrawerContainer = styled(Column)` flex: 1; -` +`; export default function AuthenticatedHeader({ account, openSettings }: { account: string; openSettings: () => void }) { - const { connector } = useWeb3React() - const { ENSName } = useENSName(account) - const dispatch = useAppDispatch() - const navigate = useNavigate() - const closeModal = useCloseModal() - const setSellPageState = useProfilePageState((state) => state.setProfilePageState) - const resetSellAssets = useSellAsset((state) => state.reset) - const clearCollectionFilters = useWalletCollections((state) => state.clearCollectionFilters) - const isClaimAvailable = useIsNftClaimAvailable((state) => state.isClaimAvailable) - const shouldShowBuyFiatButton = useIsNotOriginCountry('GB') - const { formatNumber, formatDelta } = useFormatter() - - const shouldDisableNFTRoutes = useDisableNFTRoutes() + const { connector } = useWeb3React(); + const { ENSName } = useENSName(account); + const dispatch = useAppDispatch(); + const { formatNumber, formatDelta } = useFormatter(); - const unclaimedAmount: CurrencyAmount | undefined = useUserUnclaimedAmount(account) - const isUnclaimed = useUserHasAvailableClaim(account) - const connection = getConnection(connector) - const openClaimModal = useToggleModal(ApplicationModal.ADDRESS_CLAIM) - const openNftModal = useToggleModal(ApplicationModal.UNISWAP_NFT_AIRDROP_CLAIM) + const connection = getConnection(connector); const disconnect = useCallback(() => { if (connector && connector.deactivate) { - connector.deactivate() + connector.deactivate(); } - connector.resetState() - dispatch(updateSelectedWallet({ wallet: undefined })) - }, [connector, dispatch]) - - const toggleWalletDrawer = useToggleAccountDrawer() + connector.resetState(); + dispatch(updateSelectedWallet({ wallet: undefined })); + }, [connector, dispatch]); - const navigateToProfile = useCallback(() => { - toggleWalletDrawer() - resetSellAssets() - setSellPageState(ProfilePageStateType.VIEWING) - clearCollectionFilters() - navigate('/nfts/profile') - closeModal() - }, [clearCollectionFilters, closeModal, navigate, resetSellAssets, setSellPageState, toggleWalletDrawer]) + const toggleWalletDrawer = useToggleAccountDrawer(); - const openFiatOnrampModal = useOpenModal(ApplicationModal.FIAT_ONRAMP) + const openFiatOnrampModal = useOpenModal(ApplicationModal.FIAT_ONRAMP); const openFoRModalWithAnalytics = useCallback(() => { - toggleWalletDrawer() - sendAnalyticsEvent(InterfaceEventName.FIAT_ONRAMP_WIDGET_OPENED) - openFiatOnrampModal() - }, [openFiatOnrampModal, toggleWalletDrawer]) + toggleWalletDrawer(); + openFiatOnrampModal(); + }, [openFiatOnrampModal, toggleWalletDrawer]); - const [shouldCheck, setShouldCheck] = useState(false) - const { - available: fiatOnrampAvailable, + const [shouldCheck, setShouldCheck] = useState(false); + const { available: fiatOnrampAvailable, availabilityChecked: fiatOnrampAvailabilityChecked, error, - loading: fiatOnrampAvailabilityLoading, - } = useFiatOnrampAvailability(shouldCheck, openFoRModalWithAnalytics) + loading: fiatOnrampAvailabilityLoading } = useFiatOnrampAvailability(shouldCheck, openFoRModalWithAnalytics); const handleBuyCryptoClick = useCallback(() => { if (!fiatOnrampAvailabilityChecked) { - setShouldCheck(true) + setShouldCheck(true); } else if (fiatOnrampAvailable) { - openFoRModalWithAnalytics() + openFoRModalWithAnalytics(); } - }, [fiatOnrampAvailabilityChecked, fiatOnrampAvailable, openFoRModalWithAnalytics]) + }, [fiatOnrampAvailabilityChecked, fiatOnrampAvailable, openFoRModalWithAnalytics]); const disableBuyCryptoButton = Boolean( - error || (!fiatOnrampAvailable && fiatOnrampAvailabilityChecked) || fiatOnrampAvailabilityLoading - ) - const [showFiatOnrampUnavailableTooltip, setShow] = useState(false) - const openFiatOnrampUnavailableTooltip = useCallback(() => setShow(true), [setShow]) - const closeFiatOnrampUnavailableTooltip = useCallback(() => setShow(false), [setShow]) + error || (!fiatOnrampAvailable && fiatOnrampAvailabilityChecked) || fiatOnrampAvailabilityLoading, + ); - const { data: portfolioBalances } = useCachedPortfolioBalancesQuery({ account }) - const portfolio = portfolioBalances?.portfolios?.[0] - const totalBalance = portfolio?.tokensTotalDenominatedValue?.value - const absoluteChange = portfolio?.tokensTotalDenominatedValueChange?.absolute?.value - const percentChange = portfolio?.tokensTotalDenominatedValueChange?.percentage?.value - const [showDisconnectConfirm, setShowDisconnectConfirm] = useState(false) - - const addressShort = account ? `${account.slice(0, 6)}...${account.slice(-4)}` : null + const { data: portfolioBalances } = useCachedPortfolioBalancesQuery({ account }); + const portfolio = portfolioBalances?.portfolios?.[0]; + const totalBalance = portfolio?.tokensTotalDenominatedValue?.value; + const absoluteChange = portfolio?.tokensTotalDenominatedValueChange?.absolute?.value; + const percentChange = portfolio?.tokensTotalDenominatedValueChange?.percentage?.value; + const [showDisconnectConfirm, setShowDisconnectConfirm] = useState(false); return ( @@ -235,7 +141,7 @@ export default function AuthenticatedHeader({ account, openSettings }: { account {account && ( - {ENSName ?? addressShort} + {ENSName ?? shortenAddress(account)} {/* Displays smaller view of account if ENS name was rendered above */} {ENSName && ( @@ -253,23 +159,17 @@ export default function AuthenticatedHeader({ account, openSettings }: { account onClick={openSettings} Icon={Settings} /> - - - + - {/* + {totalBalance !== undefined ? ( @@ -278,7 +178,7 @@ export default function AuthenticatedHeader({ account, openSettings }: { account type: NumberType.PortfolioBalance, })} - + {absoluteChange !== 0 && percentChange && ( <> @@ -298,68 +198,8 @@ export default function AuthenticatedHeader({ account, openSettings }: { account )} - {!shouldDisableNFTRoutes && ( - - View and sell NFTs - - )} - {shouldShowBuyFiatButton && ( - - {error ? ( - {error} - ) : ( - <> - {fiatOnrampAvailabilityLoading ? ( - - ) : ( - - )}{' '} - Buy crypto - - )} - - )} - {Boolean(!fiatOnrampAvailable && fiatOnrampAvailabilityChecked) && ( - - Not available in your region - Moonpay is not available in some regions. Click to learn more.} - > - - - - - - )} - {isUnclaimed && ( - - Claim {unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} reward - - )} - {isClaimAvailable && ( - - Claim Uniswap NFT Airdrop - - )} - */} + - ) + ); } diff --git a/src/components/AccountDrawer/DefaultMenu.tsx b/src/components/AccountDrawer/DefaultMenu.tsx index c733b0b0..43cf728d 100644 --- a/src/components/AccountDrawer/DefaultMenu.tsx +++ b/src/components/AccountDrawer/DefaultMenu.tsx @@ -1,19 +1,20 @@ -import { useWeb3React } from '@web3-react/core' -import Column from 'components/Column' -import WalletModal from 'components/WalletModal' -import { useCallback, useEffect, useMemo, useState } from 'react' -import styled from 'styled-components' +/* @ts-nocheck */ +/* eslint-disable */ +import { useWeb3React } from '@web3-react/core'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import styled from 'styled-components'; -import AuthenticatedHeader from './AuthenticatedHeader' -import LanguageMenu from './LanguageMenu' -import LocalCurrencyMenu from './LocalCurrencyMenu' -import SettingsMenu from './SettingsMenu' -import { useAccountDetails } from 'hooks/starknet-react' +import Column from 'components/Column'; +import WalletModal from 'components/WalletModal'; +import AuthenticatedHeader from './AuthenticatedHeader'; +import LanguageMenu from './LanguageMenu'; +import LocalCurrencyMenu from './LocalCurrencyMenu'; +import SettingsMenu from './SettingsMenu'; const DefaultMenuWrap = styled(Column)` width: 100%; height: 100%; -` +`; enum MenuState { DEFAULT, @@ -23,35 +24,34 @@ enum MenuState { } function DefaultMenu({ drawerOpen }: { drawerOpen: boolean }) { - // const { account } = useWeb3React() - const { address } = useAccountDetails() - const isAuthenticated = !!address + const { account } = useWeb3React(); + const isAuthenticated = !!account; - const [menu, setMenu] = useState(MenuState.DEFAULT) - const openSettings = useCallback(() => setMenu(MenuState.SETTINGS), []) - const closeSettings = useCallback(() => setMenu(MenuState.DEFAULT), []) - const openLanguageSettings = useCallback(() => setMenu(MenuState.LANGUAGE_SETTINGS), []) - const openLocalCurrencySettings = useCallback(() => setMenu(MenuState.LOCAL_CURRENCY_SETTINGS), []) + const [menu, setMenu] = useState(MenuState.DEFAULT); + const openSettings = useCallback(() => setMenu(MenuState.SETTINGS), []); + const closeSettings = useCallback(() => setMenu(MenuState.DEFAULT), []); + const openLanguageSettings = useCallback(() => setMenu(MenuState.LANGUAGE_SETTINGS), []); + const openLocalCurrencySettings = useCallback(() => setMenu(MenuState.LOCAL_CURRENCY_SETTINGS), []); + // @ts-ignore useEffect(() => { if (!drawerOpen && menu !== MenuState.DEFAULT) { // wait for the drawer to close before resetting the menu const timer = setTimeout(() => { - closeSettings() - }, 250) - return () => clearTimeout(timer) + closeSettings(); + }, 250); + return () => clearTimeout(timer); } - return - }, [drawerOpen, menu, closeSettings]) + }, [drawerOpen, menu, closeSettings]); const SubMenu = useMemo(() => { switch (menu) { case MenuState.DEFAULT: return isAuthenticated ? ( - + ) : ( - ) + ); case MenuState.SETTINGS: return ( - ) + ); case MenuState.LANGUAGE_SETTINGS: - return + return ; case MenuState.LOCAL_CURRENCY_SETTINGS: - return + return ; } - }, [address, closeSettings, isAuthenticated, menu, openLanguageSettings, openLocalCurrencySettings, openSettings]) + }, [account, closeSettings, isAuthenticated, menu, openLanguageSettings, openLocalCurrencySettings, openSettings]); - return {SubMenu} + return {SubMenu}; } -export default DefaultMenu +export default DefaultMenu; diff --git a/src/components/AccountDrawer/IconButton.tsx b/src/components/AccountDrawer/IconButton.tsx index a93f42e1..f8bb368e 100644 --- a/src/components/AccountDrawer/IconButton.tsx +++ b/src/components/AccountDrawer/IconButton.tsx @@ -1,10 +1,10 @@ -import React, { forwardRef, useCallback, useEffect, useRef, useState } from 'react' -import { Icon } from 'react-feather' -import styled, { css, DefaultTheme } from 'styled-components' -import useResizeObserver from 'use-resize-observer' +import React, { forwardRef, useCallback, useEffect, useRef, useState } from 'react'; +import { Icon } from 'react-feather'; +import styled, { css, DefaultTheme } from 'styled-components'; +import useResizeObserver from 'use-resize-observer'; -import { TRANSITION_DURATIONS } from '../../theme/styles' -import Row from '../Row' +import { TRANSITION_DURATIONS } from '../../theme/styles'; +import Row from '../Row'; export const IconHoverText = styled.span` color: ${({ theme }) => theme.neutral1}; @@ -16,13 +16,12 @@ export const IconHoverText = styled.span` font-size: 12px; padding: 5px; left: 10px; -` +`; -const getWidthTransition = ({ theme }: { theme: DefaultTheme }) => - `width ${theme.transition.timing.inOut} ${theme.transition.duration.fast}` +const getWidthTransition = ({ theme }: { theme: DefaultTheme }) => `width ${theme.transition.timing.inOut} ${theme.transition.duration.fast}`; const IconStyles = css<{ hideHorizontal?: boolean }>` - background-color: ${({ theme }) => theme.surface1}; + background: transparent; transition: ${getWidthTransition}; border-radius: 12px; display: flex; @@ -32,34 +31,18 @@ const IconStyles = css<{ hideHorizontal?: boolean }>` overflow: hidden; height: 32px; width: ${({ hideHorizontal }) => (hideHorizontal ? '0px' : '32px')}; - color: ${({ theme }) => theme.neutral2}; - :hover { - background-color: ${({ theme }) => theme.surface2}; - transition: ${({ - theme: { - transition: { duration, timing }, - }, - }) => `${duration.fast} background-color ${timing.in}, ${getWidthTransition}`}; - - ${IconHoverText} { - opacity: 1; - } - } - :active { - background-color: ${({ theme }) => theme.surface1}; - transition: background-color ${({ theme }) => theme.transition.duration.fast} linear, ${getWidthTransition}; - } -` + color: ${({ theme }) => theme.neutral1}; +`; const IconBlockLink = styled.a` ${IconStyles}; -` +`; const IconBlockButton = styled.button` ${IconStyles}; border: none; outline: none; -` +`; const IconWrapper = styled.span` width: 24px; @@ -68,7 +51,7 @@ const IconWrapper = styled.span` display: flex; align-items: center; justify-content: center; -` +`; interface BaseProps { Icon: Icon hideHorizontal?: boolean @@ -80,14 +63,14 @@ interface IconButtonProps extends React.ComponentPropsWithoutRef<'button'>, Base type IconBlockProps = React.ComponentPropsWithoutRef<'a' | 'button'> -const IconBlock = forwardRef(function IconBlock(props, ref) { +const IconBlock = forwardRef((props, ref) => { if ('href' in props) { - return } {...props} /> + return } {...props} />; } // ignoring 'button' 'type' conflict between React and styled-components // @ts-ignore - return -}) + return ; +}); const IconButton = ({ Icon, ...rest }: IconButtonProps | IconLinkProps) => ( @@ -95,7 +78,7 @@ const IconButton = ({ Icon, ...rest }: IconButtonProps | IconLinkProps) => ( -) +); type IconWithTextProps = (IconButtonProps | IconLinkProps) & { text: string @@ -111,100 +94,98 @@ const TextWrapper = styled.div` overflow: hidden; min-width: min-content; font-weight: 485; -` +`; const TextHide = styled.div` overflow: hidden; transition: width ${({ theme }) => theme.transition.timing.inOut} ${({ theme }) => theme.transition.duration.fast}, max-width ${({ theme }) => theme.transition.timing.inOut} ${({ theme }) => theme.transition.duration.fast}; -` +`; /** * Allows for hiding and showing some text next to an IconButton * Note that for width transitions to animate in CSS we need to always specify the width (no auto) * so there's resize observing and measuring going on here. */ -export const IconWithConfirmTextButton = ({ - Icon, +export const IconWithConfirmTextButton = ({ Icon, text, onConfirm, onShowConfirm, onClick, dismissOnHoverOut, dismissOnHoverDurationMs = TRANSITION_DURATIONS.slow, - ...rest -}: IconWithTextProps) => { - const [showText, setShowTextWithoutCallback] = useState(false) - const [frame, setFrame] = useState() - const frameObserver = useResizeObserver() - const hiddenObserver = useResizeObserver() + ...rest }: IconWithTextProps) => { + const [showText, setShowTextWithoutCallback] = useState(false); + const [frame, setFrame] = useState(); + const frameObserver = useResizeObserver(); + const hiddenObserver = useResizeObserver(); const setShowText = useCallback( (val: boolean) => { - setShowTextWithoutCallback(val) - onShowConfirm?.(val) + setShowTextWithoutCallback(val); + onShowConfirm?.(val); }, - [onShowConfirm] - ) + [onShowConfirm], + ); const dimensionsRef = useRef({ frame: 0, innerText: 0, - }) + }); const dimensions = (() => { // once opened, we avoid updating it to prevent constant resize loop if (!showText) { dimensionsRef.current = { frame: frameObserver.width || 0, innerText: hiddenObserver.width || 0, - } + }; } - return dimensionsRef.current - })() + return dimensionsRef.current; + })(); // keyboard action to cancel useEffect(() => { - if (typeof window === 'undefined') return - if (!showText || !frame) return + if (typeof window === 'undefined') { return; } + if (!showText || !frame) { return; } const closeAndPrevent = (e: Event) => { - setShowText(false) - e.preventDefault() - e.stopPropagation() - } + setShowText(false); + e.preventDefault(); + e.stopPropagation(); + }; const clickHandler = (e: MouseEvent) => { - const { target } = e - const shouldClose = !(target instanceof HTMLElement) || !frame.contains(target) + const { target } = e; + const shouldClose = !(target instanceof HTMLElement) || !frame.contains(target); if (shouldClose) { - closeAndPrevent(e) + closeAndPrevent(e); } - } + }; const keyHandler = (e: KeyboardEvent) => { if (e.key === 'Escape') { - closeAndPrevent(e) + closeAndPrevent(e); } - } + }; - window.addEventListener('click', clickHandler, { capture: true }) - window.addEventListener('keydown', keyHandler, { capture: true }) + window.addEventListener('click', clickHandler, { capture: true }); + window.addEventListener('keydown', keyHandler, { capture: true }); return () => { - window.removeEventListener('click', clickHandler, { capture: true }) - window.removeEventListener('keydown', keyHandler, { capture: true }) - } - }, [frame, setShowText, showText]) + window.removeEventListener('click', clickHandler, { capture: true }); + window.removeEventListener('keydown', keyHandler, { capture: true }); + }; + }, [frame, setShowText, showText]); - const xPad = showText ? 8 : 0 - const width = showText ? dimensions.frame + dimensions.innerText + xPad : 32 - const mouseLeaveTimeout = useRef() + const xPad = showText ? 8 : 0; + const width = showText ? dimensions.frame + dimensions.innerText + xPad : 32; + const mouseLeaveTimeout = useRef(); return ( { - frameObserver.ref(node) - setFrame(node) + frameObserver.ref(node); + setFrame(node); }} {...rest} style={{ @@ -216,21 +197,21 @@ export const IconWithConfirmTextButton = ({ // even manually typing this all out more specifically it still gets mad about any casting for some reason onClick={(e: MouseEvent) => { if (showText) { - onConfirm?.() + onConfirm?.(); } else { - onClick?.(e) - setShowText(!showText) + onClick?.(e); + setShowText(!showText); } }} {...(dismissOnHoverOut && { onMouseLeave() { mouseLeaveTimeout.current = setTimeout(() => { - setShowText(false) - }, dismissOnHoverDurationMs) + setShowText(false); + }, dismissOnHoverDurationMs); }, onMouseEnter() { if (mouseLeaveTimeout.current) { - clearTimeout(mouseLeaveTimeout.current) + clearTimeout(mouseLeaveTimeout.current); } }, })} @@ -246,7 +227,7 @@ export const IconWithConfirmTextButton = ({ maxWidth: showText ? dimensions.innerText : 0, width: showText ? dimensions.innerText : 0, // this negative transform offsets for the shift it does due to being 0 width - transform: showText ? undefined : `translateX(-8px)`, + transform: showText ? undefined : 'translateX(-8px)', minWidth: showText ? dimensions.innerText : 0, }} > @@ -254,7 +235,7 @@ export const IconWithConfirmTextButton = ({ - ) -} + ); +}; -export default IconButton +export default IconButton; diff --git a/src/components/AccountDrawer/MiniPortfolio/NFTs/NFTItem.tsx b/src/components/AccountDrawer/MiniPortfolio/NFTs/NFTItem.tsx deleted file mode 100644 index e9a55b66..00000000 --- a/src/components/AccountDrawer/MiniPortfolio/NFTs/NFTItem.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { InterfaceElementName, SharedEventName } from '@uniswap/analytics-events'; -import { useNavigate } from 'react-router-dom'; -import styled from 'styled-components'; - -import { sendAnalyticsEvent, useTrace } from 'analytics'; -import { useToggleAccountDrawer } from 'components/AccountDrawer'; -import Column from 'components/Column'; -import Row from 'components/Row'; -import { Box } from 'nft/components/Box'; -import { NftCard } from 'nft/components/card'; -import { detailsHref } from 'nft/components/card/utils'; -import { VerifiedIcon } from 'nft/components/icons'; -import { WalletAsset } from 'nft/types'; -import { floorFormatter } from 'nft/utils'; -import { ThemedText } from 'theme/components'; - -const FloorPrice = styled(Row)` - opacity: 0; - - // prevent empty whitespace from collapsing line height to maintain - // consistent spacing below rows - white-space: pre; -`; - -const NFTContainer = styled(Column)` - gap: 8px; - min-height: 150px; - - &:hover { - ${FloorPrice} { - opacity: 1; - } - } -`; -const NFTCollectionName = styled(ThemedText.BodySmall)` - white-space: pre; - text-overflow: ellipsis; - overflow: hidden; -`; - -export function NFT({ asset, - mediaShouldBePlaying, - setCurrentTokenPlayingMedia }: { - asset: WalletAsset - mediaShouldBePlaying: boolean - setCurrentTokenPlayingMedia: (tokenId: string | undefined) => void -}) { - const toggleWalletDrawer = useToggleAccountDrawer(); - const navigate = useNavigate(); - const trace = useTrace(); - - const navigateToNFTDetails = () => { - toggleWalletDrawer(); - navigate(detailsHref(asset)); - }; - - return ( - - sendAnalyticsEvent(SharedEventName.ELEMENT_CLICKED, { - element: InterfaceElementName.MINI_PORTFOLIO_NFT_ITEM, - collection_name: asset.collection?.name, - collection_address: asset.collection?.address, - token_id: asset.tokenId, - ...trace, - })} - mediaShouldBePlaying={mediaShouldBePlaying} - setCurrentTokenPlayingMedia={setCurrentTokenPlayingMedia} - testId="mini-portfolio-nft" - /> - - - ); -} - -function NFTDetails({ asset }: { asset: WalletAsset }) { - return ( - - - {asset.asset_contract.name} - {asset.collectionIsVerified && } - - - - {asset.floorPrice ? `${floorFormatter(asset.floorPrice)} ETH` : ' '} - - - - ); -} - -const BADGE_SIZE = '18px'; -function Verified() { - return ( - - - - ); -} diff --git a/src/components/AccountDrawer/MiniPortfolio/NFTs/index.tsx b/src/components/AccountDrawer/MiniPortfolio/NFTs/index.tsx deleted file mode 100644 index 4833df30..00000000 --- a/src/components/AccountDrawer/MiniPortfolio/NFTs/index.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { useNftBalance } from 'graphql/data/nft/NftBalance' -import { LoadingAssets } from 'nft/components/collection/CollectionAssetLoading' -import { EmptyWalletModule } from 'nft/components/profile/view/EmptyWalletContent' -import { useState } from 'react' -import InfiniteScroll from 'react-infinite-scroll-component' -import styled from 'styled-components' - -import { useAccountDrawer } from '../..' -import { DEFAULT_NFT_QUERY_AMOUNT } from '../constants' -import { NFT } from './NFTItem' - -export default function NFTs({ account }: { account: string }) { - const [walletDrawerOpen, toggleWalletDrawer] = useAccountDrawer() - const { walletAssets, loading, hasNext, loadMore } = useNftBalance( - account, - [], - [], - DEFAULT_NFT_QUERY_AMOUNT, - undefined, - undefined, - undefined, - !walletDrawerOpen - ) - - const [currentTokenPlayingMedia, setCurrentTokenPlayingMedia] = useState() - - if (loading && !walletAssets) - return ( - - - - ) - - if (!walletAssets || walletAssets?.length === 0) { - return - } - - return ( - - - - ) - } - dataLength={walletAssets?.length ?? 0} - style={{ overflow: 'unset' }} - scrollableTarget="wallet-dropdown-scroll-wrapper" - > - - {walletAssets?.length - ? walletAssets.map((asset, index) => { - return ( - - ) - }) - : null} - - - ) -} - -const AssetsContainer = styled.div` - display: grid; - gap: 12px; - - // use minmax to not let grid items escape the parent container - grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); - margin: 16px; -` diff --git a/src/components/AccountDrawer/MiniPortfolio/Pools/index.tsx b/src/components/AccountDrawer/MiniPortfolio/Pools/index.tsx index dd2d5a59..389e0952 100644 --- a/src/components/AccountDrawer/MiniPortfolio/Pools/index.tsx +++ b/src/components/AccountDrawer/MiniPortfolio/Pools/index.tsx @@ -1,27 +1,25 @@ -import { t } from '@lingui/macro' -import { BrowserEvent, InterfaceElementName, SharedEventName } from '@uniswap/analytics-events' -import { Position } from '@uniswap/v3-sdk' -import { useWeb3React } from '@web3-react/core' -import { TraceEvent } from 'analytics' -import { useToggleAccountDrawer } from 'components/AccountDrawer' -import Row from 'components/Row' -import { MouseoverTooltip } from 'components/Tooltip' -import { BIPS_BASE } from 'constants/misc' -import { useFilterPossiblyMaliciousPositions } from 'hooks/useFilterPossiblyMaliciousPositions' -import { useSwitchChain } from 'hooks/useSwitchChain' -import { EmptyWalletModule } from 'nft/components/profile/view/EmptyWalletContent' -import { useCallback, useMemo, useReducer } from 'react' -import { useNavigate } from 'react-router-dom' -import styled from 'styled-components' -import { ThemedText } from 'theme/components' -import { NumberType, useFormatter } from 'utils/formatNumbers' - -import { ExpandoRow } from '../ExpandoRow' -import { PortfolioLogo } from '../PortfolioLogo' -import PortfolioRow, { PortfolioSkeleton, PortfolioTabWrapper } from '../PortfolioRow' -import { PositionInfo } from './cache' -import { useFeeValues } from './hooks' -import useMultiChainPositions from './useMultiChainPositions' +import { t } from '@lingui/macro'; +import { Position } from '@uniswap/v3-sdk'; +import { useWeb3React } from '@web3-react/core'; +import { useCallback, useMemo, useReducer } from 'react'; +import { useNavigate } from 'react-router-dom'; +import styled from 'styled-components'; + +import { useToggleAccountDrawer } from 'components/AccountDrawer'; +import Row from 'components/Row'; +import { MouseoverTooltip } from 'components/Tooltip'; +import { BIPS_BASE } from 'constants/misc'; +import { useFilterPossiblyMaliciousPositions } from 'hooks/useFilterPossiblyMaliciousPositions'; +import { useSwitchChain } from 'hooks/useSwitchChain'; +import { EmptyWalletModule } from 'nft/components/profile/view/EmptyWalletContent'; +import { ThemedText } from 'theme/components'; +import { NumberType, useFormatter } from 'utils/formatNumbers'; +import { ExpandoRow } from '../ExpandoRow'; +import { PortfolioLogo } from '../PortfolioLogo'; +import PortfolioRow, { PortfolioSkeleton, PortfolioTabWrapper } from '../PortfolioRow'; +import { PositionInfo } from './cache'; +import { useFeeValues } from './hooks'; +import useMultiChainPositions from './useMultiChainPositions'; /** * Takes an array of PositionInfo objects (format used by the Uniswap Labs gql API). @@ -31,48 +29,47 @@ import useMultiChainPositions from './useMultiChainPositions' */ function useFilterPossiblyMaliciousPositionInfo(positions: PositionInfo[] | undefined): PositionInfo[] { const tokenIdsToPositionInfo: Record = useMemo( - () => - positions - ? positions.reduce((acc, position) => ({ ...acc, [position.details.tokenId.toString()]: position }), {}) - : {}, - [positions] - ) - const positionDetails = useMemo(() => positions?.map((position) => position.details) ?? [], [positions]) - const filteredPositionDetails = useFilterPossiblyMaliciousPositions(positionDetails) + () => (positions + ? positions.reduce((acc, position) => ({ ...acc, [position.details.tokenId.toString()]: position }), {}) + : {}), + [positions], + ); + const positionDetails = useMemo(() => positions?.map((position) => position.details) ?? [], [positions]); + const filteredPositionDetails = useFilterPossiblyMaliciousPositions(positionDetails); return useMemo( () => filteredPositionDetails.map((positionDetails) => tokenIdsToPositionInfo[positionDetails.tokenId.toString()]), - [filteredPositionDetails, tokenIdsToPositionInfo] - ) + [filteredPositionDetails, tokenIdsToPositionInfo], + ); } export default function Pools({ account }: { account: string }) { - const { positions, loading } = useMultiChainPositions(account) - const filteredPositions = useFilterPossiblyMaliciousPositionInfo(positions) - const [showClosed, toggleShowClosed] = useReducer((showClosed) => !showClosed, false) + const { positions, loading } = useMultiChainPositions(account); + const filteredPositions = useFilterPossiblyMaliciousPositionInfo(positions); + const [showClosed, toggleShowClosed] = useReducer((showClosed) => !showClosed, false); const [openPositions, closedPositions] = useMemo(() => { - const openPositions: PositionInfo[] = [] - const closedPositions: PositionInfo[] = [] + const openPositions: PositionInfo[] = []; + const closedPositions: PositionInfo[] = []; for (let i = 0; i < filteredPositions.length; i++) { - const position = filteredPositions[i] + const position = filteredPositions[i]; if (position.closed) { - closedPositions.push(position) + closedPositions.push(position); } else { - openPositions.push(position) + openPositions.push(position); } } - return [openPositions, closedPositions] - }, [filteredPositions]) + return [openPositions, closedPositions]; + }, [filteredPositions]); - const toggleWalletDrawer = useToggleAccountDrawer() + const toggleWalletDrawer = useToggleAccountDrawer(); if (!filteredPositions || loading) { - return + return ; } - if (filteredPositions.length === 0) { - return + if (!filteredPositions?.length) { + return ; } return ( @@ -97,44 +94,43 @@ export default function Pools({ account }: { account: string }) { ))} - ) + ); } const ActiveDot = styled.span<{ closed: boolean; outOfRange: boolean }>` - background-color: ${({ theme, closed, outOfRange }) => - closed ? theme.neutral2 : outOfRange ? theme.deprecated_accentWarning : theme.success}; + background-color: ${({ theme, closed, outOfRange }) => (closed ? theme.neutral2 : outOfRange ? theme.deprecated_accentWarning : theme.success)}; border-radius: 50%; height: 8px; width: 8px; margin-left: 4px; margin-top: 1px; -` +`; function calculcateLiquidityValue(price0: number | undefined, price1: number | undefined, position: Position) { - if (!price0 || !price1) return undefined + if (!price0 || !price1) { return undefined; } - const value0 = parseFloat(position.amount0.toExact()) * price0 - const value1 = parseFloat(position.amount1.toExact()) * price1 - return value0 + value1 + const value0 = parseFloat(position.amount0.toExact()) * price0; + const value1 = parseFloat(position.amount1.toExact()) * price1; + return value0 + value1; } function PositionListItem({ positionInfo }: { positionInfo: PositionInfo }) { - const { formatNumber } = useFormatter() + const { formatNumber } = useFormatter(); - const { chainId, position, pool, details, inRange, closed } = positionInfo + const { chainId, position, pool, details, inRange, closed } = positionInfo; - const { priceA, priceB, fees: feeValue } = useFeeValues(positionInfo) - const liquidityValue = calculcateLiquidityValue(priceA, priceB, position) + const { priceA, priceB, fees: feeValue } = useFeeValues(positionInfo); + const liquidityValue = calculcateLiquidityValue(priceA, priceB, position); - const navigate = useNavigate() - const toggleWalletDrawer = useToggleAccountDrawer() - const { chainId: walletChainId, connector } = useWeb3React() - const switchChain = useSwitchChain() + const navigate = useNavigate(); + const toggleWalletDrawer = useToggleAccountDrawer(); + const { chainId: walletChainId, connector } = useWeb3React(); + const switchChain = useSwitchChain(); const onClick = useCallback(async () => { - if (walletChainId !== chainId) await switchChain(connector, chainId) - toggleWalletDrawer() - navigate('/pool/' + details.tokenId) - }, [walletChainId, chainId, switchChain, connector, toggleWalletDrawer, navigate, details.tokenId]) + if (walletChainId !== chainId) { await switchChain(connector, chainId); } + toggleWalletDrawer(); + navigate(`/pool/${details.tokenId}`); + }, [walletChainId, chainId, switchChain, connector, toggleWalletDrawer, navigate, details.tokenId]); const analyticsEventProperties = useMemo( () => ({ chain_id: chainId, @@ -143,60 +139,54 @@ function PositionListItem({ positionInfo }: { positionInfo: PositionInfo }) { pool_token_0_address: pool.token0.address, pool_token_1_address: pool.token1.address, }), - [chainId, pool.token0.address, pool.token0.symbol, pool.token1.address, pool.token1.symbol] - ) + [chainId, pool.token0.address, pool.token0.symbol, pool.token1.address, pool.token1.symbol], + ); return ( - - } - title={ - - - {pool.token0.symbol} / {pool.token1?.symbol} - - - } - descriptor={{`${pool.fee / BIPS_BASE}%`}} - right={ - <> - - {`${formatNumber({ - input: liquidityValue, - type: NumberType.PortfolioBalance, - })} (liquidity) + ${formatNumber({ - input: feeValue, - type: NumberType.PortfolioBalance, - })} (fees)`} - - } - > - - {formatNumber({ - input: (liquidityValue ?? 0) + (feeValue ?? 0), + } + title={( + + + {pool.token0.symbol} / {pool.token1?.symbol} + + + )} + descriptor={{`${pool.fee / BIPS_BASE}%`}} + right={( + <> + + {`${formatNumber({ + input: liquidityValue, + type: NumberType.PortfolioBalance, + })} (liquidity) + ${formatNumber({ + input: feeValue, type: NumberType.PortfolioBalance, - })} - - - - - - {closed ? t`Closed` : inRange ? t`In range` : t`Out of range`} - - - - - } - /> - - ) + })} (fees)`} + + + )} + > + + {formatNumber({ + input: (liquidityValue ?? 0) + (feeValue ?? 0), + type: NumberType.PortfolioBalance, + })} + + + + + + {closed ? t`Closed` : inRange ? t`In range` : t`Out of range`} + + + + + )} + /> + ); } diff --git a/src/components/AccountDrawer/MiniPortfolio/Tokens/index.tsx b/src/components/AccountDrawer/MiniPortfolio/Tokens/index.tsx index 580604b7..c9cd2182 100644 --- a/src/components/AccountDrawer/MiniPortfolio/Tokens/index.tsx +++ b/src/components/AccountDrawer/MiniPortfolio/Tokens/index.tsx @@ -1,4 +1,3 @@ -import { BrowserEvent, InterfaceElementName, SharedEventName } from '@uniswap/analytics-events'; import { useAtomValue } from 'jotai/utils'; import { useCallback, useMemo, useState } from 'react'; import { useNavigate } from 'react-router-dom'; @@ -39,7 +38,7 @@ export default function Tokens({ account }: { account: string }) { return ; } - if (tokenBalances?.length === 0) { + if (!tokenBalances?.length) { // TODO: consider launching moonpay here instead of just closing the drawer return ; } @@ -60,7 +59,7 @@ export default function Tokens({ account }: { account: string }) { ); } -const TokenBalanceText = styled(ThemedText.BodySecondary)` +const TokenBalanceText = styled(ThemedText.BodySmall)` ${EllipsisStyle} `; const TokenNameText = styled(ThemedText.SubHeader)` @@ -92,42 +91,35 @@ function TokenRow({ token, quantity, denominatedValue, tokenProjectMarket }: Tok return null; } return ( - - } - title={{token?.name}} - descriptor={( - - {formatNumber({ - input: quantity, - type: NumberType.TokenNonTx, - })}{' '} - {token?.symbol} - - )} - onClick={navigateToTokenDetails} - right={ - denominatedValue && ( - <> - - {formatNumber({ - input: denominatedValue?.value, - type: NumberType.PortfolioBalance, - })} - - - - {formatDelta(percentChange)} - - - ) - } - /> - + } + title={{token?.name}} + descriptor={( + + {formatNumber({ + input: quantity, + type: NumberType.TokenNonTx, + })}{' '} + {token?.symbol} + + )} + onClick={navigateToTokenDetails} + right={ + denominatedValue && ( + <> + + {formatNumber({ + input: denominatedValue?.value, + type: NumberType.PortfolioBalance, + })} + + + + {formatDelta(percentChange)} + + + ) + } + /> ); } diff --git a/src/components/AccountDrawer/MiniPortfolio/index.tsx b/src/components/AccountDrawer/MiniPortfolio/index.tsx index c875a1a4..4f086fdf 100644 --- a/src/components/AccountDrawer/MiniPortfolio/index.tsx +++ b/src/components/AccountDrawer/MiniPortfolio/index.tsx @@ -1,22 +1,19 @@ -import { Trans } from '@lingui/macro' -import { BrowserEvent, InterfaceElementName, InterfaceSectionName, SharedEventName } from '@uniswap/analytics-events' -import { Trace, TraceEvent } from 'analytics' -import Column from 'components/Column' -import { LoaderV2 } from 'components/Icons/LoadingSpinner' -import { AutoRow } from 'components/Row' -import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes' -import { useIsNftPage } from 'hooks/useIsNftPage' -import { useEffect, useState } from 'react' -import styled, { useTheme } from 'styled-components' -import { BREAKPOINTS } from 'theme' -import { ThemedText } from 'theme/components' +import { Trans } from '@lingui/macro'; +import { useEffect, useState } from 'react'; +import styled, { useTheme } from 'styled-components'; -import { ActivityTab } from './Activity' -import { usePendingActivity } from './Activity/hooks' -import NFTs from './NFTs' -import Pools from './Pools' -import { PortfolioRowWrapper } from './PortfolioRow' -import Tokens from './Tokens' +import Column from 'components/Column'; +import { LoaderV2 } from 'components/Icons/LoadingSpinner'; +import { AutoRow } from 'components/Row'; +import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'; +import { useIsNftPage } from 'hooks/useIsNftPage'; +import { BREAKPOINTS } from 'theme'; +import { ThemedText } from 'theme/components'; +import { ActivityTab } from './Activity'; +import { usePendingActivity } from './Activity/hooks'; +import Pools from './Pools'; +import { PortfolioRowWrapper } from './PortfolioRow'; +import Tokens from './Tokens'; const Wrapper = styled(Column)` margin-top: 28px; @@ -34,11 +31,11 @@ const Wrapper = styled(Column)` background: ${({ theme }) => theme.deprecated_hoverDefault}; } } -` +`; const Nav = styled(AutoRow)` gap: 20px; -` +`; const NavItem = styled(ThemedText.SubHeader)<{ active?: boolean }>` align-items: center; @@ -51,7 +48,7 @@ const NavItem = styled(ThemedText.SubHeader)<{ active?: boolean }>` &:hover { ${({ theme, active }) => !active && `color: ${theme.neutral2}`}; } -` +`; const PageWrapper = styled.div` border-radius: 12px; @@ -59,13 +56,12 @@ const PageWrapper = styled.div` margin-left: -16px; width: calc(100% + 32px); flex: 1; -` +`; interface Page { title: React.ReactNode key: string component: ({ account }: { account: string }) => JSX.Element - loggingElementName: string } const Pages: Array = [ @@ -73,85 +69,64 @@ const Pages: Array = [ title: Tokens, key: 'tokens', component: Tokens, - loggingElementName: InterfaceElementName.MINI_PORTFOLIO_TOKENS_TAB, - }, - { - title: NFTs, - key: 'nfts', - component: NFTs, - loggingElementName: InterfaceElementName.MINI_PORTFOLIO_NFT_TAB, }, { title: Pools, key: 'pools', component: Pools, - loggingElementName: InterfaceElementName.MINI_PORTFOLIO_POOLS_TAB, }, { title: Activity, key: 'activity', component: ActivityTab, - loggingElementName: InterfaceElementName.MINI_PORTFOLIO_ACTIVITY_TAB, }, -] +]; export default function MiniPortfolio({ account }: { account: string }) { - const isNftPage = useIsNftPage() - const theme = useTheme() - const [currentPage, setCurrentPage] = useState(isNftPage ? 1 : 0) - const shouldDisableNFTRoutes = useDisableNFTRoutes() - const [activityUnread, setActivityUnread] = useState(false) + const theme = useTheme(); + const [currentPage, setCurrentPage] = useState(0); + const [activityUnread, setActivityUnread] = useState(false); - const { component: Page, key: currentKey } = Pages[currentPage] + const { component: Page, key: currentKey } = Pages[currentPage]; - const { hasPendingActivity } = usePendingActivity() + const { hasPendingActivity } = usePendingActivity(); useEffect(() => { - if (hasPendingActivity && currentKey !== 'activity') setActivityUnread(true) - }, [currentKey, hasPendingActivity]) + if (hasPendingActivity && currentKey !== 'activity') { setActivityUnread(true); } + }, [currentKey, hasPendingActivity]); return ( - - - {title} - {showActivityIndicator && ( - <> + + - - - - - - ) + + )} + + ); + })} + + + + + + ); } diff --git a/src/components/AccountDrawer/SettingsMenu.tsx b/src/components/AccountDrawer/SettingsMenu.tsx index f3acce8c..2bce27c9 100644 --- a/src/components/AccountDrawer/SettingsMenu.tsx +++ b/src/components/AccountDrawer/SettingsMenu.tsx @@ -1,22 +1,19 @@ import { Trans } from '@lingui/macro' +import { ReactNode } from 'react' +import { ChevronRight } from 'react-feather' +import styled from 'styled-components' + import Column from 'components/Column' import Row from 'components/Row' import { LOCALE_LABEL } from 'constants/locales' import { useCurrencyConversionFlagEnabled } from 'featureFlags/flags/currencyConversion' import { useActiveLocalCurrency } from 'hooks/useActiveLocalCurrency' import { useActiveLocale } from 'hooks/useActiveLocale' -import { ReactNode } from 'react' -import { ChevronRight } from 'react-feather' -import styled from 'styled-components' import { ClickableStyle, ThemedText } from 'theme/components' -import ThemeToggle from 'theme/components/ThemeToggle' - -import { AnalyticsToggle } from './AnalyticsToggle' import { GitVersionRow } from './GitVersionRow' import { LanguageMenuItems } from './LanguageMenu' import { SlideOutMenu } from './SlideOutMenu' import { SmallBalanceToggle } from './SmallBalanceToggle' -import { TestnetsToggle } from './TestnetsToggle' const Container = styled(Column)` height: 100%; @@ -36,7 +33,7 @@ const ToggleWrapper = styled.div<{ currencyConversionEnabled?: boolean }>` ` const SettingsButtonWrapper = styled(Row)` - ${ClickableStyle} + ${ClickableStyle}; padding: 16px 0px; ` @@ -85,15 +82,12 @@ export default function SettingsMenu({ Settings} onClose={onClose}>
- + {/* Preferences - - - + */} + {/* - - - + */} {!currencyConversionEnabled && ( <> diff --git a/src/components/AccountDrawer/TestnetsToggle.tsx b/src/components/AccountDrawer/TestnetsToggle.tsx deleted file mode 100644 index c914aea8..00000000 --- a/src/components/AccountDrawer/TestnetsToggle.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { t } from '@lingui/macro' -import { useAtom } from 'jotai' -import { atomWithStorage } from 'jotai/utils' - -import { SettingsToggle } from './SettingsToggle' - -export const showTestnetsAtom = atomWithStorage('showTestnets', false) - -export function TestnetsToggle() { - const [showTestnets, updateShowTestnets] = useAtom(showTestnetsAtom) - - return ( - void updateShowTestnets((value) => !value)} - /> - ) -} diff --git a/src/components/AccountDrawer/index.tsx b/src/components/AccountDrawer/index.tsx index e357444d..edaf301c 100644 --- a/src/components/AccountDrawer/index.tsx +++ b/src/components/AccountDrawer/index.tsx @@ -1,23 +1,23 @@ import { BrowserEvent, InterfaceEventName } from '@uniswap/analytics-events' -import { TraceEvent } from 'analytics' -import { ScrollBarStyles } from 'components/Common' -import useDisableScrolling from 'hooks/useDisableScrolling' -import usePrevious from 'hooks/usePrevious' -import { useWindowSize } from 'hooks/useWindowSize' import { atom } from 'jotai' import { useAtomValue, useUpdateAtom } from 'jotai/utils' import { useCallback, useEffect, useRef, useState } from 'react' import { ChevronsRight } from 'react-feather' import { useGesture } from 'react-use-gesture' import styled from 'styled-components' + +import { useWindowSize } from 'hooks/useWindowSize' +import usePrevious from 'hooks/usePrevious' +import useDisableScrolling from 'hooks/useDisableScrolling' +import { ScrollBarStyles } from 'components/Common' +import { TraceEvent } from 'analytics' import { BREAKPOINTS } from 'theme' import { ClickableStyle } from 'theme/components' import { Z_INDEX } from 'theme/zIndex' import { isMobile } from 'utils/userAgent' - import DefaultMenu from './DefaultMenu' -const DRAWER_WIDTH_XL = '390px' +const DRAWER_WIDTH_XL = '324px' const DRAWER_WIDTH = '320px' const DRAWER_MARGIN = '8px' const DRAWER_OFFSET = '10px' @@ -69,7 +69,9 @@ export const Scrim = (props: ScrimBackgroundProps) => { const { width } = useWindowSize() useEffect(() => { - if (width && width < BREAKPOINTS.sm && props.$open) document.body.style.overflow = 'hidden' + if (width && width < BREAKPOINTS.sm && props.$open) { + document.body.style.overflow = 'hidden' + } return () => { document.body.style.overflow = 'visible' } @@ -111,7 +113,7 @@ const Container = styled.div` ` const AccountDrawerWrapper = styled.div<{ open: boolean }>` - margin-right: ${({ open }) => (open ? 0 : '-' + DRAWER_WIDTH)}; + margin-right: ${({ open }) => (open ? 0 : `-${DRAWER_WIDTH}`)}; height: 100%; overflow: hidden; @@ -136,8 +138,8 @@ const AccountDrawerWrapper = styled.div<{ open: boolean }>` border-radius: 12px; width: ${DRAWER_WIDTH}; font-size: 16px; - background-color: ${({ theme }) => theme.surface1}; - border: ${({ theme }) => `1px solid ${theme.surface3}`}; + background: ${({ theme }) => theme.bgdGradient}; + border: ${({ theme }) => `1px solid ${theme.jediGrey}`}; box-shadow: ${({ theme }) => theme.deprecated_deepShadow}; transition: margin-right ${({ theme }) => theme.transition.duration.medium}; @@ -243,15 +245,9 @@ function AccountDrawer() { return ( {walletDrawerOpen && ( - - - - - + + + )} `${theme.breakpoint.md}px`}) { + margin-left: auto; + } + + @media (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { + margin-left: auto; + } ` const ActiveDot = styled.span` - background-color: ${({ theme }) => theme.success}; + background-color: ${({ theme }) => theme.signalGreen}; border-radius: 50%; height: 8px; width: 8px; @@ -30,6 +38,13 @@ const LabelText = styled.div<{ color: string }>` display: flex; flex-direction: row; justify-content: flex-end; + @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { + width: 154px; + } + + @media (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { + width: 154px; + } ` export default function RangeBadge({ removed, inRange }: { removed?: boolean; inRange?: boolean }) { @@ -53,11 +68,11 @@ export default function RangeBadge({ removed, inRange }: { removed?: boolean; in } > - + - In range + In Range - + {/* */} ) : ( @@ -68,11 +83,11 @@ export default function RangeBadge({ removed, inRange }: { removed?: boolean; in } > - + - Out of range + Closed - + {/* */} )} diff --git a/src/components/Badge/index.tsx b/src/components/Badge/index.tsx index dd7ff832..113157b6 100644 --- a/src/components/Badge/index.tsx +++ b/src/components/Badge/index.tsx @@ -1,6 +1,6 @@ -import { readableColor } from 'polished' -import { PropsWithChildren } from 'react' -import styled, { DefaultTheme } from 'styled-components' +import { readableColor } from 'polished'; +import { PropsWithChildren } from 'react'; +import styled, { DefaultTheme } from 'styled-components'; export enum BadgeVariant { DEFAULT = 'DEFAULT', @@ -22,51 +22,51 @@ interface BadgeProps { function pickBackgroundColor(variant: BadgeVariant | undefined, theme: DefaultTheme): string { switch (variant) { case BadgeVariant.BRANDED: - return theme.brandedGradient + return theme.brandedGradient; case BadgeVariant.PROMOTIONAL: - return theme.promotionalGradient + return theme.promotionalGradient; case BadgeVariant.NEGATIVE: - return theme.critical + return theme.critical; case BadgeVariant.POSITIVE: - return theme.success + return theme.success; case BadgeVariant.SOFT: - return theme.accent2 + return theme.accent2; case BadgeVariant.PRIMARY: - return theme.accent1 + return theme.accent1; case BadgeVariant.WARNING: - return theme.deprecated_accentWarning + return theme.deprecated_accentWarning; case BadgeVariant.WARNING_OUTLINE: - return 'transparent' + return 'transparent'; default: - return theme.surface2 + return theme.surface6; } } function pickBorder(variant: BadgeVariant | undefined, theme: DefaultTheme): string { switch (variant) { case BadgeVariant.WARNING_OUTLINE: - return `1px solid ${theme.deprecated_accentWarning}` + return `1px solid ${theme.deprecated_accentWarning}`; default: - return 'unset' + return 'unset'; } } function pickFontColor(variant: BadgeVariant | undefined, theme: DefaultTheme): string { switch (variant) { case BadgeVariant.BRANDED: - return theme.darkMode ? theme.neutral1 : theme.white + return theme.darkMode ? theme.neutral1 : theme.white; case BadgeVariant.NEGATIVE: - return readableColor(theme.critical) + return readableColor(theme.critical); case BadgeVariant.POSITIVE: - return readableColor(theme.success) + return readableColor(theme.success); case BadgeVariant.SOFT: - return theme.accent1 + return theme.accent1; case BadgeVariant.WARNING: - return readableColor(theme.deprecated_accentWarning) + return readableColor(theme.deprecated_accentWarning); case BadgeVariant.WARNING_OUTLINE: - return theme.deprecated_accentWarning + return theme.deprecated_accentWarning; default: - return readableColor(theme.neutral2) + return readableColor(theme.neutral2); } } @@ -80,6 +80,6 @@ const Badge = styled.div>` padding: 4px 6px; justify-content: center; font-weight: 535; -` +`; -export default Badge +export default Badge; diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index c774b109..f700e5c1 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -1,3 +1,4 @@ +// @ts-nocheck import { darken } from 'polished'; import { forwardRef } from 'react'; import { Check, ChevronDown } from 'react-feather'; @@ -30,13 +31,13 @@ type BaseButtonProps = { altDisabledStyle?: boolean } & ButtonProps -export const BaseButton = styled(RebassButton)` +export const BaseButton = styled(RebassButton) ` padding: ${({ padding }) => padding ?? '16px'}; width: ${({ width }) => width ?? '100%'}; line-height: 24px; font-weight: 535; text-align: center; - border-radius: ${({ $borderRadius }) => $borderRadius ?? '16px'}; + border-radius: ${({ $borderRadius }) => $borderRadius ?? '8px'}; outline: none; border: 1px solid transparent; color: ${({ theme }) => theme.neutral1}; @@ -67,30 +68,33 @@ export const BaseButton = styled(RebassButton)` } `; -export const ButtonPrimary = styled(BaseButton)` - background-color: ${({ theme }) => theme.accent1}; - font-size: 20px; - font-weight: 535; - padding: 16px; +export const ButtonPrimary = styled(BaseButton)` + font-family: 'Avenir LT Std'; + width: ${({ width }) => width ?? '100%'}; + background: ${({ theme }) => theme.brandedGradient}; + border: none; + height: auto; + font-size: ${pickThemeButtonFontSize}; + line-height: ${pickThemeButtonLineHeight}; + padding: ${pickThemeButtonPadding}; + font-weight: 700; color: ${({ theme }) => theme.white}; + &:focus { - //box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.accent1)}; - background-color: ${({ theme }) => darken(0.05, theme.accent1)}; + background: ${({ theme }) => theme.brandedGradientReversed}; } &:hover { - background-color: ${({ theme }) => darken(0.05, theme.accent1)}; + background: ${({ theme }) => theme.brandedGradientReversed}; } &:active { - //box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.accent1)}; - background-color: ${({ theme }) => darken(0.1, theme.accent1)}; + background: ${({ theme }) => theme.brandedGradientReversed}; } &:disabled { - background-color: ${({ theme, altDisabledStyle, disabled }) => (altDisabledStyle ? (disabled ? theme.accent1 : theme.surface3) : theme.surface3)}; - color: ${({ altDisabledStyle, disabled, theme }) => (altDisabledStyle ? (disabled ? theme.white : theme.neutral2) : theme.neutral2)}; - cursor: auto; - //box-shadow: none; - border: 1px solid transparent; - outline: none; + cursor: default; + opacity: 100%; + background: ${({ theme }) => theme.jediNavyBlue}; + color: #9B9B9B; + box-shadow: 0px 0.76977px 30.79088px 0px rgba(227, 222, 255, 0.20) inset, 0px 3.07909px 13.8559px 0px rgba(154, 146, 210, 0.30) inset, 0px 75.43767px 76.9772px -36.94907px rgba(202, 172, 255, 0.30) inset; } `; @@ -103,6 +107,7 @@ export const SmallButtonPrimary = styled(ButtonPrimary)` `; const BaseButtonLight = styled(BaseButton)` + font-family: 'Avenir LT Std'; background-color: ${({ theme }) => theme.accent2}; color: ${({ theme }) => theme.accent1}; font-size: 20px; @@ -261,28 +266,14 @@ const ButtonConfirmedStyle = styled(BaseButton)` } `; -const ButtonErrorStyle = styled(BaseButton)` - background-color: ${({ theme }) => theme.critical}; - border: 1px solid ${({ theme }) => theme.critical}; - - &:focus { - //box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.critical)}; - background-color: ${({ theme }) => darken(0.05, theme.critical)}; - } - &:hover { - background-color: ${({ theme }) => darken(0.05, theme.critical)}; - } - &:active { - //box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.critical)}; - background-color: ${({ theme }) => darken(0.1, theme.critical)}; - } - &:disabled { - opacity: 50%; - cursor: auto; - //box-shadow: none; - background-color: ${({ theme }) => theme.critical}; - border: 1px solid ${({ theme }) => theme.critical}; - } +const ButtonErrorStyle = styled(ButtonPrimary)` + font-family: 'Avenir LT Std'; + color: ${({ theme }) => theme.critical}; + + &:disabled { + color: ${({ theme }) => theme.critical}; + cursor: auto; + } `; export function ButtonConfirmed({ confirmed, @@ -296,7 +287,7 @@ export function ButtonConfirmed({ confirmed, export function ButtonError({ error, ...rest }: { error?: boolean } & BaseButtonProps) { if (error) { - return ; + return ; } return ; } @@ -324,8 +315,7 @@ export function ButtonDropdownLight({ disabled = false, children, ...rest }: { d } const ActiveOutlined = styled(ButtonOutlined)` - border: 1px solid; - border-color: ${({ theme }) => theme.accent1}; + border: ${({ theme }) => `1px solid ${theme.jediBlue}`} !important ; `; const Circle = styled.div` @@ -365,9 +355,9 @@ export function ButtonRadioChecked({ active = false, children, ...rest }: { acti {children} - + {/* - + */} @@ -391,42 +381,23 @@ export enum ButtonEmphasis { } interface BaseThemeButtonProps { size: ButtonSize - emphasis: ButtonEmphasis + emphasis?: ButtonEmphasis } -function pickThemeButtonBackgroundColor({ theme, emphasis }: { theme: DefaultTheme; emphasis: ButtonEmphasis }) { - switch (emphasis) { - case ButtonEmphasis.high: - return theme.accent1; - case ButtonEmphasis.promotional: - case ButtonEmphasis.highSoft: - return theme.accent2; - case ButtonEmphasis.low: - return 'transparent'; - case ButtonEmphasis.warning: - return theme.deprecated_accentWarningSoft; - case ButtonEmphasis.destructive: - return theme.critical; - case ButtonEmphasis.failure: - return theme.deprecated_accentFailureSoft; - case ButtonEmphasis.medium: - default: - return theme.surface3; - } -} -function pickThemeButtonFontSize({ size }: { size: ButtonSize }) { +function pickThemeButtonFontSize({ size }) { + console.log(size); switch (size) { case ButtonSize.large: - return '20px'; + return '24px'; case ButtonSize.medium: - return '16px'; + return '20px'; case ButtonSize.small: - return '14px'; + return '16px'; default: return '16px'; } } -function pickThemeButtonLineHeight({ size }: { size: ButtonSize }) { +function pickThemeButtonLineHeight({ size }) { switch (size) { case ButtonSize.large: return '24px'; @@ -438,50 +409,30 @@ function pickThemeButtonLineHeight({ size }: { size: ButtonSize }) { return '20px'; } } -function pickThemeButtonPadding({ size }: { size: ButtonSize }) { +function pickThemeButtonPadding({ size }) { switch (size) { case ButtonSize.large: - return '16px'; + return '22px 16px'; case ButtonSize.medium: - return '10px 12px'; + return '14px 12px'; case ButtonSize.small: - return '8px'; + return '10px'; default: - return '10px 12px'; - } -} -function pickThemeButtonTextColor({ theme, emphasis }: { theme: DefaultTheme; emphasis: ButtonEmphasis }) { - switch (emphasis) { - case ButtonEmphasis.high: - case ButtonEmphasis.promotional: - return theme.accent1; - case ButtonEmphasis.highSoft: - return theme.accent1; - case ButtonEmphasis.low: - return theme.neutral2; - case ButtonEmphasis.warning: - return theme.deprecated_accentWarning; - case ButtonEmphasis.destructive: - return theme.neutral1; - case ButtonEmphasis.failure: - return theme.critical; - case ButtonEmphasis.medium: - default: - return theme.neutral1; + return '10px'; } } const BaseThemeButton = styled.button` align-items: center; - background-color: ${pickThemeButtonBackgroundColor}; - border-radius: 16px; + background: ${({ theme }) => theme.brandedGradient}; + border-radius: 8px; border: 0; - color: ${pickThemeButtonTextColor}; + color: ${({ theme }) => theme.white}; cursor: pointer; display: flex; flex-direction: row; font-size: ${pickThemeButtonFontSize}; - font-weight: 535; + font-weight: 750; gap: 12px; justify-content: center; line-height: ${pickThemeButtonLineHeight}; @@ -489,21 +440,20 @@ const BaseThemeButton = styled.button` position: relative; transition: 150ms ease opacity; user-select: none; - + + :hover, + :focus { + background: ${({ theme }) => theme.brandedGradientReversed}; + } + :active { - ${ButtonOverlay} { - background-color: ${({ theme }) => theme.deprecated_stateOverlayPressed}; - } + background: ${({ theme }) => theme.brandedGradientReversed}; } :focus { - ${ButtonOverlay} { - background-color: ${({ theme }) => theme.deprecated_stateOverlayPressed}; - } + background: ${({ theme }) => theme.brandedGradientReversed}; } :hover { - ${ButtonOverlay} { - background-color: ${({ theme }) => theme.deprecated_stateOverlayHover}; - } + background: ${({ theme }) => theme.brandedGradientReversed}; } :disabled { cursor: default; @@ -518,7 +468,7 @@ const BaseThemeButton = styled.button` } `; -interface ThemeButtonProps extends React.ComponentPropsWithoutRef<'button'>, BaseThemeButtonProps {} +interface ThemeButtonProps extends React.ComponentPropsWithoutRef<'button'>, BaseThemeButtonProps { } type ThemeButtonRef = HTMLButtonElement export const ThemeButton = forwardRef(( @@ -526,7 +476,6 @@ export const ThemeButton = forwardRef(( ref, ) => ( - {children} )); diff --git a/src/components/Card/index.tsx b/src/components/Card/index.tsx index 9fb4767f..8af2c353 100644 --- a/src/components/Card/index.tsx +++ b/src/components/Card/index.tsx @@ -10,8 +10,9 @@ const Card = styled(Box)<{ width?: string; padding?: string; border?: string; $b export default Card export const LightCard = styled(Card)` - border: 1px solid ${({ theme }) => theme.surface3}; - background-color: ${({ theme }) => theme.surface2}; + border-radius: 8px; + background: rgba(196, 196, 196, 0.01); + box-shadow: 0px 0.76977px 30.79088px 0px rgba(227, 222, 255, 0.20) inset, 0px 3.07909px 13.8559px 0px rgba(154, 146, 210, 0.30) inset, 0px 75.43767px 76.9772px -36.94907px rgba(202, 172, 255, 0.30) inset, 0px -63.12132px 52.3445px -49.26542px rgba(96, 68, 144, 0.30) inset; ` export const GrayCard = styled(Card)` @@ -28,8 +29,7 @@ export const DarkCard = styled(Card)` ` export const OutlineCard = styled(Card)` - border: 1px solid ${({ theme }) => theme.surface3}; - background-color: ${({ theme }) => theme.surface2}; + border: 1px solid ${({ theme }) => theme.jediGreyBorder}; ` export const YellowCard = styled(Card)` @@ -39,7 +39,7 @@ export const YellowCard = styled(Card)` ` export const BlueCard = styled(Card)` - background-color: ${({ theme }) => theme.accent2}; - color: ${({ theme }) => theme.accent1}; + background-color: ${({ theme }) => theme.jediNavyBlue}; + color: ${({ theme }) => theme.jediWhite}; border-radius: 12px; ` diff --git a/src/components/Column/index.tsx b/src/components/Column/index.tsx index f2fb29de..3c93f5e4 100644 --- a/src/components/Column/index.tsx +++ b/src/components/Column/index.tsx @@ -1,5 +1,6 @@ -import styled from 'styled-components' -import { Gap } from 'theme' +import styled from 'styled-components'; + +import { Gap } from 'theme'; export const Column = styled.div<{ gap?: Gap @@ -8,11 +9,11 @@ export const Column = styled.div<{ flex-direction: column; justify-content: flex-start; gap: ${({ gap, theme }) => gap && theme.grids[gap]}; -` +`; export const ColumnCenter = styled(Column)` width: 100%; align-items: center; -` +`; export const AutoColumn = styled.div<{ gap?: Gap | string @@ -24,6 +25,6 @@ export const AutoColumn = styled.div<{ grid-row-gap: ${({ gap, theme }) => (gap && theme.grids[gap as Gap]) || gap}; justify-items: ${({ justify }) => justify && justify}; flex-grow: ${({ grow }) => grow && 1}; -` +`; -export default Column +export default Column; diff --git a/src/components/CurrencyInputPanel/FiatValue.tsx b/src/components/CurrencyInputPanel/FiatValue.tsx index cf7a3bde..163270cd 100644 --- a/src/components/CurrencyInputPanel/FiatValue.tsx +++ b/src/components/CurrencyInputPanel/FiatValue.tsx @@ -1,45 +1,44 @@ -import { Trans } from '@lingui/macro' -import { Percent } from '@uniswap/sdk-core' -import Row from 'components/Row' -import { LoadingBubble } from 'components/Tokens/loading' -import { MouseoverTooltip } from 'components/Tooltip' -import { useMemo } from 'react' -import styled from 'styled-components' -import { ThemedText } from 'theme/components' -import { NumberType, useFormatter } from 'utils/formatNumbers' -import { warningSeverity } from 'utils/prices' +import { Trans } from '@lingui/macro'; +import { Percent } from '@uniswap/sdk-core'; +import { useMemo } from 'react'; +import styled from 'styled-components'; + +import Row from 'components/Row'; +import { LoadingBubble } from 'components/Tokens/loading'; +import { MouseoverTooltip } from 'components/Tooltip'; +import { ThemedText } from 'theme/components'; +import { NumberType, useFormatter } from 'utils/formatNumbers'; +import { warningSeverity } from 'utils/prices'; const FiatLoadingBubble = styled(LoadingBubble)` border-radius: 4px; width: 4rem; height: 1rem; -` +`; -export function FiatValue({ - fiatValue, - priceImpact, -}: { +export function FiatValue({ fiatValue, + priceImpact }: { fiatValue: { data?: number; isLoading: boolean } priceImpact?: Percent }) { - const { formatNumber, formatPercent } = useFormatter() + const { formatNumber, formatPercent } = useFormatter(); const priceImpactColor = useMemo(() => { - if (!priceImpact) return undefined - if (priceImpact.lessThan('0')) return 'success' - const severity = warningSeverity(priceImpact) - if (severity < 1) return 'neutral3' - if (severity < 3) return 'deprecated_yellow1' - return 'critical' - }, [priceImpact]) + if (!priceImpact) { return undefined; } + if (priceImpact.lessThan('0')) { return 'success'; } + const severity = warningSeverity(priceImpact); + if (severity < 1) { return 'neutral3'; } + if (severity < 3) { return 'deprecated_yellow1'; } + return 'critical'; + }, [priceImpact]); if (fiatValue.isLoading) { - return + return ; } return ( - + {fiatValue.data ? ( formatNumber({ input: fiatValue.data, @@ -48,7 +47,7 @@ export function FiatValue({ ) : ( Not enough liquidity to show accurate USD value.}>- )} - + {priceImpact && ( )} - ) + ); } diff --git a/src/components/CurrencyInputPanel/SwapCurrencyInputPanel.tsx b/src/components/CurrencyInputPanel/SwapCurrencyInputPanel.tsx index a9de21dc..4d56838e 100644 --- a/src/components/CurrencyInputPanel/SwapCurrencyInputPanel.tsx +++ b/src/components/CurrencyInputPanel/SwapCurrencyInputPanel.tsx @@ -1,32 +1,30 @@ -import { Trans } from '@lingui/macro' -import { BrowserEvent, InterfaceElementName, SwapEventName } from '@uniswap/analytics-events' -import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core' -import { Pair } from '@uniswap/v2-sdk' -import { useWeb3React } from '@web3-react/core' -import { TraceEvent } from 'analytics' -import { AutoColumn } from 'components/Column' -import { LoadingOpacityContainer, loadingOpacityMixin } from 'components/Loader/styled' -import CurrencyLogo from 'components/Logo/CurrencyLogo' -import PrefetchBalancesWrapper from 'components/PrefetchBalancesWrapper/PrefetchBalancesWrapper' -import Tooltip from 'components/Tooltip' -import { isSupportedChain } from 'constants/chains' -import ms from 'ms' -import { darken } from 'polished' -import { forwardRef, ReactNode, useCallback, useEffect, useState } from 'react' -import { Lock } from 'react-feather' -import styled, { useTheme } from 'styled-components' -import { ThemedText } from 'theme/components' -import { flexColumnNoWrap, flexRowNoWrap } from 'theme/styles' -import { NumberType, useFormatter } from 'utils/formatNumbers' - -import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg' -import { useCurrencyBalance } from '../../state/connection/hooks' -import { ButtonGray } from '../Button' -import DoubleCurrencyLogo from '../DoubleLogo' -import { Input as NumericalInput } from '../NumericalInput' -import { RowBetween, RowFixed } from '../Row' -import CurrencySearchModal from '../SearchModal/CurrencySearchModal' -import { FiatValue } from './FiatValue' +import { Trans } from '@lingui/macro'; +import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'; +import { Pair } from '@uniswap/v2-sdk'; +import { useWeb3React } from '@web3-react/core'; +import ms from 'ms'; +import { darken } from 'polished'; +import { forwardRef, ReactNode, useCallback, useEffect, useState } from 'react'; +import { Lock } from 'react-feather'; +import styled, { useTheme } from 'styled-components'; + +import { AutoColumn } from 'components/Column'; +import { LoadingOpacityContainer, loadingOpacityMixin } from 'components/Loader/styled'; +import CurrencyLogo from 'components/Logo/CurrencyLogo'; +import PrefetchBalancesWrapper from 'components/PrefetchBalancesWrapper/PrefetchBalancesWrapper'; +import Tooltip from 'components/Tooltip'; +import { isSupportedChain } from 'constants/chains'; +import { ThemedText } from 'theme/components'; +import { flexColumnNoWrap, flexRowNoWrap } from 'theme/styles'; +import { NumberType, useFormatter } from 'utils/formatNumbers'; +import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'; +import { useCurrencyBalance } from '../../state/connection/hooks'; +import { BaseButton, ButtonGray } from '../Button'; +import DoubleCurrencyLogo from '../DoubleLogo'; +import { Input as NumericalInput } from '../NumericalInput'; +import { RowBetween, RowFixed } from '../Row'; +import CurrencySearchModal from '../SearchModal/CurrencySearchModal'; +import { FiatValue } from './FiatValue'; const InputPanel = styled.div<{ hideInput?: boolean }>` ${flexColumnNoWrap}; @@ -36,7 +34,7 @@ const InputPanel = styled.div<{ hideInput?: boolean }>` width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')}; transition: height 1s ease; will-change: height; -` +`; const FixedContainer = styled.div` width: 100%; @@ -47,15 +45,18 @@ const FixedContainer = styled.div` align-items: center; justify-content: center; z-index: 2; -` +`; const Container = styled.div<{ hideInput: boolean }>` min-height: 44px; border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')}; width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')}; -` + display: flex; + flex-direction: column; + row-gap: 12px; +`; -const CurrencySelect = styled(ButtonGray)<{ +const CurrencySelect = styled(BaseButton)<{ visible: boolean selected: boolean hideInput?: boolean @@ -63,27 +64,27 @@ const CurrencySelect = styled(ButtonGray)<{ animateShake?: boolean }>` align-items: center; - background-color: ${({ selected, theme }) => (selected ? theme.surface1 : theme.accent1)}; + background-color: ${({ theme }) => theme.jediNavyBlue}; opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)}; color: ${({ selected, theme }) => (selected ? theme.neutral1 : theme.white)}; cursor: pointer; - height: 36px; - border-radius: 18px; + height: 40px; + border-radius: 8px; outline: none; user-select: none; - border: 1px solid ${({ selected, theme }) => (selected ? theme.surface3 : theme.accent1)}; - font-size: 24px; - font-weight: 485; + border: 1px solid ${({ selected, theme }) => (selected ? '#fff' : 'transparent')}; + font-size: 14px; + font-weight: 500; width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')}; - padding: ${({ selected }) => (selected ? '4px 8px 4px 4px' : '6px 6px 6px 8px')}; + padding: 0 8px; gap: 8px; justify-content: space-between; - margin-left: ${({ hideInput }) => (hideInput ? '0' : '12px')}; + margin-right: ${({ hideInput }) => (hideInput ? '0' : '12px')}; box-shadow: ${({ theme }) => theme.deprecated_shallowShadow}; &:hover, &:active { - background-color: ${({ theme, selected }) => (selected ? theme.surface2 : theme.accent1)}; + border: 1px solid #fff; } &:before { @@ -99,14 +100,6 @@ const CurrencySelect = styled(ButtonGray)<{ content: ''; } - &:hover:before { - background-color: ${({ theme }) => theme.deprecated_stateOverlayHover}; - } - - &:active:before { - background-color: ${({ theme }) => theme.deprecated_stateOverlayPressed}; - } - visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')}; @keyframes horizontal-shaking { @@ -136,13 +129,13 @@ const CurrencySelect = styled(ButtonGray)<{ } } animation: ${({ animateShake }) => (animateShake ? 'horizontal-shaking 300ms' : 'none')}; -` +`; const InputRow = styled.div` ${flexRowNoWrap}; align-items: center; justify-content: space-between; -` +`; const LabelRow = styled.div` ${flexRowNoWrap}; @@ -155,23 +148,22 @@ const LabelRow = styled.div` cursor: pointer; color: ${({ theme }) => darken(0.2, theme.neutral2)}; } -` +`; const FiatRow = styled(LabelRow)` justify-content: flex-end; - min-height: 24px; - padding: 8px 0px 0px 0px; -` + //min-height: 24px; +`; const Aligner = styled.span` display: flex; align-items: center; justify-content: space-between; width: 100%; -` +`; const StyledDropDown = styled(DropDown)<{ selected: boolean }>` - margin: 0 0.25rem 0 0.35rem; + margin: 0; height: 35%; margin-left: 8px; @@ -179,23 +171,26 @@ const StyledDropDown = styled(DropDown)<{ selected: boolean }>` stroke: ${({ selected, theme }) => (selected ? theme.neutral1 : theme.white)}; stroke-width: 2px; } -` +`; const StyledTokenName = styled.span<{ active?: boolean }>` ${({ active }) => (active ? ' margin: 0 0.25rem 0 0.25rem;' : ' margin: 0 0.25rem 0 0.25rem;')} - font-size: 20px; - font-weight: 535; -` + font-size: 14px; + font-weight: 500; +`; const StyledBalanceMax = styled.button<{ disabled?: boolean }>` - background-color: transparent; - border: none; - color: ${({ theme }) => theme.accent1}; + border-radius: 4px; + border: 1px solid #FFF; + background: transparent; + color: ${({ theme }) => theme.neutral1}; cursor: pointer; - font-size: 14px; - font-weight: 535; + font-size: 12px; + line-height: 1; + font-weight: 400; opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)}; - padding: 4px 6px; + padding: 4px 12px; + text-transform: uppercase; pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')}; :hover { @@ -205,15 +200,15 @@ const StyledBalanceMax = styled.button<{ disabled?: boolean }>` :focus { outline: none; } -` +`; const StyledNumericalInput = styled(NumericalInput)<{ $loading: boolean }>` ${loadingOpacityMixin}; - text-align: left; + text-align: right; font-size: 36px; font-weight: 485; max-height: 44px; -` +`; interface SwapCurrencyInputPanelProps { value: string @@ -246,8 +241,7 @@ interface SwapCurrencyInputPanelProps { const SwapCurrencyInputPanel = forwardRef( ( - { - value, + { value, onUserInput, onMax, showMaxButton, @@ -269,33 +263,32 @@ const SwapCurrencyInputPanel = forwardRef { - const [modalOpen, setModalOpen] = useState(false) - const { account, chainId } = useWeb3React() - const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined) - const theme = useTheme() - const { formatCurrencyAmount } = useFormatter() + const [modalOpen, setModalOpen] = useState(false); + const { account, chainId } = useWeb3React(); + const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined); + const theme = useTheme(); + const { formatCurrencyAmount } = useFormatter(); const handleDismissSearch = useCallback(() => { - setModalOpen(false) - }, [setModalOpen]) + setModalOpen(false); + }, [setModalOpen]); - const [tooltipVisible, setTooltipVisible] = useState(false) + const [tooltipVisible, setTooltipVisible] = useState(false); const handleDisabledNumericalInputClick = useCallback(() => { if (numericalInputSettings?.disabled && !tooltipVisible) { - setTooltipVisible(true) - setTimeout(() => setTooltipVisible(false), ms('4s')) // reset shake animation state after 4s - numericalInputSettings.onDisabledClick?.() + setTooltipVisible(true); + setTimeout(() => setTooltipVisible(false), ms('4s')); // reset shake animation state after 4s + numericalInputSettings.onDisabledClick?.(); } - }, [tooltipVisible, numericalInputSettings]) + }, [tooltipVisible, numericalInputSettings]); - const chainAllowed = isSupportedChain(chainId) + const chainAllowed = isSupportedChain(chainId); // reset tooltip state when currency changes - useEffect(() => setTooltipVisible(false), [currency]) + useEffect(() => setTooltipVisible(false), [currency]); return ( @@ -311,21 +304,15 @@ const SwapCurrencyInputPanel = forwardRef - {label} + + {label} + {showMaxButton && selectedCurrencyBalance ? ( + + Max + + ) : null} + - {!hideInput && ( -
- -
- )} { if (onCurrencySelect) { - setModalOpen(true) + setModalOpen(true); } }} animateShake={tooltipVisible} @@ -350,7 +337,7 @@ const SwapCurrencyInputPanel = forwardRef {pair ? ( - + ) : currency ? ( @@ -365,9 +352,9 @@ const SwapCurrencyInputPanel = forwardRef {(currency && currency.symbol && currency.symbol.length > 20 - ? currency.symbol.slice(0, 4) + - '...' + - currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length) + ? `${currency.symbol.slice(0, 4) + }...${ + currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length)}` : currency?.symbol) || Select token} )} @@ -377,22 +364,26 @@ const SwapCurrencyInputPanel = forwardRef + {!hideInput && ( +
+ +
+ )}
- {Boolean(!hideInput && !hideBalance) && ( + {!(hideInput && hideBalance) && ( - - {fiatValue && } - {account ? ( - + {!hideBalance && currency && selectedCurrencyBalance ? ( renderBalance ? ( renderBalance(selectedCurrencyBalance) @@ -406,22 +397,14 @@ const SwapCurrencyInputPanel = forwardRef ) ) : null} - - {showMaxButton && selectedCurrencyBalance ? ( - - - Max - - - ) : null} + ) : ( )} + + {fiatValue && } + )} @@ -439,9 +422,9 @@ const SwapCurrencyInputPanel = forwardRef )}
- ) - } -) -SwapCurrencyInputPanel.displayName = 'SwapCurrencyInputPanel' + ); + }, +); +SwapCurrencyInputPanel.displayName = 'SwapCurrencyInputPanel'; -export default SwapCurrencyInputPanel +export default SwapCurrencyInputPanel; diff --git a/src/components/CurrencyInputPanel/index.tsx b/src/components/CurrencyInputPanel/index.tsx index f28bfcdd..4d5f666e 100644 --- a/src/components/CurrencyInputPanel/index.tsx +++ b/src/components/CurrencyInputPanel/index.tsx @@ -3,17 +3,17 @@ import { BrowserEvent, InterfaceElementName, SwapEventName } from '@uniswap/anal import { Currency, CurrencyAmount } from '@uniswap/sdk-core' import { Pair } from '@uniswap/v2-sdk' import { useWeb3React } from '@web3-react/core' +import { darken } from 'polished' +import { ReactNode, useCallback, useState } from 'react' +import styled, { useTheme } from 'styled-components' + import { TraceEvent } from 'analytics' import { LoadingOpacityContainer, loadingOpacityMixin } from 'components/Loader/styled' import PrefetchBalancesWrapper from 'components/PrefetchBalancesWrapper/PrefetchBalancesWrapper' import { isSupportedChain } from 'constants/chains' -import { darken } from 'polished' -import { ReactNode, useCallback, useState } from 'react' -import styled, { useTheme } from 'styled-components' import { ThemedText } from 'theme/components' import { flexColumnNoWrap, flexRowNoWrap } from 'theme/styles' import { formatCurrencyAmount } from 'utils/formatCurrencyAmount' - import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg' import { useCurrencyBalance } from '../../state/connection/hooks' import { ButtonGray } from '../Button' @@ -28,7 +28,7 @@ const InputPanel = styled.div<{ hideInput?: boolean }>` ${flexColumnNoWrap}; position: relative; border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')}; - background-color: ${({ theme, hideInput }) => (hideInput ? 'transparent' : theme.surface2)}; + // background-color: ${({ theme, hideInput }) => (hideInput ? 'transparent' : theme.surface2)}; z-index: 1; width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')}; @@ -37,10 +37,13 @@ const InputPanel = styled.div<{ hideInput?: boolean }>` ` const Container = styled.div<{ hideInput: boolean; disabled: boolean }>` - border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')}; - border: 1px solid ${({ theme }) => theme.surface3}; - background-color: ${({ theme }) => theme.surface2}; - width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')}; + border-radius: ${({ hideInput }) => (hideInput ? '8px' : '8px')}; + background-color: rgba(196, 196, 196, 0.01); + box-shadow: 0px 0.76977px 30.79088px 0px rgba(227, 222, 255, 0.2) inset, + 0px 3.07909px 13.8559px 0px rgba(154, 146, 210, 0.3) inset, + 0px 75.43767px 76.9772px -36.94907px rgba(202, 172, 255, 0.3) inset, + 0px -63.12132px 52.3445px -49.26542px rgba(96, 68, 144, 0.3) inset; + width: ${({ hideInput }) => (hideInput ? '90%' : 'initial')}; ${({ theme, hideInput, disabled }) => !disabled && ` @@ -59,7 +62,8 @@ const CurrencySelect = styled(ButtonGray)<{ pointerEvents?: string }>` align-items: center; - background-color: ${({ selected, theme }) => (selected ? theme.surface1 : theme.accent1)}; + // background-color: ${({ selected, theme }) => (selected ? theme.surface1 : theme.accent1)}; + background-color: ${({ theme }) => theme.jediNavyBlue}; opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)}; box-shadow: ${({ theme }) => theme.deprecated_shallowShadow}; color: ${({ selected, theme }) => (selected ? theme.neutral1 : theme.white)}; @@ -75,19 +79,15 @@ const CurrencySelect = styled(ButtonGray)<{ padding: 0 8px; justify-content: space-between; margin-left: ${({ hideInput }) => (hideInput ? '0' : '12px')}; - :focus, - :hover { - background-color: ${({ selected, theme }) => (selected ? theme.surface2 : darken(0.05, theme.accent1))}; - } visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')}; - ${({ pointerEvents }) => pointerEvents && `pointer-events: none`} + ${({ pointerEvents }) => pointerEvents && 'pointer-events: none'} ` const InputRow = styled.div<{ selected: boolean }>` ${flexRowNoWrap}; align-items: center; justify-content: space-between; - padding: ${({ selected }) => (selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 1rem 1rem')}; + padding: ${({ selected }) => (selected ? ' 1rem 1rem 0.75rem 0' : '1rem 1rem 1rem 0')}; ` const LabelRow = styled.div` @@ -130,19 +130,21 @@ const StyledDropDown = styled(DropDown)<{ selected: boolean }>` const StyledTokenName = styled.span<{ active?: boolean }>` ${({ active }) => (active ? ' margin: 0 0.25rem 0 0.25rem;' : ' margin: 0 0.25rem 0 0.25rem;')} - font-size: 20px; + font-size: 14px; + font-weight: 700; + font-family: 'DM Sans'; ` const StyledBalanceMax = styled.button<{ disabled?: boolean }>` background-color: transparent; - background-color: ${({ theme }) => theme.accent2}; - border: none; - border-radius: 12px; - color: ${({ theme }) => theme.accent1}; + // background-color: ${({ theme }) => theme.accent2}; + border: 1px solid #444; + border-radius: 4px; + color: ${({ theme }) => theme.jediWhite}; cursor: pointer; font-size: 11px; font-weight: 535; - margin-left: 0.25rem; + margin-right: 0.5rem; opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)}; padding: 4px 6px; pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')}; @@ -185,6 +187,7 @@ interface CurrencyInputPanelProps { renderBalance?: (amount: CurrencyAmount) => ReactNode locked?: boolean loading?: boolean + hideShadow?: boolean } export default function CurrencyInputPanel({ @@ -206,6 +209,7 @@ export default function CurrencyInputPanel({ hideInput = false, locked = false, loading = false, + hideShadow = false, ...rest }: CurrencyInputPanelProps) { const [modalOpen, setModalOpen] = useState(false) @@ -219,105 +223,104 @@ export default function CurrencyInputPanel({ const chainAllowed = isSupportedChain(chainId) + const containerStyles = hideShadow ? { boxShadow: 'none' } : {} return ( {!locked && ( - <> - - - {!hideInput && ( + + + + { + if (onCurrencySelect) { + setModalOpen(true) + } + }} + pointerEvents={!onCurrencySelect ? 'none' : undefined} + > + + + {pair ? ( + + + + ) : ( + currency && + )} + {pair ? ( + + {pair?.token0.symbol}:{pair?.token1.symbol} + + ) : ( + + {(currency && currency.symbol && currency.symbol.length > 20 + ? `${currency.symbol.slice(0, 4)}...${currency.symbol.slice( + currency.symbol.length - 5, + currency.symbol.length + )}` + : currency?.symbol) || Select a token} + + )} + + {onCurrencySelect && } + + + + {!hideInput && ( +
- )} - - - { - if (onCurrencySelect) { - setModalOpen(true) - } - }} - pointerEvents={!onCurrencySelect ? 'none' : undefined} - > - - - {pair ? ( - - - - ) : ( - currency && - )} - {pair ? ( - - {pair?.token0.symbol}:{pair?.token1.symbol} - - ) : ( - - {(currency && currency.symbol && currency.symbol.length > 20 - ? currency.symbol.slice(0, 4) + - '...' + - currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length) - : currency?.symbol) || Select a token} - - )} - - {onCurrencySelect && } - - - - - {Boolean(!hideInput && !hideBalance && currency) && ( - - - - {fiatValue && } - - {account && ( - - - {Boolean(!hideBalance && currency && selectedCurrencyBalance) && - (renderBalance?.(selectedCurrencyBalance as CurrencyAmount) || ( - Balance: {formatCurrencyAmount(selectedCurrencyBalance, 4)} - ))} - - {Boolean(showMaxButton && selectedCurrencyBalance) && ( - - - MAX - - - )} - - )} - - +
)} -
- +
+ {Boolean(!hideInput && !hideBalance && currency) && ( + + + {account && ( + + {Boolean(showMaxButton && selectedCurrencyBalance) && ( + + + MAX + + + )} + + {Boolean(!hideBalance && currency && selectedCurrencyBalance) && + (renderBalance?.(selectedCurrencyBalance as CurrencyAmount) || ( + Bal: {formatCurrencyAmount(selectedCurrencyBalance, 4)} + ))} + + + )} + + {fiatValue && } + + + + )} +
)} {onCurrencySelect && ( + diff --git a/src/components/FeeSelector/FeeTierPercentageBadge.tsx b/src/components/FeeSelector/FeeTierPercentageBadge.tsx index 611dec8a..f0a84a7e 100644 --- a/src/components/FeeSelector/FeeTierPercentageBadge.tsx +++ b/src/components/FeeSelector/FeeTierPercentageBadge.tsx @@ -16,8 +16,8 @@ export function FeeTierPercentageBadge({ poolState: PoolState }) { return ( - - + + {!distributions || poolState === PoolState.NOT_EXISTS || poolState === PoolState.INVALID ? ( Not created ) : distributions[feeAmount] !== undefined ? ( diff --git a/src/components/FeeSelector/index.tsx b/src/components/FeeSelector/index.tsx index 1ef77a04..45d8cc62 100644 --- a/src/components/FeeSelector/index.tsx +++ b/src/components/FeeSelector/index.tsx @@ -1,25 +1,26 @@ -import { Trans } from '@lingui/macro' -import { FeePoolSelectAction, LiquidityEventName } from '@uniswap/analytics-events' -import { Currency } from '@uniswap/sdk-core' -import { FeeAmount } from '@uniswap/v3-sdk' -import { useWeb3React } from '@web3-react/core' -import { sendAnalyticsEvent, useTrace } from 'analytics' -import { ButtonGray } from 'components/Button' -import Card from 'components/Card' -import { AutoColumn } from 'components/Column' -import { RowBetween } from 'components/Row' -import { useFeeTierDistribution } from 'hooks/useFeeTierDistribution' -import { PoolState, usePools } from 'hooks/usePools' -import usePrevious from 'hooks/usePrevious' -import { DynamicSection } from 'pages/AddLiquidity/styled' -import { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { Box } from 'rebass' -import styled, { keyframes } from 'styled-components' -import { ThemedText } from 'theme/components' - -import { FeeOption } from './FeeOption' -import { FeeTierPercentageBadge } from './FeeTierPercentageBadge' -import { FEE_AMOUNT_DETAIL } from './shared' +// @ts-nocheck +import { Trans } from '@lingui/macro'; +import { FeePoolSelectAction, LiquidityEventName } from '@uniswap/analytics-events'; +import { Currency } from '@uniswap/sdk-core'; +import { FeeAmount } from '@uniswap/v3-sdk'; +import { useWeb3React } from '@web3-react/core'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { Box } from 'rebass'; +import styled, { keyframes } from 'styled-components'; + +import { sendAnalyticsEvent, useTrace } from 'analytics'; +import { ButtonGray } from 'components/Button'; +import Card from 'components/Card'; +import { AutoColumn } from 'components/Column'; +import { RowBetween } from 'components/Row'; +import { useFeeTierDistribution } from 'hooks/useFeeTierDistribution'; +import { PoolState, usePools } from 'hooks/usePools'; +import usePrevious from 'hooks/usePrevious'; +import { DynamicSection } from 'pages/AddLiquidity/styled'; +import { ThemedText } from 'theme/components'; +import { FeeOption } from './FeeOption'; +import { FeeTierPercentageBadge } from './FeeTierPercentageBadge'; +import { FEE_AMOUNT_DETAIL } from './shared'; const pulse = (color: string) => keyframes` 0% { @@ -33,37 +34,36 @@ const pulse = (color: string) => keyframes` 100% { box-shadow: 0 0 0 0 ${color}; } -` -const FocusedOutlineCard = styled(Card)<{ pulsing: boolean }>` - border: 1px solid ${({ theme }) => theme.surface3}; +`; +const FocusedOutlineCard = styled(Card) <{ pulsing: boolean, feeAmount: FeeAmount }>` + border: 1px solid ${({ theme, feeAmount }) => (feeAmount ? theme.jediBlue : theme.surface3)}; animation: ${({ pulsing, theme }) => pulsing && pulse(theme.accent1)} 0.6s linear; align-self: center; -` + border-radius: 8px; +`; const Select = styled.div` align-items: flex-start; display: grid; grid-auto-flow: column; grid-gap: 8px; -` +`; -export default function FeeSelector({ - disabled = false, +export default function FeeSelector({ disabled = false, feeAmount, handleFeePoolSelect, currencyA, - currencyB, -}: { + currencyB }: { disabled?: boolean feeAmount?: FeeAmount handleFeePoolSelect: (feeAmount: FeeAmount) => void currencyA?: Currency currencyB?: Currency }) { - const { chainId } = useWeb3React() - const trace = useTrace() + const { chainId } = useWeb3React(); + const trace = useTrace(); - const { isLoading, isError, largestUsageFeeTier, distributions } = useFeeTierDistribution(currencyA, currencyB) + const { isLoading, isError, largestUsageFeeTier, distributions } = useFeeTierDistribution(currencyA, currencyB); // get pool data on-chain for latest states const pools = usePools([ @@ -71,99 +71,101 @@ export default function FeeSelector({ [currencyA, currencyB, FeeAmount.LOW], [currencyA, currencyB, FeeAmount.MEDIUM], [currencyA, currencyB, FeeAmount.HIGH], - ]) + ]); const poolsByFeeTier: Record = useMemo( - () => - pools.reduce( - (acc, [curPoolState, curPool]) => { - acc = { - ...acc, - ...{ [curPool?.fee as FeeAmount]: curPoolState }, - } - return acc - }, - { - // default all states to NOT_EXISTS - [FeeAmount.LOWEST]: PoolState.NOT_EXISTS, - [FeeAmount.LOW]: PoolState.NOT_EXISTS, - [FeeAmount.MEDIUM]: PoolState.NOT_EXISTS, - [FeeAmount.HIGH]: PoolState.NOT_EXISTS, - } - ), - [pools] - ) - - const [showOptions, setShowOptions] = useState(false) - const [pulsing, setPulsing] = useState(false) - - const previousFeeAmount = usePrevious(feeAmount) - - const recommended = useRef(false) + () => pools.reduce( + (acc, [curPoolState, curPool]) => { + acc = { + ...acc, + ...{ [curPool?.fee as FeeAmount]: curPoolState }, + }; + return acc; + }, + { + // default all states to NOT_EXISTS + [FeeAmount.LOWEST]: PoolState.NOT_EXISTS, + [FeeAmount.LOW]: PoolState.NOT_EXISTS, + [FeeAmount.MEDIUM]: PoolState.NOT_EXISTS, + [FeeAmount.HIGH]: PoolState.NOT_EXISTS, + }, + ), + [pools], + ); + + const [showOptions, setShowOptions] = useState(false); + const [pulsing, setPulsing] = useState(false); + + const previousFeeAmount = usePrevious(feeAmount); + + const recommended = useRef(false); const handleFeePoolSelectWithEvent = useCallback( (fee: FeeAmount) => { sendAnalyticsEvent(LiquidityEventName.SELECT_LIQUIDITY_POOL_FEE_TIER, { action: FeePoolSelectAction.MANUAL, ...trace, - }) - handleFeePoolSelect(fee) + }); + handleFeePoolSelect(fee); }, - [handleFeePoolSelect, trace] - ) + [handleFeePoolSelect, trace], + ); useEffect(() => { if (feeAmount || isLoading || isError) { - return + return; } if (!largestUsageFeeTier) { // cannot recommend, open options - setShowOptions(true) + setShowOptions(true); } else { - setShowOptions(false) + setShowOptions(false); - recommended.current = true + recommended.current = true; sendAnalyticsEvent(LiquidityEventName.SELECT_LIQUIDITY_POOL_FEE_TIER, { action: FeePoolSelectAction.RECOMMENDED, ...trace, - }) + }); - handleFeePoolSelect(largestUsageFeeTier) + handleFeePoolSelect(largestUsageFeeTier); } - }, [feeAmount, isLoading, isError, largestUsageFeeTier, handleFeePoolSelect, trace]) + }, [feeAmount, isLoading, isError, largestUsageFeeTier, handleFeePoolSelect, trace]); useEffect(() => { - setShowOptions(isError) - }, [isError]) + setShowOptions(isError); + }, [isError]); useEffect(() => { if (feeAmount && previousFeeAmount !== feeAmount) { - setPulsing(true) + setPulsing(true); } - }, [previousFeeAmount, feeAmount]) + }, [previousFeeAmount, feeAmount]); return ( - setPulsing(false)}> + setPulsing(false)} feeAmount={feeAmount}> {!feeAmount ? ( <> - - Fee tier + + Select Fee Tier - + The % you will earn in fees. ) : ( <> - {FEE_AMOUNT_DETAIL[feeAmount].label}% fee tier + {FEE_AMOUNT_DETAIL[feeAmount].label}% Fee Tier - + {/* {distributions && ( )} - + */} )} - setShowOptions(!showOptions)} width="auto" padding="4px" $borderRadius="6px"> + {/* setShowOptions(!showOptions)} width="auto" padding="4px" $borderRadius="6px"> {showOptions ? Hide : Edit} - + */} - {chainId && showOptions && ( + {chainId && ( )} - ) + ); } diff --git a/src/components/FiatOnrampModal/constants.ts b/src/components/FiatOnrampModal/constants.ts deleted file mode 100644 index db0ea4b7..00000000 --- a/src/components/FiatOnrampModal/constants.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const MOONPAY_SUPPORTED_CURRENCY_CODES = [ - 'eth', - 'eth_arbitrum', - 'eth_optimism', - 'eth_polygon', - 'weth', - 'wbtc', - 'matic_polygon', - 'polygon', - 'usdc_arbitrum', - 'usdc_optimism', - 'usdc_polygon', - 'usdc', - 'usdt', -] as const - -export type MoonpaySupportedCurrencyCode = (typeof MOONPAY_SUPPORTED_CURRENCY_CODES)[number] diff --git a/src/components/FiatOnrampModal/index.tsx b/src/components/FiatOnrampModal/index.tsx deleted file mode 100644 index 9e77ec95..00000000 --- a/src/components/FiatOnrampModal/index.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import { Trans } from '@lingui/macro' -import { useWeb3React } from '@web3-react/core' -import { useCallback, useEffect, useState } from 'react' -import { useHref } from 'react-router-dom' -import { useCloseModal, useModalIsOpen } from 'state/application/hooks' -import { ApplicationModal } from 'state/application/reducer' -import styled, { useTheme } from 'styled-components' -import { CustomLightSpinner, ThemedText } from 'theme/components' -import { useIsDarkMode } from 'theme/components/ThemeToggle' - -import Circle from '../../assets/images/blue-loader.svg' -import Modal from '../Modal' -import { MOONPAY_SUPPORTED_CURRENCY_CODES } from './constants' -import { getDefaultCurrencyCode, parsePathParts } from './utils' - -const MOONPAY_DARK_BACKGROUND = '#1c1c1e' -const Wrapper = styled.div<{ isDarkMode: boolean }>` - // #1c1c1e is the background color for the darkmode moonpay iframe as of 2/16/2023 - background-color: ${({ isDarkMode, theme }) => (isDarkMode ? MOONPAY_DARK_BACKGROUND : theme.white)}; - border-radius: 20px; - box-shadow: ${({ theme }) => theme.deprecated_deepShadow}; - display: flex; - flex-flow: column nowrap; - margin: 0; - flex: 1 1; - min-width: 375px; - position: relative; - width: 100%; -` - -const ErrorText = styled(ThemedText.BodyPrimary)` - color: ${({ theme }) => theme.critical}; - margin: auto !important; - text-align: center; - width: 90%; -` -const StyledIframe = styled.iframe<{ isDarkMode: boolean }>` - // #1c1c1e is the background color for the darkmode moonpay iframe as of 2/16/2023 - background-color: ${({ isDarkMode, theme }) => (isDarkMode ? MOONPAY_DARK_BACKGROUND : theme.white)}; - border-radius: 12px; - bottom: 0; - left: 0; - height: calc(100% - 16px); - margin: 8px; - padding: 0; - position: absolute; - right: 0; - top: 0; - width: calc(100% - 16px); -` -const StyledSpinner = styled(CustomLightSpinner)` - bottom: 0; - left: 0; - margin: auto; - position: absolute; - right: 0; - top: 0; -` - -export default function FiatOnrampModal() { - const { account } = useWeb3React() - const theme = useTheme() - const isDarkMode = useIsDarkMode() - const closeModal = useCloseModal() - const fiatOnrampModalOpen = useModalIsOpen(ApplicationModal.FIAT_ONRAMP) - - const { network, tokenAddress } = parsePathParts(location.pathname) - - const [signedIframeUrl, setSignedIframeUrl] = useState(null) - const [error, setError] = useState(null) - const [loading, setLoading] = useState(false) - - const swapUrl = useHref('/swap') - - const fetchSignedIframeUrl = useCallback(async () => { - if (!account) { - setError('Please connect an account before making a purchase.') - return - } - setLoading(true) - setError(null) - try { - const signedIframeUrlFetchEndpoint = process.env.REACT_APP_MOONPAY_LINK as string - const res = await fetch(signedIframeUrlFetchEndpoint, { - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - method: 'POST', - body: JSON.stringify({ - theme: isDarkMode ? 'dark' : 'light', - colorCode: theme.accent1, - defaultCurrencyCode: getDefaultCurrencyCode(tokenAddress, network), - redirectUrl: swapUrl, - walletAddresses: JSON.stringify( - MOONPAY_SUPPORTED_CURRENCY_CODES.reduce( - (acc, currencyCode) => ({ - ...acc, - [currencyCode]: account, - }), - {} - ) - ), - }), - }) - const { url } = await res.json() - setSignedIframeUrl(url) - } catch (e) { - console.log('there was an error fetching the link', e) - setError(e.toString()) - } finally { - setLoading(false) - } - }, [account, isDarkMode, network, swapUrl, theme.accent1, tokenAddress]) - - useEffect(() => { - fetchSignedIframeUrl() - }, [fetchSignedIframeUrl]) - - return ( - closeModal()} height={80 /* vh */}> - - {error ? ( - <> - - MoonPay fiat on-ramp iframe - - - Something went wrong! -
- {error} -
- - ) : loading ? ( - - ) : ( - - )} -
-
- ) -} diff --git a/src/components/FiatOnrampModal/utils.test.ts b/src/components/FiatOnrampModal/utils.test.ts deleted file mode 100644 index e20eb2c9..00000000 --- a/src/components/FiatOnrampModal/utils.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { ChainId, WETH9 } from '@uniswap/sdk-core' -import { - MATIC_MAINNET, - USDC_ARBITRUM, - USDC_MAINNET, - USDC_OPTIMISM, - USDC_POLYGON, - USDT, - WBTC, - WETH_POLYGON, -} from 'constants/tokens' - -import { getDefaultCurrencyCode, parsePathParts } from './utils' - -describe('getDefaultCurrencyCode', () => { - it('NATIVE/arbitrum should return the correct currency code', () => { - expect(getDefaultCurrencyCode('NATIVE', 'arbitrum')).toBe('eth_arbitrum') - }) - it('NATIVE/optimism should return the correct currency code', () => { - expect(getDefaultCurrencyCode('NATIVE', 'optimism')).toBe('eth_optimism') - }) - it('WETH/polygon should return the correct currency code', () => { - expect(getDefaultCurrencyCode(WETH_POLYGON.address, 'polygon')).toBe('eth_polygon') - }) - it('WETH/ethereum should return the correct currency code', () => { - expect(getDefaultCurrencyCode(WETH9[ChainId.MAINNET].address, 'ethereum')).toBe('weth') - }) - it('WBTC/ethereum should return the correct currency code', () => { - expect(getDefaultCurrencyCode(WBTC.address, 'ethereum')).toBe('wbtc') - }) - it('NATIVE/polygon should return the correct currency code', () => { - expect(getDefaultCurrencyCode('NATIVE', 'polygon')).toBe('matic_polygon') - }) - it('MATIC/ethereum should return the correct currency code', () => { - expect(getDefaultCurrencyCode(MATIC_MAINNET.address, 'ethereum')).toBe('polygon') - }) - it('USDC/arbitrum should return the correct currency code', () => { - expect(getDefaultCurrencyCode(USDC_ARBITRUM.address, 'arbitrum')).toBe('usdc_arbitrum') - }) - it('USDC/optimism should return the correct currency code', () => { - expect(getDefaultCurrencyCode(USDC_OPTIMISM.address, 'optimism')).toBe('usdc_optimism') - }) - it('USDC/polygon should return the correct currency code', () => { - expect(getDefaultCurrencyCode(USDC_POLYGON.address, 'polygon')).toBe('usdc_polygon') - }) - it('native/ethereum should return the correct currency code', () => { - expect(getDefaultCurrencyCode('NATIVE', 'ethereum')).toBe('eth') - }) - it('usdc/ethereum should return the correct currency code', () => { - expect(getDefaultCurrencyCode(USDC_MAINNET.address, 'ethereum')).toBe('usdc') - }) - it('usdt/ethereum should return the correct currency code', () => { - expect(getDefaultCurrencyCode(USDT.address, 'ethereum')).toBe('usdt') - }) - it('chain/token mismatch should default to eth', () => { - expect(getDefaultCurrencyCode(USDC_ARBITRUM.address, 'ethereum')).toBe('eth') - expect(getDefaultCurrencyCode(USDC_OPTIMISM.address, 'ethereum')).toBe('eth') - expect(getDefaultCurrencyCode(USDC_POLYGON.address, 'ethereum')).toBe('eth') - expect(getDefaultCurrencyCode(MATIC_MAINNET.address, 'arbitrum')).toBe('eth') - }) -}) - -describe('parseLocation', () => { - it('should parse the URL correctly', () => { - expect(parsePathParts('/tokens/ethereum/0x2260fac5e5542a773aa44fbcfedf7c193bc2c599')).toEqual({ - network: 'ethereum', - tokenAddress: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - }) - expect(parsePathParts('tokens/ethereum/0x2260fac5e5542a773aa44fbcfedf7c193bc2c599')).toEqual({ - network: 'ethereum', - tokenAddress: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - }) - expect(parsePathParts('/swap')).toEqual({ - network: undefined, - tokenAddress: undefined, - }) - }) -}) diff --git a/src/components/FiatOnrampModal/utils.ts b/src/components/FiatOnrampModal/utils.ts deleted file mode 100644 index 69b70e3f..00000000 --- a/src/components/FiatOnrampModal/utils.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { ChainId, WETH9 } from '@uniswap/sdk-core'; - -import { MATIC_MAINNET, - USDC_ARBITRUM, - USDC_MAINNET, - USDC_OPTIMISM, - USDC_POLYGON, - USDT, - WBTC, - WETH_POLYGON } from 'constants/tokens'; -import { Chain } from 'graphql/data/types-and-hooks'; -import { validateUrlChainParam } from 'graphql/data/util'; -import { MoonpaySupportedCurrencyCode } from './constants'; - -type MoonpaySupportedChain = Chain.Ethereum | Chain.Polygon | Chain.Arbitrum | Chain.Optimism -const moonPaySupportedChains = [Chain.Ethereum, Chain.Polygon, Chain.Arbitrum, Chain.Optimism]; - -const CURRENCY_CODES: { - [K in MoonpaySupportedChain]: { - [key: string]: MoonpaySupportedCurrencyCode - native: MoonpaySupportedCurrencyCode - } -} = { - [Chain.Ethereum]: { - [WETH9[ChainId.MAINNET]?.address.toLowerCase()]: 'weth', - [USDC_MAINNET.address.toLowerCase()]: 'usdc', - [USDT.address.toLowerCase()]: 'usdt', - [WBTC.address.toLowerCase()]: 'wbtc', - [MATIC_MAINNET.address.toLowerCase()]: 'polygon', - native: 'eth', - }, - [Chain.Arbitrum]: { - [USDC_ARBITRUM.address.toLowerCase()]: 'usdc_arbitrum', - native: 'eth_arbitrum', - }, - [Chain.Optimism]: { - [USDC_OPTIMISM.address.toLowerCase()]: 'usdc_optimism', - native: 'eth_optimism', - }, - [Chain.Polygon]: { - [USDC_POLYGON.address.toLowerCase()]: 'usdc_polygon', - [WETH_POLYGON.address.toLowerCase()]: 'eth_polygon', - native: 'matic_polygon', - }, -}; - -export function getDefaultCurrencyCode( - address: string | undefined, - chainName: string | undefined, -): MoonpaySupportedCurrencyCode { - const chain = validateUrlChainParam(chainName); - if (!address || !chain) { return 'eth'; } - if (moonPaySupportedChains.includes(chain)) { - const code = CURRENCY_CODES[chain as MoonpaySupportedChain]?.[address.toLowerCase()]; - return code ?? 'eth'; - } - return 'eth'; -} - -/** - * You should use useParams() from react-router-dom instead of this function if possible. - * This function is only used in the case where we need to parse the path outside the scope of the router. - */ -export function parsePathParts(pathname: string): { network?: string; tokenAddress?: string } { - const pathParts = pathname.split('/'); - // Matches the /tokens// path. - const network = pathParts.length > 2 ? pathParts[pathParts.length - 2] : undefined; - const tokenAddress = pathParts.length > 2 ? pathParts[pathParts.length - 1] : undefined; - return { network, tokenAddress }; -} diff --git a/src/components/Icons/Gas.tsx b/src/components/Icons/Gas.tsx index 60ba30e7..217e0bf1 100644 --- a/src/components/Icons/Gas.tsx +++ b/src/components/Icons/Gas.tsx @@ -1,12 +1,8 @@ -import { ComponentProps } from 'react' +import { ComponentProps } from 'react'; export const Gas = (props: ComponentProps<'svg'>) => ( - - + + + -) +); diff --git a/src/components/Icons/Power.tsx b/src/components/Icons/Power.tsx index a6988696..5e653c5c 100644 --- a/src/components/Icons/Power.tsx +++ b/src/components/Icons/Power.tsx @@ -1,10 +1,8 @@ -import { ComponentProps } from 'react' +import { ComponentProps } from 'react'; export const Power = (props: ComponentProps<'svg'>) => ( - - + + + -) +); diff --git a/src/components/Icons/Router.tsx b/src/components/Icons/Router.tsx new file mode 100644 index 00000000..2cd60984 --- /dev/null +++ b/src/components/Icons/Router.tsx @@ -0,0 +1,18 @@ +import { ComponentProps } from 'react'; + +export const Router = (props: ComponentProps<'svg'>) => ( + + + +); +export const ThemedRouter = (props: ComponentProps<'svg'>) => ( + + + + + + + + + +); diff --git a/src/components/Icons/Settings.tsx b/src/components/Icons/Settings.tsx index afc25d65..c5c5f1bf 100644 --- a/src/components/Icons/Settings.tsx +++ b/src/components/Icons/Settings.tsx @@ -1,10 +1,7 @@ -import { ComponentProps } from 'react' +import { ComponentProps } from 'react'; export const Settings = (props: ComponentProps<'svg'>) => ( - - + + -) +); diff --git a/src/components/Identicon/StatusIcon.tsx b/src/components/Identicon/StatusIcon.tsx index 6d5c18d4..046b708b 100644 --- a/src/components/Identicon/StatusIcon.tsx +++ b/src/components/Identicon/StatusIcon.tsx @@ -1,13 +1,12 @@ -import styled from 'styled-components' +import styled from 'styled-components'; -import { Unicon } from 'components/Unicon' -import { Connection, ConnectionType } from 'connection/types' -import useENSAvatar from 'hooks/useENSAvatar' -import { useIsDarkMode } from 'theme/components/ThemeToggle' -import { flexColumnNoWrap } from 'theme/styles' -import sockImg from '../../assets/svg/socks.svg' -import { useHasSocks } from '../../hooks/useSocksBalance' -import Identicon from '.' +import { Unicon } from 'components/Unicon'; +import { Connection, ConnectionType } from 'connection/types'; +import useENSAvatar from 'hooks/useENSAvatar'; +import { flexColumnNoWrap } from 'theme/styles'; +import sockImg from '../../assets/svg/socks.svg'; +import { useHasSocks } from '../../hooks/useSocksBalance'; +import Identicon from '.'; export const IconWrapper = styled.div<{ size?: number }>` position: relative; @@ -23,7 +22,7 @@ export const IconWrapper = styled.div<{ size?: number }>` ${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium` align-items: flex-end; `}; -` +`; const MiniIconContainer = styled.div<{ side: 'left' | 'right' }>` position: absolute; @@ -42,46 +41,41 @@ const MiniIconContainer = styled.div<{ side: 'left' | 'right' }>` @supports (overflow: clip) { overflow: clip; } -` +`; const MiniImg = styled.img` width: 16px; height: 16px; -` +`; const Socks = () => ( -) +); -const MiniWalletIcon = ({ connection, side }: { connection: Connection; side: 'left' | 'right' }) => { - const isDarkMode = useIsDarkMode() - return ( - - - - ) -} +const MiniWalletIcon = ({ connection, side }: { connection: Connection; side: 'left' | 'right' }) => ( + + + +); const MainWalletIcon = ({ account, connection, size }: { account: string; connection: Connection; size: number }) => { - const { avatar } = useENSAvatar(account ?? undefined) + const { avatar } = useENSAvatar(account ?? undefined); if (!account) { - return null + return null; } if (avatar || (connection.type === ConnectionType.INJECTED && connection.getName() === 'MetaMask')) { - return + return ; } - return -} + return ; +}; -export default function StatusIcon({ - account, +export default function StatusIcon({ account, connection, size = 16, - showMiniIcons = true, -}: { + showMiniIcons = true }: { account: string connection: Connection size?: number @@ -109,5 +103,5 @@ export default function StatusIcon({ // // ) // } - return null + return null; } diff --git a/src/components/InputStepCounter/InputStepCounter.tsx b/src/components/InputStepCounter/InputStepCounter.tsx index 1699491a..be20c266 100644 --- a/src/components/InputStepCounter/InputStepCounter.tsx +++ b/src/components/InputStepCounter/InputStepCounter.tsx @@ -31,6 +31,8 @@ const InputRow = styled.div` const SmallButton = styled(ButtonGray)` border-radius: 8px; padding: 4px; + background-color: transparent; + border: 1px solid #444; ` const FocusedOutlineCard = styled(OutlineCard)<{ active?: boolean; pulsing?: boolean }>` diff --git a/src/components/LiquidityChartRangeInput/Zoom.tsx b/src/components/LiquidityChartRangeInput/Zoom.tsx index c28c796f..d428009b 100644 --- a/src/components/LiquidityChartRangeInput/Zoom.tsx +++ b/src/components/LiquidityChartRangeInput/Zoom.tsx @@ -7,7 +7,7 @@ import styled from 'styled-components' import { ZoomLevels } from './types' const Wrapper = styled.div<{ count: number }>` - display: grid; + display: none; grid-template-columns: repeat(${({ count }) => count.toString()}, 1fr); grid-gap: 6px; diff --git a/src/components/Logo/ChainLogo.tsx b/src/components/Logo/ChainLogo.tsx index b15b135e..3b256c03 100644 --- a/src/components/Logo/ChainLogo.tsx +++ b/src/components/Logo/ChainLogo.tsx @@ -1,25 +1,24 @@ -import { ChainId } from '@uniswap/sdk-core' -import { getChainInfo } from 'constants/chainInfo' -import { isSupportedChain, SupportedInterfaceChain } from 'constants/chains' -import { CSSProperties, FunctionComponent } from 'react' -import { useTheme } from 'styled-components' -import { useIsDarkMode } from 'theme/components/ThemeToggle' +import { ChainId } from '@uniswap/sdk-core'; +import { CSSProperties, FunctionComponent } from 'react'; +import { useTheme } from 'styled-components'; -import { ReactComponent as arbitrum } from './ChainSymbols/arbitrum.svg' -import { ReactComponent as avax } from './ChainSymbols/avax.svg' -import { ReactComponent as base } from './ChainSymbols/base.svg' -import { ReactComponent as bnb } from './ChainSymbols/bnb.svg' -import { ReactComponent as celo } from './ChainSymbols/celo.svg' -import { ReactComponent as celoLight } from './ChainSymbols/celo_light.svg' -import { ReactComponent as ethereum } from './ChainSymbols/ethereum.svg' -import { ReactComponent as optimism } from './ChainSymbols/optimism.svg' -import { ReactComponent as polygon } from './ChainSymbols/polygon.svg' +import { getChainInfo } from 'constants/chainInfo'; +import { isSupportedChain, SupportedInterfaceChain } from 'constants/chains'; +import { ReactComponent as arbitrum } from './ChainSymbols/arbitrum.svg'; +import { ReactComponent as avax } from './ChainSymbols/avax.svg'; +import { ReactComponent as base } from './ChainSymbols/base.svg'; +import { ReactComponent as bnb } from './ChainSymbols/bnb.svg'; +import { ReactComponent as celo } from './ChainSymbols/celo.svg'; +import { ReactComponent as celoLight } from './ChainSymbols/celo_light.svg'; +import { ReactComponent as ethereum } from './ChainSymbols/ethereum.svg'; +import { ReactComponent as optimism } from './ChainSymbols/optimism.svg'; +import { ReactComponent as polygon } from './ChainSymbols/polygon.svg'; type SVG = FunctionComponent> type ChainUI = { Symbol: SVG; bgColor: string; textColor: string } -export function getChainUI(chainId: SupportedInterfaceChain, darkMode: boolean): ChainUI -export function getChainUI(chainId: ChainId, darkMode: boolean): ChainUI | undefined { +export function getChainUI(chainId: SupportedInterfaceChain): ChainUI +export function getChainUI(chainId: ChainId): ChainUI | undefined { switch (chainId) { case ChainId.MAINNET: case ChainId.GOERLI: @@ -28,65 +27,59 @@ export function getChainUI(chainId: ChainId, darkMode: boolean): ChainUI | undef Symbol: ethereum, bgColor: '#6B8AFF33', textColor: '#6B8AFF', - } + }; case ChainId.POLYGON: case ChainId.POLYGON_MUMBAI: return { Symbol: polygon, bgColor: '#9558FF33', textColor: '#9558FF', - } + }; case ChainId.ARBITRUM_ONE: case ChainId.ARBITRUM_GOERLI: return { Symbol: arbitrum, bgColor: '#00A3FF33', textColor: '#00A3FF', - } + }; case ChainId.OPTIMISM: case ChainId.OPTIMISM_GOERLI: return { Symbol: optimism, bgColor: '#FF042033', textColor: '#FF0420', - } + }; case ChainId.CELO: case ChainId.CELO_ALFAJORES: - return darkMode - ? { - Symbol: celo, - bgColor: '#FCFF5233', - textColor: '#FCFF52', - } - : { - Symbol: celoLight, - bgColor: '#FCFF5299', - textColor: '#655947', - } + return { + Symbol: celo, + bgColor: '#FCFF5233', + textColor: '#FCFF52', + }; case ChainId.AVALANCHE: return { Symbol: avax, bgColor: '#E8414233', textColor: '#E84142', - } + }; case ChainId.BNB: return { Symbol: bnb, bgColor: '#EAB20033', textColor: '#EAB200', - } + }; case ChainId.BASE: return { Symbol: base, bgColor: '#0052FF33', textColor: '#0052FF', - } + }; default: - return undefined + return undefined; } } -export const getDefaultBorderRadius = (size: number) => size / 2 - 4 +export const getDefaultBorderRadius = (size: number) => size / 2 - 4; type ChainLogoProps = { chainId: ChainId @@ -96,21 +89,18 @@ type ChainLogoProps = { style?: CSSProperties testId?: string } -export function ChainLogo({ - chainId, +export function ChainLogo({ chainId, className, style, size = 16, borderRadius = getDefaultBorderRadius(size), - testId, -}: ChainLogoProps) { - const darkMode = useIsDarkMode() - const { surface2 } = useTheme() + testId }: ChainLogoProps) { + const { surface2 } = useTheme(); - if (!isSupportedChain(chainId)) return null - const { label } = getChainInfo(chainId) + if (!isSupportedChain(chainId)) { return null; } + const { label } = getChainInfo(chainId); - const { Symbol, bgColor } = getChainUI(chainId, darkMode) + const { Symbol, bgColor } = getChainUI(chainId); return ( {`${label} logo`} @@ -118,5 +108,5 @@ export function ChainLogo({ - ) + ); } diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index 42d1b579..47fe0afc 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -1,15 +1,16 @@ -import { DialogContent, DialogOverlay } from '@reach/dialog' -import React from 'react' -import { animated, useSpring, useTransition } from 'react-spring' -import { useGesture } from 'react-use-gesture' -import styled, { css } from 'styled-components' -import { Z_INDEX } from 'theme/zIndex' +import { DialogContent, DialogOverlay } from '@reach/dialog'; +import React from 'react'; +import { animated, useSpring, useTransition } from 'react-spring'; +import { useGesture } from 'react-use-gesture'; +import styled, { css } from 'styled-components'; -import { isMobile } from '../../utils/userAgent' +import { Z_INDEX } from 'theme/zIndex'; +import { isMobile } from '../../utils/userAgent'; +import { themeBorderGradient, themeBoxBorderGradient } from '../../theme/styles'; -export const MODAL_TRANSITION_DURATION = 200 +export const MODAL_TRANSITION_DURATION = 200; -const AnimatedDialogOverlay = animated(DialogOverlay) +const AnimatedDialogOverlay = animated(DialogOverlay); const StyledDialogOverlay = styled(AnimatedDialogOverlay)<{ $scrollOverlay?: boolean }>` &[data-reach-dialog-overlay] { @@ -27,7 +28,7 @@ const StyledDialogOverlay = styled(AnimatedDialogOverlay)<{ $scrollOverlay?: boo background-color: ${({ theme }) => theme.scrim}; } -` +`; type StyledDialogProps = { $minHeight?: number | false @@ -37,32 +38,33 @@ type StyledDialogProps = { $maxWidth: number } -const AnimatedDialogContent = animated(DialogContent) +const AnimatedDialogContent = animated(DialogContent); const StyledDialogContent = styled(AnimatedDialogContent)` overflow-y: auto; &[data-reach-dialog-content] { margin: auto; background-color: ${({ theme }) => theme.surface2}; - border: ${({ theme, $hideBorder }) => !$hideBorder && `1px solid ${theme.surface3}`}; + ${({ theme }) => themeBoxBorderGradient({ + gradientColor: theme.brandedGradientReversed, + })}; + box-shadow: ${({ theme }) => theme.deprecated_deepShadow}; padding: 0px; width: 50vw; overflow-y: auto; overflow-x: hidden; max-width: ${({ $maxWidth }) => $maxWidth}px; - ${({ $maxHeight }) => - $maxHeight && - css` + ${({ $maxHeight }) => $maxHeight + && css` max-height: ${$maxHeight}vh; `} - ${({ $minHeight }) => - $minHeight && - css` + ${({ $minHeight }) => $minHeight + && css` min-height: ${$minHeight}vh; `} display: ${({ $scrollOverlay }) => ($scrollOverlay ? 'inline-table' : 'flex')}; - border-radius: 20px; + border-radius: 8px; @media screen and (max-width: ${({ theme }) => theme.breakpoint.md}px) { width: 65vw; @@ -70,12 +72,12 @@ const StyledDialogContent = styled(AnimatedDialogContent)` @media screen and (max-width: ${({ theme }) => theme.breakpoint.sm}px) { margin: 0; width: 100vw; - border-radius: 20px; + border-radius: 8px; border-bottom-left-radius: 0; border-bottom-right-radius: 0; } } -` +`; interface ModalProps { isOpen: boolean @@ -91,8 +93,7 @@ interface ModalProps { hideBorder?: boolean } -export default function Modal({ - isOpen, +export default function Modal({ isOpen, onDismiss, minHeight = false, maxHeight = 90, @@ -102,60 +103,58 @@ export default function Modal({ children, onSwipe = onDismiss, $scrollOverlay, - hideBorder = false, -}: ModalProps) { + hideBorder = false }: ModalProps) { const fadeTransition = useTransition(isOpen, { config: { duration: MODAL_TRANSITION_DURATION }, from: { opacity: 0 }, enter: { opacity: 1 }, leave: { opacity: 0 }, - }) + }); - const [{ y }, set] = useSpring(() => ({ y: 0, config: { mass: 1, tension: 210, friction: 20 } })) + const [{ y }, set] = useSpring(() => ({ y: 0, config: { mass: 1, tension: 210, friction: 20 } })); const bind = useGesture({ onDrag: (state) => { set({ y: state.down ? state.movement[1] : 0, - }) + }); if (state.movement[1] > 300 || (state.velocity > 3 && state.direction[1] > 0)) { - onSwipe?.() + onSwipe?.(); } }, - }) + }); return ( <> {fadeTransition( - ({ opacity }, item) => - item && ( - item && ( + + `translateY(${(y as number) > 0 ? y : 0}px)`) }, + } + : {})} + aria-label="dialog" + $minHeight={height ?? minHeight} + $maxHeight={height ?? maxHeight} $scrollOverlay={$scrollOverlay} + $hideBorder={hideBorder} + $maxWidth={maxWidth} > - `translateY(${(y as number) > 0 ? y : 0}px)`) }, - } - : {})} - aria-label="dialog" - $minHeight={height ?? minHeight} - $maxHeight={height ?? maxHeight} - $scrollOverlay={$scrollOverlay} - $hideBorder={hideBorder} - $maxWidth={maxWidth} - > - {/* prevents the automatic focusing of inputs on mobile by the reach dialog */} - {!initialFocusRef && isMobile ?
: null} - {children} - - - ) + {/* prevents the automatic focusing of inputs on mobile by the reach dialog */} + {!initialFocusRef && isMobile ?
: null} + {children} + + + ), )} - ) + ); } diff --git a/src/components/NavBar/NetworkName.tsx b/src/components/NavBar/NetworkName.tsx new file mode 100644 index 00000000..31bf8f88 --- /dev/null +++ b/src/components/NavBar/NetworkName.tsx @@ -0,0 +1,53 @@ +import { useWeb3React } from '@web3-react/core'; +import { useRef } from 'react'; +import styled from 'styled-components'; + +import { getChainInfo } from 'constants/chainInfo'; +import useSyncChainQuery from 'hooks/useSyncChainQuery'; + +const ChainSelectorRow = styled.div` + width: 162px; + height: 38px; + flex-shrink: 0; + + border-radius: 8px; + background: ${({ theme }) => theme.jediNavyBlue};; + + display: flex; + align-items: center; + justify-content: center; + + // text content + color: ${({ theme }) => theme.jediWhite};; + text-align: center; + font-feature-settings: 'clig' off, 'liga' off; + font-family: Avenir LT Std; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 20px; /* 125% */ +`; + +export const NetworkName = () => { + const { chainId, account } = useWeb3React(); + + const info = getChainInfo(chainId); + + useSyncChainQuery(); + + if (!account || !chainId) { + return null; + } + + const isSupported = !!info; + + return ( + + {!isSupported ? ( + 'Unsupported Network' + ) : ( + info.label + )} + + ); +}; diff --git a/src/components/NavBar/index.tsx b/src/components/NavBar/index.tsx index 4507b2d6..c8f17cf9 100644 --- a/src/components/NavBar/index.tsx +++ b/src/components/NavBar/index.tsx @@ -1,14 +1,15 @@ // @ts-nocheck import { Trans } from '@lingui/macro'; -import { ReactNode, useCallback } from 'react'; -import { NavLinkProps, useLocation, useNavigate } from 'react-router-dom'; +import { useCallback } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; +import Logo from 'assets/jedi/logo.png'; +import MobileLogo from 'assets/jedi/squareLogo.png'; import { useAccountDrawer } from 'components/AccountDrawer'; import Web3Status from 'components/Web3Status'; import { useIsPoolsPage } from 'hooks/useIsPoolsPage'; -import { Row } from 'nft/components/Flex'; -import Logo from 'assets/jedi/logo.png'; -import { Nav, LogoContainer, MenuContainer, StatusContainer, MenuItem, ActiveMenuItem, ExternalMenuItem } from './styled'; +import { NetworkName } from './NetworkName'; +import { ActiveMenuItem, ExternalMenuItem, LogoContainer, MenuContainer, MenuItem, Nav, StatusContainer } from './styled'; const MenuItemLink = ({ to, dataTestId, id, isActive, children }) => { const Component = isActive ? ActiveMenuItem : MenuItem; @@ -64,17 +65,17 @@ const Navbar = () => { return ( diff --git a/src/components/NavBar/styled.tsx b/src/components/NavBar/styled.tsx index 3544824e..5f62a730 100644 --- a/src/components/NavBar/styled.tsx +++ b/src/components/NavBar/styled.tsx @@ -19,7 +19,7 @@ export const Nav = styled.nav` } `; -export const LogoContainer = styled(Box)` +export const LogoContainer = styled.div` display: flex; margin-right: 12px; align-items: center; @@ -27,11 +27,30 @@ export const LogoContainer = styled(Box)` img { cursor: pointer; } + img.desktop { + display: none; + } + + @media screen and (min-width: ${({ theme }) => theme.breakpoint.lg}px) { + img.mobile { + display: none; + } + img.desktop { + display: block; + } + } `; -export const MenuContainer = styled(Box)` +export const MenuContainer = styled.div` + display: none; align-items: center; justify-content: space-around; + margin: 0px 12px; + + @media screen and (min-width: ${({ theme }) => theme.breakpoint.md}px) { + margin: 0px; + display: flex; + } `; const baseLinkCss = css` @@ -46,7 +65,7 @@ const baseLinkCss = css` text-align: center; text-decoration: none; line-height: 22px; - font-weight: 800; + font-weight: 750; text-transform: uppercase; color: ${({ theme }) => theme.neutral1}; @@ -63,6 +82,10 @@ export const StatusContainer = styled(Box)` display: flex; align-items: center; justify-content: flex-end; + gap: 8px; + @media screen and (min-width: ${({ theme }) => theme.breakpoint.lg}px) { + gap: 16px; + } `; export const MenuItem = styled(BaseMenuItem)``; diff --git a/src/components/NavigationTabs/index.tsx b/src/components/NavigationTabs/index.tsx index eefbf4ae..01795b94 100644 --- a/src/components/NavigationTabs/index.tsx +++ b/src/components/NavigationTabs/index.tsx @@ -46,7 +46,7 @@ const StyledArrowLeft = styled(ArrowLeft)` export function FindPoolTabs({ origin }: { origin: string }) { return ( - + @@ -61,6 +61,9 @@ export function FindPoolTabs({ origin }: { origin: string }) { const AddRemoveTitleText = styled(ThemedText.SubHeaderLarge)` flex: 1; margin: auto; + font-family: 'Avenir LT Std', sans-serif; + font-size: 24px !important; + font-weight: 700 !important; ` export function AddRemoveTabs({ @@ -90,7 +93,7 @@ export function AddRemoveTabs({ return ( - + { @@ -102,13 +105,13 @@ export function AddRemoveTabs({ }} flex={children ? '1' : undefined} > - + {creating ? ( Create a pair ) : adding ? ( - Add liquidity + Add Liquidity ) : ( Remove liquidity )} diff --git a/src/components/NetworkAlert/NetworkAlert.tsx b/src/components/NetworkAlert/NetworkAlert.tsx deleted file mode 100644 index dca61014..00000000 --- a/src/components/NetworkAlert/NetworkAlert.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { Trans } from '@lingui/macro' -import { useWeb3React } from '@web3-react/core' -import { getChainUI } from 'components/Logo/ChainLogo' -import { RowBetween } from 'components/Row' -import { getChainInfo } from 'constants/chainInfo' -import { isSupportedChain } from 'constants/chains' -import { ArrowUpRight } from 'react-feather' -import styled from 'styled-components' -import { ExternalLink, HideSmall, ThemedText } from 'theme/components' -import { useIsDarkMode } from 'theme/components/ThemeToggle' - -import Column from '../Column' - -const BridgeLink = styled(ExternalLink)<{ bgColor: string }>` - color: ${({ color }) => color}; - background: ${({ bgColor }) => bgColor}; - align-items: center; - border-radius: 8px; - color: white; - display: flex; - font-size: 16px; - justify-content: space-between; - padding: 12px 18px 12px 12px; - text-decoration: none !important; - width: 100%; - - border-radius: 20px; - display: flex; - flex-direction: row; - gap: 12px; - overflow: hidden; - position: relative; - width: 100%; - - margin-top: 16px; -` - -const TitleText = styled(ThemedText.BodyPrimary)<{ $color: string }>` - font-weight: 535; - color: ${({ $color }) => $color}; -` - -const SubtitleText = styled(ThemedText.BodySmall)<{ $color: string }>` - line-height: 20px; - color: ${({ $color }) => $color}; -` - -export function NetworkAlert() { - const { chainId } = useWeb3React() - const darkMode = useIsDarkMode() - - if (!chainId || !isSupportedChain(chainId)) return null - - const { Symbol: ChainSymbol, bgColor, textColor } = getChainUI(chainId, darkMode) - const { label, bridge } = getChainInfo(chainId) - - return bridge ? ( - - - - - - {label} token bridge - - - - Deposit tokens to the {label} network. - - - - - - - ) : null -} diff --git a/src/components/NumericalInput/index.tsx b/src/components/NumericalInput/index.tsx index f619dd49..fa7c1d69 100644 --- a/src/components/NumericalInput/index.tsx +++ b/src/components/NumericalInput/index.tsx @@ -102,7 +102,7 @@ const Input = forwardRef( // text-specific options type="text" pattern="^[0-9]*[.,]?[0-9]*$" - placeholder={placeholder || '0'} + placeholder={placeholder || '0.0'} minLength={1} maxLength={79} spellCheck="false" diff --git a/src/components/Popover/index.tsx b/src/components/Popover/index.tsx index 067ee209..7dc4050d 100644 --- a/src/components/Popover/index.tsx +++ b/src/components/Popover/index.tsx @@ -1,10 +1,11 @@ -import { Options, Placement } from '@popperjs/core' -import Portal from '@reach/portal' -import useInterval from 'lib/hooks/useInterval' -import React, { CSSProperties, useCallback, useMemo, useState } from 'react' -import { usePopper } from 'react-popper' -import styled from 'styled-components' -import { Z_INDEX } from 'theme/zIndex' +import { Options, Placement } from '@popperjs/core'; +import { Portal } from '@reach/portal'; +import React, { CSSProperties, useCallback, useMemo, useState } from 'react'; +import { usePopper } from 'react-popper'; +import styled from 'styled-components'; + +import useInterval from 'lib/hooks/useInterval'; +import { Z_INDEX } from 'theme/zIndex'; const PopoverContainer = styled.div<{ show: boolean }>` z-index: ${Z_INDEX.popover}; @@ -13,12 +14,12 @@ const PopoverContainer = styled.div<{ show: boolean }>` opacity: ${(props) => (props.show ? 1 : 0)}; transition: visibility 150ms linear, opacity 150ms linear; color: ${({ theme }) => theme.neutral2}; -` +`; const ReferenceElement = styled.div` display: inline-block; height: inherit; -` +`; export const Arrow = styled.div` width: 8px; @@ -70,7 +71,7 @@ export const Arrow = styled.div` border-top: none; } } -` +`; export interface PopoverProps { content: React.ReactNode @@ -84,8 +85,7 @@ export interface PopoverProps { style?: CSSProperties } -export default function Popover({ - content, +export default function Popover({ content, show, children, placement = 'auto', @@ -93,11 +93,10 @@ export default function Popover({ offsetY = 8, hideArrow = false, showInline = false, - style, -}: PopoverProps) { - const [referenceElement, setReferenceElement] = useState(null) - const [popperElement, setPopperElement] = useState(null) - const [arrowElement, setArrowElement] = useState(null) + style }: PopoverProps) { + const [referenceElement, setReferenceElement] = useState(null); + const [popperElement, setPopperElement] = useState(null); + const [arrowElement, setArrowElement] = useState(null); const options: Options = useMemo( () => ({ @@ -109,15 +108,15 @@ export default function Popover({ { name: 'preventOverflow', options: { padding: 8 } }, ], }), - [placement, offsetX, offsetY, arrowElement] - ) + [placement, offsetX, offsetY, arrowElement], + ); - const { styles, update, attributes } = usePopper(referenceElement, show ? popperElement : null, options) + const { styles, update, attributes } = usePopper(referenceElement, show ? popperElement : null, options); const updateCallback = useCallback(() => { - update && update() - }, [update]) - useInterval(updateCallback, show ? 100 : null) + update && update(); + }, [update]); + useInterval(updateCallback, show ? 100 : null); return showInline ? ( {content} @@ -140,5 +139,5 @@ export default function Popover({ - ) + ); } diff --git a/src/components/PositionList/index.tsx b/src/components/PositionList/index.tsx index d066737c..096b1ddd 100644 --- a/src/components/PositionList/index.tsx +++ b/src/components/PositionList/index.tsx @@ -1,63 +1,65 @@ -import { Trans } from '@lingui/macro' -import PositionListItem from 'components/PositionListItem' -import React from 'react' -import styled from 'styled-components' -import { MEDIA_WIDTHS } from 'theme' -import { PositionDetails } from 'types/position' +import { Trans } from '@lingui/macro'; +import React from 'react'; +import styled from 'styled-components'; +import { PositionDetails } from 'types/position'; -const DesktopHeader = styled.div` - display: none; - font-size: 14px; +import PositionListItem from 'components/PositionListItem'; + +const Header = styled.div` + display: grid; + grid-template-columns: 1.5fr 0.5fr 0.5fr 1fr; + grid-template-areas: "MyPositions Liqidity Fee Toggle"; + gap: 12px; padding: 16px; - border-bottom: 1px solid ${({ theme }) => theme.surface3}; + text-align: right; - @media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) { - align-items: center; - display: flex; - justify-content: space-between; - & > div:last-child { - text-align: right; - margin-right: 12px; - } + @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { + grid-template-areas: "MyPositions MyPositions Toggle Toggle"; } -` +`; -const MobileHeader = styled.div` - font-weight: medium; - padding: 8px; - font-weight: 535; - padding: 16px; - display: flex; - justify-content: space-between; - align-items: center; - padding: 16px; - border-bottom: 1px solid ${({ theme }) => theme.surface3}; - - @media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) { +const HeaderMyPositionCell = styled.div` + text-align: left; + grid-area: MyPositions; +`; +const HeaderLiquidityCell = styled.div` + grid-area: Liqidity; + + @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { display: none; } - - @media screen and (max-width: ${MEDIA_WIDTHS.deprecated_upToExtraSmall}px) { - display: flex; - flex-direction: row; - justify-content: space-between; +`; +const HeaderFeeCell = styled.div` + grid-area: Fee; + + @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { + display: none; } -` +`; +const HeaderToggleLabelCell = styled.div` + grid-area: Toggle; +`; const ToggleWrap = styled.div` display: flex; flex-direction: row; align-items: center; -` +`; const ToggleLabel = styled.button` cursor: pointer; background-color: transparent; border: none; - color: ${({ theme }) => theme.accent1}; + color: ${({ theme }) => theme.jediBlue}; font-size: 14px; - font-weight: 485; -` + font-style: normal; + font-weight: 700; + margin-left: auto; +`; +const Divider = styled.div` + height: 1px; + background-color: ${({ theme }) => theme.divider}; +`; type PositionListProps = React.PropsWithChildren<{ positions: PositionDetails[] @@ -65,43 +67,40 @@ type PositionListProps = React.PropsWithChildren<{ userHideClosedPositions: boolean }> -export default function PositionList({ - positions, +export default function PositionList({ positions, setUserHideClosedPositions, - userHideClosedPositions, -}: PositionListProps) { + userHideClosedPositions }: PositionListProps) { return ( <> - -
- Your positions - {positions && ' (' + positions.length + ')'} -
- - { - setUserHideClosedPositions(!userHideClosedPositions) - }} - > - {userHideClosedPositions ? Show closed positions : Hide closed positions} - -
- - Your positions - +
+ + My positions + {positions && ` (${positions.length})`} + + + Liquidity + + + Fees earned + + { - setUserHideClosedPositions(!userHideClosedPositions) + setUserHideClosedPositions(!userHideClosedPositions); }} > {userHideClosedPositions ? Show closed positions : Hide closed positions} - - - {positions.map((p) => ( - + +
+ + {positions.map((p, index) => ( + <> + + {positions.length !== index + 1 ? : null} + ))} - ) + ); } diff --git a/src/components/PositionListItem/index.tsx b/src/components/PositionListItem/index.tsx index e961e625..2475af32 100644 --- a/src/components/PositionListItem/index.tsx +++ b/src/components/PositionListItem/index.tsx @@ -1,28 +1,27 @@ -import { BigNumber } from '@ethersproject/bignumber' -import { Trans } from '@lingui/macro' -import { Percent, Price, Token } from '@uniswap/sdk-core' -import { Position } from '@uniswap/v3-sdk' -import RangeBadge from 'components/Badge/RangeBadge' -import DoubleCurrencyLogo from 'components/DoubleLogo' -import HoverInlineText from 'components/HoverInlineText' -import Loader from 'components/Icons/LoadingSpinner' -import { RowBetween } from 'components/Row' -import { useToken } from 'hooks/Tokens' -import useIsTickAtLimit from 'hooks/useIsTickAtLimit' -import { usePool } from 'hooks/usePools' -import { useMemo } from 'react' -import { Link } from 'react-router-dom' -import { Bound } from 'state/mint/v3/actions' -import styled from 'styled-components' -import { MEDIA_WIDTHS } from 'theme' -import { HideSmall, SmallOnly, ThemedText } from 'theme/components' -import { useFormatter } from 'utils/formatNumbers' -import { unwrappedToken } from 'utils/unwrappedToken' - -import { DAI, USDC_MAINNET, USDT, WBTC, WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens' +import { BigNumber } from '@ethersproject/bignumber'; +import { Trans } from '@lingui/macro'; +import { Percent, Price, Token } from '@uniswap/sdk-core'; +import { Position } from '@uniswap/v3-sdk'; +import { useMemo } from 'react'; +import { Link } from 'react-router-dom'; +import styled from 'styled-components'; + +import RangeBadge from 'components/Badge/RangeBadge'; +import DoubleCurrencyLogo from 'components/DoubleLogo'; +import HoverInlineText from 'components/HoverInlineText'; +import Loader from 'components/Icons/LoadingSpinner'; +import { RowBetween } from 'components/Row'; +import { useToken } from 'hooks/Tokens'; +import useIsTickAtLimit from 'hooks/useIsTickAtLimit'; +import { usePool } from 'hooks/usePools'; +import { Bound } from 'state/mint/v3/actions'; +import { MEDIA_WIDTHS } from 'theme'; +import { HideSmall, MediumOnly, SmallOnly, ThemedText } from 'theme/components'; +import { useFormatter } from 'utils/formatNumbers'; +import { unwrappedToken } from 'utils/unwrappedToken'; +import { DAI, USDC_MAINNET, USDT, WBTC, WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens'; const LinkRow = styled(Link)` - align-items: center; display: flex; cursor: pointer; user-select: none; @@ -49,11 +48,27 @@ const LinkRow = styled(Link)` flex-direction: column; row-gap: 8px; `}; -` +`; + +const PositionListItemWrapper = styled.div` + display: grid; + grid-template-columns: 1.5fr 0.5fr 0.5fr 1fr; + grid-template-areas: "MyPositions Liqidity Fee Range"; + text-align: right; + + @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { + grid-template-areas: "MyPositions MyPositions MyPositions Range"; + } +`; const DataLineItem = styled.div` font-size: 14px; -` +`; +const CurrencyText = styled.div` + font-size: 14px; + font-family: 'DM Sans'; + font-weight: 700; +`; const RangeLineItem = styled(DataLineItem)` display: flex; @@ -61,26 +76,42 @@ const RangeLineItem = styled(DataLineItem)` align-items: center; margin-top: 4px; width: 100%; -` +`; const DoubleArrow = styled.span` font-size: 12px; margin: 0 2px; color: ${({ theme }) => theme.neutral1}; -` +`; const RangeText = styled(ThemedText.BodySmall)` - font-size: 14px !important; + font-size: 12px !important; word-break: break-word; padding: 0.25rem 0.25rem; border-radius: 8px; -` + font-family: 'DM Sans'; + font-weight: 400; +`; +const PositionListItemText = styled.div` + grid-area: Liqidity; + + @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { + display: none; + } +`; + +const PositionListItemFee = styled.div` + grid-area: Fee; + + @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { + display: none; + } +`; + +const RangeBadgeWrapper = styled.div` + grid-area: Range; +`; -const FeeTierText = styled(ThemedText.UtilityBadge)` - font-size: 16px !important; - margin-left: 8px !important; - color: ${({ theme }) => theme.neutral3}; -` const ExtentsText = styled(ThemedText.BodySmall)` color: ${({ theme }) => theme.neutral2}; display: inline-block; @@ -89,16 +120,43 @@ const ExtentsText = styled(ThemedText.BodySmall)` ${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall` display: none; `}; -` +`; const PrimaryPositionIdData = styled.div` + grid-area: MyPositions; + display: flex; flex-direction: row; align-items: center; > * { margin-right: 8px; } -` +`; +const PositionListItemHeading = styled.div` + color: ${({ theme }) => theme.neutral2}; + font-family: DM Sans; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: 12px; + flex: 0.3; +`; +const PositionListItemMobileWrapper = styled.div` + display: flex; +`; +const PositionListItemMobile = styled.div` + font-family: DM Sans; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: 12px; + flex: 0.3; + margin-top: 4px; +`; + +const FeeTierText = styled(ThemedText.UtilityBadge)` + +`; interface PositionListItemProps { token0: string @@ -117,32 +175,32 @@ export function getPriceOrderingFromPositionForUI(position?: Position): { base?: Token } { if (!position) { - return {} + return {}; } - const token0 = position.amount0.currency - const token1 = position.amount1.currency + const token0 = position.amount0.currency; + const token1 = position.amount1.currency; // if token0 is a dollar-stable asset, set it as the quote token - const stables = [DAI, USDC_MAINNET, USDT] + const stables = [DAI, USDC_MAINNET, USDT]; if (stables.some((stable) => stable.equals(token0))) { return { priceLower: position.token0PriceUpper.invert(), priceUpper: position.token0PriceLower.invert(), quote: token0, base: token1, - } + }; } // if token1 is an ETH-/BTC-stable asset, set it as the base token - const bases = [...Object.values(WRAPPED_NATIVE_CURRENCY), WBTC] + const bases = [...Object.values(WRAPPED_NATIVE_CURRENCY), WBTC]; if (bases.some((base) => base && base.equals(token1))) { return { priceLower: position.token0PriceUpper.invert(), priceUpper: position.token0PriceLower.invert(), quote: token0, base: token1, - } + }; } // if both prices are below 1, invert @@ -152,7 +210,7 @@ export function getPriceOrderingFromPositionForUI(position?: Position): { priceUpper: position.token0PriceLower.invert(), quote: token0, base: token1, - } + }; } // otherwise, just return the default @@ -161,66 +219,66 @@ export function getPriceOrderingFromPositionForUI(position?: Position): { priceUpper: position.token0PriceUpper, quote: token1, base: token0, - } + }; } -export default function PositionListItem({ - token0: token0Address, +export default function PositionListItem({ token0: token0Address, token1: token1Address, tokenId, fee: feeAmount, liquidity, tickLower, - tickUpper, -}: PositionListItemProps) { - const { formatTickPrice } = useFormatter() + tickUpper }: PositionListItemProps) { + const { formatTickPrice } = useFormatter(); - const token0 = useToken(token0Address) - const token1 = useToken(token1Address) + const token0 = useToken(token0Address); + const token1 = useToken(token1Address); - const currency0 = token0 ? unwrappedToken(token0) : undefined - const currency1 = token1 ? unwrappedToken(token1) : undefined + const currency0 = token0 ? unwrappedToken(token0) : undefined; + const currency1 = token1 ? unwrappedToken(token1) : undefined; // construct Position from details returned - const [, pool] = usePool(currency0 ?? undefined, currency1 ?? undefined, feeAmount) + const [, pool] = usePool(currency0 ?? undefined, currency1 ?? undefined, feeAmount); const position = useMemo(() => { if (pool) { - return new Position({ pool, liquidity: liquidity.toString(), tickLower, tickUpper }) + return new Position({ pool, liquidity: liquidity.toString(), tickLower, tickUpper }); } - return undefined - }, [liquidity, pool, tickLower, tickUpper]) + return undefined; + }, [liquidity, pool, tickLower, tickUpper]); - const tickAtLimit = useIsTickAtLimit(feeAmount, tickLower, tickUpper) + const tickAtLimit = useIsTickAtLimit(feeAmount, tickLower, tickUpper); // prices - const { priceLower, priceUpper, quote, base } = getPriceOrderingFromPositionForUI(position) + const { priceLower, priceUpper, quote, base } = getPriceOrderingFromPositionForUI(position); - const currencyQuote = quote && unwrappedToken(quote) - const currencyBase = base && unwrappedToken(base) + const currencyQuote = quote && unwrappedToken(quote); + const currencyBase = base && unwrappedToken(base); // check if price is within range - const outOfRange: boolean = pool ? pool.tickCurrent < tickLower || pool.tickCurrent >= tickUpper : false + const outOfRange: boolean = pool ? pool.tickCurrent < tickLower || pool.tickCurrent >= tickUpper : false; - const positionSummaryLink = '/pools/' + tokenId + const positionSummaryLink = `/pools/${tokenId}`; - const removed = liquidity?.eq(0) + const removed = liquidity?.eq(0); return ( - + - + -  {currencyQuote?.symbol} / {currencyBase?.symbol} + +  {currencyQuote?.symbol} / {currencyBase?.symbol} + - - - {new Percent(feeAmount, 1_000_000).toSignificant()}% - - - + $16.89 + $0 + + + + {priceLower && priceUpper ? ( @@ -242,9 +300,9 @@ export default function PositionListItem({ {' '} - + {' '} - + Max: @@ -265,6 +323,16 @@ export default function PositionListItem({ ) : ( )} + + + Liquidity + Fees earned + + + $16.89 + $3467.26 + + - ) + ); } diff --git a/src/components/QuestionHelper/index.tsx b/src/components/QuestionHelper/index.tsx index adb1e140..d7803065 100644 --- a/src/components/QuestionHelper/index.tsx +++ b/src/components/QuestionHelper/index.tsx @@ -1,8 +1,8 @@ -import { ReactNode, useCallback, useState } from 'react' -import { HelpCircle } from 'react-feather' -import styled from 'styled-components' +import { ReactNode, useCallback, useState } from 'react'; +import { HelpCircle } from 'react-feather'; +import styled from 'styled-components'; -import Tooltip from '../Tooltip' +import Tooltip from '../Tooltip'; const QuestionWrapper = styled.div` display: flex; @@ -23,21 +23,20 @@ const QuestionWrapper = styled.div` :focus { opacity: 0.7; } -` +`; const QuestionMark = styled.span` + display: flex; font-size: 14px; - margin-left: 8px; align-items: center; color: ${({ theme }) => theme.neutral2}; - margin-top: 2.5px; -` +`; export default function QuestionHelper({ text }: { text: ReactNode; size?: number }) { - const [show, setShow] = useState(false) + const [show, setShow] = useState(false); - const open = useCallback(() => setShow(true), [setShow]) - const close = useCallback(() => setShow(false), [setShow]) + const open = useCallback(() => setShow(true), [setShow]); + const close = useCallback(() => setShow(false), [setShow]); return ( @@ -48,5 +47,5 @@ export default function QuestionHelper({ text }: { text: ReactNode; size?: numbe - ) + ); } diff --git a/src/components/RateToggle/index.tsx b/src/components/RateToggle/index.tsx index 13c7b93c..d8dce97e 100644 --- a/src/components/RateToggle/index.tsx +++ b/src/components/RateToggle/index.tsx @@ -21,10 +21,10 @@ export default function RateToggle({ return tokenA && tokenB ? (
- + {isSorted ? currencyA.symbol : currencyB.symbol} - + {isSorted ? currencyB.symbol : currencyA.symbol} diff --git a/src/components/RoutingDiagram/RoutingDiagram.tsx b/src/components/RoutingDiagram/RoutingDiagram.tsx index 38ece6ee..a0b3150c 100644 --- a/src/components/RoutingDiagram/RoutingDiagram.tsx +++ b/src/components/RoutingDiagram/RoutingDiagram.tsx @@ -1,31 +1,31 @@ -import { Trans } from '@lingui/macro' -import { Protocol } from '@uniswap/router-sdk' -import { Currency } from '@uniswap/sdk-core' -import { FeeAmount } from '@uniswap/v3-sdk' -import Badge from 'components/Badge' -import DoubleCurrencyLogo from 'components/DoubleLogo' -import CurrencyLogo from 'components/Logo/CurrencyLogo' -import Row, { AutoRow } from 'components/Row' -import { BIPS_BASE } from 'constants/misc' -import { useTokenInfoFromActiveList } from 'hooks/useTokenInfoFromActiveList' -import { Box } from 'rebass' -import styled from 'styled-components' -import { ThemedText } from 'theme/components' -import { Z_INDEX } from 'theme/zIndex' -import { RoutingDiagramEntry } from 'utils/getRoutingDiagramEntries' - -import { ReactComponent as DotLine } from '../../assets/svg/dot_line.svg' -import { MouseoverTooltip, TooltipSize } from '../Tooltip' +import { Trans } from '@lingui/macro'; +import { Protocol } from '@uniswap/router-sdk'; +import { Currency } from '@uniswap/sdk-core'; +import { FeeAmount } from '@uniswap/v3-sdk'; +import { Box } from 'rebass'; +import styled from 'styled-components'; + +import Badge from 'components/Badge'; +import DoubleCurrencyLogo from 'components/DoubleLogo'; +import CurrencyLogo from 'components/Logo/CurrencyLogo'; +import Row, { AutoRow } from 'components/Row'; +import { BIPS_BASE } from 'constants/misc'; +import { useTokenInfoFromActiveList } from 'hooks/useTokenInfoFromActiveList'; +import { ThemedText } from 'theme/components'; +import { Z_INDEX } from 'theme/zIndex'; +import { RoutingDiagramEntry } from 'utils/getRoutingDiagramEntries'; +import { ReactComponent as DotLine } from '../../assets/svg/dot_line.svg'; +import { MouseoverTooltip, TooltipSize } from '../Tooltip'; const Wrapper = styled(Box)` align-items: center; width: 100%; -` +`; const RouteContainerRow = styled(Row)` display: grid; grid-template-columns: 24px 1fr 24px; -` +`; const RouteRow = styled(Row)` align-items: center; @@ -33,12 +33,12 @@ const RouteRow = styled(Row)` justify-content: center; padding: 0.1rem 0.5rem; position: relative; -` +`; const PoolBadge = styled(Badge)` display: flex; padding: 4px 4px; -` +`; const DottedLine = styled.div` display: flex; @@ -46,25 +46,24 @@ const DottedLine = styled.div` position: absolute; width: calc(100%); z-index: 1; - opacity: 0.5; -` +`; const DotColor = styled(DotLine)` path { - stroke: ${({ theme }) => theme.surface3}; + stroke: ${({ theme }) => theme.white}; } -` +`; const OpaqueBadge = styled(Badge)` - background-color: ${({ theme }) => theme.surface2}; - border-radius: 8px; + background-color: ${({ theme }) => theme.surface6}; + border-radius: 4px; display: grid; grid-gap: 4px; grid-auto-flow: column; justify-content: start; padding: 4px 6px; z-index: ${Z_INDEX.sticky}; -` +`; const ProtocolBadge = styled(Badge)` background-color: ${({ theme }) => theme.surface2}; @@ -73,27 +72,25 @@ const ProtocolBadge = styled(Badge)` font-size: 10px; padding: 2px 4px; z-index: ${Z_INDEX.sticky + 1}; -` +`; const MixedProtocolBadge = styled(ProtocolBadge)` width: 60px; -` +`; const BadgeText = styled(ThemedText.LabelMicro)` word-break: normal; -` +`; -export default function RoutingDiagram({ - currencyIn, +export default function RoutingDiagram({ currencyIn, currencyOut, - routes, -}: { + routes }: { currencyIn: Currency currencyOut: Currency routes: RoutingDiagramEntry[] }) { - const tokenIn = useTokenInfoFromActiveList(currencyIn) - const tokenOut = useTokenInfoFromActiveList(currencyOut) + const tokenIn = useTokenInfoFromActiveList(currencyIn); + const tokenOut = useTokenInfoFromActiveList(currencyOut); return ( @@ -105,7 +102,7 @@ export default function RoutingDiagram({ ))} - ) + ); } function Route({ entry: { percent, path, protocol } }: { entry: RoutingDiagramEntry }) { @@ -114,35 +111,22 @@ function Route({ entry: { percent, path, protocol } }: { entry: RoutingDiagramEn - - {protocol === Protocol.MIXED ? ( - - V3 + V2 - - ) : ( - - {protocol.toUpperCase()} - - )} - {percent.toSignificant(2)}% - {path.map(([currency0, currency1, feeAmount], index) => ( ))} - ) + ); } function Pool({ currency0, currency1, feeAmount }: { currency0: Currency; currency1: Currency; feeAmount: FeeAmount }) { - const tokenInfo0 = useTokenInfoFromActiveList(currency0) - const tokenInfo1 = useTokenInfoFromActiveList(currency1) + const tokenInfo0 = useTokenInfoFromActiveList(currency0); + const tokenInfo1 = useTokenInfoFromActiveList(currency1); - // TODO - link pool icon to info.uniswap.org via query params return ( {tokenInfo0?.symbol + '/' + tokenInfo1?.symbol + ' ' + feeAmount / 10000}% pool} + text={{`${tokenInfo0?.symbol}/${tokenInfo1?.symbol} ${feeAmount / 10000}`}% pool} size={TooltipSize.ExtraSmall} > @@ -152,5 +136,5 @@ function Pool({ currency0, currency1, feeAmount }: { currency0: Currency; curren {feeAmount / BIPS_BASE}% - ) + ); } diff --git a/src/components/SearchModal/CommonBases.tsx b/src/components/SearchModal/CommonBases.tsx index c5198153..27243b15 100644 --- a/src/components/SearchModal/CommonBases.tsx +++ b/src/components/SearchModal/CommonBases.tsx @@ -1,10 +1,8 @@ -import { BrowserEvent, InterfaceElementName, InterfaceEventName } from '@uniswap/analytics-events'; import { Currency } from '@uniswap/sdk-core'; import { useWeb3React } from '@web3-react/core'; import { Text } from 'rebass'; import styled from 'styled-components'; -import { TraceEvent } from 'analytics'; import CurrencyLogo from 'components/Logo/CurrencyLogo'; import { useCachedPortfolioBalancesQuery } from 'components/PrefetchBalancesWrapper/PrefetchBalancesWrapper'; import { AutoRow } from 'components/Row'; @@ -14,43 +12,25 @@ import { getTokenAddress } from 'lib/utils/analytics'; import { currencyId } from 'utils/currencyId'; const BaseWrapper = styled.div<{ disable?: boolean }>` - border: 1px solid ${({ theme }) => theme.surface3}; - border-radius: 18px; + border: 1px solid ${({ theme }) => theme.white}; + border-radius: 4px; display: flex; padding: 6px; padding-top: 5px; padding-bottom: 5px; padding-right: 12px; line-height: 0px; - align-items: center; + :hover { cursor: ${({ disable }) => !disable && 'pointer'}; background-color: ${({ theme }) => theme.deprecated_hoverDefault}; } color: ${({ theme, disable }) => disable && theme.neutral1}; - background-color: ${({ theme, disable }) => disable && theme.surface3}; + background-color: ${({ theme, disable }) => disable && theme.deprecated_hoverDefault}; `; -const formatAnalyticsEventProperties = ( - currency: Currency, - searchQuery: string, - isAddressSearch: string | false, - portfolioBalanceUsd: number | undefined, -) => ({ - token_symbol: currency?.symbol, - token_chain_id: currency?.chainId, - token_address: getTokenAddress(currency), - is_suggested_token: true, - is_selected_from_list: false, - is_imported_by_user: false, - total_balances_usd: portfolioBalanceUsd, - ...(isAddressSearch === false - ? { search_token_symbol_input: searchQuery } - : { search_token_address_input: isAddressSearch }), -}); - export default function CommonBases({ chainId, onSelect, selectedCurrency, @@ -74,27 +54,20 @@ export default function CommonBases({ chainId, const isSelected = selectedCurrency?.equals(currency); return ( - !isSelected && e.key === 'Enter' && onSelect(currency)} + onClick={() => !isSelected && onSelect(currency)} + disable={isSelected} key={currencyId(currency)} + data-testid={`common-base-${currency.symbol}`} > - !isSelected && e.key === 'Enter' && onSelect(currency)} - onClick={() => !isSelected && onSelect(currency)} - disable={isSelected} - key={currencyId(currency)} - data-testid={`common-base-${currency.symbol}`} - > - - - {currency.symbol} - - - + + + {currency.symbol} + + ); })} diff --git a/src/components/SearchModal/CurrencySearch.tsx b/src/components/SearchModal/CurrencySearch.tsx index 0e93a523..d20d81b7 100644 --- a/src/components/SearchModal/CurrencySearch.tsx +++ b/src/components/SearchModal/CurrencySearch.tsx @@ -19,7 +19,7 @@ import useNativeCurrency from 'lib/hooks/useNativeCurrency'; import { getTokenFilter } from 'lib/hooks/useTokenList/filtering'; import { TokenBalances, tokenComparator, useSortTokensByQuery } from 'lib/hooks/useTokenList/sorting'; import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'; -import { CloseIcon, ThemedText } from 'theme/components'; +import { CloseIcon, ThemedSeparator, ThemedText } from 'theme/components'; import { UserAddedToken } from 'types/tokens'; import { useDefaultActiveTokens, useIsUserAddedToken, useSearchInactiveTokenLists, useToken } from '../../hooks/Tokens'; import { isAddress } from '../../utils'; @@ -35,7 +35,7 @@ const ContentWrapper = styled(Column)` overflow: hidden; flex: 1 1; position: relative; - border-radius: 20px; + border-radius: 4px; `; interface CurrencySearchProps { @@ -235,94 +235,88 @@ export function CurrencySearch({ selectedCurrency, return ( - - - - - Select a token - - - - - } - onChange={handleInput} - onKeyDown={handleEnter} - /> - - {showCommonBases && ( - - )} - - - {searchToken && !searchTokenIsAdded ? ( - - searchToken && handleCurrencySelect(searchToken, hasWarning)} - otherSelected={Boolean(searchToken && otherSelectedCurrency && otherSelectedCurrency.equals(searchToken))} - showCurrencyAmount={showCurrencyAmount} - eventProperties={formatAnalyticsEventProperties( - searchToken, - 0, - [searchToken], - searchQuery, - isAddressSearch, - )} - balance={ - tryParseCurrencyAmount( - String(balances[searchToken.isNative ? 'ETH' : searchToken.address?.toLowerCase()]?.balance ?? 0), - searchToken, - ) ?? CurrencyAmount.fromRawAmount(searchToken, 0) - } - /> - - ) : searchCurrencies?.length > 0 || filteredInactiveTokens?.length > 0 || isLoading ? ( -
- - {({ height }) => ( - - )} - -
- ) : ( - - - No results found. - - + + + + Select a token + + + + + } + onChange={handleInput} + onKeyDown={handleEnter} + /> + + {showCommonBases && ( + )} -
+ + + {searchToken && !searchTokenIsAdded ? ( + + searchToken && handleCurrencySelect(searchToken, hasWarning)} + otherSelected={Boolean(searchToken && otherSelectedCurrency && otherSelectedCurrency.equals(searchToken))} + showCurrencyAmount={showCurrencyAmount} + eventProperties={formatAnalyticsEventProperties( + searchToken, + 0, + [searchToken], + searchQuery, + isAddressSearch, + )} + balance={ + tryParseCurrencyAmount( + String(balances[searchToken.isNative ? 'ETH' : searchToken.address?.toLowerCase()]?.balance ?? 0), + searchToken, + ) ?? CurrencyAmount.fromRawAmount(searchToken, 0) + } + /> + + ) : searchCurrencies?.length > 0 || filteredInactiveTokens?.length > 0 || isLoading ? ( +
+ + {({ height }) => ( + + )} + +
+ ) : ( + + + No results found. + + + )}
); } diff --git a/src/components/SearchModal/styled.tsx b/src/components/SearchModal/styled.tsx index 100b8868..1373c626 100644 --- a/src/components/SearchModal/styled.tsx +++ b/src/components/SearchModal/styled.tsx @@ -36,21 +36,27 @@ export const SearchInput = styled.input` align-items: center; width: 100%; white-space: nowrap; - background-color: ${({ theme }) => theme.surface2}; - border: none; + background-color: ${({ theme }) => theme.surface4}; outline: none; - border-radius: 12px; + border-radius: 8px; color: ${({ theme }) => theme.neutral1}; - border-style: solid; + border: 1px solid ${({ theme }) => theme.surface3}; -webkit-appearance: none; - font-weight: 485; + + color: ${({ theme }) => theme.jediWhite}; - font-size: 16px; + font-size: 14px; + font-weight: 400; + box-shadow: inset 0px -63.1213px 52.3445px -49.2654px rgba(96, 68, 145, 0.3), + inset 0px 75.4377px 76.9772px -36.9491px rgba(202, 172, 255, 0.3), + inset 0px 3.07909px 13.8559px rgba(154, 146, 210, 0.3), inset 0px 0.769772px 30.7909px rgba(227, 222, 255, 0.2); + + ::placeholder { color: ${({ theme }) => theme.neutral3}; - font-size: 16px; + font-size: 14px; } transition: border 100ms; :focus { diff --git a/src/components/Settings/Input/index.tsx b/src/components/Settings/Input/index.tsx index eaaba7b4..cc0a0465 100644 --- a/src/components/Settings/Input/index.tsx +++ b/src/components/Settings/Input/index.tsx @@ -1,6 +1,6 @@ -import styled from 'styled-components' +import styled from 'styled-components'; -import Row from '../../Row' +import Row from '../../Row'; export const Input = styled.input` width: 100%; @@ -18,30 +18,29 @@ export const Input = styled.input` ::placeholder { color: ${({ theme }) => theme.neutral3}; } -` +`; export const InputContainer = styled(Row)<{ error?: boolean }>` padding: 8px 16px; - border-radius: 12px; + border-radius: 4px; width: auto; min-width: 100px; flex: 1; input { color: ${({ theme, error }) => (error ? theme.critical : theme.neutral1)}; } - border: 1px solid ${({ theme, error }) => (error ? theme.critical : theme.surface2)}; - ${({ theme, error }) => - error - ? ` + border: 1px solid #fff; + ${({ theme, error }) => (error + ? ` border: 1px solid ${theme.critical}; :focus-within { border-color: ${theme.deprecated_accentFailureSoft}; } ` - : ` - border: 1px solid ${theme.surface3}; + : ` + border: 1px solid #fff; :focus-within { - border-color: ${theme.accent2}; + border-color: ${theme.accent1}; } - `} -` + `)} +`; diff --git a/src/components/Settings/MaxSlippageSettings/index.tsx b/src/components/Settings/MaxSlippageSettings/index.tsx index e9c8f978..c989a2d2 100644 --- a/src/components/Settings/MaxSlippageSettings/index.tsx +++ b/src/components/Settings/MaxSlippageSettings/index.tsx @@ -1,16 +1,16 @@ -import { Trans } from '@lingui/macro' -import { Percent } from '@uniswap/sdk-core' -import Expand from 'components/Expand' -import QuestionHelper from 'components/QuestionHelper' -import Row, { RowBetween } from 'components/Row' -import React, { useState } from 'react' -import { useUserSlippageTolerance } from 'state/user/hooks' -import { SlippageTolerance } from 'state/user/types' -import styled from 'styled-components' -import { CautionTriangle, ThemedText } from 'theme/components' -import { useFormatter } from 'utils/formatNumbers' - -import { Input, InputContainer } from '../Input' +import { Trans } from '@lingui/macro'; +import { Percent } from '@uniswap/sdk-core'; +import React, { useState } from 'react'; +import styled from 'styled-components'; + +import Expand from 'components/Expand'; +import QuestionHelper from 'components/QuestionHelper'; +import Row, { RowBetween } from 'components/Row'; +import { useUserSlippageTolerance } from 'state/user/hooks'; +import { SlippageTolerance } from 'state/user/types'; +import { CautionTriangle, ThemedText } from 'theme/components'; +import { useFormatter } from 'utils/formatNumbers'; +import { Input, InputContainer } from '../Input'; enum SlippageError { InvalidInput = 'InvalidInput', @@ -22,104 +22,101 @@ const Option = styled(Row)<{ isActive: boolean }>` padding: 6px 12px; text-align: center; gap: 4px; - border-radius: 12px; + border-radius: 4px; background: ${({ isActive, theme }) => (isActive ? theme.surface3 : 'transparent')}; pointer-events: ${({ isActive }) => isActive && 'none'}; -` +`; const Switch = styled(Row)` width: auto; padding: 4px; - border: 1px solid ${({ theme }) => theme.surface3}; - border-radius: 16px; -` + border: 1px solid #fff; + border-radius: 4px; +`; -const NUMBER_WITH_MAX_TWO_DECIMAL_PLACES = /^(?:\d*\.\d{0,2}|\d+)$/ -const MINIMUM_RECOMMENDED_SLIPPAGE = new Percent(5, 10_000) -const MAXIMUM_RECOMMENDED_SLIPPAGE = new Percent(1, 100) +const NUMBER_WITH_MAX_TWO_DECIMAL_PLACES = /^(?:\d*\.\d{0,2}|\d+)$/; +const MINIMUM_RECOMMENDED_SLIPPAGE = new Percent(5, 10_000); +const MAXIMUM_RECOMMENDED_SLIPPAGE = new Percent(1, 100); function useFormatPercentInput() { - const { formatPercent } = useFormatter() + const { formatPercent } = useFormatter(); - return (slippage: Percent) => formatPercent(slippage).slice(0, -1) // remove % sign + return (slippage: Percent) => formatPercent(slippage).slice(0, -1); // remove % sign } export default function MaxSlippageSettings({ autoSlippage }: { autoSlippage: Percent }) { - const [userSlippageTolerance, setUserSlippageTolerance] = useUserSlippageTolerance() - const { formatPercent } = useFormatter() - const formatPercentInput = useFormatPercentInput() + const [userSlippageTolerance, setUserSlippageTolerance] = useUserSlippageTolerance(); + const { formatPercent } = useFormatter(); + const formatPercentInput = useFormatPercentInput(); // In order to trigger `custom` mode, we need to set `userSlippageTolerance` to a value that is not `auto`. // To do so, we use `autoSlippage` value. However, since users are likely to change that value, // we render it as a placeholder instead of a value. - const defaultSlippageInputValue = - userSlippageTolerance !== SlippageTolerance.Auto && !userSlippageTolerance.equalTo(autoSlippage) - ? formatPercentInput(userSlippageTolerance) - : '' + const defaultSlippageInputValue = userSlippageTolerance !== SlippageTolerance.Auto && !userSlippageTolerance.equalTo(autoSlippage) + ? formatPercentInput(userSlippageTolerance) + : ''; // If user has previously entered a custom slippage, we want to show that value in the input field // instead of a placeholder. - const [slippageInput, setSlippageInput] = useState(defaultSlippageInputValue) - const [slippageError, setSlippageError] = useState(false) + const [slippageInput, setSlippageInput] = useState(defaultSlippageInputValue); + const [slippageError, setSlippageError] = useState(false); // If user has previously entered a custom slippage, we want to show the settings expanded by default. - const [isOpen, setIsOpen] = useState(defaultSlippageInputValue.length > 0) + const [isOpen, setIsOpen] = useState(defaultSlippageInputValue.length > 0); const parseSlippageInput = (value: string) => { // Do not allow non-numerical characters in the input field or more than two decimals if (value.length > 0 && !NUMBER_WITH_MAX_TWO_DECIMAL_PLACES.test(value)) { - return + return; } - setSlippageInput(value) - setSlippageError(false) + setSlippageInput(value); + setSlippageError(false); // If the input is empty, set the slippage to the default if (value.length === 0) { - setUserSlippageTolerance(SlippageTolerance.Auto) - return + setUserSlippageTolerance(SlippageTolerance.Auto); + return; } if (value === '.') { - return + return; } // Parse user input and set the slippage if valid, error otherwise try { - const parsed = Math.floor(Number.parseFloat(value) * 100) + const parsed = Math.floor(Number.parseFloat(value) * 100); if (parsed > 5000) { - setSlippageError(SlippageError.InvalidInput) + setSlippageError(SlippageError.InvalidInput); } else { - setUserSlippageTolerance(new Percent(parsed, 10_000)) + setUserSlippageTolerance(new Percent(parsed, 10_000)); } } catch (e) { - setSlippageError(SlippageError.InvalidInput) + setSlippageError(SlippageError.InvalidInput); } - } + }; - const tooLow = - userSlippageTolerance !== SlippageTolerance.Auto && userSlippageTolerance.lessThan(MINIMUM_RECOMMENDED_SLIPPAGE) - const tooHigh = - userSlippageTolerance !== SlippageTolerance.Auto && userSlippageTolerance.greaterThan(MAXIMUM_RECOMMENDED_SLIPPAGE) + const tooLow = userSlippageTolerance !== SlippageTolerance.Auto && userSlippageTolerance.lessThan(MINIMUM_RECOMMENDED_SLIPPAGE); + const tooHigh = userSlippageTolerance !== SlippageTolerance.Auto && userSlippageTolerance.greaterThan(MAXIMUM_RECOMMENDED_SLIPPAGE); return ( setIsOpen(!isOpen)} - header={ - - + header={( + + Max. slippage - + Your transaction will revert if the price changes unfavorably by more than this percentage. } /> - } - button={ + )} + button={( {userSlippageTolerance === SlippageTolerance.Auto ? ( Auto @@ -127,15 +124,15 @@ export default function MaxSlippageSettings({ autoSlippage }: { autoSlippage: Pe formatPercent(userSlippageTolerance) )} - } + )} > ) : null} - ) + ); } diff --git a/src/components/Settings/MenuButton/index.tsx b/src/components/Settings/MenuButton/index.tsx index f4b49eed..acbb79a3 100644 --- a/src/components/Settings/MenuButton/index.tsx +++ b/src/components/Settings/MenuButton/index.tsx @@ -1,24 +1,24 @@ -import { t, Trans } from '@lingui/macro' -import { Settings } from 'components/Icons/Settings' -import Row from 'components/Row' -import { InterfaceTrade } from 'state/routing/types' -import { isUniswapXTrade } from 'state/routing/utils' -import { useUserSlippageTolerance } from 'state/user/hooks' -import { SlippageTolerance } from 'state/user/types' -import styled from 'styled-components' -import { ThemedText } from 'theme/components' -import { useFormatter } from 'utils/formatNumbers' -import validateUserSlippageTolerance, { SlippageValidationResult } from 'utils/validateUserSlippageTolerance' +import { t, Trans } from '@lingui/macro'; +import styled from 'styled-components'; + +import { Settings } from 'components/Icons/Settings'; +import Row from 'components/Row'; +import { InterfaceTrade } from 'state/routing/types'; +import { isUniswapXTrade } from 'state/routing/utils'; +import { useUserSlippageTolerance } from 'state/user/hooks'; +import { SlippageTolerance } from 'state/user/types'; +import { ThemedText } from 'theme/components'; +import { useFormatter } from 'utils/formatNumbers'; +import validateUserSlippageTolerance, { SlippageValidationResult } from 'utils/validateUserSlippageTolerance'; const Icon = styled(Settings)` - height: 24px; - width: 24px; - > * { - fill: ${({ theme }) => theme.neutral2}; - } -` + color: ${({ theme }) => theme.neutral1}; + height: 40px; + width: 40px; +`; const Button = styled.button<{ isActive: boolean }>` + display: flex; border: none; background-color: transparent; margin: 0; @@ -30,36 +30,36 @@ const Button = styled.button<{ isActive: boolean }>` opacity: 0.7; } - ${({ isActive }) => isActive && `opacity: 0.7`} -` + ${({ isActive }) => isActive && 'opacity: 0.7'} +`; const IconContainer = styled(Row)` - padding: 6px 12px; - border-radius: 16px; -` + padding: 0 6px; + border-radius: 4px; + background: transparent; + border: 1px solid transparent; +`; const IconContainerWithSlippage = styled(IconContainer)<{ displayWarning?: boolean }>` div { - color: ${({ theme, displayWarning }) => (displayWarning ? theme.deprecated_accentWarning : theme.neutral2)}; + color: ${({ theme, displayWarning }) => (displayWarning ? theme.deprecated_accentWarning : theme.neutral1)}; } - - background-color: ${({ theme, displayWarning }) => - displayWarning ? theme.deprecated_accentWarningSoft : theme.surface2}; -` + border-color: #fff; +`; const ButtonContent = ({ trade }: { trade?: InterfaceTrade }) => { - const [userSlippageTolerance] = useUserSlippageTolerance() - const { formatPercent } = useFormatter() + const [userSlippageTolerance] = useUserSlippageTolerance(); + const { formatPercent } = useFormatter(); if (userSlippageTolerance === SlippageTolerance.Auto || isUniswapXTrade(trade)) { return ( - ) + ); } - const isInvalidSlippage = validateUserSlippageTolerance(userSlippageTolerance) !== SlippageValidationResult.Valid + const isInvalidSlippage = validateUserSlippageTolerance(userSlippageTolerance) !== SlippageValidationResult.Valid; return ( @@ -68,15 +68,13 @@ const ButtonContent = ({ trade }: { trade?: InterfaceTrade }) => { - ) -} + ); +}; -export default function MenuButton({ - disabled, +export default function MenuButton({ disabled, onClick, isActive, - trade, -}: { + trade }: { disabled: boolean onClick: () => void isActive: boolean @@ -93,5 +91,5 @@ export default function MenuButton({ > - ) + ); } diff --git a/src/components/Settings/RouterPreferenceSettings/index.tsx b/src/components/Settings/RouterPreferenceSettings/index.tsx index 7b721334..099a3b85 100644 --- a/src/components/Settings/RouterPreferenceSettings/index.tsx +++ b/src/components/Settings/RouterPreferenceSettings/index.tsx @@ -1,15 +1,16 @@ -import { Trans } from '@lingui/macro' -import Column from 'components/Column' -import UniswapXBrandMark from 'components/Logo/UniswapXBrandMark' -import { RowBetween, RowFixed } from 'components/Row' -import Toggle from 'components/Toggle' -import { useUniswapXDefaultEnabled } from 'featureFlags/flags/uniswapXDefault' -import { useAppDispatch } from 'state/hooks' -import { RouterPreference } from 'state/routing/types' -import { useRouterPreference, useUserOptedOutOfUniswapX } from 'state/user/hooks' -import { updateDisabledUniswapX, updateOptedOutOfUniswapX } from 'state/user/reducer' -import styled from 'styled-components' -import { ExternalLink, ThemedText } from 'theme/components' +import { Trans } from '@lingui/macro'; +import styled from 'styled-components'; + +import Column from 'components/Column'; +import UniswapXBrandMark from 'components/Logo/UniswapXBrandMark'; +import { RowBetween, RowFixed } from 'components/Row'; +import Toggle from 'components/Toggle'; +import { useUniswapXDefaultEnabled } from 'featureFlags/flags/uniswapXDefault'; +import { useAppDispatch } from 'state/hooks'; +import { RouterPreference } from 'state/routing/types'; +import { useRouterPreference, useUserOptedOutOfUniswapX } from 'state/user/hooks'; +import { updateDisabledUniswapX, updateOptedOutOfUniswapX } from 'state/user/reducer'; +import { ExternalLink, ThemedText } from 'theme/components'; const InlineLink = styled(ThemedText.BodySmall)` color: ${({ theme }) => theme.accent1}; @@ -18,16 +19,16 @@ const InlineLink = styled(ThemedText.BodySmall)` &:hover { opacity: 0.8; } -` +`; export default function RouterPreferenceSettings() { - const [routerPreference, setRouterPreference] = useRouterPreference() - const dispatch = useAppDispatch() - const userOptedOutOfUniswapX = useUserOptedOutOfUniswapX() - const isUniswapXDefaultEnabled = useUniswapXDefaultEnabled() - const isUniswapXOverrideEnabled = isUniswapXDefaultEnabled && !userOptedOutOfUniswapX + const [routerPreference, setRouterPreference] = useRouterPreference(); + const dispatch = useAppDispatch(); + const userOptedOutOfUniswapX = useUserOptedOutOfUniswapX(); + const isUniswapXDefaultEnabled = useUniswapXDefaultEnabled(); + const isUniswapXOverrideEnabled = isUniswapXDefaultEnabled && !userOptedOutOfUniswapX; - const uniswapXInEffect = routerPreference === RouterPreference.X || isUniswapXOverrideEnabled + const uniswapXInEffect = routerPreference === RouterPreference.X || isUniswapXOverrideEnabled; return ( @@ -54,15 +55,15 @@ export default function RouterPreferenceSettings() { if (uniswapXInEffect) { if (isUniswapXDefaultEnabled) { // We need to remember if a opts out of UniswapX, so we don't request UniswapX quotes. - dispatch(updateOptedOutOfUniswapX({ optedOutOfUniswapX: true })) + dispatch(updateOptedOutOfUniswapX({ optedOutOfUniswapX: true })); } else { // We need to remember if a user disables Uniswap X, so we don't show the opt-in flow again. - dispatch(updateDisabledUniswapX({ disabledUniswapX: true })) + dispatch(updateDisabledUniswapX({ disabledUniswapX: true })); } } - setRouterPreference(uniswapXInEffect ? RouterPreference.API : RouterPreference.X) + setRouterPreference(uniswapXInEffect ? RouterPreference.API : RouterPreference.X); }} /> - ) + ); } diff --git a/src/components/Settings/index.tsx b/src/components/Settings/index.tsx index 80075595..646b5bb3 100644 --- a/src/components/Settings/index.tsx +++ b/src/components/Settings/index.tsx @@ -1,29 +1,29 @@ -import { Trans } from '@lingui/macro' -import { Percent } from '@uniswap/sdk-core' -import { useWeb3React } from '@web3-react/core' -import { Scrim } from 'components/AccountDrawer' -import AnimatedDropdown from 'components/AnimatedDropdown' -import Column, { AutoColumn } from 'components/Column' -import Row from 'components/Row' -import { isSupportedChain, isUniswapXSupportedChain, L2_CHAIN_IDS } from 'constants/chains' -import useDisableScrolling from 'hooks/useDisableScrolling' -import { useOnClickOutside } from 'hooks/useOnClickOutside' -import { Portal } from 'nft/components/common/Portal' -import { useIsMobile } from 'nft/hooks' -import { useCallback, useMemo, useRef } from 'react' -import { X } from 'react-feather' -import { useCloseModal, useModalIsOpen, useToggleSettingsMenu } from 'state/application/hooks' -import { ApplicationModal } from 'state/application/reducer' -import { InterfaceTrade } from 'state/routing/types' -import { isUniswapXTrade } from 'state/routing/utils' -import styled from 'styled-components' -import { Divider, ThemedText } from 'theme/components' -import { Z_INDEX } from 'theme/zIndex' +import { Trans } from '@lingui/macro'; +import { Percent } from '@uniswap/sdk-core'; +import { useWeb3React } from '@web3-react/core'; +import { useCallback, useMemo, useRef } from 'react'; +import { X } from 'react-feather'; +import styled from 'styled-components'; -import MaxSlippageSettings from './MaxSlippageSettings' -import MenuButton from './MenuButton' -import RouterPreferenceSettings from './RouterPreferenceSettings' -import TransactionDeadlineSettings from './TransactionDeadlineSettings' +import { Scrim } from 'components/AccountDrawer'; +import AnimatedDropdown from 'components/AnimatedDropdown'; +import Column, { AutoColumn } from 'components/Column'; +import Row from 'components/Row'; +import { isSupportedChain, isUniswapXSupportedChain, L2_CHAIN_IDS } from 'constants/chains'; +import useDisableScrolling from 'hooks/useDisableScrolling'; +import { useOnClickOutside } from 'hooks/useOnClickOutside'; +import { Portal } from 'nft/components/common/Portal'; +import { useIsMobile } from 'nft/hooks'; +import { useCloseModal, useModalIsOpen, useToggleSettingsMenu } from 'state/application/hooks'; +import { ApplicationModal } from 'state/application/reducer'; +import { InterfaceTrade } from 'state/routing/types'; +import { isUniswapXTrade } from 'state/routing/utils'; +import { Divider, ThemedText } from 'theme/components'; +import { Z_INDEX } from 'theme/zIndex'; +import MaxSlippageSettings from './MaxSlippageSettings'; +import MenuButton from './MenuButton'; +import RouterPreferenceSettings from './RouterPreferenceSettings'; +import TransactionDeadlineSettings from './TransactionDeadlineSettings'; const CloseButton = styled.button` background: transparent; @@ -33,11 +33,11 @@ const CloseButton = styled.button` height: 24px; padding: 0; width: 24px; -` +`; const Menu = styled.div` position: relative; -` +`; const MenuFlyout = styled(AutoColumn)` min-width: 20.125rem; @@ -57,12 +57,12 @@ const MenuFlyout = styled(AutoColumn)` `}; user-select: none; padding: 16px; -` +`; -const ExpandColumn = styled(AutoColumn)<{ $padTop: boolean }>` +const ExpandColumn = styled(AutoColumn)` gap: 16px; - padding-top: ${({ $padTop }) => ($padTop ? '16px' : '0')}; -` + padding-top:0; +`; const MobileMenuContainer = styled(Row)` overflow: visible; @@ -73,7 +73,7 @@ const MobileMenuContainer = styled(Row)` right: 0; width: 100%; z-index: ${Z_INDEX.fixed}; -` +`; const MobileMenuWrapper = styled(Column)<{ $open: boolean }>` height: min-content; @@ -82,7 +82,7 @@ const MobileMenuWrapper = styled(Column)<{ $open: boolean }>` background-color: ${({ theme }) => theme.surface1}; overflow: hidden; position: absolute; - bottom: ${({ $open }) => ($open ? `100vh` : 0)}; + bottom: ${({ $open }) => ($open ? '100vh' : 0)}; transition: bottom ${({ theme }) => theme.transition.duration.medium}; border: ${({ theme }) => `1px solid ${theme.surface3}`}; border-radius: 12px; @@ -91,67 +91,47 @@ const MobileMenuWrapper = styled(Column)<{ $open: boolean }>` font-size: 16px; box-shadow: unset; z-index: ${Z_INDEX.modal}; -` +`; const MobileMenuHeader = styled(Row)` margin-bottom: 16px; -` +`; -export default function SettingsTab({ - autoSlippage, +export default function SettingsTab({ autoSlippage, chainId, trade, - hideRoutingSettings = false, -}: { + hideRoutingSettings = false }: { autoSlippage: Percent chainId?: number trade?: InterfaceTrade hideRoutingSettings?: boolean }) { - const { chainId: connectedChainId } = useWeb3React() - const showDeadlineSettings = Boolean(chainId && !L2_CHAIN_IDS.includes(chainId)) - const node = useRef(null) - const isOpen = useModalIsOpen(ApplicationModal.SETTINGS) + const { chainId: connectedChainId } = useWeb3React(); + const node = useRef(null); + const isOpen = useModalIsOpen(ApplicationModal.SETTINGS); - const closeModal = useCloseModal() - const closeMenu = useCallback(() => closeModal(ApplicationModal.SETTINGS), [closeModal]) - const toggleMenu = useToggleSettingsMenu() + const closeModal = useCloseModal(); + const closeMenu = useCallback(() => closeModal(ApplicationModal.SETTINGS), [closeModal]); + const toggleMenu = useToggleSettingsMenu(); - const isMobile = useIsMobile() - const isOpenMobile = isOpen && isMobile - const isOpenDesktop = isOpen && !isMobile + const isMobile = useIsMobile(); + const isOpenMobile = isOpen && isMobile; + const isOpenDesktop = isOpen && !isMobile; - useOnClickOutside(node, isOpenDesktop ? closeMenu : undefined) - useDisableScrolling(isOpen) + useOnClickOutside(node, isOpenDesktop ? closeMenu : undefined); + useDisableScrolling(isOpen); - const uniswapXEnabled = chainId && isUniswapXSupportedChain(chainId) - const showRoutingSettings = Boolean(uniswapXEnabled && !hideRoutingSettings) - - const isChainSupported = isSupportedChain(chainId) + const isChainSupported = isSupportedChain(chainId); const Settings = useMemo( () => ( - <> - {showRoutingSettings && ( - - - - )} - - - {showRoutingSettings && } - - {showDeadlineSettings && ( - <> - - - - )} - - - + + + + + ), - [autoSlippage, showDeadlineSettings, showRoutingSettings, trade] - ) + [autoSlippage, trade], + ); return ( @@ -183,5 +163,5 @@ export default function SettingsTab({ )} - ) + ); } diff --git a/src/components/Toggle/MultiToggle.tsx b/src/components/Toggle/MultiToggle.tsx index 30985af7..22b91a2f 100644 --- a/src/components/Toggle/MultiToggle.tsx +++ b/src/components/Toggle/MultiToggle.tsx @@ -4,10 +4,10 @@ export const ToggleWrapper = styled.button<{ width?: string }>` display: flex; align-items: center; width: ${({ width }) => width ?? '100%'}; - padding: 1px; + padding: 0; background: ${({ theme }) => theme.surface2}; border-radius: 8px; - border: ${({ theme }) => '1px solid ' + theme.surface3}; + border: none; cursor: pointer; outline: none; ` @@ -17,11 +17,12 @@ export const ToggleElement = styled.span<{ isActive?: boolean; fontSize?: string align-items: center; width: 100%; padding: 4px 0.5rem; - border-radius: 6px; + border-radius: 4px; justify-content: center; height: 100%; - background: ${({ theme, isActive }) => (isActive ? theme.surface1 : 'none')}; - color: ${({ theme, isActive }) => (isActive ? theme.neutral1 : theme.neutral3)}; + background: ${({ theme, isActive }) => (isActive ? theme.jediWhite : '#572c95')}; + box-shadow: 0px 0.76977px 30.79088px 0px rgba(227, 222, 255, 0.20) inset, 0px 3.07909px 13.8559px 0px rgba(154, 146, 210, 0.30) inset; + color: ${({ theme, isActive }) => (isActive ? theme.jediPink : theme.jediWhite)}; font-size: ${({ fontSize }) => fontSize ?? '1rem'}; font-weight: 535; white-space: nowrap; diff --git a/src/components/Tooltip/index.tsx b/src/components/Tooltip/index.tsx index e84d0474..8ee69924 100644 --- a/src/components/Tooltip/index.tsx +++ b/src/components/Tooltip/index.tsx @@ -1,9 +1,9 @@ -import { transparentize } from 'polished' -import { PropsWithChildren, ReactNode, useEffect, useState } from 'react' -import styled from 'styled-components' -import noop from 'utils/noop' +import { transparentize } from 'polished'; +import { PropsWithChildren, ReactNode, useEffect, useState } from 'react'; +import styled from 'styled-components'; -import Popover, { PopoverProps } from '../Popover' +import noop from 'utils/noop'; +import Popover, { PopoverProps } from '../Popover'; export enum TooltipSize { ExtraSmall = '200px', @@ -14,13 +14,13 @@ export enum TooltipSize { const getPaddingForSize = (size: TooltipSize) => { switch (size) { case TooltipSize.ExtraSmall: - return '8px' + return '8px'; case TooltipSize.Small: - return '12px' + return '12px'; case TooltipSize.Large: - return '16px 20px' + return '16px 20px'; } -} +}; const TooltipContainer = styled.div<{ size: TooltipSize }>` max-width: ${({ size }) => size}; @@ -36,10 +36,10 @@ const TooltipContainer = styled.div<{ size: TooltipSize }>` word-break: break-word; background: ${({ theme }) => theme.surface1}; - border-radius: 12px; + border-radius: 4px; border: 1px solid ${({ theme }) => theme.surface3}; box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.9, theme.shadow1)}; -` +`; type TooltipProps = Omit & { text: ReactNode @@ -64,7 +64,7 @@ export default function Tooltip({ text, open, close, disabled, size = TooltipSiz } {...rest} /> - ) + ); } // TODO(WEB-2024) @@ -81,26 +81,26 @@ type MouseoverTooltipProps = Omit & }> export function MouseoverTooltip(props: MouseoverTooltipProps) { - const { text, disabled, children, onOpen, forceShow, timeout, ...rest } = props - const [show, setShow] = useState(false) + const { text, disabled, children, onOpen, forceShow, timeout, ...rest } = props; + const [show, setShow] = useState(false); const open = () => { - setShow(true) - onOpen?.() - } - const close = () => setShow(false) + setShow(true); + onOpen?.(); + }; + const close = () => setShow(false); useEffect(() => { - if (show && timeout) { - const tooltipTimer = setTimeout(() => { - setShow(false) - }, timeout) - - return () => { - clearTimeout(tooltipTimer) - } + if (!(show && timeout)) { + return; } - return - }, [timeout, show]) + const tooltipTimer = setTimeout(() => { + setShow(false); + }, timeout); + + return () => { + clearTimeout(tooltipTimer); + }; + }, [timeout, show]); return ( - ) + ); } diff --git a/src/components/TopLevelModals/index.tsx b/src/components/TopLevelModals/index.tsx index 9fd5537a..59da49fb 100644 --- a/src/components/TopLevelModals/index.tsx +++ b/src/components/TopLevelModals/index.tsx @@ -8,7 +8,6 @@ import AirdropModal from 'components/AirdropModal'; import BaseAnnouncementBanner from 'components/Banner/BaseAnnouncementBanner'; import AddressClaimModal from 'components/claim/AddressClaimModal'; import ConnectedAccountBlocked from 'components/ConnectedAccountBlocked'; -import FiatOnrampModal from 'components/FiatOnrampModal'; import DevFlagsBox from 'dev/DevFlagsBox'; import useAccountRiskCheck from 'hooks/useAccountRiskCheck'; import Bag from 'nft/components/bag/Bag'; @@ -36,7 +35,6 @@ export default function TopLevelModals() { - {shouldShowDevFlags && } diff --git a/src/components/TransactionConfirmationModal/AnimatedConfirmation.tsx b/src/components/TransactionConfirmationModal/AnimatedConfirmation.tsx index 84e07438..0460bffc 100644 --- a/src/components/TransactionConfirmationModal/AnimatedConfirmation.tsx +++ b/src/components/TransactionConfirmationModal/AnimatedConfirmation.tsx @@ -1,9 +1,9 @@ -import styled, { keyframes, useTheme } from 'styled-components' +import styled, { keyframes, useTheme } from 'styled-components'; const Wrapper = styled.div<{ size?: string }>` height: 90px; width: 90px; -` +`; const dash = keyframes` 0% { @@ -12,7 +12,7 @@ const dash = keyframes` 100% { stroke-dashoffset: 0; } -` +`; const dashCheck = keyframes` 0% { @@ -21,14 +21,14 @@ const dashCheck = keyframes` 100% { stroke-dashoffset: 900; } -` +`; const Circle = styled.circle` stroke-dasharray: 1000; stroke-dashoffset: 0; -webkit-animation: ${dash} 0.9s ease-in-out; animation: ${dash} 0.9s ease-in-out; -` +`; const PolyLine = styled.polyline` stroke-dasharray: 1000; @@ -36,10 +36,10 @@ const PolyLine = styled.polyline` stroke-dashoffset: -100; -webkit-animation: ${dashCheck} 0.9s 0.35s ease-in-out forwards; animation: ${dashCheck} 0.9s 0.35s ease-in-out forwards; -` +`; export default function AnimatedConfirmation({ className }: { className?: string }) { - const theme = useTheme() + const theme = useTheme(); return ( @@ -65,5 +65,5 @@ export default function AnimatedConfirmation({ className }: { className?: string /> - ) + ); } diff --git a/src/components/TransactionConfirmationModal/index.tsx b/src/components/TransactionConfirmationModal/index.tsx index 80410226..35fb44cd 100644 --- a/src/components/TransactionConfirmationModal/index.tsx +++ b/src/components/TransactionConfirmationModal/index.tsx @@ -1,54 +1,51 @@ -import { t, Trans } from '@lingui/macro' -import { ChainId, Currency } from '@uniswap/sdk-core' -import { useWeb3React } from '@web3-react/core' -import Badge from 'components/Badge' -import { ChainLogo } from 'components/Logo/ChainLogo' -import { getChainInfo } from 'constants/chainInfo' -import { SupportedL2ChainId } from 'constants/chains' -import useCurrencyLogoURIs from 'lib/hooks/useCurrencyLogoURIs' -import { ReactNode, useCallback, useState } from 'react' -import { AlertCircle, ArrowUpCircle, CheckCircle } from 'react-feather' -import { useIsTransactionConfirmed, useTransaction } from 'state/transactions/hooks' -import styled, { useTheme } from 'styled-components' -import { ExternalLink, ThemedText } from 'theme/components' -import { CloseIcon, CustomLightSpinner } from 'theme/components' -import { isL2ChainId } from 'utils/chains' -import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink' +import { t, Trans } from '@lingui/macro'; +import { ChainId, Currency } from '@uniswap/sdk-core'; +import { useWeb3React } from '@web3-react/core'; +import { ReactNode, useCallback, useState } from 'react'; +import { AlertCircle, ArrowUpCircle, CheckCircle } from 'react-feather'; +import styled, { useTheme } from 'styled-components'; -import Circle from '../../assets/images/blue-loader.svg' -import { TransactionSummary } from '../AccountDetails/TransactionSummary' -import { ButtonLight, ButtonPrimary } from '../Button' -import { AutoColumn, ColumnCenter } from '../Column' -import Modal from '../Modal' -import Row, { RowBetween, RowFixed } from '../Row' -import AnimatedConfirmation from './AnimatedConfirmation' +import Badge from 'components/Badge'; +import { ChainLogo } from 'components/Logo/ChainLogo'; +import { getChainInfo } from 'constants/chainInfo'; +import { SupportedL2ChainId } from 'constants/chains'; +import useCurrencyLogoURIs from 'lib/hooks/useCurrencyLogoURIs'; +import { useIsTransactionConfirmed, useTransaction } from 'state/transactions/hooks'; +import { ExternalLink, ThemedText, CloseIcon, CustomLightSpinner } from 'theme/components'; +import { isL2ChainId } from 'utils/chains'; +import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'; +import Circle from '../../assets/images/blue-loader.svg'; +import { TransactionSummary } from '../AccountDetails/TransactionSummary'; +import { ButtonLight, ButtonPrimary } from '../Button'; +import { AutoColumn, ColumnCenter } from '../Column'; +import Modal from '../Modal'; +import Row, { RowBetween, RowFixed } from '../Row'; +import AnimatedConfirmation from './AnimatedConfirmation'; const Wrapper = styled.div` background-color: ${({ theme }) => theme.surface1}; - border-radius: 20px; + border-radius: 8px; outline: 1px solid ${({ theme }) => theme.surface3}; width: 100%; - padding: 16px; -` + padding: 32px; +`; const BottomSection = styled(AutoColumn)` border-bottom-left-radius: 20px; border-bottom-right-radius: 20px; -` +`; const ConfirmedIcon = styled(ColumnCenter)<{ inline?: boolean }>` padding: ${({ inline }) => (inline ? '20px 0' : '32px 0;')}; -` +`; const ConfirmationModalContentWrapper = styled(AutoColumn)` padding-bottom: 12px; -` +`; -function ConfirmationPendingContent({ - onDismiss, +function ConfirmationPendingContent({ onDismiss, pendingText, - inline, -}: { + inline }: { onDismiss: () => void pendingText: ReactNode inline?: boolean // not in modal @@ -78,32 +75,30 @@ function ConfirmationPendingContent({ - ) + ); } -function TransactionSubmittedContent({ - onDismiss, +function TransactionSubmittedContent({ onDismiss, chainId, hash, currencyToAdd, - inline, -}: { + inline }: { onDismiss: () => void hash?: string chainId: number currencyToAdd?: Currency inline?: boolean // not in modal }) { - const theme = useTheme() + const theme = useTheme(); - const { connector } = useWeb3React() + const { connector } = useWeb3React(); - const token = currencyToAdd?.wrapped - const logoURL = useCurrencyLogoURIs(token)[0] + const token = currencyToAdd?.wrapped; + const logoURL = useCurrencyLogoURIs(token)[0]; - const [success, setSuccess] = useState() + const [success, setSuccess] = useState(); const addToken = useCallback(() => { - if (!token?.symbol || !connector.watchAsset) return + if (!token?.symbol || !connector.watchAsset) { return; } connector .watchAsset({ address: token.address, @@ -112,10 +107,10 @@ function TransactionSubmittedContent({ image: logoURL, }) .then(() => setSuccess(true)) - .catch(() => setSuccess(false)) - }, [connector, logoURL, token]) + .catch(() => setSuccess(false)); + }, [connector, logoURL, token]); - const explorerText = chainId === ChainId.MAINNET ? t`View on Etherscan` : t`View on Block Explorer` + const explorerText = chainId === ChainId.MAINNET ? t`View on Etherscan` : t`View on Block Explorer`; return ( @@ -160,16 +155,14 @@ function TransactionSubmittedContent({ - ) + ); } -export function ConfirmationModalContent({ - title, +export function ConfirmationModalContent({ title, bottomContent, onDismiss, topContent, - headerContent, -}: { + headerContent }: { title: ReactNode onDismiss: () => void topContent: () => ReactNode @@ -181,7 +174,7 @@ export function ConfirmationModalContent({ {headerContent?.()} - + {title} @@ -190,20 +183,18 @@ export function ConfirmationModalContent({ {bottomContent && {bottomContent()}} - ) + ); } const StyledL2Badge = styled(Badge)` padding: 6px 8px; -` +`; -function L2Content({ - onDismiss, +function L2Content({ onDismiss, chainId, hash, pendingText, - inline, -}: { + inline }: { onDismiss: () => void hash?: string chainId: SupportedL2ChainId @@ -211,18 +202,18 @@ function L2Content({ pendingText: ReactNode inline?: boolean // not in modal }) { - const theme = useTheme() + const theme = useTheme(); - const transaction = useTransaction(hash) - const confirmed = useIsTransactionConfirmed(hash) - const transactionSuccess = transaction?.receipt?.status === 1 + const transaction = useTransaction(hash); + const confirmed = useIsTransactionConfirmed(hash); + const transactionSuccess = transaction?.receipt?.status === 1; // convert unix time difference to seconds const secondsToConfirm = transaction?.confirmedTime ? (transaction.confirmedTime - transaction.addedTime) / 1000 - : undefined + : undefined; - const info = getChainInfo(chainId) + const info = getChainInfo(chainId); return ( @@ -294,7 +285,7 @@ function L2Content({ - ) + ); } interface ConfirmationModalProps { @@ -307,22 +298,20 @@ interface ConfirmationModalProps { currencyToAdd?: Currency } -export default function TransactionConfirmationModal({ - isOpen, +export default function TransactionConfirmationModal({ isOpen, onDismiss, attemptingTxn, hash, pendingText, reviewContent, - currencyToAdd, -}: ConfirmationModalProps) { - const { chainId } = useWeb3React() + currencyToAdd }: ConfirmationModalProps) { + const { chainId } = useWeb3React(); - if (!chainId) return null + if (!chainId) { return null; } // confirmation screen return ( - + {isL2ChainId(chainId) && (hash || attemptingTxn) ? ( ) : attemptingTxn ? ( @@ -338,5 +327,5 @@ export default function TransactionConfirmationModal({ reviewContent() )} - ) + ); } diff --git a/src/components/Unicon/index.tsx b/src/components/Unicon/index.tsx index 60ec6e60..0055df48 100644 --- a/src/components/Unicon/index.tsx +++ b/src/components/Unicon/index.tsx @@ -1,18 +1,15 @@ -import React, { memo, useMemo } from 'react' -import { useIsDarkMode } from 'theme/components/ThemeToggle' +import React, { memo, useMemo } from 'react'; -import { blurs, UniconAttributeData, UniconAttributes, UniconAttributesToIndices } from './types' -import { deriveUniconAttributeIndices, getUniconAttributeData, isEthAddress } from './utils' +import { blurs, UniconAttributeData, UniconAttributes, UniconAttributesToIndices } from './types'; +import { deriveUniconAttributeIndices, getUniconAttributeData, isEthAddress } from './utils'; -const ORIGINAL_CONTAINER_SIZE = 36 -const EMBLEM_XY_SHIFT = 10 +const ORIGINAL_CONTAINER_SIZE = 36; +const EMBLEM_XY_SHIFT = 10; -function PathMask({ - id, +function PathMask({ id, paths, scale, - shift = 0, -}: { + shift = 0 }: { id: string paths: React.SVGProps[] scale: number @@ -27,13 +24,13 @@ function PathMask({ ))} - ) + ); } type UniconMaskProps = { maskId: string; attributeData: UniconAttributeData; size: number } function UniconMask({ maskId, attributeData, size }: UniconMaskProps) { - const shapeMaskId = `shape-${maskId}` - const containerMaskId = `container-${maskId}` + const shapeMaskId = `shape-${maskId}`; + const containerMaskId = `container-${maskId}`; return ( @@ -70,7 +67,7 @@ function UniconMask({ maskId, attributeData, size }: UniconMaskProps) { - ) + ); } type UniconGradientProps = { gradientId: string; attributeData: UniconAttributeData } @@ -80,7 +77,7 @@ function UniconGradient({ gradientId, attributeData }: UniconGradientProps) { - ) + ); } function UniconBlur({ blurId, size }: { blurId: string; size: number }) { @@ -88,30 +85,27 @@ function UniconBlur({ blurId, size }: { blurId: string; size: number }) { - ) + ); } -function UniconSvg({ - attributeIndices, +function UniconSvg({ attributeIndices, size, - address, -}: { + address }: { attributeIndices: UniconAttributesToIndices size: number address: string mobile?: boolean }) { - const isDarkMode = useIsDarkMode() - const attributeData = useMemo(() => getUniconAttributeData(attributeIndices), [attributeIndices]) + const attributeData = useMemo(() => getUniconAttributeData(attributeIndices), [attributeIndices]); - const gradientId = `gradient${address + size}` - const maskId = `mask${address + size}` - const blurId = `blur${address + size}` + const gradientId = `gradient${address + size}`; + const maskId = `mask${address + size}`; + const blurId = `blur${address + size}`; const svgProps = { viewBox: `0 0 ${size} ${size}`, - } + }; - if (!attributeIndices || !attributeData) return null + if (!attributeIndices || !attributeData) { return null; } return ( @@ -123,7 +117,6 @@ function UniconSvg({ - {!isDarkMode && } - ) + ); } interface Props { @@ -146,9 +139,9 @@ interface Props { } function _Unicon({ address, size = 24, randomSeed = 0, mobile }: Props) { - const attributeIndices = useMemo(() => deriveUniconAttributeIndices(address, randomSeed), [address, randomSeed]) + const attributeIndices = useMemo(() => deriveUniconAttributeIndices(address, randomSeed), [address, randomSeed]); - if (!address || !isEthAddress(address) || !attributeIndices) return null + if (!address || !isEthAddress(address) || !attributeIndices) { return null; } return (
@@ -156,7 +149,7 @@ function _Unicon({ address, size = 24, randomSeed = 0, mobile }: Props) {
- ) + ); } -export const Unicon = memo(_Unicon) +export const Unicon = memo(_Unicon); diff --git a/src/components/WalletModal/Option.test.tsx b/src/components/WalletModal/Option.test.tsx index 544e5349..21617ea2 100644 --- a/src/components/WalletModal/Option.test.tsx +++ b/src/components/WalletModal/Option.test.tsx @@ -36,48 +36,48 @@ const mockConnection2: Connection = { type: ConnectionType.INJECTED, } as unknown as Connection -describe('Wallet Option', () => { - it('renders default state', () => { - const component = render(
- )} - { - setShowConfirm(true); - }} - disabled={ - !isValid - || (!argentWalletContract && approvalA !== ApprovalState.APPROVED && !depositADisabled) - || (!argentWalletContract && approvalB !== ApprovalState.APPROVED && !depositBDisabled) - } - error={!isValid && !!parsedAmounts[Field.CURRENCY_A] && !!parsedAmounts[Field.CURRENCY_B]} - > - {errorMessage || Preview} - - - )); - - const usdcValueCurrencyA = usdcValues[Field.CURRENCY_A]; - const usdcValueCurrencyB = usdcValues[Field.CURRENCY_B]; + { + setShowConfirm(true) + }} + disabled={ + !isValid || + (!argentWalletContract && approvalA !== ApprovalState.APPROVED && !depositADisabled) || + (!argentWalletContract && approvalB !== ApprovalState.APPROVED && !depositBDisabled) + } + error={!isValid && !!parsedAmounts[Field.CURRENCY_A] && !!parsedAmounts[Field.CURRENCY_B]} + > + {errorMessage || Preview} + + + ) + + const usdcValueCurrencyA = usdcValues[Field.CURRENCY_A] + const usdcValueCurrencyB = usdcValues[Field.CURRENCY_B] const currencyAFiat = useMemo( () => ({ data: usdcValueCurrencyA ? parseFloat(usdcValueCurrencyA.toSignificant()) : undefined, isLoading: false, }), - [usdcValueCurrencyA], - ); + [usdcValueCurrencyA] + ) const currencyBFiat = useMemo( () => ({ data: usdcValueCurrencyB ? parseFloat(usdcValueCurrencyB.toSignificant()) : undefined, isLoading: false, }), - [usdcValueCurrencyB], - ); + [usdcValueCurrencyB] + ) - const owner = useSingleCallResult(tokenId ? positionManager : null, 'ownerOf', [tokenId]).result?.[0]; - const ownsNFT = addressesAreEquivalent(owner, account) || addressesAreEquivalent(existingPositionDetails?.operator, account); - const showOwnershipWarning = Boolean(hasExistingPosition && account && !ownsNFT); + const owner = useSingleCallResult(tokenId ? positionManager : null, 'ownerOf', [tokenId]).result?.[0] + const ownsNFT = + addressesAreEquivalent(owner, account) || addressesAreEquivalent(existingPositionDetails?.operator, account) + const showOwnershipWarning = Boolean(hasExistingPosition && account && !ownsNFT) return ( <> @@ -586,7 +608,7 @@ function AddLiquidity() { - + Clear all @@ -601,9 +623,9 @@ function AddLiquidity() { <> - + Select pair - + { - onFieldAInput(maxAmounts[Field.CURRENCY_A]?.toExact() ?? ''); + onFieldAInput(maxAmounts[Field.CURRENCY_A]?.toExact() ?? '') }} onCurrencySelect={handleCurrencyASelect} showMaxButton={!atMaxAmounts[Field.CURRENCY_A]} currency={currencies[Field.CURRENCY_A] ?? null} id="add-liquidity-input-tokena" showCommonBases + hideShadow /> { - onFieldBInput(maxAmounts[Field.CURRENCY_B]?.toExact() ?? ''); + onFieldBInput(maxAmounts[Field.CURRENCY_B]?.toExact() ?? '') }} showMaxButton={!atMaxAmounts[Field.CURRENCY_B]} currency={currencies[Field.CURRENCY_B] ?? null} id="add-liquidity-input-tokenb" showCommonBases + hideShadow /> @@ -672,24 +696,34 @@ function AddLiquidity() { handleRateToggle={() => { if (!ticksAtLimit[Bound.LOWER] && !ticksAtLimit[Bound.UPPER]) { onLeftRangeInput( - (invertPrice ? priceLower : priceUpper?.invert())?.toSignificant(6) ?? '', - ); + (invertPrice ? priceLower : priceUpper?.invert())?.toSignificant(6) ?? '' + ) onRightRangeInput( - (invertPrice ? priceUpper : priceLower?.invert())?.toSignificant(6) ?? '', - ); - onFieldAInput(formattedAmounts[Field.CURRENCY_B] ?? ''); + (invertPrice ? priceUpper : priceLower?.invert())?.toSignificant(6) ?? '' + ) + onFieldAInput(formattedAmounts[Field.CURRENCY_B] ?? '') } navigate( - `/add/${currencyIdB as string}/${currencyIdA as string}${ - feeAmount ? `/${feeAmount}` : '' - }`, - ); + `/add/${currencyIdB as string}/${currencyIdA as string}${feeAmount ? `/${feeAmount}` : '' + }` + ) }} /> )}
- + )} - ) : ( @@ -783,29 +803,25 @@ function AddLiquidity() { }} > - This pool must be initialized before you can add liquidity. To initialize, select a - starting price for the pool. Then, enter your liquidity price range and deposit amount. - Gas fees will be higher than usual due to the initialization transaction. + This pool must be initialized before you can add liquidity. To initialize, select a starting price for the pool. Then, enter your liquidity price range and deposit amount. Gas fees will be higher than usual due to the initialization transaction. )} - + - + { - onFieldAInput(maxAmounts[Field.CURRENCY_A]?.toExact() ?? ''); + onFieldAInput(maxAmounts[Field.CURRENCY_A]?.toExact() ?? '') }} showMaxButton={!atMaxAmounts[Field.CURRENCY_A]} currency={currencies[Field.CURRENCY_A] ?? null} @@ -861,7 +877,7 @@ function AddLiquidity() { value={formattedAmounts[Field.CURRENCY_B]} onUserInput={onFieldBInput} onMax={() => { - onFieldBInput(maxAmounts[Field.CURRENCY_B]?.toExact() ?? ''); + onFieldBInput(maxAmounts[Field.CURRENCY_B]?.toExact() ?? '') }} showMaxButton={!atMaxAmounts[Field.CURRENCY_B]} fiatValue={currencyBFiat} @@ -887,5 +903,5 @@ function AddLiquidity() { - ); + ) } diff --git a/src/pages/AddLiquidity/styled.tsx b/src/pages/AddLiquidity/styled.tsx index cd1d3b12..08b4e5ef 100644 --- a/src/pages/AddLiquidity/styled.tsx +++ b/src/pages/AddLiquidity/styled.tsx @@ -1,18 +1,24 @@ -import styled from 'styled-components'; +import styled from 'styled-components' -import { AutoColumn } from 'components/Column'; -import { Input } from 'components/NumericalInput'; +import { AutoColumn } from 'components/Column' +import { Input } from 'components/NumericalInput' export const Wrapper = styled.div` position: relative; - padding: 26px 16px; -`; +` export const ScrollablePage = styled.div` - padding: 20px 8px 0px; position: relative; display: flex; flex-direction: column; + border-radius: 8px; + background: rgba(196, 196, 196, 0.01); + box-shadow: 0px 0.76977px 30.79088px 0px rgba(227, 222, 255, 0.2) inset, + 0px 3.07909px 13.8559px 0px rgba(154, 146, 210, 0.3) inset, + 0px 75.43767px 76.9772px -36.94907px rgba(202, 172, 255, 0.3) inset, + 0px -63.12132px 52.3445px -49.26542px rgba(96, 68, 144, 0.3) inset, 0px 5.38841px 8.46749px -3.07909px #fff inset, + 0px 30.02111px 43.10724px -27.7118px rgba(255, 255, 255, 0.5) inset; + backdrop-filter: blur(38.48860168457031px); ${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium` margin: 0 auto; @@ -25,36 +31,35 @@ export const ScrollablePage = styled.div` @media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { padding-top: 20px; } -`; +` export const DynamicSection = styled(AutoColumn)<{ disabled?: boolean }>` opacity: ${({ disabled }) => (disabled ? '0.2' : '1')}; pointer-events: ${({ disabled }) => (disabled ? 'none' : 'initial')}; -`; +` export const StyledInput = styled(Input)` - background-color: ${({ theme }) => theme.surface1}; text-align: left; font-size: 18px; width: 100%; -`; +` /* two-column layout where DepositAmount is moved at the very end on mobile. */ export const ResponsiveTwoColumns = styled.div<{ wide: boolean }>` display: flex; flex-direction: column; gap: 20px; - padding-top: 20px; + padding: 20px; - border-top: 1px solid ${({ theme }) => theme.surface3}; + border-top: 1px solid rgba(255, 255, 255, 0.2); ${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium` margin-top: 0; `}; -`; +` export const MediumOnly = styled.div` ${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium` display: none; `}; -`; +` diff --git a/src/pages/App.tsx b/src/pages/App.tsx index 14810a32..64f4d924 100644 --- a/src/pages/App.tsx +++ b/src/pages/App.tsx @@ -1,23 +1,22 @@ -import { useWeb3React } from '@web3-react/core'; -import { useAtom } from 'jotai'; -import { lazy, Suspense, useEffect, useLayoutEffect, useMemo, useState } from 'react'; -import { Navigate, Route, Routes, useLocation, useSearchParams } from 'react-router-dom'; -import styled from 'styled-components'; - -import ErrorBoundary from 'components/ErrorBoundary'; -import Loader from 'components/Icons/LoadingSpinner'; -import NavBar, { PageTabs } from 'components/NavBar'; -import { FeatureFlag, useFeatureFlagsIsLoaded } from 'featureFlags'; -import { useUniswapXDefaultEnabled } from 'featureFlags/flags/uniswapXDefault'; -import { shouldDisableNFTRoutesAtom } from 'state/application/atoms'; -import { useAppSelector } from 'state/hooks'; -import { AppState } from 'state/reducer'; -import { RouterPreference } from 'state/routing/types'; -import { useRouterPreference, useUserOptedOutOfUniswapX } from 'state/user/hooks'; -import { useIsDarkMode } from 'theme/components/ThemeToggle'; -import { flexRowNoWrap } from 'theme/styles'; -import { Z_INDEX } from 'theme/zIndex'; -import { RouteDefinition, routes, useRouterConfig } from './RouteDefinitions'; +import { useWeb3React } from '@web3-react/core' +import { useAtom } from 'jotai' +import { lazy, Suspense, useEffect, useLayoutEffect, useMemo, useState } from 'react' +import { Navigate, Route, Routes, useLocation, useSearchParams } from 'react-router-dom' +import styled from 'styled-components' + +import ErrorBoundary from 'components/ErrorBoundary' +import Loader from 'components/Icons/LoadingSpinner' +import NavBar, { PageTabs } from 'components/NavBar' +import { FeatureFlag, useFeatureFlagsIsLoaded } from 'featureFlags' +import { useUniswapXDefaultEnabled } from 'featureFlags/flags/uniswapXDefault' +import { shouldDisableNFTRoutesAtom } from 'state/application/atoms' +import { useAppSelector } from 'state/hooks' +import { AppState } from 'state/reducer' +import { RouterPreference } from 'state/routing/types' +import { useRouterPreference, useUserOptedOutOfUniswapX } from 'state/user/hooks' +import { flexRowNoWrap } from 'theme/styles' +import { Z_INDEX } from 'theme/zIndex' +import { RouteDefinition, routes, useRouterConfig } from './RouteDefinitions' const BodyWrapper = styled.div<{ bannerIsVisible?: boolean }>` display: flex; @@ -27,6 +26,8 @@ const BodyWrapper = styled.div<{ bannerIsVisible?: boolean }>` padding: ${({ theme }) => theme.navHeight}px 0px 5rem 0px; align-items: center; flex: 1; + margin-top: 78px; + font-family: 'DM Sans'; @media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { min-height: calc(100vh); @@ -35,7 +36,7 @@ const BodyWrapper = styled.div<{ bannerIsVisible?: boolean }>` @media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { min-height: calc(100vh); } -`; +` const MobileBottomBar = styled.div` z-index: ${Z_INDEX.sticky}; @@ -52,16 +53,17 @@ const MobileBottomBar = styled.div` margin: 0; border-radius: 0; width: 100%; - + @media screen and (min-width: ${({ theme }) => theme.breakpoint.md}px) { display: none; } -`; +` const HeaderWrapper = styled.div<{ transparent?: boolean; bannerIsVisible?: boolean; scrollY: number }>` ${flexRowNoWrap}; - background-color: ${({ theme, transparent }) => !transparent && theme.surface1}; + background-color: transparent; border-bottom: ${({ theme }) => `1px solid ${theme.surface3}`}; + backdrop-filter: ${({ theme, transparent }) => (!transparent ? 'blur(38px)' : 'none')}; width: 100%; justify-content: space-between; position: fixed; @@ -70,7 +72,8 @@ const HeaderWrapper = styled.div<{ transparent?: boolean; bannerIsVisible?: bool transition-property: background-color; transition-duration: ${({ theme }) => theme.transition.duration.fast}; transition-timing-function: linear; - + font-family: 'Avenir LT Std'; + @media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { top: 0; } @@ -78,32 +81,32 @@ const HeaderWrapper = styled.div<{ transparent?: boolean; bannerIsVisible?: bool @media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { top: 0; } -`; +` export default function App() { - const isLoaded = useFeatureFlagsIsLoaded(); + const isLoaded = useFeatureFlagsIsLoaded() - const location = useLocation(); - const { pathname } = location; + const location = useLocation() + const { pathname } = location - const [scrollY, setScrollY] = useState(0); - const scrolledState = scrollY > 0; - const routerConfig = useRouterConfig(); + const [scrollY, setScrollY] = useState(0) + const scrolledState = scrollY > 0 + const routerConfig = useRouterConfig() - const isHeaderTransparent = !scrolledState; + const isHeaderTransparent = !scrolledState useEffect(() => { - window.scrollTo(0, 0); - setScrollY(0); - }, [pathname]); + window.scrollTo(0, 0) + setScrollY(0) + }, [pathname]) useEffect(() => { const scrollListener = () => { - setScrollY(window.scrollY); - }; - window.addEventListener('scroll', scrollListener); - return () => window.removeEventListener('scroll', scrollListener); - }, []); + setScrollY(window.scrollY) + } + window.addEventListener('scroll', scrollListener) + return () => window.removeEventListener('scroll', scrollListener) + }, []) return ( @@ -114,13 +117,15 @@ export default function App() { }> {isLoaded ? ( - {routes.map((route: RouteDefinition) => (route.enabled(routerConfig) ? ( - - {route.nestedPaths.map((nestedPath) => ( - - ))} - - ) : null))} + {routes.map((route: RouteDefinition) => + route.enabled(routerConfig) ? ( + + {route.nestedPaths.map((nestedPath) => ( + + ))} + + ) : null + )} ) : ( @@ -131,5 +136,5 @@ export default function App() { - ); + ) } diff --git a/src/pages/AppBody.tsx b/src/pages/AppBody.tsx index d7dc2110..081ba723 100644 --- a/src/pages/AppBody.tsx +++ b/src/pages/AppBody.tsx @@ -1,7 +1,7 @@ -import { PropsWithChildren } from 'react'; -import styled from 'styled-components'; +import { PropsWithChildren } from 'react' +import styled from 'styled-components' -import { Z_INDEX } from 'theme/zIndex'; +import { Z_INDEX } from 'theme/zIndex' interface BodyWrapperProps { $margin?: string @@ -13,19 +13,15 @@ export const BodyWrapper = styled.main` margin-top: ${({ $margin }) => $margin ?? '0px'}; max-width: ${({ $maxWidth }) => $maxWidth ?? '420px'}; width: 100%; - background: ${({ theme }) => theme.surface1}; - border-radius: 16px; - border: 1px solid ${({ theme }) => theme.surface3}; - margin-top: 1rem; margin-left: auto; margin-right: auto; z-index: ${Z_INDEX.default}; font-feature-settings: 'ss01' on, 'ss02' on, 'cv01' on, 'cv03' on; -`; +` /** * The styled container element that wraps the content of most pages and the tabs. */ export default function AppBody(props: PropsWithChildren) { - return ; + return } diff --git a/src/pages/NotFound/index.tsx b/src/pages/NotFound/index.tsx index 64916ad0..b4d65354 100644 --- a/src/pages/NotFound/index.tsx +++ b/src/pages/NotFound/index.tsx @@ -7,7 +7,6 @@ import { Trace } from 'analytics'; import { SmallButtonPrimary } from 'components/Button'; import { useIsMobile } from 'nft/hooks'; import { ThemedText } from 'theme/components'; -import { useIsDarkMode } from 'theme/components/ThemeToggle'; import darkImage from '../../assets/images/404-page-dark.png'; import lightImage from '../../assets/images/404-page-light.png'; @@ -39,7 +38,6 @@ const PageWrapper = styled(Container)` `; export default function NotFound() { - const isDarkMode = useIsDarkMode(); const isMobile = useIsMobile(); const Title = isMobile ? ThemedText.LargeHeader : ThemedText.Hero; @@ -55,7 +53,7 @@ export default function NotFound() { Page not found! - Liluni + Liluni Oops, take me back to Swap diff --git a/src/pages/Pool/CTACards.tsx b/src/pages/Pool/CTACards.tsx index 39c95be1..9ff99373 100644 --- a/src/pages/Pool/CTACards.tsx +++ b/src/pages/Pool/CTACards.tsx @@ -1,92 +1,68 @@ -import { Trans } from '@lingui/macro'; -import { useWeb3React } from '@web3-react/core'; -import styled from 'styled-components'; +import { Trans } from '@lingui/macro' +import { useWeb3React } from '@web3-react/core' +import styled from 'styled-components' -import { AutoColumn } from 'components/Column'; -import { getChainInfoOrDefault } from 'constants/chainInfo'; -import { ThemedText, ExternalLink } from 'theme/components'; +import { AutoColumn } from 'components/Column' +import { getChainInfoOrDefault } from 'constants/chainInfo' +import { ThemedText, ExternalLink } from 'theme/components' +import ExternalLinkIcon from '../../assets/images/ExternalLinkIcon.png' const CTASection = styled.section` - display: grid; - grid-template-columns: 1fr 1fr; - gap: 8px; - opacity: 0.8; - - ${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall` - grid-template-columns: auto; - grid-template-rows: auto; - `}; -`; + display: flex; + justify-content: center; +` const CTA = styled(ExternalLink)` - padding: 16px; - border-radius: 20px; - position: relative; - overflow: hidden; - border: 1px solid ${({ theme }) => theme.surface3}; - - * { - color: ${({ theme }) => theme.neutral1}; - text-decoration: none !important; - } - - :hover { - border: 1px solid ${({ theme }) => theme.surface3}; - - text-decoration: none; - * { - text-decoration: none !important; - } - } -`; + justify-content: center; + border-radius: 8px; + border: 1px solid ${({ theme }) => theme.jediGrey}; + align-items: center; + width: 293px; + height: 56px; + text-align: center; + display: flex; +` const HeaderText = styled(ThemedText.DeprecatedLabel)` align-items: center; - display: flex; + color: ${({ theme }) => theme.jediBlue}; + font-feature-settings: 'clig' off, 'liga' off; + font-family: 'Avenir LT Std', sans-serif; font-size: 16px; - font-weight: 535 !important; + font-style: normal; + font-weight: 700; + line-height: 24px; ${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium` font-size: 16px; `}; -`; +` const ResponsiveColumn = styled(AutoColumn)` - grid-template-columns: 1fr; - width: 100%; - gap: 8px; + display: flex; +` - ${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium` - gap: 8px; - `}; - justify-content: space-between; -`; +const IconWrapper = styled.div` + display: flex; + flex-flow: row nowrap; + align-items: center; + justify-content: center; + margin-left: 4px; +` export default function CTACards() { - const { chainId } = useWeb3React(); - const { infoLink } = getChainInfoOrDefault(chainId); + const { chainId } = useWeb3React() + const { infoLink } = getChainInfoOrDefault(chainId) return ( - - Learn about providing liquidity ↗ - - - Check out our v3 LP walkthrough and migration guides. - - - - - - - Top pools ↗ - - - Explore Uniswap Analytics. - + Checkout Top Pools + + {'Icon'} + - ); + ) } diff --git a/src/pages/Pool/index.tsx b/src/pages/Pool/index.tsx index 5f51ffff..eef0f5f4 100644 --- a/src/pages/Pool/index.tsx +++ b/src/pages/Pool/index.tsx @@ -1,7 +1,7 @@ import { Trans } from '@lingui/macro'; import { BrowserEvent, InterfaceElementName, InterfaceEventName, InterfacePageName } from '@uniswap/analytics-events'; import { useWeb3React } from '@web3-react/core'; -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; import { AlertTriangle, BookOpen, ChevronDown, ChevronsRight, Inbox, Layers } from 'react-feather'; import { Link } from 'react-router-dom'; import styled, { css, useTheme } from 'styled-components'; @@ -13,7 +13,7 @@ import { ButtonGray, ButtonPrimary, ButtonText } from 'components/Button'; import { AutoColumn } from 'components/Column'; import { FlyoutAlignment, Menu } from 'components/Menu'; import PositionList from 'components/PositionList'; -import { RowBetween, RowFixed } from 'components/Row'; +import Row, { AutoRow, RowBetween, RowFixed } from 'components/Row'; import { SwitchLocaleLink } from 'components/SwitchLocaleLink'; import { isSupportedChain } from 'constants/chains'; import { useFilterPossiblyMaliciousPositions } from 'hooks/useFilterPossiblyMaliciousPositions'; @@ -23,19 +23,15 @@ import { useUserHideClosedPositions } from 'state/user/hooks'; import { HideSmall, ThemedText } from 'theme/components'; import CTACards from './CTACards'; import { LoadingRows } from './styled'; +import WalletIcon from '../../assets/wallets/Wallet.png'; +import NoPositionsIcon from '../../assets/images/noPosition.png'; const PageWrapper = styled(AutoColumn)` - padding: 68px 8px 0px; - max-width: 870px; + padding: 0px 8px 0px; + max-width: 920px; width: 100%; @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { - max-width: 800px; - padding-top: 48px; - } - - @media (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { - max-width: 500px; padding-top: 20px; } `; @@ -45,24 +41,112 @@ const TitleRow = styled(RowBetween)` flex-wrap: wrap; gap: 12px; width: 100%; + padding-left: 12px; } `; -const ButtonRow = styled(RowFixed)` - & > *:not(:last-child) { - margin-left: 8px; +const PoolStats = styled.div` + display: grid; + gap: 12px; + grid-template-columns: repeat(3, 1fr); + @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { + grid-template-columns: 1fr; } +`; - @media (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { - width: 100%; - flex-direction: row; - justify-content: space-between; +const PoolsCard = styled.div` + padding: 20px; + border-radius: 8px; + backdrop-filter: blur(38px); + background-color: rgba(196, 196, 196, 0.01); + box-shadow: 0px 0.76977px 30.79088px 0px rgba(227, 222, 255, 0.20) inset, 0px 3.07909px 13.8559px 0px rgba(154, 146, 210, 0.30) inset, 0px 75.43767px 76.9772px -36.94907px rgba(202, 172, 255, 0.30) inset, 0px -63.12132px 52.3445px -49.26542px rgba(96, 68, 144, 0.30) inset, 0px 5.38841px 8.46749px -3.07909px #FFF inset, 0px 30.02111px 43.10724px -27.7118px rgba(255, 255, 255, 0.50) inset; + color: ${({ theme }) => theme.jediWhite}; +`; +const PoolsCardHeader = styled.div` + color: ${({ theme }) => theme.notice}; + font-family: DM Sans; + font-size: 16px; + font-style: normal; + font-weight: 700; + line-height: 20px; + margin-bottom: 20px; + + @media (max-width: ${({ theme }) => `${theme.breakpoint.lg}px`}) { + font-size: 14px; + } +`; +const PoolsCardDetails = styled.div` + display: flex; + align-items: center; + +`; + +const PoolsCardNumbers = styled.div` + color: ${({ theme }) => theme.jediWhite}; + font-family: DM Sans; + font-size: 24px; + font-style: normal; + font-weight: 500; + line-height: 20px; + + @media (max-width: ${({ theme }) => `${theme.breakpoint.lg}px`}) { + font-size: 18px; } `; + +const PoolsCardPercent = styled.div` + color: ${({ theme }) => theme.signalGreen}; + text-align: right; + margin-left: auto; + font-family: DM Sans; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 100%; + + @media (max-width: ${({ theme }) => `${theme.breakpoint.lg}px`}) { + font-size: 12px; + } +`; +const PoolsCardPercentNegative = styled(PoolsCardPercent)` + color: ${({ theme }) => theme.signalRed}; +`; + +const NoPositions = styled.div` + align-items: center; + display: flex; + flex-direction: column; + justify-content: center; + margin: auto; + min-height: 25vh; + height: 240px; +`; + +const NewPositionText = styled.div` + margin-top: 12px; + margin-bottom: 20px; +`; + +const PoolsHeading = styled.div` + color: ${({ theme }) => theme.jediWhite}; + font-family: 'Avenir LT Std', sans-serif; + text-transform: uppercase; + font-size: 24px; + font-weight: 750; +`; +const PositionsText = styled.div` + color: ${({ theme }) => theme.jediWhite}; + font-family: DM Sans; + font-size: 20px; + font-style: normal; + font-weight: 500; + line-height: 100%; /* 20px */ +`; +const ButtonRow = styled(AutoRow)``; + const PoolMenu = styled(Menu)` margin-left: 0; @media (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { flex: 1 1 auto; - width: 50%; } a { @@ -97,8 +181,14 @@ const ErrorContainer = styled.div` flex-direction: column; justify-content: center; margin: auto; - max-width: 300px; min-height: 25vh; + @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { + padding: 0px 52px; + } + + @media (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { + padding: 0px 52px; + } `; const IconStyle = css` @@ -107,6 +197,14 @@ const IconStyle = css` margin-bottom: 0.5rem; `; +const IconWrapper = styled.div` + display: flex; + flex-flow: row nowrap; + align-items: center; + justify-content: center; + margin-top: 20px; +`; + const NetworkIcon = styled(AlertTriangle)` ${IconStyle} `; @@ -116,26 +214,31 @@ const InboxIcon = styled(Inbox)` `; const ResponsiveButtonPrimary = styled(ButtonPrimary)` - border-radius: 12px; + border-radius: 8px; font-size: 16px; padding: 6px 8px; - width: fit-content; + width: 175px; + margin-left: auto; + height: 38px; @media (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { - flex: 1 1 auto; - width: 50%; + width: 132px; } `; -const MainContentWrapper = styled.main` - background-color: ${({ theme }) => theme.surface1}; - border: 1px solid ${({ theme }) => theme.surface3}; - padding: 0; - border-radius: 16px; - display: flex; - flex-direction: column; - overflow: hidden; +const MainContentWrapper = styled.main<{ isWalletConnected?: boolean; filteredPositions?: any }>` + background-color: ${({ theme, isWalletConnected, filteredPositions }) => (isWalletConnected && filteredPositions ? 'rgba(196, 196, 196, 0.01)' : theme.jediNavyBlue)}; + border-radius: 8px; + box-shadow: ${({ isWalletConnected, filteredPositions }) => (isWalletConnected && filteredPositions + ? `0px 0.76977px 30.79088px 0px rgba(227, 222, 255, 0.2) inset, + 0px 3.07909px 13.8559px 0px rgba(154, 146, 210, 0.3) inset, + 0px 75.43767px 76.9772px -36.94907px rgba(202, 172, 255, 0.3) inset, + 0px -63.12132px 52.3445px -49.26542px rgba(96, 68, 144, 0.3) inset` + : '')}; + @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { } `; +const PositionWrapper = styled.div``; + function PositionsLoadingPlaceholder() { return ( @@ -189,6 +292,7 @@ function WrongNetworkCard() { export default function Pool() { const { account, chainId } = useWeb3React(); + const [isWalletConnected, setIsWalletConnected] = useState(true); const networkSupportsV2 = useNetworkSupportsV2(); const toggleWalletDrawer = useToggleAccountDrawer(); @@ -258,9 +362,9 @@ export default function Pool() { - Pools + Pools - + {/* {networkSupportsV2 && ( + New position - + */} + + + Total Liquidity + + US$16,006,030 + +0.70% + + + + Total Volume (24hr) + + US$3,001,359 + +40.09% + + + + Total Fees (24hr) + + US$16,006,030 + -1.96% + + + - - {positionsLoading ? ( - - ) : filteredPositions && closedPositions && filteredPositions.length > 0 ? ( - + + My Positions + + + New position + + + + + {isWalletConnected ? ( + !filteredPositions.length && !positionsLoading ? ( + + + {'Icon'} + + + Open a new position + + + + New position + + + ) : positionsLoading ? ( + + ) : ( + + ) ) : ( - + + {'Icon'} +
- Your active V3 liquidity positions will appear here. + Connect wallet to see your positions or open a new position
{!showConnectAWallet && closedPositions.length > 0 && ( @@ -314,19 +464,17 @@ export default function Pool() { element={InterfaceElementName.CONNECT_WALLET_BUTTON} > - Connect a wallet + Connect wallet )}
)}
- - - + {filteredPositions.length ? null : }
diff --git a/src/pages/PoolDetails/PoolDetailsStatsButtons.tsx b/src/pages/PoolDetails/PoolDetailsStatsButtons.tsx index bde42f39..477b4f5f 100644 --- a/src/pages/PoolDetails/PoolDetailsStatsButtons.tsx +++ b/src/pages/PoolDetails/PoolDetailsStatsButtons.tsx @@ -86,7 +86,6 @@ export function PoolDetailsStatsButtons({ chainId, token0, token1, feeTier, load handleOnClick(false)} data-testid="pool-details-add-liquidity-button" > @@ -94,7 +93,6 @@ export function PoolDetailsStatsButtons({ chainId, token0, token1, feeTier, load handleOnClick(true)} data-testid="pool-details-swap-button" > diff --git a/src/pages/PoolFinder/index.tsx b/src/pages/PoolFinder/index.tsx index 766c05f3..94e9ab6e 100644 --- a/src/pages/PoolFinder/index.tsx +++ b/src/pages/PoolFinder/index.tsx @@ -1,33 +1,33 @@ -import { Trans } from '@lingui/macro'; -import { InterfacePageName } from '@uniswap/analytics-events'; -import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'; -import { useWeb3React } from '@web3-react/core'; -import JSBI from 'jsbi'; -import { useCallback, useEffect, useState } from 'react'; -import { Plus } from 'react-feather'; -import { useLocation } from 'react-router'; -import { Text } from 'rebass'; - -import { V2Unsupported } from 'components/V2Unsupported'; -import { useNetworkSupportsV2 } from 'hooks/useNetworkSupportsV2'; -import { Trace } from 'analytics'; -import { StyledInternalLink, ThemedText } from 'theme/components'; -import { ButtonDropdownLight } from '../../components/Button'; -import { LightCard, BlueCard } from '../../components/Card'; -import { AutoColumn, ColumnCenter } from '../../components/Column'; -import CurrencyLogo from '../../components/Logo/CurrencyLogo'; -import { FindPoolTabs } from '../../components/NavigationTabs'; -import { MinimalPositionCard } from '../../components/PositionCard'; -import Row from '../../components/Row'; -import CurrencySearchModal from '../../components/SearchModal/CurrencySearchModal'; -import { SwitchLocaleLink } from '../../components/SwitchLocaleLink'; -import { nativeOnChain } from '../../constants/tokens'; -import { PairState, useV2Pair } from '../../hooks/useV2Pairs'; -import { useTokenBalance } from '../../state/connection/hooks'; -import { usePairAdder } from '../../state/user/hooks'; -import { currencyId } from '../../utils/currencyId'; -import AppBody from '../AppBody'; -import { Dots } from '../Pool/styled'; +import { Trans } from '@lingui/macro' +import { InterfacePageName } from '@uniswap/analytics-events' +import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core' +import { useWeb3React } from '@web3-react/core' +import JSBI from 'jsbi' +import { useCallback, useEffect, useState } from 'react' +import { Plus } from 'react-feather' +import { useLocation } from 'react-router' +import { Text } from 'rebass' + +import { V2Unsupported } from 'components/V2Unsupported' +import { useNetworkSupportsV2 } from 'hooks/useNetworkSupportsV2' +import { Trace } from 'analytics' +import { StyledInternalLink, ThemedText } from 'theme/components' +import { ButtonDropdownLight } from '../../components/Button' +import { LightCard, BlueCard } from '../../components/Card' +import { AutoColumn, ColumnCenter } from '../../components/Column' +import CurrencyLogo from '../../components/Logo/CurrencyLogo' +import { FindPoolTabs } from '../../components/NavigationTabs' +import { MinimalPositionCard } from '../../components/PositionCard' +import Row from '../../components/Row' +import CurrencySearchModal from '../../components/SearchModal/CurrencySearchModal' +import { SwitchLocaleLink } from '../../components/SwitchLocaleLink' +import { nativeOnChain } from '../../constants/tokens' +import { PairState, useV2Pair } from '../../hooks/useV2Pairs' +import { useTokenBalance } from '../../state/connection/hooks' +import { usePairAdder } from '../../state/user/hooks' +import { currencyId } from '../../utils/currencyId' +import AppBody from '../AppBody' +import { Dots } from '../Pool/styled' enum Fields { TOKEN0 = 0, @@ -35,53 +35,54 @@ enum Fields { } function useQuery() { - return new URLSearchParams(useLocation().search); + return new URLSearchParams(useLocation().search) } export default function PoolFinder() { - const query = useQuery(); + const query = useQuery() - const { account, chainId } = useWeb3React(); + const { account, chainId } = useWeb3React() - const [showSearch, setShowSearch] = useState(false); - const [activeField, setActiveField] = useState(Fields.TOKEN1); + const [showSearch, setShowSearch] = useState(false) + const [activeField, setActiveField] = useState(Fields.TOKEN1) - const [currency0, setCurrency0] = useState(() => (chainId ? nativeOnChain(chainId) : null)); - const [currency1, setCurrency1] = useState(null); + const [currency0, setCurrency0] = useState(() => (chainId ? nativeOnChain(chainId) : null)) + const [currency1, setCurrency1] = useState(null) - const [pairState, pair] = useV2Pair(currency0 ?? undefined, currency1 ?? undefined); - const addPair = usePairAdder(); + const [pairState, pair] = useV2Pair(currency0 ?? undefined, currency1 ?? undefined) + const addPair = usePairAdder() useEffect(() => { if (pair) { - addPair(pair); + addPair(pair) } - }, [pair, addPair]); + }, [pair, addPair]) - const validPairNoLiquidity: boolean = pairState === PairState.NOT_EXISTS - || Boolean( - pairState === PairState.EXISTS - && pair - && JSBI.equal(pair.reserve0.quotient, JSBI.BigInt(0)) - && JSBI.equal(pair.reserve1.quotient, JSBI.BigInt(0)), - ); + const validPairNoLiquidity: boolean = + pairState === PairState.NOT_EXISTS || + Boolean( + pairState === PairState.EXISTS && + pair && + JSBI.equal(pair.reserve0.quotient, JSBI.BigInt(0)) && + JSBI.equal(pair.reserve1.quotient, JSBI.BigInt(0)) + ) - const position: CurrencyAmount | undefined = useTokenBalance(account ?? undefined, pair?.liquidityToken); - const hasPosition = Boolean(position && JSBI.greaterThan(position.quotient, JSBI.BigInt(0))); + const position: CurrencyAmount | undefined = useTokenBalance(account ?? undefined, pair?.liquidityToken) + const hasPosition = Boolean(position && JSBI.greaterThan(position.quotient, JSBI.BigInt(0))) const handleCurrencySelect = useCallback( (currency: Currency) => { if (activeField === Fields.TOKEN0) { - setCurrency0(currency); + setCurrency0(currency) } else { - setCurrency1(currency); + setCurrency1(currency) } }, - [activeField], - ); + [activeField] + ) const handleSearchDismiss = useCallback(() => { - setShowSearch(false); - }, [setShowSearch]); + setShowSearch(false) + }, [setShowSearch]) const prerequisiteMessage = ( @@ -93,10 +94,12 @@ export default function PoolFinder() { )} - ); + ) - const networkSupportsV2 = useNetworkSupportsV2(); - if (!networkSupportsV2) { return ; } + const networkSupportsV2 = useNetworkSupportsV2() + if (!networkSupportsV2) { + return + } return ( @@ -115,8 +118,8 @@ export default function PoolFinder() { { - setShowSearch(true); - setActiveField(Fields.TOKEN0); + setShowSearch(true) + setActiveField(Fields.TOKEN0) }} > {currency0 ? ( @@ -127,7 +130,7 @@ export default function PoolFinder() { ) : ( - + Select a token )} @@ -139,8 +142,8 @@ export default function PoolFinder() { { - setShowSearch(true); - setActiveField(Fields.TOKEN1); + setShowSearch(true) + setActiveField(Fields.TOKEN1) }} > {currency1 ? ( @@ -151,7 +154,7 @@ export default function PoolFinder() { ) : ( - + Select a token )} @@ -235,5 +238,5 @@ export default function PoolFinder() { - ); + ) } diff --git a/src/pages/Swap/UniswapXOptIn.tsx b/src/pages/Swap/UniswapXOptIn.tsx deleted file mode 100644 index 5d9ce35e..00000000 --- a/src/pages/Swap/UniswapXOptIn.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import { Trans } from '@lingui/macro'; -import { PropsWithChildren, useRef, useState } from 'react'; -import { X } from 'react-feather'; -import { useLocation } from 'react-router-dom'; -import { Text } from 'rebass'; -import styled from 'styled-components'; - -import { sendAnalyticsEvent, Trace } from 'analytics'; -import Column from 'components/Column'; -import UniswapXBrandMark from 'components/Logo/UniswapXBrandMark'; -import { Arrow } from 'components/Popover'; -import UniswapXRouterLabel from 'components/RouterLabel/UniswapXRouterLabel'; -import Row from 'components/Row'; -import { SwapMustache, - SwapMustacheShadow, - SwapOptInSmallContainer, - UniswapPopoverContainer, - UniswapXOptInLargeContainer, - UniswapXOptInLargeContainerPositioner, - UniswapXShine } from 'components/swap/styled'; -import { formatCommonPropertiesForTrade } from 'lib/utils/analytics'; -import { useAppDispatch } from 'state/hooks'; -import { RouterPreference } from 'state/routing/types'; -import { isClassicTrade } from 'state/routing/utils'; -import { SwapInfo } from 'state/swap/hooks'; -import { useRouterPreference, useUserDisabledUniswapX } from 'state/user/hooks'; -import { updateDisabledUniswapX } from 'state/user/reducer'; -import { ThemedText } from 'theme/components'; - -export const UniswapXOptIn = (props: { swapInfo: SwapInfo; isSmall: boolean }) => { - const { trade: { trade }, - allowedSlippage } = props.swapInfo; - const userDisabledUniswapX = useUserDisabledUniswapX(); - const isOnClassic = Boolean(trade && isClassicTrade(trade) && trade.isUniswapXBetter && !userDisabledUniswapX); - const [hasEverShown, setHasEverShown] = useState(false); - - if (isOnClassic && !hasEverShown) { - setHasEverShown(true); - } - - // avoid some work if never needed to show - if (!hasEverShown) { - return null; - } - - return ( - - - - ); -}; - -const OptInContents = ({ swapInfo, - isOnClassic, - isSmall }: { - swapInfo: SwapInfo - isOnClassic: boolean - isSmall: boolean -}) => { - const { trade: { trade }, - allowedSlippage } = swapInfo; - const [, setRouterPreference] = useRouterPreference(); - const dispatch = useAppDispatch(); - const [showYoureIn, setShowYoureIn] = useState(false); - const isVisible = isOnClassic; - const location = useLocation(); - - const tryItNowElement = ( - { - // slight delay before hiding - setTimeout(() => { - setShowYoureIn(true); - setTimeout(() => { - setShowYoureIn(false); - }, 5000); - }, 200); - - if (!trade) { return; } - sendAnalyticsEvent('UniswapX Opt In Toggled', { - ...formatCommonPropertiesForTrade(trade, allowedSlippage), - new_preference: RouterPreference.X, - }); - setRouterPreference(RouterPreference.X); - }} - style={{ - cursor: 'pointer', - }} - > - Try it now - - ); - - const containerRef = useRef(); - - if (isSmall || location.pathname.includes('/tokens/')) { - return ( - - - - - - - Try gas free swaps with the -
- {' '} - Beta -
- {tryItNowElement} -
-
-
- ); - } - - return ( - <> - {/* first popover: intro */} - - { - if (!trade) { return; } - sendAnalyticsEvent('UniswapX Opt In Toggled', { - ...formatCommonPropertiesForTrade(trade, allowedSlippage), - new_preference: RouterPreference.API, - }); - setRouterPreference(RouterPreference.API); - dispatch(updateDisabledUniswapX({ disabledUniswapX: true })); - }} - /> - - - - Try the{' '} - {' '} - Beta -
    -
  • - Gas free swaps -
  • -
  • - MEV protection -
  • -
  • - Better prices and more liquidity -
  • -
-
-
- - {tryItNowElement} -
- - {/* second popover: you're in! */} - - - - You're in! - - - - - You can turn it off at anytime in settings - - - - ); -}; - -const UniswapXOptInPopover = (props: PropsWithChildren<{ visible: boolean; shiny?: boolean }>) => ( - // positioner ensures no matter the height of the inner content - // it sits at the same position from the top of the swap area - - - - - {props.shiny && } - {props.children} - - - -); - -const CloseIcon = styled(X)` - color: ${({ theme }) => theme.neutral3}; - cursor: pointer; - position: absolute; - top: 14px; - right: 14px; -`; diff --git a/src/pages/Swap/index.tsx b/src/pages/Swap/index.tsx index 497704b0..b57a329c 100644 --- a/src/pages/Swap/index.tsx +++ b/src/pages/Swap/index.tsx @@ -1,11 +1,6 @@ +/* eslint-disable no-nested-ternary */ + import { Trans } from '@lingui/macro'; -import { BrowserEvent, - InterfaceElementName, - InterfaceEventName, - InterfacePageName, - InterfaceSectionName, - SharedEventName, - SwapEventName } from '@uniswap/analytics-events'; import { ChainId, Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'; import { UNIVERSAL_ROUTER_ADDRESS } from '@uniswap/universal-router-sdk'; import { useWeb3React } from '@web3-react/core'; @@ -15,15 +10,20 @@ import { ArrowDown } from 'react-feather'; import { useLocation, useNavigate } from 'react-router-dom'; import { Text } from 'rebass'; import styled, { useTheme } from 'styled-components'; +import { InterfaceSectionName } from '@uniswap/analytics-events'; -import { sendAnalyticsEvent, Trace, TraceEvent, useTrace } from 'analytics'; import { useToggleAccountDrawer } from 'components/AccountDrawer'; import AddressInputPanel from 'components/AddressInputPanel'; -import { ButtonError, ButtonLight, ButtonPrimary } from 'components/Button'; +import { ButtonEmphasis, + ButtonError, + ButtonGray, + ButtonLight, + ButtonPrimary, + ButtonSize, + ThemeButton } from 'components/Button'; import { GrayCard } from 'components/Card'; import { AutoColumn } from 'components/Column'; import SwapCurrencyInputPanel from 'components/CurrencyInputPanel/SwapCurrencyInputPanel'; -import { NetworkAlert } from 'components/NetworkAlert/NetworkAlert'; import { AutoRow } from 'components/Row'; import confirmPriceImpactWithoutFee from 'components/swap/confirmPriceImpactWithoutFee'; import ConfirmSwapModal from 'components/swap/ConfirmSwapModal'; @@ -62,9 +62,7 @@ import { maxAmountSpend } from 'utils/maxAmountSpend'; import { computeRealizedPriceImpact, warningSeverity } from 'utils/prices'; import { didUserReject } from 'utils/swapErrorToUserReadableMessage'; import { useScreenSize } from '../../hooks/useScreenSize'; -import { useIsDarkMode } from '../../theme/components/ThemeToggle'; import { OutputTaxTooltipBody } from './TaxTooltipBody'; -import { UniswapXOptIn } from './UniswapXOptIn'; export const ArrowContainer = styled.div` display: inline-flex; @@ -76,16 +74,17 @@ export const ArrowContainer = styled.div` `; const SwapSection = styled.div` - background-color: ${({ theme }) => theme.surface2}; - border-radius: 16px; + background-color: ${({ theme }) => theme.surface4}; + border-radius: 8px; color: ${({ theme }) => theme.neutral2}; + box-shadow: 0px 0.76977px 30.79088px 0px rgba(227, 222, 255, 0.20) inset, 0px 3.07909px 13.8559px 0px rgba(154, 146, 210, 0.30) inset, 0px 75.43767px 76.9772px -36.94907px rgba(202, 172, 255, 0.30) inset, 0px -63.12132px 52.3445px -49.26542px rgba(96, 68, 144, 0.30) inset; font-size: 14px; font-weight: 500; - height: 120px; + //height: 120px; line-height: 20px; padding: 16px; position: relative; - + &:before { box-sizing: border-box; background-size: 100%; @@ -101,14 +100,6 @@ const SwapSection = styled.div` content: ''; border: 1px solid ${({ theme }) => theme.surface2}; } - - &:hover:before { - border-color: ${({ theme }) => theme.deprecated_stateOverlayHover}; - } - - &:focus-within:before { - border-color: ${({ theme }) => theme.deprecated_stateOverlayPressed}; - } `; const OutputSwapSection = styled(SwapSection)` @@ -142,24 +133,18 @@ export default function SwapPage({ className }: { className?: string }) { const { chainId: connectedChainId } = useWeb3React(); const loadedUrlParams = useDefaultsFromURLSearch(); - const location = useLocation(); - const supportedChainId = asSupportedChain(connectedChainId); return ( - - - - - - {location.pathname === '/swap' && } - + + + ); } @@ -185,7 +170,6 @@ export function Swap({ className, }) { const connectionReady = useConnectionReady(); const { account, chainId: connectedChainId, connector } = useWeb3React(); - const trace = useTrace(); // token warning stuff const prefilledInputCurrency = useCurrency(initialInputCurrencyId, chainId); @@ -358,13 +342,13 @@ export function Swap({ className, (value: string) => { onUserInput(Field.INPUT, value); }, - [onUserInput, trace], + [onUserInput], ); const handleTypeOutput = useCallback( (value: string) => { onUserInput(Field.OUTPUT, value); }, - [onUserInput, trace], + [onUserInput], ); const navigate = useNavigate(); @@ -485,13 +469,6 @@ export function Swap({ className, })); onUserInput(Field.INPUT, ''); } catch (error) { - if (!didUserReject(error)) { - sendAnalyticsEvent(SwapEventName.SWAP_ERROR, { - wrapType, - input: currencies[Field.INPUT], - output: currencies[Field.OUTPUT], - }); - } console.error('Could not wrap/unwrap', error); setSwapState((currentState) => ({ ...currentState, @@ -508,8 +485,8 @@ export function Swap({ className, } const marketPriceImpact = trade?.priceImpact ? computeRealizedPriceImpact(trade) : undefined; - const largerPriceImpact = largerPercentValue(marketPriceImpact, preTaxStablecoinPriceImpact); - return { priceImpactSeverity: warningSeverity(largerPriceImpact), largerPriceImpact }; + const newLargerPriceImpact = largerPercentValue(marketPriceImpact, preTaxStablecoinPriceImpact); + return { priceImpactSeverity: warningSeverity(newLargerPriceImpact), newLargerPriceImpact }; }, [preTaxStablecoinPriceImpact, trade]); const handleConfirmDismiss = useCallback(() => { @@ -534,13 +511,14 @@ export function Swap({ className, [Field.OUTPUT]: state[Field.OUTPUT], }); }, - [onCurrencyChange, onCurrencySelection, state, trace], + [onCurrencyChange, onCurrencySelection, state], ); const inputCurrencyNumericalInputRef = useRef(null); const handleMaxInput = useCallback(() => { - maxInputAmount && onUserInput(Field.INPUT, maxInputAmount.toExact()); - }, [maxInputAmount, onUserInput, trace]); + if (!maxInputAmount) { return; } + onUserInput(Field.INPUT, maxInputAmount.toExact()); + }, [maxInputAmount, onUserInput]); const handleOutputSelect = useCallback( (outputCurrency: Currency) => { @@ -552,41 +530,24 @@ export function Swap({ className, }, }); }, - [onCurrencyChange, onCurrencySelection, state, trace], + [onCurrencyChange, onCurrencySelection, state], ); const showPriceImpactWarning = isClassicTrade(trade) && largerPriceImpact && priceImpactSeverity > 3; const prevTrade = usePrevious(trade); useEffect(() => { - if (!trade || prevTrade === trade) { return; } // no new swap quote to log - - sendAnalyticsEvent(SwapEventName.SWAP_QUOTE_RECEIVED, { - ...formatSwapQuoteReceivedEventProperties( - trade, - allowedSlippage, - swapQuoteLatency, - inputTax, - outputTax, - outputFeeFiatValue, - ), - ...trace, - }); - }, [prevTrade, trade, trace, allowedSlippage, swapQuoteLatency, inputTax, outputTax, outputFeeFiatValue]); + if (!trade || prevTrade === trade) { } // no new swap quote to log + }, [prevTrade, trade, allowedSlippage, swapQuoteLatency, inputTax, outputTax, outputFeeFiatValue]); const showDetailsDropdown = Boolean( !showWrap && userHasSpecifiedInputOutput && (trade || routeIsLoading || routeIsSyncing), ); const inputCurrency = currencies[Field.INPUT] ?? undefined; - const switchChain = useSwitchChain(); - const switchingChain = useAppSelector((state) => state.wallets.switchingChain); - const showOptInSmall = !useScreenSize().navSearchInputVisible; - const isDark = useIsDarkMode(); - const isUniswapXDefaultEnabled = useUniswapXDefaultEnabled(); const swapElement = ( - + 0 && !dismissTokenWarning} tokenAddress={importTokensNotInDefault[0]?.address} @@ -627,72 +588,62 @@ export function Swap({ className,
- - You pay} - disabled={disableTokenInputs} - value={formattedAmounts[Field.INPUT]} - showMaxButton={showMaxButton} - currency={currencies[Field.INPUT] ?? null} - onUserInput={handleTypeInput} - onMax={handleMaxInput} - fiatValue={showFiatValueInput ? fiatValueInput : undefined} - onCurrencySelect={handleInputSelect} - otherCurrency={currencies[Field.OUTPUT]} - showCommonBases - id={InterfaceSectionName.CURRENCY_INPUT_PANEL} - loading={independentField === Field.OUTPUT && routeIsSyncing} - ref={inputCurrencyNumericalInputRef} - /> - + From} + disabled={disableTokenInputs} + value={formattedAmounts[Field.INPUT]} + showMaxButton={showMaxButton} + currency={currencies[Field.INPUT] ?? null} + onUserInput={handleTypeInput} + onMax={handleMaxInput} + fiatValue={showFiatValueInput ? fiatValueInput : undefined} + onCurrencySelect={handleInputSelect} + otherCurrency={currencies[Field.OUTPUT]} + showCommonBases + id={InterfaceSectionName.CURRENCY_INPUT_PANEL} + loading={independentField === Field.OUTPUT && routeIsSyncing} + ref={inputCurrencyNumericalInputRef} + /> - { + if (disableTokenInputs) { return; } + onSwitchTokens(inputTokenHasTax, formattedAmounts[dependentField]); + }} + color={theme.neutral1} > - { - if (disableTokenInputs) { return; } - onSwitchTokens(inputTokenHasTax, formattedAmounts[dependentField]); - }} - color={theme.neutral1} - > - - - + +
- +
- - You receive} - showMaxButton={false} - hideBalance={false} - fiatValue={showFiatValueOutput ? fiatValueOutput : undefined} - priceImpact={stablecoinPriceImpact} - currency={currencies[Field.OUTPUT] ?? null} - onCurrencySelect={handleOutputSelect} - otherCurrency={currencies[Field.INPUT]} - showCommonBases - id={InterfaceSectionName.CURRENCY_OUTPUT_PANEL} - loading={independentField === Field.INPUT && routeIsSyncing} - numericalInputSettings={{ - // We disable numerical input here if the selected token has tax, since we cannot guarantee exact_outputs for FOT tokens - disabled: outputTokenHasTax, - // Focus the input currency panel if the user tries to type into the disabled output currency panel - onDisabledClick: () => inputCurrencyNumericalInputRef.current?.focus(), - disabledTooltipBody: , - }} - /> - + To} + showMaxButton={false} + hideBalance={false} + fiatValue={showFiatValueOutput ? fiatValueOutput : undefined} + priceImpact={stablecoinPriceImpact} + currency={currencies[Field.OUTPUT] ?? null} + onCurrencySelect={handleOutputSelect} + otherCurrency={currencies[Field.INPUT]} + showCommonBases + id={InterfaceSectionName.CURRENCY_OUTPUT_PANEL} + loading={independentField === Field.INPUT && routeIsSyncing} + numericalInputSettings={{ + // We disable numerical input here if the selected token has tax, since we cannot guarantee exact_outputs for FOT tokens + disabled: outputTokenHasTax, + // Focus the input currency panel if the user tries to type into the disabled output currency panel + onDisabledClick: () => inputCurrencyNumericalInputRef.current?.focus(), + disabledTooltipBody: , + }} + /> {recipient !== null && !showWrap ? ( <> @@ -708,114 +659,61 @@ export function Swap({ className, ) : null}
- {showDetailsDropdown && ( - - )} - {showPriceImpactWarning && }
{swapIsUnsupported ? ( - - - Unsupported asset - - - ) : switchingChain ? ( - - Connecting to {getChainInfo(switchingChain)?.label} + + Unsupported asset ) : connectionReady && !account ? ( - - - Connect wallet - - - ) : chainId && chainId !== connectedChainId ? ( - { - try { - await switchChain(connector, chainId); - } catch (error) { - if (didUserReject(error)) { - // Ignore error, which keeps the user on the previous chain. - } else { - // TODO(WEB-3306): This UX could be improved to show an error state. - throw error; - } - } - }} - > - Connect to {getChainInfo(chainId)?.label} - - ) : showWrap ? ( - - {wrapInputError ? ( - - ) : wrapType === WrapType.WRAP ? ( - Wrap - ) : wrapType === WrapType.UNWRAP ? ( - Unwrap - ) : null} + + Connect wallet ) : routeNotFound && userHasSpecifiedInputOutput && !routeIsLoading && !routeIsSyncing ? ( - - - Insufficient liquidity for this trade. - - + + Insufficient liquidity + + ) : routeIsSyncing || routeIsLoading ? ( + + Fetching... + ) : ( - { + if (!showPriceImpactWarning) { + handleContinueToReview(); + return; + } + setShowPriceImpactModal(true); + }} + id="swap-button" + data-testid="swap-button" + size={ButtonSize.large} + disabled={!getIsReviewableQuote(trade, tradeState, swapInputError)} + error={!swapInputError && priceImpactSeverity > 2 && allowance.state === AllowanceState.ALLOWED} > - { - showPriceImpactWarning ? setShowPriceImpactModal(true) : handleContinueToReview(); - }} - id="swap-button" - data-testid="swap-button" - disabled={!getIsReviewableQuote(trade, tradeState, swapInputError)} - error={!swapInputError && priceImpactSeverity > 2 && allowance.state === AllowanceState.ALLOWED} - > - - {swapInputError || (routeIsSyncing || routeIsLoading ? ( - Swap - ) : priceImpactSeverity > 2 ? ( - Swap anyway - ) : ( - Swap - ))} - - - + {swapInputError || (routeIsSyncing || routeIsLoading ? (Swap) : priceImpactSeverity > 2 ? ( + Swap anyway + ) : ( + Swap + ))} + )}
+ + {showDetailsDropdown && ( + + )} + {showPriceImpactWarning && }
- {!showOptInSmall && !isUniswapXDefaultEnabled && }
); return ( - <> - {swapElement} - {showOptInSmall && !isUniswapXDefaultEnabled && } - + swapElement ); } diff --git a/src/theme/colors.ts b/src/theme/colors.ts index a96dca0c..df80a3cd 100644 --- a/src/theme/colors.ts +++ b/src/theme/colors.ts @@ -1,14 +1,20 @@ // Based mostly on https://github.com/Uniswap/interface/blob/main/src/theme/index.tsx -const jediBlue = '#50D5FF'; -const jediPink = '#FF00E9'; -const jediWhite = '#fff'; -const jediGrey = '#959595'; -const jediNavyBlue = '#141451'; +const jediBlue = '#50D5FF' +const jediPink = '#FF00E9' +const jediWhite = '#fff' +const jediGrey = '#959595' +const jediNavyBlue = '#141451' +const signalGreen = '#21E70F' +const jediGreyBorder = '#444' +const signalRed = '#FC4D4D' export const colors = { white: '#FFFFFF', black: '#000000', + + greyLight: '#F2F2F2', + gray50: '#F5F6FC', gray100: '#E8ECFB', gray150: '#D2D9EE', @@ -65,6 +71,8 @@ export const colors = { gold200: '#EEB317', gold400: '#B17900', goldVibrant: '#FEB239', + + green1: signalGreen, green50: '#E3F3E6', green100: '#BFEECA', green200: '#76D191', @@ -106,19 +114,20 @@ export const colors = { // NEW COLORS FOR SPORE - need to define light/dark here cause they are root colors now (different system) neutral1_dark: '#FFFFFF', - neutral2_dark: '#9B9B9B', - neutral3_dark: '#5E5E5E', + neutral2_dark: '#959595', + neutral3_dark: jediGrey, - surface1_dark: '#212429', + surface1_dark: jediNavyBlue, surface2_dark: '#2C2F36', surface3_dark: '#40444F', - surface4_dark: '#565A69', + surface4_dark: '#C4C4C403', surface5_dark: '#ffffff26', + surface6_dark: '#323C5C', accent1_dark: jediBlue, accent2_dark: jediPink, - neutral1_light: '#222222', + neutral1_light: jediWhite, neutral2_light: '#7D7D7D', neutral3_light: '#CECECE', @@ -127,13 +136,15 @@ export const colors = { surface3_light: '#22222212', surface4_light: '#FFFFFF64', surface5_light: '#00000004', + surface6_light: '#00000004', - accent1_light: '#FC72FF', + accent1_light: jediBlue, accent2_light: '#FFEFFF', success: '#40B66B', - critical: '#FF5F52', + critical: '#FF3257', scrim: 'rgba(0, 0, 0, 0.60)', -}; + divider: 'rgba(255, 255, 255, 0.40)', +} type Theme = typeof darkTheme @@ -161,17 +172,22 @@ const commonTheme = { chain_84531: colors.networkBase, chain_56_background: colors.networkBsc, promotional: colors.magenta300, + notice: colors.greyLight, brandedGradient: 'linear-gradient(95.64deg, #29aafd 8.08%, #ff00e9 105.91%)', brandedGradientReversed: 'linear-gradient(95.64deg, #ff00e9 8.08%, #29aafd 105.91%)', promotionalGradient: 'linear-gradient(95.64deg, #ff00e9 8.08%, #29aafd 105.91%)', + bgdGradient: 'linear-gradient(180deg, #03001E 0%, #2C045C 46.35%, #930BD3 100%)', jediBlue, jediPink, jediWhite, jediGrey, jediNavyBlue, -}; + signalGreen, + jediGreyBorder, + signalRed, +} export const darkTheme = { ...commonTheme, @@ -187,33 +203,12 @@ export const darkTheme = { surface3: colors.surface3_dark, surface4: colors.surface4_dark, surface5: colors.surface5_dark, + surface6: colors.surface6_dark, accent1: colors.accent1_dark, accent2: colors.accent2_dark, success: colors.success, critical: colors.critical, scrim: colors.scrim, -}; - -export const lightTheme: Theme = { - ...commonTheme, - - background: colors.white, - - neutral1: colors.neutral1_light, - neutral2: colors.neutral2_light, - neutral3: colors.neutral3_light, - - surface1: colors.surface1_light, - surface2: colors.surface2_light, - surface3: colors.surface3_light, - surface4: colors.surface4_light, - surface5: colors.surface5_light, - - accent1: colors.accent1_light, - accent2: colors.accent2_light, - - success: colors.success, - critical: colors.critical, - scrim: colors.scrim, -}; + divider: colors.divider, +} diff --git a/src/theme/components/DarkModeQueryParamReader.tsx b/src/theme/components/DarkModeQueryParamReader.tsx deleted file mode 100644 index 9bdf6c4a..00000000 --- a/src/theme/components/DarkModeQueryParamReader.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { parse } from 'qs' -import { useEffect } from 'react' -import { useLocation } from 'react-router-dom' - -import { ThemeMode, useDarkModeManager } from './ThemeToggle' - -export default function DarkModeQueryParamReader(): null { - const { search } = useLocation() - const [, updateMode] = useDarkModeManager() - - useEffect(() => { - if (!search) return - if (search.length < 2) return - - const parsed = parse(search, { - parseArrays: false, - ignoreQueryPrefix: true, - }) - - const theme = parsed.theme - - if (typeof theme !== 'string') return - - if (theme.toLowerCase() === 'light') { - updateMode(ThemeMode.LIGHT) - } else if (theme.toLowerCase() === 'dark') { - updateMode(ThemeMode.DARK) - } - }, [search, updateMode]) - - return null -} diff --git a/src/theme/components/RadialGradientByChainUpdater.ts b/src/theme/components/RadialGradientByChainUpdater.ts index 02f8e995..b45760fd 100644 --- a/src/theme/components/RadialGradientByChainUpdater.ts +++ b/src/theme/components/RadialGradientByChainUpdater.ts @@ -1,129 +1,111 @@ -import { ChainId } from '@uniswap/sdk-core' -import { useWeb3React } from '@web3-react/core' -import { useIsNftPage } from 'hooks/useIsNftPage' -import { useEffect } from 'react' -import { useDarkModeManager } from 'theme/components/ThemeToggle' +import { ChainId } from '@uniswap/sdk-core'; +import { useWeb3React } from '@web3-react/core'; +import { useEffect } from 'react'; -import { darkTheme, lightTheme } from '../colors' +import { useIsNftPage } from 'hooks/useIsNftPage'; +import { darkTheme } from '../colors'; const initialStyles = { width: '200vw', height: '200vh', transform: 'translate(-50vw, -100vh)', -} +}; const backgroundResetStyles = { width: '100vw', height: '100vh', transform: 'unset', -} +}; type TargetBackgroundStyles = typeof initialStyles | typeof backgroundResetStyles -const backgroundRadialGradientElement = document.getElementById('background-radial-gradient') -const setBackground = (newValues: TargetBackgroundStyles) => - Object.entries(newValues).forEach(([key, value]) => { - if (backgroundRadialGradientElement) { - backgroundRadialGradientElement.style[key as keyof typeof backgroundResetStyles] = value - } - }) +const backgroundRadialGradientElement = document.getElementById('background-radial-gradient'); +const setBackground = (newValues: TargetBackgroundStyles) => Object.entries(newValues).forEach(([key, value]) => { + if (backgroundRadialGradientElement) { + backgroundRadialGradientElement.style[key as keyof typeof backgroundResetStyles] = value; + } +}); -function setDefaultBackground(backgroundRadialGradientElement: HTMLElement, darkMode?: boolean) { - setBackground(initialStyles) - const defaultLightGradient = - 'radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF' - const defaultDarkGradient = 'linear-gradient(180deg, #131313 0%, #131313 100%)' - backgroundRadialGradientElement.style.background = darkMode ? defaultDarkGradient : defaultLightGradient +function setDefaultBackground(backgroundRadialGradientElement: HTMLElement) { + setBackground(initialStyles); + const defaultLightGradient = 'radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF'; + const defaultDarkGradient = 'linear-gradient(180deg, #131313 0%, #131313 100%)'; + backgroundRadialGradientElement.style.background = defaultDarkGradient; } export default function RadialGradientByChainUpdater(): null { - const { chainId } = useWeb3React() - const [darkMode] = useDarkModeManager() - const isNftPage = useIsNftPage() + const { chainId } = useWeb3React(); + const isNftPage = useIsNftPage(); // manage background color useEffect(() => { if (!backgroundRadialGradientElement) { - return + return; } if (isNftPage) { - setBackground(initialStyles) - backgroundRadialGradientElement.style.background = darkMode ? darkTheme.surface1 : lightTheme.surface1 - return + setBackground(initialStyles); + backgroundRadialGradientElement.style.background = darkTheme.surface1; + return; } switch (chainId) { case ChainId.ARBITRUM_ONE: case ChainId.ARBITRUM_GOERLI: { - setBackground(backgroundResetStyles) - const arbitrumLightGradient = - 'radial-gradient(100% 100% at 50% 0%, rgba(205, 232, 251, 0) 0%, rgba(252, 243, 249, 0) 49.48%, rgba(255, 255, 255, 0) 100%), #FFFFFF' - const arbitrumDarkGradient = - 'radial-gradient(100% 100% at 50% 0%, rgba(10, 41, 75, 0) 0%, rgba(34, 30, 48, 0) 49.48%, rgba(31, 33, 40, 0) 100%), #0D0E0E' - backgroundRadialGradientElement.style.background = darkMode ? arbitrumDarkGradient : arbitrumLightGradient - break + setBackground(backgroundResetStyles); + const arbitrumLightGradient = 'radial-gradient(100% 100% at 50% 0%, rgba(205, 232, 251, 0) 0%, rgba(252, 243, 249, 0) 49.48%, rgba(255, 255, 255, 0) 100%), #FFFFFF'; + const arbitrumDarkGradient = 'radial-gradient(100% 100% at 50% 0%, rgba(10, 41, 75, 0) 0%, rgba(34, 30, 48, 0) 49.48%, rgba(31, 33, 40, 0) 100%), #0D0E0E'; + backgroundRadialGradientElement.style.background = arbitrumDarkGradient; + break; } case ChainId.OPTIMISM: case ChainId.OPTIMISM_GOERLI: { - setBackground(backgroundResetStyles) - const optimismLightGradient = - 'radial-gradient(100% 100% at 50% 0%, rgba(255, 251, 242, 0) 0%, rgba(255, 244, 249, 0) 50.52%, rgba(255, 255, 255, 0) 100%), #FFFFFF' - const optimismDarkGradient = - 'radial-gradient(100% 100% at 50% 0%, rgba(62, 46, 56, 0) 0%, rgba(44, 31, 45, 0) 50.52%, rgba(31, 33, 40, 0) 100%), #0D0E0E' - backgroundRadialGradientElement.style.background = darkMode ? optimismDarkGradient : optimismLightGradient - break + setBackground(backgroundResetStyles); + const optimismLightGradient = 'radial-gradient(100% 100% at 50% 0%, rgba(255, 251, 242, 0) 0%, rgba(255, 244, 249, 0) 50.52%, rgba(255, 255, 255, 0) 100%), #FFFFFF'; + const optimismDarkGradient = 'radial-gradient(100% 100% at 50% 0%, rgba(62, 46, 56, 0) 0%, rgba(44, 31, 45, 0) 50.52%, rgba(31, 33, 40, 0) 100%), #0D0E0E'; + backgroundRadialGradientElement.style.background = optimismDarkGradient; + break; } case ChainId.POLYGON: case ChainId.POLYGON_MUMBAI: { - setBackground(backgroundResetStyles) - const polygonLightGradient = - 'radial-gradient(100% 100% at 50% 0%, rgba(130, 71, 229, 0) 0%, rgba(200, 168, 255, 0.05) 52.6%, rgba(0, 0, 0, 0) 100%), #FFFFFF' - const polygonDarkGradient = - 'radial-gradient(100% 100% at 50% 0%, rgba(130, 71, 229, 0) 0%, rgba(200, 168, 255, 0.05) 52.6%, rgba(0, 0, 0, 0) 100%), #0D0E0E' - backgroundRadialGradientElement.style.background = darkMode ? polygonDarkGradient : polygonLightGradient - break + setBackground(backgroundResetStyles); + const polygonLightGradient = 'radial-gradient(100% 100% at 50% 0%, rgba(130, 71, 229, 0) 0%, rgba(200, 168, 255, 0.05) 52.6%, rgba(0, 0, 0, 0) 100%), #FFFFFF'; + const polygonDarkGradient = 'radial-gradient(100% 100% at 50% 0%, rgba(130, 71, 229, 0) 0%, rgba(200, 168, 255, 0.05) 52.6%, rgba(0, 0, 0, 0) 100%), #0D0E0E'; + backgroundRadialGradientElement.style.background = polygonDarkGradient; + break; } case ChainId.CELO: case ChainId.CELO_ALFAJORES: { - setBackground(backgroundResetStyles) - const celoLightGradient = - 'radial-gradient(100% 100% at 50% 0%, rgba(186, 228, 210, 0) 0%, rgba(252, 243, 249, 0) 49.48%, rgba(255, 255, 255, 0) 100%), #FFFFFF' - const celoDarkGradient = - 'radial-gradient(100% 100% at 50% 0%, rgba(20, 49, 37, 0) 0%, rgba(12, 31, 23, 0) 49.48%, rgba(31, 33, 40, 0) 100%, rgba(31, 33, 40, 0) 100%), #0D0E0E' - backgroundRadialGradientElement.style.background = darkMode ? celoDarkGradient : celoLightGradient - break + setBackground(backgroundResetStyles); + const celoLightGradient = 'radial-gradient(100% 100% at 50% 0%, rgba(186, 228, 210, 0) 0%, rgba(252, 243, 249, 0) 49.48%, rgba(255, 255, 255, 0) 100%), #FFFFFF'; + const celoDarkGradient = 'radial-gradient(100% 100% at 50% 0%, rgba(20, 49, 37, 0) 0%, rgba(12, 31, 23, 0) 49.48%, rgba(31, 33, 40, 0) 100%, rgba(31, 33, 40, 0) 100%), #0D0E0E'; + backgroundRadialGradientElement.style.background = celoDarkGradient; + break; } case ChainId.BNB: { - setBackground(backgroundResetStyles) - const bscLightGradient = - 'radial-gradient(100% 100% at 50% 0%, rgba(242 , 186, 8, 0) 0%, rgba(238, 182, 6, 0) 50%, rgba(140, 185, 11, 0) 100%), #FFFFFF' - const bscDarkGradient = - 'radial-gradient(100% 100% at 50% 0%, rgba(169, 132, 17, 0) 0%, rgba(128, 100, 14, 0) 50%, rgba(140, 185, 11, 0) 100%), #0D0E0E' - backgroundRadialGradientElement.style.background = darkMode ? bscDarkGradient : bscLightGradient - break + setBackground(backgroundResetStyles); + const bscLightGradient = 'radial-gradient(100% 100% at 50% 0%, rgba(242 , 186, 8, 0) 0%, rgba(238, 182, 6, 0) 50%, rgba(140, 185, 11, 0) 100%), #FFFFFF'; + const bscDarkGradient = 'radial-gradient(100% 100% at 50% 0%, rgba(169, 132, 17, 0) 0%, rgba(128, 100, 14, 0) 50%, rgba(140, 185, 11, 0) 100%), #0D0E0E'; + backgroundRadialGradientElement.style.background = bscDarkGradient; + break; } case ChainId.AVALANCHE: { - setBackground(backgroundResetStyles) - const avaxLightGradient = - 'radial-gradient(100% 100% at 50% 0%, rgba(255, 251, 242, 0) 0%, rgba(255, 244, 249, 0.0) 50.52%, rgba(255, 255, 255, 0) 100%), #FFFFFF' - const avaxDarkGradient = - 'radial-gradient(100% 100% at 50% 0%, rgba(62, 46, 56, 0) 0%, rgba(44, 31, 45, 0.0) 50.52%, rgba(31, 33, 40, 0) 100%), #0D0E0E' - backgroundRadialGradientElement.style.background = darkMode ? avaxDarkGradient : avaxLightGradient - break + setBackground(backgroundResetStyles); + const avaxLightGradient = 'radial-gradient(100% 100% at 50% 0%, rgba(255, 251, 242, 0) 0%, rgba(255, 244, 249, 0.0) 50.52%, rgba(255, 255, 255, 0) 100%), #FFFFFF'; + const avaxDarkGradient = 'radial-gradient(100% 100% at 50% 0%, rgba(62, 46, 56, 0) 0%, rgba(44, 31, 45, 0.0) 50.52%, rgba(31, 33, 40, 0) 100%), #0D0E0E'; + backgroundRadialGradientElement.style.background = avaxDarkGradient; + break; } case ChainId.BASE: { - setBackground(backgroundResetStyles) - const baseLightGradient = - 'radial-gradient(100% 100% at 50% 0%, rgba(0, 82, 255, 0) 0%, rgba(0, 82, 255, 0) 40.0%, rgba(252, 255, 82, 0.00) 100%), rgb(255, 255, 255)' - const baseDarkGradient = - 'radial-gradient(100% 100% at 50% 0%, rgba(10, 41, 75, 0) 0%, rgba(0, 82, 255, 0) 40%, rgba(0, 82, 255, 0) 100%), rgb(13, 14, 14)' - backgroundRadialGradientElement.style.background = darkMode ? baseDarkGradient : baseLightGradient - break + setBackground(backgroundResetStyles); + const baseLightGradient = 'radial-gradient(100% 100% at 50% 0%, rgba(0, 82, 255, 0) 0%, rgba(0, 82, 255, 0) 40.0%, rgba(252, 255, 82, 0.00) 100%), rgb(255, 255, 255)'; + const baseDarkGradient = 'radial-gradient(100% 100% at 50% 0%, rgba(10, 41, 75, 0) 0%, rgba(0, 82, 255, 0) 40%, rgba(0, 82, 255, 0) 100%), rgb(13, 14, 14)'; + backgroundRadialGradientElement.style.background = baseDarkGradient; + break; } default: { - setDefaultBackground(backgroundRadialGradientElement, darkMode) + setDefaultBackground(backgroundRadialGradientElement); } } - }, [darkMode, chainId, isNftPage]) - return null + }, [chainId, isNftPage]); + return null; } diff --git a/src/theme/components/ThemeToggle.tsx b/src/theme/components/ThemeToggle.tsx deleted file mode 100644 index ac542763..00000000 --- a/src/theme/components/ThemeToggle.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import { Trans } from '@lingui/macro' -import Row from 'components/Row' -import { atom, useAtom } from 'jotai' -import { atomWithStorage, useAtomValue, useUpdateAtom } from 'jotai/utils' -import ms from 'ms' -import { useCallback, useEffect, useMemo } from 'react' -import { Moon, Sun } from 'react-feather' -import { addMediaQueryListener, removeMediaQueryListener } from 'utils/matchMedia' - -import { Segment, SegmentedControl } from './SegmentedControl' -import { ThemedText } from './text' - -const THEME_UPDATE_DELAY = ms(`0.1s`) -const DARKMODE_MEDIA_QUERY = window.matchMedia('(prefers-color-scheme: dark)') - -export enum ThemeMode { - LIGHT, - DARK, - AUTO, -} - -// Tracks the device theme -const systemThemeAtom = atom( - DARKMODE_MEDIA_QUERY.matches ? ThemeMode.DARK : ThemeMode.LIGHT -) - -// Tracks the user's selected theme mode -const themeModeAtom = atomWithStorage('interface_color_theme', ThemeMode.AUTO) - -export function SystemThemeUpdater() { - const setSystemTheme = useUpdateAtom(systemThemeAtom) - - const listener = useCallback( - (event: MediaQueryListEvent) => { - setSystemTheme(event.matches ? ThemeMode.DARK : ThemeMode.LIGHT) - }, - [setSystemTheme] - ) - - useEffect(() => { - addMediaQueryListener(DARKMODE_MEDIA_QUERY, listener) - return () => removeMediaQueryListener(DARKMODE_MEDIA_QUERY, listener) - }, [setSystemTheme, listener]) - - return null -} - -export function ThemeColorMetaUpdater() { - const isDark = useIsDarkMode() - - useEffect(() => { - const meta = document.querySelector('meta[name=theme-color]') - if (!meta) return - - if (isDark) { - // this color comes from #background-radial-gradient - meta.setAttribute('content', 'rgb(19, 19, 19)') - } else { - meta.setAttribute('content', '#fff') - } - }, [isDark]) - - return null -} - -export function useIsDarkMode(): boolean { - const mode = useAtomValue(themeModeAtom) - const systemTheme = useAtomValue(systemThemeAtom) - - return (mode === ThemeMode.AUTO ? systemTheme : mode) === ThemeMode.DARK -} - -export function useDarkModeManager(): [boolean, (mode: ThemeMode) => void] { - const isDarkMode = useIsDarkMode() - const setMode = useUpdateAtom(themeModeAtom) - - return useMemo(() => { - return [isDarkMode, setMode] - }, [isDarkMode, setMode]) -} - -export default function ThemeToggle({ disabled }: { disabled?: boolean }) { - const [mode, setMode] = useAtom(themeModeAtom) - const switchMode = useCallback( - (mode: ThemeMode) => { - // Switch feels less jittery with short delay - !disabled && setTimeout(() => setMode(mode), THEME_UPDATE_DELAY) - }, - [disabled, setMode] - ) - - return ( - - - - Theme - - - - - - Auto - - - - - - - ) -} diff --git a/src/theme/components/index.tsx b/src/theme/components/index.tsx index 0323f453..7e03899c 100644 --- a/src/theme/components/index.tsx +++ b/src/theme/components/index.tsx @@ -1,33 +1,32 @@ -import { Trans } from '@lingui/macro' -import { outboundLink } from 'components/analytics' -import { MOBILE_MEDIA_BREAKPOINT } from 'components/Tokens/constants' -import useCopyClipboard from 'hooks/useCopyClipboard' -import React, { - forwardRef, +import { Trans } from '@lingui/macro'; +import React, { forwardRef, HTMLProps, PropsWithChildren, ReactNode, useCallback, useImperativeHandle, useRef, - useState, -} from 'react' -import { AlertTriangle, ArrowLeft, CheckCircle, Copy, Icon, X } from 'react-feather' -import { Link } from 'react-router-dom' -import styled, { css, keyframes } from 'styled-components' -import { Z_INDEX } from 'theme/zIndex' - -import { ReactComponent as TooltipTriangle } from '../../assets/svg/tooltip_triangle.svg' -import { anonymizeLink } from '../../utils/anonymizeLink' + useState } from 'react'; +import { AlertTriangle, ArrowLeft, CheckCircle, Copy, Icon, X } from 'react-feather'; +import { Link } from 'react-router-dom'; +import styled, { css, keyframes } from 'styled-components'; + +import useCopyClipboard from 'hooks/useCopyClipboard'; +import { MOBILE_MEDIA_BREAKPOINT } from 'components/Tokens/constants'; +import { outboundLink } from 'components/analytics'; +import { Z_INDEX } from 'theme/zIndex'; +import { ReactComponent as TooltipTriangle } from '../../assets/svg/tooltip_triangle.svg'; +import { anonymizeLink } from '../../utils/anonymizeLink'; +import { themeBorderGradient } from '../styles'; // TODO: Break this file into a components folder -export { ThemedText } from './text' +export { ThemedText } from './text'; export const CloseIcon = styled(X)<{ onClick: () => void }>` color: ${({ theme }) => theme.neutral1}; cursor: pointer; -` +`; // A button that triggers some onClick result, but looks like a link. export const LinkStyledButton = styled.button<{ disabled?: boolean }>` @@ -51,7 +50,7 @@ export const LinkStyledButton = styled.button<{ disabled?: boolean }>` :active { text-decoration: none; } -` +`; export const ButtonText = styled.button` outline: none; @@ -72,13 +71,13 @@ export const ButtonText = styled.button` :focus { text-decoration: underline; } -` +`; export const EllipsisStyle = css` white-space: nowrap; overflow: hidden; text-overflow: ellipsis; -` +`; export const ClickableStyle = css` text-decoration: none; @@ -91,32 +90,32 @@ export const ClickableStyle = css` :active { opacity: ${({ theme }) => theme.opacity.click}; } -` +`; const LinkStyle = css` color: ${({ theme }) => theme.accent1}; stroke: ${({ theme }) => theme.accent1}; font-weight: 500; -` +`; // An internal link from the react-router-dom library that is correctly styled export const StyledInternalLink = styled(Link)` ${ClickableStyle} ${LinkStyle} -` +`; const IconStyle = css` height: 16px; width: 18px; margin-left: 10px; -` +`; const CopyIcon = styled(Copy)` ${IconStyle} ${ClickableStyle} ${LinkStyle} stroke: ${({ theme }) => theme.accent1}; -` +`; const rotateImg = keyframes` 0% { @@ -126,69 +125,66 @@ const rotateImg = keyframes` 100% { transform: perspective(1000px) rotateY(360deg); } -` +`; export const UniTokenAnimated = styled.img` animation: ${rotateImg} 5s cubic-bezier(0.83, 0, 0.17, 1) infinite; padding: 2rem 0 0 0; filter: drop-shadow(0px 2px 4px rgba(0, 0, 0, 0.15)); -` +`; function handleClickExternalLink(event: React.MouseEvent) { - const { target, href } = event.currentTarget + const { target, href } = event.currentTarget; - const anonymizedHref = anonymizeLink(href) + const anonymizedHref = anonymizeLink(href); // don't prevent default, don't redirect if it's a new tab if (target === '_blank' || event.ctrlKey || event.metaKey) { - outboundLink({ label: anonymizedHref }) + outboundLink({ label: anonymizedHref }); } else { - event.preventDefault() + event.preventDefault(); // send a ReactGA event and then trigger a location change - outboundLink({ label: anonymizedHref }) + outboundLink({ label: anonymizedHref }); } } const StyledLink = styled.a` ${ClickableStyle} ${LinkStyle} -` +`; export const StyledRouterLink = styled(Link)` ${ClickableStyle} ${LinkStyle} -` +`; /** * Outbound link that handles firing google analytics events */ -export function ExternalLink({ - target = '_blank', +export function ExternalLink({ target = '_blank', href, rel = 'noopener noreferrer', - ...rest -}: Omit, 'as' | 'ref' | 'onClick'> & { href: string }) { - return + ...rest }: Omit, 'as' | 'ref' | 'onClick'> & { href: string }) { + return ; } -const TOOLTIP_WIDTH = 60 +const TOOLTIP_WIDTH = 60; const ToolTipWrapper = styled.div<{ isCopyContractTooltip?: boolean; tooltipX?: number }>` display: flex; flex-direction: column; align-items: center; position: absolute; - left: ${({ isCopyContractTooltip, tooltipX }) => - isCopyContractTooltip && (tooltipX ? `${tooltipX - TOOLTIP_WIDTH / 2}px` : '50%')}; + left: ${({ isCopyContractTooltip, tooltipX }) => isCopyContractTooltip && (tooltipX ? `${tooltipX - TOOLTIP_WIDTH / 2}px` : '50%')}; transform: translate(5px, 32px); z-index: ${Z_INDEX.tooltip}; -` +`; const StyledTooltipTriangle = styled(TooltipTriangle)` path { fill: ${({ theme }) => theme.black}; } -` +`; const CopiedTooltip = styled.div<{ isCopyContractTooltip?: boolean }>` background-color: ${({ theme }) => theme.black}; @@ -203,7 +199,7 @@ const CopiedTooltip = styled.div<{ isCopyContractTooltip?: boolean }>` color: ${({ theme }) => theme.white}; font-size: 12px; -` +`; function Tooltip({ isCopyContractTooltip, tooltipX }: { isCopyContractTooltip: boolean; tooltipX?: number }) { return ( @@ -211,7 +207,7 @@ function Tooltip({ isCopyContractTooltip, tooltipX }: { isCopyContractTooltip: b Copied! - ) + ); } const CopyIconWrapper = styled.div` @@ -220,19 +216,19 @@ const CopyIconWrapper = styled.div` align-items: center; justify-content: center; display: flex; -` +`; export function CopyToClipboard({ toCopy, children }: PropsWithChildren<{ toCopy: string }>) { - const [isCopied, setCopied] = useCopyClipboard() + const [isCopied, setCopied] = useCopyClipboard(); const copy = useCallback(() => { - setCopied(toCopy) - }, [toCopy, setCopied]) + setCopied(toCopy); + }, [toCopy, setCopied]); return ( {children} {isCopied && } - ) + ); } export function CopyLinkIcon({ toCopy }: { toCopy: string }) { @@ -240,20 +236,20 @@ 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} @@ -265,26 +261,26 @@ const CopyAddressRow = styled.div<{ isClicked: boolean }>` display: flex; gap: 6px; ${({ theme, isClicked }) => isClicked && `opacity: ${theme.opacity.click} !important`} -` +`; const CopyContractAddressWrapper = styled.div` align-items: center; justify-content: center; display: flex; -` +`; export function CopyContractAddress({ address }: { address: string }) { - const [isCopied, setCopied] = useCopyClipboard() - const [tooltipX, setTooltipX] = useState() + const [isCopied, setCopied] = useCopyClipboard(); + const [tooltipX, setTooltipX] = useState(); const copy = useCallback( (e: React.MouseEvent) => { - setTooltipX(e.clientX) - setCopied(address) + setTooltipX(e.clientX); + setCopied(address); }, - [address, setCopied] - ) + [address, setCopied], + ); - const truncated = `${address.slice(0, 4)}...${address.slice(-3)}` + const truncated = `${address.slice(0, 4)}...${address.slice(-3)}`; return ( @@ -294,31 +290,31 @@ export function CopyContractAddress({ address }: { address: string }) { {isCopied && } - ) + ); } const CopyHelperContainer = styled.div<{ clicked: boolean; color?: string; gap: number }>` ${ClickableStyle} display: flex; flex-direction: row; - gap: ${({ gap }) => gap + 'px'}; + gap: ${({ gap }) => `${gap}px`}; align-items: center; color: ${({ color }) => color ?? 'inherit'}; -` +`; const CopyHelperText = styled.div<{ fontSize?: number; offset: number }>` ${EllipsisStyle} - ${({ fontSize }) => (fontSize ? 'font-size: ' + fontSize + 'px' : 'inherit')}; - max-width: calc(100% - ${({ offset }) => offset + 'px'}); -` + ${({ fontSize }) => (fontSize ? `font-size: ${fontSize}px` : 'inherit')}; + max-width: calc(100% - ${({ offset }) => `${offset}px`}); +`; const StyledCheckCircle = styled(CheckCircle)` color: ${({ theme }) => theme.success}; stroke-width: 1.5px; -` +`; function isEllipsisActive(element: HTMLDivElement | null) { - return Boolean(element && element.offsetWidth < element.scrollWidth) + return Boolean(element && element.offsetWidth < element.scrollWidth); } interface CopyHelperProps { @@ -337,8 +333,7 @@ interface CopyHelperProps { export type CopyHelperRefType = { forceCopy: () => void } export const CopyHelper = forwardRef( ( - { - InitialIcon = Copy, + { InitialIcon = Copy, CopiedIcon = StyledCheckCircle, toCopy, color, @@ -347,35 +342,34 @@ export const CopyHelper = forwardRef( gap = 4, iconPosition = 'left', iconColor = 'currentColor', - children, - }: CopyHelperProps, - ref + children }: CopyHelperProps, + ref, ) => { - const [isCopied, setCopied] = useCopyClipboard() + const [isCopied, setCopied] = useCopyClipboard(); const copy = useCallback(() => { - setCopied(toCopy) - }, [toCopy, setCopied]) + setCopied(toCopy); + }, [toCopy, setCopied]); useImperativeHandle(ref, () => ({ forceCopy() { - copy() + copy(); }, - })) + })); // Detects is text is ellipsing in order to shorten gap caused by extra space browsers add after ... chars - const textRef = useRef(null) - const isEllipsis = isEllipsisActive(textRef.current) - const displayGap = isEllipsis ? gap - 4 : gap + const textRef = useRef(null); + const isEllipsis = isEllipsisActive(textRef.current); + const displayGap = isEllipsis ? gap - 4 : gap; - const [isHover, setIsHover] = useState(false) - const onHover = useCallback(() => setIsHover(true), []) - const offHover = useCallback(() => setIsHover(false), []) + const [isHover, setIsHover] = useState(false); + const onHover = useCallback(() => setIsHover(true), []); + const offHover = useCallback(() => setIsHover(false), []); // Copy-helpers w/ left icon always show icon & display "Copied!" in copied state // Copy-helpers w/ right icon show icon on hover & do not change text - const showIcon = Boolean(iconPosition === 'left' || isHover || isCopied) - const Icon = isCopied ? CopiedIcon : showIcon ? InitialIcon : null - const offset = showIcon ? gap + iconSize : 0 + const showIcon = Boolean(iconPosition === 'left' || isHover || isCopied); + const Icon = isCopied ? CopiedIcon : showIcon ? InitialIcon : null; + const offset = showIcon ? gap + iconSize : 0; return ( (
{iconPosition === 'right' && Icon && } - ) - } -) -CopyHelper.displayName = 'CopyHelper' + ); + }, +); +CopyHelper.displayName = 'CopyHelper'; const rotate = keyframes` from { @@ -404,72 +398,81 @@ const rotate = keyframes` to { transform: rotate(360deg); } -` +`; const SpinnerCss = css` animation: 2s ${rotate} linear infinite; -` +`; const Spinner = styled.img` ${SpinnerCss} width: 16px; height: 16px; -` +`; export const SpinnerSVG = styled.svg` ${SpinnerCss} -` +`; const BackArrowIcon = styled(ArrowLeft)` color: ${({ theme }) => theme.neutral1}; -` +`; export function BackArrowLink({ to }: { to: string }) { return ( - ) + ); } export const CustomLightSpinner = styled(Spinner)<{ size: string }>` height: ${({ size }) => size}; width: ${({ size }) => size}; -` +`; export const HideSmall = styled.span` ${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall` display: none; `}; -` +`; export const HideExtraSmall = styled.span` ${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToExtraSmall` display: none; `}; -` +`; export const SmallOnly = styled.span` display: none; ${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall` display: block; `}; -` +`; export const MediumOnly = styled.span` display: none; @media (max-width: ${({ theme }) => theme.breakpoint.md}px) { display: block; } -` +`; -export const Separator = styled.div` +export const Separator = styled.div<{ size?: number }>` width: 100%; - height: 1px; - background-color: ${({ theme }) => theme.surface3}; -` + height: ${({ size }) => (size ? `${size}px` : '1px')}; + background-color: ${({ theme }) => theme.surface5}; +`; + +export const ThemedSeparator = styled.div<{ reversed?: boolean }>` + width: 100%; + ${({ theme, reversed }) => themeBorderGradient({ + gradientColor: (reversed) ? theme.brandedGradientReversed : theme.brandedGradient, + size: 0, + })}; + border-top-width: 1px; +`; export const CautionTriangle = styled(AlertTriangle)` color: ${({ theme }) => theme.deprecated_accentWarning}; -` +`; export const Divider = styled.div` width: 100%; @@ -477,4 +480,4 @@ export const Divider = styled.div` border-width: 0; margin: 0; background-color: ${({ theme }) => theme.surface3}; -` +`; diff --git a/src/theme/components/text.tsx b/src/theme/components/text.tsx index 43bfd2dd..4881d5bc 100644 --- a/src/theme/components/text.tsx +++ b/src/theme/components/text.tsx @@ -17,73 +17,73 @@ type TextProps = Omit export const ThemedText = { // todo: there should be just one `Body` with default color, no need to make all variations BodyPrimary(props: TextProps) { - return + return }, BodySecondary(props: TextProps) { - return + return }, BodySmall(props: TextProps) { - return + return }, HeadlineSmall(props: TextProps) { - return + return }, HeadlineMedium(props: TextProps) { - return + return }, HeadlineLarge(props: TextProps) { - return + return }, LargeHeader(props: TextProps) { - return + return }, Hero(props: TextProps) { - return + return }, LabelSmall(props: TextProps) { - return + return }, LabelMicro(props: TextProps) { - return + return }, Caption(props: TextProps) { - return + return }, Link(props: TextProps) { - return + return }, MediumHeader(props: TextProps) { - return + return }, SubHeaderLarge(props: TextProps) { - return + return }, SubHeader(props: TextProps) { - return + return }, SubHeaderSmall(props: TextProps) { - return + return }, UtilityBadge(props: TextProps) { - return + return }, DeprecatedMain(props: TextProps) { - return + return }, DeprecatedLink(props: TextProps) { - return + return }, DeprecatedLabel(props: TextProps) { - return + return }, DeprecatedBlack(props: TextProps) { - return + return }, DeprecatedWhite(props: TextProps) { - return + return }, DeprecatedBody(props: TextProps) { - return + return }, DeprecatedLargeHeader(props: TextProps) { return @@ -92,27 +92,27 @@ export const ThemedText = { return }, DeprecatedSubHeader(props: TextProps) { - return + return }, DeprecatedSmall(props: TextProps) { - return + return }, DeprecatedBlue(props: TextProps) { - return + return }, DeprecatedYellow(props: TextProps) { - return + return }, DeprecatedDarkGray(props: TextProps) { - return + return }, DeprecatedGray(props: TextProps) { - return + return }, DeprecatedItalic(props: TextProps) { - return + return }, DeprecatedError({ error, ...props }: { error: boolean } & TextProps) { - return + return }, } diff --git a/src/theme/index.tsx b/src/theme/index.tsx index a1995e97..4e4ab67b 100644 --- a/src/theme/index.tsx +++ b/src/theme/index.tsx @@ -1,31 +1,30 @@ -import { rootCssString } from 'nft/css/cssStringFromTheme' -import React, { useMemo } from 'react' -import { createGlobalStyle, css, ThemeProvider as StyledComponentsThemeProvider } from 'styled-components' -import { useIsDarkMode } from 'theme/components/ThemeToggle' +import React, { useMemo } from 'react'; +import { createGlobalStyle, css, ThemeProvider as StyledComponentsThemeProvider } from 'styled-components'; -import { navDimensions } from '../nft/css/sprinkles.css' -import { darkTheme, lightTheme } from './colors' -import { darkDeprecatedTheme, lightDeprecatedTheme } from './deprecatedColors' +import { rootCssString } from 'nft/css/cssStringFromTheme'; +import { navDimensions } from '../nft/css/sprinkles.css'; +import { darkTheme } from './colors'; +import { darkDeprecatedTheme } from './deprecatedColors'; export const MEDIA_WIDTHS = { deprecated_upToExtraSmall: 500, deprecated_upToSmall: 720, deprecated_upToMedium: 960, deprecated_upToLarge: 1280, -} +}; -const MAX_CONTENT_WIDTH = '1200px' +const MAX_CONTENT_WIDTH = '1200px'; -const deprecated_mediaWidthTemplates: { [width in keyof typeof MEDIA_WIDTHS]: typeof css } = Object.keys( - MEDIA_WIDTHS +const deprecatedMediaWidthTemplates: { [width in keyof typeof MEDIA_WIDTHS]: typeof css } = Object.keys( + MEDIA_WIDTHS, ).reduce((acc, size) => { acc[size] = (a: any, b: any, c: any) => css` @media (max-width: ${(MEDIA_WIDTHS as any)[size]}px) { ${css(a, b, c)} } - ` - return acc -}, {} as any) + `; + return acc; +}, {} as any); export const BREAKPOINTS = { xs: 396, @@ -35,7 +34,7 @@ export const BREAKPOINTS = { xl: 1280, xxl: 1536, xxxl: 1920, -} +}; // deprecated - please use the ones in styles.ts file const transitions = { @@ -50,38 +49,38 @@ const transitions = { out: 'ease-out', inOut: 'ease-in-out', }, -} +}; const opacities = { hover: 0.6, click: 0.4, disabled: 0.5, enabled: 1, -} +}; const fonts = { code: 'courier, courier new, serif', -} +}; const gapValues = { xs: '4px', sm: '8px', md: '12px', - lg: '24px', - xl: '32px', -} + lg: '16px', + xl: '24px', +}; export type Gap = keyof typeof gapValues -function getSettings(darkMode: boolean) { +function getSettings() { return { grids: gapValues, fonts, // shadows - shadow1: darkMode ? '#000' : '#2F80ED', + shadow1: '#000', // media queries - deprecated_mediaWidth: deprecated_mediaWidthTemplates, + deprecated_mediaWidth: deprecatedMediaWidthTemplates, navHeight: navDimensions.height, navVerticalPad: navDimensions.verticalPad, @@ -93,33 +92,32 @@ function getSettings(darkMode: boolean) { breakpoint: BREAKPOINTS, transition: transitions, opacity: opacities, - } + }; } // eslint-disable-next-line import/no-unused-modules -- used in styled.d.ts -export function getTheme(darkMode: boolean) { +export function getTheme() { return { - darkMode, - ...(darkMode ? darkTheme : lightTheme), - ...(darkMode ? darkDeprecatedTheme : lightDeprecatedTheme), - ...getSettings(darkMode), - } + darkMode: true, + ...(darkTheme), + ...(darkDeprecatedTheme), + ...getSettings(), + }; } export default function ThemeProvider({ children }: { children: React.ReactNode }) { - const darkMode = useIsDarkMode() - const themeObject = useMemo(() => getTheme(darkMode), [darkMode]) - return {children} + const themeObject = getTheme(); + return {children}; } export const ThemedGlobalStyle = createGlobalStyle` html, input, textarea, button { - font-family: 'Avenir LT Std', sans-serif; + font-family: 'DM Sans', sans-serif; font-display: fallback; } @supports (font-variation-settings: normal) { html, input, textarea, button { - font-family: 'Avenir LT Std', sans-serif; + font-family: 'DM Sans', sans-serif; } } @@ -127,6 +125,10 @@ export const ThemedGlobalStyle = createGlobalStyle` body { margin: 0; padding: 0; + overscroll-behavior: none; + background: ${({ theme }) => theme.bgdGradient}; + background-repeat: no-repeat; + background-size: cover; } * { @@ -146,22 +148,13 @@ export const ThemedGlobalStyle = createGlobalStyle` font-feature-settings: 'ss01' on, 'ss02' on, 'cv01' on, 'cv03' on; color: ${({ theme }) => theme.neutral1}; - background: linear-gradient(108.58deg, #03001E 20.7%, #EC38BC 36.65%, #7303C0 57.02%, #2A3EF5 71.08%, #38742F 93.32%); - background-repeat: no-repeat; - background-size: cover; } body { min-height: 100vh; - background: linear-gradient(66.46deg, #03001E 24.27%, rgba(3, 0, 30, 0.612102) 57.29%, rgba(3, 0, 30, 0) 100%); - background-repeat: no-repeat; - background-size: cover; } - // a { - // color: ${({ theme }) => theme.accent1}; - // } - + :root { ${({ theme }) => rootCssString(theme.darkMode)} } -` +`; diff --git a/src/theme/styles.ts b/src/theme/styles.ts index 9061e544..1e56fa80 100644 --- a/src/theme/styles.ts +++ b/src/theme/styles.ts @@ -1,14 +1,15 @@ -import { css, keyframes } from 'styled-components' +// @ts-nocheck +import { css, keyframes } from 'styled-components'; export const flexColumnNoWrap = css` display: flex; flex-flow: column nowrap; -` +`; export const flexRowNoWrap = css` display: flex; flex-flow: row nowrap; -` +`; export enum TRANSITION_DURATIONS { slow = 500, @@ -28,7 +29,7 @@ const transitions = { out: 'ease-out', inOut: 'ease-in-out', }, -} +}; const fadeIn = keyframes` from { @@ -37,8 +38,43 @@ const fadeIn = keyframes` to { opacity: 1; } -` +`; export const textFadeIn = css` animation: ${fadeIn} ${transitions.duration.fast} ${transitions.timing.in}; -` +`; + +// Gradient with a fallback to solid color. +export const themeTextGradient = ({ fallbackColor, gradientColor }: { fallbackColor?: string, gradientColor?: string}) => css` + background-color: ${({ theme }) => fallbackColor ?? theme.neutral1}; + + @supports (-webkit-background-clip: text) and (-webkit-text-fill-color: transparent) { + background-image: ${({ theme }) => gradientColor ?? theme.brandedGradient}; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } +`; + +export const themeBorderGradient = ({ size = 1, gradientColor }: { size?: number, gradientColor?: string}) => css` + border-style: solid; + border-width: ${size}px; + border-image-slice: 1; + border-image-source: ${({ theme }) => gradientColor ?? theme.brandedGradient} +`; + +export const themeBoxBorderGradient = ({ ignoreRelative = false, size = '2px', borderRadius = '8px', gradientColor }: { ignoreRelative?: boolean, size?: string, borderRadius?: string, gradientColor?: string}) => css` + ${!ignoreRelative && css`position: relative;`} + + &:before { + content: ""; + position: absolute; + inset: 0; + border-radius: ${borderRadius}; + padding: ${size}; + background: ${({ theme }) => gradientColor ?? theme.brandedGradient}; + mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; + } +`; diff --git a/src/utils/walletMeta.ts b/src/utils/walletMeta.ts index 7242f681..230f1044 100644 --- a/src/utils/walletMeta.ts +++ b/src/utils/walletMeta.ts @@ -1,12 +1,12 @@ -import type { ExternalProvider, JsonRpcProvider, Web3Provider } from '@ethersproject/providers' -import type WalletConnectProvider from '@walletconnect/ethereum-provider' +import type { ExternalProvider, JsonRpcProvider, Web3Provider } from '@ethersproject/providers'; +import type WalletConnectProvider from '@walletconnect/ethereum-provider'; function isWeb3Provider(provider: JsonRpcProvider): provider is Web3Provider { - return 'provider' in provider + return 'provider' in provider; } function isWalletConnectProvider(provider: ExternalProvider): provider is WalletConnectProvider { - return (provider as WalletConnectProvider).isWalletConnect + return (provider as WalletConnectProvider).isWalletConnect; } export enum WalletType { @@ -47,29 +47,28 @@ export interface WalletMeta { } function getWalletConnectMeta(provider: WalletConnectProvider): WalletMeta { - const metadata = provider.session?.peer.metadata + const metadata = provider.session?.peer.metadata; return { type: WalletType.WALLET_CONNECT, agent: metadata ? `${metadata.name} (WalletConnect)` : '(WalletConnect)', ...metadata, - } + }; } function getInjectedMeta(provider: ExternalProvider & Record): WalletMeta { - const properties = Object.getOwnPropertyNames(provider) - const names = - properties - .filter((name) => name.match(/^is.*$/) && (provider as Record)[name] === true) - .map((name) => name.slice(2)) ?? [] + const properties = Object.getOwnPropertyNames(provider); + const names = properties + .filter((name) => name.match(/^is.*$/) && (provider as Record)[name] === true) + .map((name) => name.slice(2)) ?? []; // Many wallets spoof MetaMask by setting `isMetaMask` along with their own identifier, // so we sort MetaMask last so that these wallets' names come first. - names.sort((a, b) => (a === 'MetaMask' ? 1 : b === 'MetaMask' ? -1 : 0)) + names.sort((a, b) => (a === 'MetaMask' ? 1 : b === 'MetaMask' ? -1 : 0)); // Coinbase Wallet can be connected through an extension or a QR code, with `qrUrl` as the only differentiator, // so we capture `qrUrl` in the agent string. - if (properties.includes('qrUrl') && provider['qrUrl']) { - names.push('qrUrl') + if (properties.includes('qrUrl') && provider.qrUrl) { + names.push('qrUrl'); } return { @@ -77,15 +76,14 @@ function getInjectedMeta(provider: ExternalProvider & Record): agent: [...names, '(Injected)'].join(' '), name: names[0], // TODO(WEB-2914): Populate description, url, and icons for known wallets. - } + }; } export function getWalletMeta(provider: JsonRpcProvider): WalletMeta | undefined { - if (!isWeb3Provider(provider)) return undefined + if (!isWeb3Provider(provider)) { return undefined; } if (isWalletConnectProvider(provider.provider)) { - return getWalletConnectMeta(provider.provider) - } else { - return getInjectedMeta(provider.provider) + return getWalletConnectMeta(provider.provider); } + return getInjectedMeta(provider.provider); } diff --git a/yarn.lock b/yarn.lock index 9c58ade8..d8985908 100644 --- a/yarn.lock +++ b/yarn.lock @@ -164,6 +164,30 @@ dependencies: node-fetch "^2.6.1" +"@argent/starknet-react-webwallet-connector@^6.4.2": + version "6.6.0" + resolved "https://registry.yarnpkg.com/@argent/starknet-react-webwallet-connector/-/starknet-react-webwallet-connector-6.6.0.tgz#5f0ba8c5d2535959d6623abb7d1e22e8f0b0d5e6" + integrity sha512-megg03CygKEFbvfq7VELB1PUU8pWNXD+abmrb1vBglw7gcR1b5/9VcZy34mKDnKkN+Q2ABlRcfqtmvxmjv639g== + dependencies: + "@argent/web-sdk" "^6.4.1" + "@argent/x-window" "^6.4.0" + "@starknet-react/core" "2.0.0-next.0" + +"@argent/web-sdk@^6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@argent/web-sdk/-/web-sdk-6.4.1.tgz#33540a421e4511d2d8892e60b14bc6a8760d4b09" + integrity sha512-QDWmxlv0KhJKE6vqyQ1nWhcM3eRXyz0b0poA1u4r/205gnJcD8sy+EvuAAmav4mUdkmHvReEnqSXevDZ2iriuA== + dependencies: + "@argent/x-window" "^6.4.0" + get-starknet-core "^3.0.1" + lodash-es "^4.17.21" + trpc-browser "^1.3.2" + +"@argent/x-window@^6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@argent/x-window/-/x-window-6.4.0.tgz#60f5fd56567c84588b3c90ab8f66f5f83f4689a9" + integrity sha512-fGFUWCrtnH8OJl+OnvUh/EdCdru8cfnftOls6XgzqwZLqhNC7PMQnKEP3WR+xL+C6pzja1K2hlK84qSap+WMYA== + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.22.5", "@babel/code-frame@^7.8.3": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" @@ -3514,26 +3538,26 @@ resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== -"@lingui/babel-plugin-extract-messages@4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-4.3.0.tgz#2cf9486880318804d846e9ef96cef9841a68c14e" - integrity sha512-X23evX574Pv5wRpg9HytCuE0dsRaFi0kkrj8uQC++Dk1Iti0ptRFeuMU4jxINlfa972Ri3GF6ckGQ+Req3drYg== +"@lingui/babel-plugin-extract-messages@4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-4.5.0.tgz#71e56cc2eae73890caeea15a00ae4965413430ec" + integrity sha512-jZq3Gbi691jsHyQ4+OPnGgIqZt5eKEGnmI75akYlZpwTPxF7n+hiuKlQS+YB3xfKvcvlAED76ZAMCcwYG5fNrQ== -"@lingui/cli@^4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@lingui/cli/-/cli-4.3.0.tgz#7c0530ef2caf0902c13fafd4db4b8b552d57446e" - integrity sha512-Z44IxdK7kzmL+5TB2Kuu2xB3O6n1WCsBG0nUXOMtwTWHNxBLdr89hFogO3Orf7QNyxpfw6yZ3Go3xRm3JO3rJg== +"@lingui/cli@^4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@lingui/cli/-/cli-4.5.0.tgz#fb8685cb682cf624b6e5b6d1207f8127b98ed95a" + integrity sha512-MzhxNUNd+YYEmK79TwmneUow5BuLwpOlrUrZq9EyIAWUM4N6kkCVkZ8VIMYCL4TXGQ4kBQjstgDpkF8wdFRtNg== dependencies: "@babel/core" "^7.21.0" "@babel/generator" "^7.21.1" "@babel/parser" "^7.21.2" "@babel/runtime" "^7.21.0" "@babel/types" "^7.21.2" - "@lingui/babel-plugin-extract-messages" "4.3.0" - "@lingui/conf" "4.3.0" - "@lingui/core" "4.3.0" - "@lingui/format-po" "4.3.0" - "@lingui/message-utils" "4.3.0" + "@lingui/babel-plugin-extract-messages" "4.5.0" + "@lingui/conf" "4.5.0" + "@lingui/core" "4.5.0" + "@lingui/format-po" "4.5.0" + "@lingui/message-utils" "4.5.0" babel-plugin-macros "^3.0.1" chalk "^4.1.0" chokidar "3.5.1" @@ -3566,6 +3590,18 @@ jiti "^1.17.1" lodash.get "^4.4.2" +"@lingui/conf@4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@lingui/conf/-/conf-4.5.0.tgz#09b39de3a03a9017cd8299b1d8df923c78447ebf" + integrity sha512-OBm4RQQtbpvmuazLWVpvpaOpt/xvu1PBv8WUX8QoW1vsROe/3P5BpRHRYFyMeZz5mhORJgis9lQtDTq145Ruug== + dependencies: + "@babel/runtime" "^7.20.13" + chalk "^4.1.0" + cosmiconfig "^8.0.0" + jest-validate "^29.4.3" + jiti "^1.17.1" + lodash.get "^4.4.2" + "@lingui/core@4.3.0", "@lingui/core@^4.3.0": version "4.3.0" resolved "https://registry.yarnpkg.com/@lingui/core/-/core-4.3.0.tgz#239e9fed6e858185ecfd59355b09678df1335393" @@ -3575,13 +3611,22 @@ "@lingui/message-utils" "4.3.0" unraw "^2.0.1" -"@lingui/format-po@4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@lingui/format-po/-/format-po-4.3.0.tgz#ce4281ea047749842c1ebcbe4cb01fefd102571a" - integrity sha512-iwqMve+VMxfRisTCk2evcnhAfn54v5qXTXZUDGZQrSQ60r2A4VGtS8VdCzqHwKxvbIatjq4NfWGa0v0zb092jg== +"@lingui/core@4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@lingui/core/-/core-4.5.0.tgz#8b907238bd0b420b372272d0a757c56bed3aed14" + integrity sha512-8zTuIXJo5Qvjato7LWE6Q4RHiO4LjTBVOoRlqfOGYDp8VZ9w9P7Z7IJgxI7UP5Z1wiuEvnMdVF9I1C4acqXGlQ== dependencies: - "@lingui/conf" "4.3.0" - "@lingui/message-utils" "4.3.0" + "@babel/runtime" "^7.20.13" + "@lingui/message-utils" "4.5.0" + unraw "^3.0.0" + +"@lingui/format-po@4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@lingui/format-po/-/format-po-4.5.0.tgz#88dabeaa565ce5e4ff2a1b9865da7759b393af27" + integrity sha512-xQNzZ4RCQfh6TjzjUsyHz3B0R9FJuzhBit9R37NyMn6mL3kBTCUExpPczknm8gWZjtfFO4T8EH5eJhhC5vgJYg== + dependencies: + "@lingui/conf" "4.5.0" + "@lingui/message-utils" "4.5.0" date-fns "^2.29.3" pofile "^1.1.4" @@ -3603,6 +3648,13 @@ dependencies: "@messageformat/parser" "^5.0.0" +"@lingui/message-utils@4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@lingui/message-utils/-/message-utils-4.5.0.tgz#7ae9dc6cb65cbb5e2dc1b8cdcc4c8b92d5c7189f" + integrity sha512-iRqh2wvNtzJO3NStB77nEXEfeI53aVVjzD7/mBrEm/P0lC7sqPHk0WBQCfzE0N9xm6a+XHmHu3J+x2nnQ2OjcA== + dependencies: + "@messageformat/parser" "^5.0.0" + "@lingui/react@^4.3.0": version "4.3.0" resolved "https://registry.yarnpkg.com/@lingui/react/-/react-4.3.0.tgz#d08fe2b3a6b97c891adc26bee68c83451ba24198" @@ -4828,6 +4880,17 @@ resolved "https://registry.yarnpkg.com/@starknet-react/chains/-/chains-0.1.0.tgz#3728f0c5d391b8ce34526a47c975d0b648ff7ca9" integrity sha512-tzhLuNbFLWUGKYDZC2mbWuyO/bohQW1Q9D7Q0dCDDvowXgG1/Z0WykVusuCl0Pe4iVVrjqkiFKJRR717FzsPUQ== +"@starknet-react/core@2.0.0-next.0": + version "2.0.0-next.0" + resolved "https://registry.yarnpkg.com/@starknet-react/core/-/core-2.0.0-next.0.tgz#3aae8f84af791a0b29e0f83fc177b1c2dd66aeab" + integrity sha512-OZf45Na0NyfDyd4utFkY32I4x0MWzpbA5XG6Q6tWHbsYSWBA7YIxdOktY5iLqhzUgxzN6g/9QMWaRtxx5Iw5WA== + dependencies: + "@starknet-react/chains" "^0.1.0-next.0" + "@tanstack/react-query" "^4.35.3" + eventemitter3 "^5.0.1" + immutable "^4.3.4" + zod "^3.22.2" + "@starknet-react/core@^2.0.0-next.3": version "2.0.0-next.3" resolved "https://registry.yarnpkg.com/@starknet-react/core/-/core-2.0.0-next.3.tgz#18469392f5bdb42a5b39a9a5f1bd2e1cc1d9b3b4" @@ -12312,7 +12375,7 @@ get-source@^2.0.12: data-uri-to-buffer "^2.0.0" source-map "^0.6.1" -get-starknet-core@^3.2.0: +get-starknet-core@^3.0.1, get-starknet-core@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/get-starknet-core/-/get-starknet-core-3.2.0.tgz#44cabcbd573262340d575cb6b9a38e469a8029ed" integrity sha512-SZhxtLlKoPKLZ2H3l9WIU7CiNmkL3qLWGksALmvZdAXa/9PykYfLtvIB5B8A2UZMpf2ojTZlWLfuo1KhgmVobA== @@ -15165,6 +15228,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash-es@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" @@ -20046,6 +20114,11 @@ trough@^1.0.0: resolved "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== +trpc-browser@^1.3.2: + version "1.3.6" + resolved "https://registry.yarnpkg.com/trpc-browser/-/trpc-browser-1.3.6.tgz#7aa9823dd5b278a87a00f8f001007e517beea157" + integrity sha512-4Z3zEE7ODupQ6OZ7ABZD8PnC9BP4FBi42eH+5OTxua4VRsnkk0fDvSJL3sS7sb5wJTASdsE5lSbqM7xGQjKknA== + tryer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" @@ -20494,6 +20567,11 @@ unraw@^2.0.1: resolved "https://registry.yarnpkg.com/unraw/-/unraw-2.0.1.tgz#7b51dcdfb1e43d59d5e52cdb44d349d029edbaba" integrity sha512-tdOvLfRzHolwYcHS6HIX860MkK9LQ4+oLuNwFYL7bpgTEO64PZrcQxkisgwJYCfF8sKiWLwwu1c83DvMkbefIQ== +unraw@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unraw/-/unraw-3.0.0.tgz#73443ed70d2ab09ccbac2b00525602d5991fbbe3" + integrity sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg== + untildify@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"