diff --git a/src/custom/assets/cow-swap/carret-down.svg b/src/custom/assets/cow-swap/carret-down.svg new file mode 100644 index 0000000000..1831affe5b --- /dev/null +++ b/src/custom/assets/cow-swap/carret-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/custom/assets/cow-swap/code.svg b/src/custom/assets/cow-swap/code.svg new file mode 100644 index 0000000000..2ced6ce64d --- /dev/null +++ b/src/custom/assets/cow-swap/code.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/custom/assets/cow-swap/discord.svg b/src/custom/assets/cow-swap/discord.svg index 58721be551..734c765981 100644 --- a/src/custom/assets/cow-swap/discord.svg +++ b/src/custom/assets/cow-swap/discord.svg @@ -1,3 +1,3 @@ - - - \ No newline at end of file + + + diff --git a/src/custom/assets/cow-swap/doc.svg b/src/custom/assets/cow-swap/doc.svg new file mode 100644 index 0000000000..673e82bb95 --- /dev/null +++ b/src/custom/assets/cow-swap/doc.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/custom/assets/cow-swap/info.svg b/src/custom/assets/cow-swap/info.svg new file mode 100644 index 0000000000..3bc8b57bf4 --- /dev/null +++ b/src/custom/assets/cow-swap/info.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/custom/assets/cow-swap/menu.svg b/src/custom/assets/cow-swap/menu.svg new file mode 100644 index 0000000000..b838f69f60 --- /dev/null +++ b/src/custom/assets/cow-swap/menu.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/custom/assets/cow-swap/moon.svg b/src/custom/assets/cow-swap/moon.svg new file mode 100644 index 0000000000..d37991f2af --- /dev/null +++ b/src/custom/assets/cow-swap/moon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/custom/assets/cow-swap/pie.svg b/src/custom/assets/cow-swap/pie.svg new file mode 100644 index 0000000000..b6c9dcf6d9 --- /dev/null +++ b/src/custom/assets/cow-swap/pie.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/custom/assets/cow-swap/sun.svg b/src/custom/assets/cow-swap/sun.svg new file mode 100644 index 0000000000..93e1029721 --- /dev/null +++ b/src/custom/assets/cow-swap/sun.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/custom/assets/cow-swap/twitter.svg b/src/custom/assets/cow-swap/twitter.svg index 69882d6e27..7d68909264 100644 --- a/src/custom/assets/cow-swap/twitter.svg +++ b/src/custom/assets/cow-swap/twitter.svg @@ -1,3 +1,3 @@ - - - \ No newline at end of file + + + diff --git a/src/custom/components/AccountDetails/Transaction/CancelationModal.tsx b/src/custom/components/AccountDetails/Transaction/CancelationModal.tsx index 98538b3d74..b18a1f7b29 100644 --- a/src/custom/components/AccountDetails/Transaction/CancelationModal.tsx +++ b/src/custom/components/AccountDetails/Transaction/CancelationModal.tsx @@ -12,6 +12,7 @@ import { TransactionErrorContent, } from 'components/TransactionConfirmationModal' import { CancellationSummary } from './styled' +import { Routes } from 'constants/routes' import { shortenOrderId } from 'utils' @@ -48,7 +49,7 @@ function RequestCancellationModal(props: RequestCancellationModalProps): JSX.Ele

This means that a solver might already have included the order in a solution even if this cancellation is successful. Read more in the{' '} - + FAQ . diff --git a/src/custom/components/AppziButton/index.tsx b/src/custom/components/AppziButton/index.tsx index b72c08710e..85d0639644 100644 --- a/src/custom/components/AppziButton/index.tsx +++ b/src/custom/components/AppziButton/index.tsx @@ -23,11 +23,14 @@ const Wrapper = styled(ButtonPrimary)` z-index: 2; ${({ theme }) => theme.mediaWidth.upToMedium` - bottom: 86px; - `}; - - ${({ theme }) => theme.mediaWidth.upToSmall` - right: 14px; + left: 14px; + height: 42px; + width: 42px; + bottom: 11px; + right: initial; + z-index: 10; + box-shadow: none; + border-width: 3px; `}; &::after { diff --git a/src/custom/components/ClickWrap/index.tsx b/src/custom/components/ClickWrap/index.tsx index 09852010b1..dcc39efd7e 100644 --- a/src/custom/components/ClickWrap/index.tsx +++ b/src/custom/components/ClickWrap/index.tsx @@ -1,6 +1,7 @@ import styled from 'styled-components/macro' import { NavLink } from 'react-router-dom' import { ButtonPrimary, ButtonOutlined } from 'components/Button' +import { Routes } from 'constants/routes' const Wrapper = styled.div` display: flex; @@ -111,9 +112,9 @@ export default function ClickWrap() {

We use cookies to provide you with the best experience and to help improve our website and application. Please - read our Cookie Policy for more information. By clicking 'Accept - all', you agree to the storing of cookies on your device to enhance site navigation, analyze site usage - and provide customer support. + read our Cookie Policy for more information. By clicking + 'Accept all', you agree to the storing of cookies on your device to enhance site navigation, analyze + site usage and provide customer support.

diff --git a/src/custom/components/CowBalanceButton/index.tsx b/src/custom/components/CowBalanceButton/index.tsx index 2d10154cfc..84b0d69707 100644 --- a/src/custom/components/CowBalanceButton/index.tsx +++ b/src/custom/components/CowBalanceButton/index.tsx @@ -4,25 +4,35 @@ import CowProtocolLogo from 'components/CowProtocolLogo' import { useCombinedBalance } from 'state/cowToken/hooks' import { ChainId } from 'state/lists/actions/actionsMod' import { formatMax, formatSmartLocaleAware } from 'utils/format' -import { AMOUNT_PRECISION } from 'constants/index' import { COW } from 'constants/tokens' import { transparentize } from 'polished' export const Wrapper = styled.div<{ isLoading: boolean }>` background-color: ${({ theme }) => theme.bg4}; color: ${({ theme }) => theme.text1}; - padding: 7px 12px; - border: 1px solid transparent; + padding: 6px 12px; + border: 2px solid transparent; font-weight: 500; + width: auto; display: flex; align-items: center; position: relative; border-radius: 21px; pointer-events: auto; - transition: border 0.2s ease-in-out; + transition: width 0.2s ease-in-out, border 0.2s ease-in-out; + + ${({ theme }) => theme.mediaWidth.upToMedium` + height: 100%; + width: auto; + padding: 6px 12px 6px 8px; + `}; + + ${({ theme }) => theme.mediaWidth.upToSmall` + padding: 6px 8px; + `}; &:hover { - border: 1px solid ${({ theme }) => transparentize(0.4, theme.text1)}; + border: 2px solid ${({ theme }) => transparentize(0.7, theme.text1)}; } ${({ theme, isLoading }) => @@ -76,22 +86,25 @@ interface CowBalanceButtonProps { account?: string | null | undefined chainId: ChainId | undefined onClick?: () => void + isUpToSmall?: boolean } const COW_DECIMALS = COW[ChainId.MAINNET].decimals -export default function CowBalanceButton({ onClick }: CowBalanceButtonProps) { +export default function CowBalanceButton({ onClick, isUpToSmall }: CowBalanceButtonProps) { const { balance, isLoading } = useCombinedBalance() - const formattedBalance = formatSmartLocaleAware(balance, AMOUNT_PRECISION) + const formattedBalance = formatSmartLocaleAware(balance, 0) const formattedMaxBalance = formatMax(balance, COW_DECIMALS) return ( - - {formattedBalance || 0} - + {!isUpToSmall && ( + + {formattedBalance || 0} + + )} ) } diff --git a/src/custom/components/CowProtocolLogo/index.tsx b/src/custom/components/CowProtocolLogo/index.tsx index 3de61e7955..f5f3f5043c 100644 --- a/src/custom/components/CowProtocolLogo/index.tsx +++ b/src/custom/components/CowProtocolLogo/index.tsx @@ -3,7 +3,7 @@ import CowProtocolIcon from 'assets/cow-swap/cow_v2.svg' export const Icon = styled.span` --defaultSize: 24px; - --smallSize: ${({ size }) => (size ? `calc(${size}px / 1.5)` : 'calc(var(--defaultSize) / 1.5)')}; + --smallSize: 28px; background: url(${CowProtocolIcon}) no-repeat center/contain; height: ${({ size }) => (size ? `${size}px` : 'var(--defaultSize)')}; width: ${({ size }) => (size ? `${size}px` : 'var(--defaultSize)')}; @@ -12,7 +12,7 @@ export const Icon = styled.span` border-radius: ${({ size }) => (size ? `${size}px` : 'var(--defaultSize)')}; position: relative; - ${({ theme }) => theme.mediaWidth.upToSmall` + ${({ theme }) => theme.mediaWidth.upToMedium` width: var(--smallSize); height: var(--smallSize); border-radius: var(--smallSize); diff --git a/src/custom/components/ErrorBoundary/ErrorBoundaryMod.tsx b/src/custom/components/ErrorBoundary/ErrorBoundaryMod.tsx index bbfa6c0f62..53e580507f 100644 --- a/src/custom/components/ErrorBoundary/ErrorBoundaryMod.tsx +++ b/src/custom/components/ErrorBoundary/ErrorBoundaryMod.tsx @@ -13,10 +13,12 @@ import { AutoRow } from 'components/Row' import Page, { Title } from 'components/Page' import { MEDIA_WIDTHS } from '@src/theme' import CowError from 'assets/cow-swap/CowError.png' -import { UniIcon, LogoImage } from '../Header' +// import { UniIcon, LogoImage } from '../Header' +import { UniIcon, LogoImage } from 'components/Header/styled' // mod import { HeaderRow } from 'components/Header/HeaderMod' import Footer from 'components/Footer' import { DISCORD_LINK, CODE_LINK } from 'constants/index' +import { Routes } from 'constants/routes' /* const FallbackWrapper = styled.div` display: flex; @@ -172,7 +174,7 @@ export default class ErrorBoundary extends React.Component - + diff --git a/src/custom/components/Footer/index.tsx b/src/custom/components/Footer/index.tsx index ed6cc4d7c9..9c419beb5d 100644 --- a/src/custom/components/Footer/index.tsx +++ b/src/custom/components/Footer/index.tsx @@ -20,6 +20,10 @@ const Wrapper = styled.div` justify-content: space-evenly; margin: auto 16px; width: 100%; + + ${({ theme }) => theme.mediaWidth.upToSmall` + margin: auto 0 100px; + `} ` const FooterWrapper = styled.div` diff --git a/src/custom/components/Header/HeaderMod.tsx b/src/custom/components/Header/HeaderMod.tsx index 86a5799cf7..185268f147 100644 --- a/src/custom/components/Header/HeaderMod.tsx +++ b/src/custom/components/Header/HeaderMod.tsx @@ -88,14 +88,14 @@ export const HeaderElement = styled.div` display: flex; align-items: center; - &:not(:first-child) { + /* &:not(:first-child) { margin-left: 0.5em; - } + } */ /* addresses safari's lack of support for "gap" */ - & > *:not(:first-child) { + /* & > *:not(:first-child) { margin-left: 8px; - } + } */ ${({ theme }) => theme.mediaWidth.upToMedium` flex-direction: row-reverse; diff --git a/src/custom/components/Header/MobileMenuIcon/index.tsx b/src/custom/components/Header/MobileMenuIcon/index.tsx new file mode 100644 index 0000000000..ea893b9d08 --- /dev/null +++ b/src/custom/components/Header/MobileMenuIcon/index.tsx @@ -0,0 +1,82 @@ +import styled, { css } from 'styled-components/macro' + +const Wrapper = styled.div<{ isMobileMenuOpen: boolean; height?: number; width?: number; lineSize?: number }>` + z-index: 102; + display: flex; + cursor: pointer; + margin: 0 6px 0 16px; + position: relative; + width: ${({ width }) => (width ? width + 'px' : '34px')}; + height: ${({ height }) => (height ? height + 'px' : '18px')}; + + + span { + background-color: ${({ theme }) => theme.text1}; + border-radius: 3px; + height: ${({ lineSize }) => (lineSize ? lineSize + 'px' : '2px')}; + position: absolute; + transition: all 0.15s cubic-bezier(0.8, 0.5, 0.2, 1.4); + width: 100%; + margin: auto; + } + + span:nth-child(1) { + left: 0; + top: 0; + } + + span:nth-child(2) { + left: 0; + opacity: 1; + top: 50%; + bottom: 50%; + } + + span:nth-child(3) { + bottom: 0; + left: 0; + width: 75%; + } + + + ${({ isMobileMenuOpen }) => + isMobileMenuOpen && + css` + span:nth-child(1) { + transform: rotate(45deg); + top: 50%; + bottom: 50%; + } + + span:nth-child(2) { + opacity: 0; + } + + span:nth-child(3) { + transform: rotate(-45deg); + top: 50%; + bottom: 50%; + width: 100%; + } + `}; + +} +` + +interface IconProps { + isMobileMenuOpen: boolean + width?: number + height?: number + lineSize?: number + onClick: () => void +} + +export default function MobileMenuIcon({ isMobileMenuOpen, width, height, lineSize, onClick }: IconProps) { + return ( + + + + + + ) +} diff --git a/src/custom/components/Header/NetworkSelector/NetworkSelectorMod.tsx b/src/custom/components/Header/NetworkSelector/NetworkSelectorMod.tsx index 346886d5a2..9685e06aab 100644 --- a/src/custom/components/Header/NetworkSelector/NetworkSelectorMod.tsx +++ b/src/custom/components/Header/NetworkSelector/NetworkSelectorMod.tsx @@ -67,19 +67,19 @@ const FlyoutHeader = styled.div` color: ${({ theme }) => theme.text2}; font-weight: 400; ` */ -const FlyoutMenu = styled.div` +export const FlyoutMenu = styled.div` position: absolute; top: 54px; width: 272px; z-index: 99; padding-top: 10px; - @media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { + /* @media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { top: 38px; - } + } */ - ${({ theme }) => theme.mediaWidth.upToExtraSmall` + /* ${({ theme }) => theme.mediaWidth.upToExtraSmall` right: 20%; - `} + `} */ ` // mod: actually, this is closer to original version but I haven't yet pulled latest from uniswap const FlyoutMenuContents = styled.div` diff --git a/src/custom/components/Header/NetworkSelector/index.tsx b/src/custom/components/Header/NetworkSelector/index.tsx index 8fa05fba87..10ebf6ec02 100644 --- a/src/custom/components/Header/NetworkSelector/index.tsx +++ b/src/custom/components/Header/NetworkSelector/index.tsx @@ -1,10 +1,22 @@ import styled from 'styled-components/macro' -import NetworkSelectorMod, { SelectorLabel, SelectorControls } from './NetworkSelectorMod' +import NetworkSelectorMod, { SelectorLabel, SelectorControls, FlyoutMenu } from './NetworkSelectorMod' import { transparentize } from 'polished' +export { getChainNameFromId, getParsedChainId } from './NetworkSelectorMod' const Wrapper = styled.div` display: flex; + ${FlyoutMenu} { + top: 38px; + right: 0; + + ${({ theme }) => theme.mediaWidth.upToSmall` + width: 100%; + left: 0; + top: 58px; + `}; + } + ${SelectorLabel} { ${({ theme }) => theme.mediaWidth.upToMedium` display: none; @@ -13,12 +25,25 @@ const Wrapper = styled.div` ${SelectorControls} { border-radius: 21px; - border: 1px solid transparent; - padding: 6px 12px; + border: 2px solid transparent; + padding: 6px; transition: border 0.2s ease-in-out; + > img { + width: 24px; + height: 24px; + object-fit: contain; + margin: 0 6px 0 0; + } + + ${SelectorLabel} + svg { + width: 18px; + height: 18px; + stroke-width: 3px; + } + &:hover { - border: 1px solid ${({ theme }) => transparentize(0.4, theme.text1)}; + border: 2px solid ${({ theme }) => transparentize(0.7, theme.text1)}; } } ` diff --git a/src/custom/components/Header/index.tsx b/src/custom/components/Header/index.tsx index 549a5dda2e..63c4716798 100644 --- a/src/custom/components/Header/index.tsx +++ b/src/custom/components/Header/index.tsx @@ -1,45 +1,45 @@ -import { useState, useEffect } from 'react' +import { useState, useEffect, useCallback, useMemo } from 'react' import { SupportedChainId as ChainId } from 'constants/chains' -// import { ExternalLink } from 'theme' +import { Routes } from 'constants/routes' import { useHistory } from 'react-router-dom' - -import HeaderMod, { - Title as TitleMod, - HeaderLinks as HeaderLinksMod, - HeaderRow, - HeaderControls as HeaderControlsUni, - BalanceText as BalanceTextUni, - HeaderElement, - AccountElement as AccountElementUni, - // HeaderElementWrap, - StyledNavLink as StyledNavLinkUni, - StyledMenuButton, - HeaderFrame, - UNIWrapper, -} from './HeaderMod' -import MenuDropdown from 'components/MenuDropdown' -// import { Moon, Sun } from 'react-feather' -import styled from 'styled-components/macro' import { useActiveWeb3React } from 'hooks/web3' import { useNativeCurrencyBalances } from 'state/wallet/hooks' +import { useDarkModeManager } from 'state/user/hooks' +import { useMediaQuery, upToSmall, upToLarge } from 'hooks/useMediaQuery' import { AMOUNT_PRECISION } from 'constants/index' -// import { useDarkModeManager } from 'state/user/hooks' -import { darken } from 'polished' -// import TwitterImage from 'assets/cow-swap/twitter.svg' -import OrdersPanel from 'components/OrdersPanel' - +import { MAIN_MENU, MAIN_MENU_TYPE } from 'constants/mainMenu' import { supportedChainId } from 'utils/supportedChainId' import { formatSmart } from 'utils/format' +import { toggleBodyClass } from 'utils/toggleBodyClass' +import SVG from 'react-inlinesvg' + +// Components +import { ExternalLink } from 'theme/components' +import { HeaderRow } from './HeaderMod' +import { + Wrapper, + Title, + LogoImage, + HeaderLinks, + HeaderModWrapper, + UniIcon, + StyledNavLink, + AccountElement, + BalanceText, + HeaderControls, + HeaderElement, +} from './styled' +import MobileMenuIcon from './MobileMenuIcon' +import MenuDropdown from 'components/MenuDropdown' +import { MenuTitle, MenuSection } from 'components/MenuDropdown/styled' import Web3Status from 'components/Web3Status' +import OrdersPanel from 'components/OrdersPanel' import NetworkSelector from 'components/Header/NetworkSelector' -// import SVG from 'react-inlinesvg' - -//import { useUserHasAvailableClaim } from 'state/claim/hooks' - -// import Modal from 'components/Modal' -// import ClaimModal from 'components/claim/ClaimModal' import CowBalanceButton from 'components/CowBalanceButton' -import { transparentize } from 'polished' + +// Assets +import IMAGE_MOON from 'assets/cow-swap/moon.svg' +import IMAGE_SUN from 'assets/cow-swap/sun.svg' export const NETWORK_LABELS: { [chainId in ChainId]?: string } = { [ChainId.RINKEBY]: 'Rinkeby', @@ -59,250 +59,119 @@ export interface LinkType { path: string } -const StyledNavLink = styled(StyledNavLinkUni)` - transition: color 0.15s ease-in-out; - color: ${({ theme }) => darken(0.3, theme.text1)}; - - &:first-of-type { - margin: 0 12px 0 0; - } - - &:hover, - &:focus { - color: ${({ theme }) => theme.text1}; - } -` - -const BalanceText = styled(BalanceTextUni)` - font-weight: 500; - padding: 0 6px 0 12px; - - ${({ theme }) => theme.mediaWidth.upToMedium` - overflow: hidden; - max-width: 100px; - text-overflow: ellipsis; - `}; - - ${({ theme }) => theme.mediaWidth.upToSmall` - display: none; - `}; -` - -const HeaderControls = styled(HeaderControlsUni)` - justify-content: flex-end; - - ${({ theme }) => theme.mediaWidth.upToMedium` - max-width: 100%; - padding: 0; - height: auto; - width: 100%; - `}; -` -export const Wrapper = styled.div` - width: 100%; - - ${HeaderFrame} { - padding: 16px; - grid-template-columns: auto auto; - grid-gap: 16px; - - ${({ theme }) => theme.mediaWidth.upToExtraSmall` - padding: 10px; - `} - } - - ${HeaderElement} { - ${({ theme }) => theme.mediaWidth.upToSmall` - width: 100%; - `}; - - ${({ theme }) => theme.mediaWidth.upToMedium` - flex-direction: initial; - align-items: inherit; - `}; - } - - ${StyledMenuButton} { - margin-left: 0.5rem; - padding: 0; - height: 38px; - width: 38px; - } -` - -export const HeaderModWrapper = styled(HeaderMod)`` - -const Title = styled(TitleMod)` - margin: 0; - text-decoration: none; - color: ${({ theme }) => theme.text1}; -` - -export const HeaderLinks = styled(HeaderLinksMod)` - margin: 5px 0 0 0; - - ${({ theme }) => theme.mediaWidth.upToLarge` - display: none; - `}; -` - -export const TwitterLink = styled(StyledMenuButton)` - > a { - ${({ theme }) => theme.cursor}; - padding: 8px; - display: flex; - align-items: center; - justify-content: center; - height: 100%; - width: 100%; - } - - > a > svg { - width: 100%; - height: 100%; - object-fit: contain; - border: 0; - display: flex; - margin: 0; - padding: 0; - stroke: transparent; - } - - > a > svg > path { - fill: ${({ theme }) => theme.text1}; - } - - > a:hover > svg > path { - fill: ${({ theme }) => theme.primary1}; - } -` - -export const LogoImage = styled.div` - width: 190px; - height: 48px; - background: ${({ theme }) => `url(${theme.logo.src}) no-repeat center/contain`}; - margin: 0 32px 0 0; - position: relative; - - ${({ theme }) => theme.mediaWidth.upToMedium` - background: ${({ theme }) => `url(${theme.logo.srcIcon}) no-repeat left/contain`}; - height: 34px; - `} - - > svg { - width: 100%; - height: 100%; - object-fit: contain; - } -` - -export const UniIcon = styled.div` - display: flex; - position: relative; - transition: transform 0.3s ease; - - &:hover { - transform: rotate(-5deg); - } -` - -const VCowWrapper = styled(UNIWrapper)` - ${({ theme }) => theme.mediaWidth.upToSmall` - display: none; - `} -` - -const AccountElement = styled(AccountElementUni)<{ active: boolean }>` - background-color: ${({ theme, active }) => (!active ? theme.bg1 : theme.bg4)}; - border-radius: 21px; - border: 1px solid transparent; - transition: border 0.2s ease-in-out; - pointer-events: auto; - - &:hover, - &:focus { - border: 1px solid ${({ theme }) => transparentize(0.4, theme.text1)}; - } - - ${BalanceText} { - min-width: initial; - } -` - export default function Header() { const { account, chainId: connectedChainId } = useActiveWeb3React() const chainId = supportedChainId(connectedChainId) const userEthBalance = useNativeCurrencyBalances(account ? [account] : [])?.[account ?? ''] const nativeToken = chainId && (CHAIN_CURRENCY_LABELS[chainId] || 'ETH') - // const [darkMode, toggleDarkMode] = useDarkModeManager() - - // const toggleClaimModal = useToggleSelfClaimModal() - // const availableClaim: boolean = useUserHasAvailableClaim(account) - // const [showUniBalanceModal, setShowUniBalanceModal] = useState(false) - // const showClaimPopup = useShowClaimPopup() + const [darkMode, toggleDarkMode] = useDarkModeManager() const [isOrdersPanelOpen, setIsOrdersPanelOpen] = useState(false) const closeOrdersPanel = () => setIsOrdersPanelOpen(false) - const openOrdersPanel = () => setIsOrdersPanelOpen(true) + const openOrdersPanel = () => account && setIsOrdersPanelOpen(true) const history = useHistory() const handleBalanceButtonClick = () => history.push('/account') + const isUpToLarge = useMediaQuery(upToLarge) + const isUpToSmall = useMediaQuery(upToSmall) + + const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false) + const handleMobileMenuOnClick = useCallback( + () => isUpToLarge && setIsMobileMenuOpen(!isMobileMenuOpen), + [isUpToLarge, isMobileMenuOpen] + ) // Toggle the 'noScroll' class on body, whenever the orders panel is open. // This removes the inner scrollbar on the page body, to prevent showing double scrollbars. useEffect(() => { - isOrdersPanelOpen ? document.body.classList.add('noScroll') : document.body.classList.remove('noScroll') - }, [isOrdersPanelOpen]) - - // const close = useToggleModal(ApplicationModal.MENU) + isOrdersPanelOpen ? toggleBodyClass('noScroll', true) : toggleBodyClass('noScroll', false) + isUpToLarge && isMobileMenuOpen ? toggleBodyClass('noScroll', true) : toggleBodyClass('noScroll', false) + }, [isOrdersPanelOpen, isMobileMenuOpen, isUpToLarge]) + + const getMainMenu = useMemo( + () => + MAIN_MENU.map(({ title, url, externalURL, items }: MAIN_MENU_TYPE, index) => + !items && !externalURL && url ? ( + + {title} + + ) : !items && externalURL && url ? ( + + {title} + + ) : items ? ( + + {items.map(({ sectionTitle, links }, index) => { + return ( + + {sectionTitle && {sectionTitle}} + {links.map(({ title, url, externalURL, icon, iconSVG, action }, index) => { + return action && action === 'setColorMode' ? ( + + ) : !externalURL && url ? ( + + {iconSVG ? ( + + ) : icon ? ( + {`${title} + ) : null}{' '} + {title} + + ) : url ? ( + + {iconSVG ? ( + + ) : icon ? ( + {`${title} + ) : null}{' '} + {title} + + ) : null + })} + + ) + })} + + ) : null + ), + [darkMode, handleMobileMenuOnClick, toggleDarkMode] + ) return ( - + - - {/* setShowUniBalanceModal(false)}> - - */} - + <HeaderRow> + <Title href={Routes.HOME} isMobileMenuOpen={isMobileMenuOpen}> <UniIcon> - <LogoImage /> + <LogoImage isMobileMenuOpen={isMobileMenuOpen} /> </UniIcon> - - Swap - Account - FAQ - -
- Account - Account - Account - Account - Account - Account -
- -
- Account - Account - Account - Account - Account - Account -
-
-
+ {getMainMenu}
+ + - - - - - - + {account && userEthBalance && ( @@ -313,18 +182,9 @@ export default function Header() { - {/* */} - {/* - - - - */} - {/* toggleDarkMode()}> - {darkMode ? : } - */} - {/* */} - {/* */} + + {isUpToLarge && } {isOrdersPanelOpen && } diff --git a/src/custom/components/Header/styled.ts b/src/custom/components/Header/styled.ts new file mode 100644 index 0000000000..950f5a2a3f --- /dev/null +++ b/src/custom/components/Header/styled.ts @@ -0,0 +1,353 @@ +import styled, { css } from 'styled-components/macro' +import { transparentize, darken } from 'polished' +import HeaderMod, { + Title as TitleMod, + HeaderLinks as HeaderLinksMod, + HeaderControls as HeaderControlsUni, + BalanceText as BalanceTextUni, + AccountElement as AccountElementUni, + StyledNavLink as StyledNavLinkUni, + StyledMenuButton, + HeaderFrame, + HeaderElement as HeaderElementUni, +} from './HeaderMod' +import { MenuFlyout, MenuSection, Content as MenuContent, MenuTitle } from 'components/MenuDropdown/styled' + +export const StyledNavLink = styled(StyledNavLinkUni)` + transition: color 0.15s ease-in-out; + color: ${({ theme }) => darken(0.3, theme.text1)}; + + &:first-of-type { + margin: 0 12px 0 0; + } + + &:hover, + &:focus { + color: ${({ theme }) => theme.text1}; + } +` + +export const BalanceText = styled(BalanceTextUni)` + font-weight: 500; + padding: 0 6px 0 12px; + + ${({ theme }) => theme.mediaWidth.upToMedium` + overflow: hidden; + max-width: 100px; + text-overflow: ellipsis; + `}; + + ${({ theme }) => theme.mediaWidth.upToSmall` + display: none; + `}; +` + +export const HeaderControls = styled(HeaderControlsUni)` + justify-content: flex-end; + gap: 12px; + + ${({ theme }) => theme.mediaWidth.upToLarge` + max-width: 100%; + margin: 0 0 0 auto; + padding: 0; + height: auto; + width: auto; + `}; +` + +export const HeaderElement = styled(HeaderElementUni)` + border-radius: 0; + gap: 12px; + + ${({ theme }) => theme.mediaWidth.upToMedium` + flex-direction: row; + justify-content: flex-end; + position: fixed; + bottom: 0; + left: 0; + width: 100%; + border-radius: 0; + height: 64px; + background-color: ${({ theme }) => transparentize(0.1, theme.bg3)}; + border-top: 1px solid ${({ theme }) => transparentize(0.7, theme.border)}; + backdrop-filter: blur(21px); + padding: 10px 16px; + gap: 8px; + `} +` + +export const Wrapper = styled.div<{ isMobileMenuOpen: boolean }>` + width: 100%; + + ${HeaderFrame} { + padding: 16px; + display: flex; + + ${({ theme, isMobileMenuOpen }) => theme.mediaWidth.upToLarge` + grid-template-columns: unset; + + ${ + isMobileMenuOpen && + css` + position: absolute; + top: 0; + ` + } + `} + } + + ${StyledMenuButton} { + margin-left: 0.5rem; + padding: 0; + height: 38px; + width: 38px; + } +` + +export const HeaderModWrapper = styled(HeaderMod)`` + +export const Title = styled(TitleMod)<{ isMobileMenuOpen: boolean }>` + margin: 0; + text-decoration: none; + color: ${({ theme }) => theme.text1}; + + ${({ theme, isMobileMenuOpen }) => theme.mediaWidth.upToLarge` + ${ + isMobileMenuOpen && + css` + z-index: 101; + ` + } + `}; +` + +export const HeaderLinks = styled(HeaderLinksMod)<{ isMobileMenuOpen: boolean }>` + margin: 0; + + // Enforce uniform styling of different menu items/components + > ${StyledNavLink}, > ${MenuFlyout} > button { + font-size: 16px; + position: relative; + border-radius: 16px; + display: flex; + align-items: center; + font-weight: 500; + appearance: none; + outline: 0; + margin: 0 4px; + padding: 8px 12px; + background: 0; + border: 0; + cursor: pointer; + background: transparent; + transition: background 0.15s ease-in-out, color 0.15s ease-in-out; + color: ${({ theme }) => transparentize(0.4, theme.text1)}; + + ${({ theme }) => theme.mediaWidth.upToLarge` + width: 100%; + border-radius: 0; + margin: 0; + font-weight: 600; + font-size: 17px; + padding: 28px 10px; + color: ${({ theme }) => theme.text1}; + border-bottom: 1px solid ${({ theme }) => transparentize(0.9, theme.text1)}; + `}; + + > svg > path { + fill: ${({ theme }) => transparentize(0.4, theme.text1)}; + transition: fill 0.15s ease-in-out; + } + + &:hover { + color: ${({ theme }) => theme.text1}; + background: ${({ theme }) => transparentize(0.95, theme.text1)}; + + ${({ theme }) => theme.mediaWidth.upToLarge` + background: transparent; + `}; + + > svg > path { + fill: ${({ theme }) => theme.text1}; + } + } + + &.expanded { + border: 0; + } + + &.expanded + ${MenuContent} { + ${({ theme }) => theme.mediaWidth.upToLarge` + border-bottom: 1px solid ${({ theme }) => transparentize(0.9, theme.text1)}; + `}; + } + + &.ACTIVE { + color: ${({ theme }) => theme.text1}; + } + } + + ${MenuFlyout} { + ${({ theme }) => theme.mediaWidth.upToLarge` + width: 100%; + flex-flow: column wrap; + + > button > svg { + margin: 0 0 0 auto; + height: 10px; + } + `}; + } + + ${MenuContent} { + ${({ theme }) => theme.mediaWidth.upToLarge` + padding: 8px 10px 28px; + gap: 36px; + margin: 0; + `}; + } + + ${MenuSection} { + ${({ theme }) => theme.mediaWidth.upToLarge` + gap 36px; + opacity: 0.7; + `}; + }} + + ${MenuTitle} { + ${({ theme }) => theme.mediaWidth.upToLarge` + display: none; + `}; + }} + + ${({ theme, isMobileMenuOpen }) => theme.mediaWidth.upToLarge` + display: none; + width: 100%; + height: 100%; + position: fixed; + flex-flow: column nowrap; + justify-content: flex-start; + align-items: flex-start; + top: 0; + left: 0; + bottom: 0; + z-index: 100; + background: ${({ theme }) => theme.bg4}; + outline: 0; + padding: 60px 8px; + overflow-y: auto; + + ${ + isMobileMenuOpen && + css` + display: flex; + + &::before { + content: ''; + width: 100%; + display: flex; + height: 60px; + background: ${({ theme }) => theme.bg4}; + position: fixed; + top: 0; + left: 0; + z-index: 1; + } + + // transform: translate3d(100%, 0, 0); + ` + } + `}; +` + +export const TwitterLink = styled(StyledMenuButton)` + > a { + ${({ theme }) => theme.cursor}; + padding: 8px; + display: flex; + align-items: center; + justify-content: center; + height: 100%; + width: 100%; + } + + > a > svg { + width: 100%; + height: 100%; + object-fit: contain; + border: 0; + display: flex; + margin: 0; + padding: 0; + stroke: transparent; + } + + > a > svg > path { + fill: ${({ theme }) => theme.text1}; + } + + > a:hover > svg > path { + fill: ${({ theme }) => theme.primary1}; + } +` + +export const LogoImage = styled.div<{ isMobileMenuOpen?: boolean }>` + width: 190px; + height: 48px; + background: ${({ theme }) => `url(${theme.logo.src}) no-repeat center/contain`}; + margin: 0 32px 0 0; + position: relative; + + ${({ theme }) => theme.mediaWidth.upToMedium` + background: ${({ theme }) => `url(${theme.logo.srcIcon}) no-repeat left/contain`}; + height: 34px; + `} + + ${({ theme, isMobileMenuOpen }) => theme.mediaWidth.upToLarge` + ${ + isMobileMenuOpen && + css` + background: ${({ theme }) => `url(${theme.logo.srcIcon}) no-repeat left/contain`}; + height: 34px; + ` + } + `} + + > svg { + width: 100%; + height: 100%; + object-fit: contain; + } +` + +export const UniIcon = styled.div` + display: flex; + position: relative; + transition: transform 0.3s ease; + + &:hover { + transform: rotate(-5deg); + } +` + +export const AccountElement = styled(AccountElementUni)<{ active: boolean }>` + background-color: ${({ theme, active }) => (!active ? theme.bg1 : theme.bg4)}; + border-radius: 21px; + border: 2px solid transparent; + transition: border 0.2s ease-in-out; + pointer-events: auto; + width: auto; + + ${({ theme }) => theme.mediaWidth.upToMedium` + height: 100%; + `} + + &:hover, + &:focus { + border: 2px solid ${({ theme }) => transparentize(0.7, theme.text1)}; + } + + ${BalanceText} { + min-width: initial; + } +` diff --git a/src/custom/components/MenuDropdown/index.tsx b/src/custom/components/MenuDropdown/index.tsx index 4a39788bea..f52ab03b3a 100644 --- a/src/custom/components/MenuDropdown/index.tsx +++ b/src/custom/components/MenuDropdown/index.tsx @@ -1,5 +1,8 @@ import { useState } from 'react' import { MenuFlyout, Content } from './styled' +import IMAGE_CARRET_DOWN from 'assets/cow-swap/carret-down.svg' +import SVG from 'react-inlinesvg' +import { useMediaQuery, upToLarge } from 'hooks/useMediaQuery' interface MenuProps { title: string @@ -7,15 +10,21 @@ interface MenuProps { } export function Menu({ title, children }: MenuProps) { + const isUpToLarge = useMediaQuery(upToLarge) const [showMenu, setShowMenu] = useState(false) - const handleOnClick = () => setShowMenu(true) - const handleMouseEnter = () => setShowMenu(true) - const handleMouseLeave = () => setShowMenu(false) + const handleOnClick = () => isUpToLarge && setShowMenu(!showMenu) + const handleMouseEnter = () => !isUpToLarge && setShowMenu(true) + const handleMouseLeave = () => !isUpToLarge && setShowMenu(false) return ( - {showMenu && ( diff --git a/src/custom/components/MenuDropdown/styled.ts b/src/custom/components/MenuDropdown/styled.ts index 1a02750998..e3985c5822 100644 --- a/src/custom/components/MenuDropdown/styled.ts +++ b/src/custom/components/MenuDropdown/styled.ts @@ -5,21 +5,126 @@ export const MenuFlyout = styled.ol` padding: 0; margin: 0; position: relative; + + > button { + &.expanded { + border: none; + } + + &:hover { + &::after { + content: ''; + display: block; + position: absolute; + height: 18px; + width: 100%; + bottom: -18px; + left: 0; + background: transparent; + + ${({ theme }) => theme.mediaWidth.upToLarge` + content: none; + `}; + } + } + + > svg { + margin: 0 0 0 3px; + width: 16px; + height: 6px; + object-fit: contain; + } + + > svg.expanded { + transition: transform 0.3s ease-in-out; + transform: rotate(180deg); + } + } ` + export const Content = styled.div` display: flex; position: absolute; top: 100%; left: 0; - background: red; border-radius: 16px; - background: #091e32; - box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25); - padding: 16px; - gap: 16px; + background: ${({ theme }) => theme.bg4}; + box-shadow: 0 12px 18px ${({ theme }) => theme.bg5}; + padding: 32px; + gap: 62px; + margin: 12px 0 0; + + ${({ theme }) => theme.mediaWidth.upToLarge` + box-shadow: none; + background: transparent; + padding: 0; + position: relative; + top: initial; + left: initial; + border-radius: 0; + display: flex; + flex-flow: column wrap; + `}; > div { display: flex; flex-flow: column wrap; } ` + +export const MenuTitle = styled.b` + font-size: 12px; + text-transform: uppercase; + font-weight: 600; + opacity: 0.75; + letter-spacing: 2px; + display: flex; + margin: 0 0 6px; +` + +export const MenuSection = styled.div` + display: flex; + flex-flow: column wrap; + align-items: flex-start; + align-content: flex-start; + justify-content: flex-start; + justify-items: flex-start; + margin: 0; + gap: 16px; + + a, + button { + display: flex; + background: transparent; + appearance: none; + outline: 0; + border: 0; + cursor: pointer; + font-size: 15px; + white-space: nowrap; + font-weight: 500; + margin: 0; + padding: 0; + color: ${({ theme }) => theme.text1}; + gap: 12px; + + &:hover, + &.ACTIVE { + text-decoration: underline; + font-weight: 500; + } + } + + a > svg, + a > img { + width: 18px; + height: auto; + max-height: 21px; + object-fit: contain; + color: ${({ theme }) => theme.text1}; + } + + a > svg > path { + fill: ${({ theme }) => theme.text1}; + } +` diff --git a/src/custom/components/OrdersPanel/index.tsx b/src/custom/components/OrdersPanel/index.tsx index 7a8cf1a66a..a2ae163b71 100644 --- a/src/custom/components/OrdersPanel/index.tsx +++ b/src/custom/components/OrdersPanel/index.tsx @@ -23,7 +23,7 @@ const SideBar = styled.div` margin: auto; bottom: 0; left: 0; - z-index: 99; + z-index: 102; padding: 0; cursor: default; overflow-y: auto; // fallback for 'overlay' diff --git a/src/custom/components/TransactionConfirmationModal/index.tsx b/src/custom/components/TransactionConfirmationModal/index.tsx index 9749487aaa..277966daf0 100644 --- a/src/custom/components/TransactionConfirmationModal/index.tsx +++ b/src/custom/components/TransactionConfirmationModal/index.tsx @@ -21,6 +21,7 @@ import { ColumnCenter } from 'components/Column' import { getStatusIcon } from 'components/AccountDetails' import { shortenAddress } from 'utils' import { getChainCurrencySymbols } from 'utils/xdai/hack' +import { Routes } from 'constants/routes' const Wrapper = styled.div` width: 100%; @@ -550,7 +551,7 @@ export function TransactionSubmittedContent({ )} - + Play the Cow Runner Game! diff --git a/src/custom/components/WalletModal/index.tsx b/src/custom/components/WalletModal/index.tsx index af3cbc021f..3e5be9a00f 100644 --- a/src/custom/components/WalletModal/index.tsx +++ b/src/custom/components/WalletModal/index.tsx @@ -3,6 +3,7 @@ import styled from 'styled-components/macro' import WalletModalMod, { WalletModalProps } from './WalletModalMod' import { ExternalLink } from 'theme' import { Trans } from '@lingui/macro' +import { Routes } from 'constants/routes' // export * from '@src/components/WalletModal' @@ -15,7 +16,7 @@ function CustomTerms() { By connecting a wallet, you agree to GnosisDAO's{' '} - Terms & Conditions and acknowledge that you have + Terms & Conditions and acknowledge that you have read, understood, and agree to them.{' '} diff --git a/src/custom/components/Web3Status/index.tsx b/src/custom/components/Web3Status/index.tsx index 9f6b4aab74..66acb883d3 100644 --- a/src/custom/components/Web3Status/index.tsx +++ b/src/custom/components/Web3Status/index.tsx @@ -11,28 +11,24 @@ import { STORAGE_KEY_LAST_PROVIDER } from 'constants/index' export const Wrapper = styled.div` color: ${({ theme }) => theme.wallet?.color}; - padding: 1px; height: 40px; - border: 1px solid transparent; width: 100%; display: flex; + padding: 0; + margin: 0; justify-content: center; + ${({ theme }) => theme.mediaWidth.upToMedium` + width: auto; + height: 100%; + margin: 0 0 0 auto; + `}; + button { height: auto; - max-width: 180px; - - ${({ theme }) => theme.mediaWidth.upToVerySmall` - max-width: 100%; - `}; - - > p { - margin: 0; - - ${({ theme }) => theme.mediaWidth.upToExtraSmall` - font-size: 13px; - `}; - } + border-radius: 21px; + padding: 6px 12px; + width: max-content; } ${Web3StatusConnected} { @@ -40,6 +36,7 @@ export const Wrapper = styled.div` color: ${({ theme }) => theme.wallet?.color}; background: ${({ theme }) => theme.wallet?.background}; height: 100%; + width: 100%; border: 0; box-shadow: none; padding: 6px 8px; @@ -55,10 +52,6 @@ export const Wrapper = styled.div` } ${Text} { - ${({ theme }) => theme.mediaWidth.upToExtraSmall` - font-size: 13px; - margin: 0 0.5rem 0 0.25rem; - `} } ` diff --git a/src/custom/constants/index.ts b/src/custom/constants/index.ts index c02a5dc91f..de05524a60 100644 --- a/src/custom/constants/index.ts +++ b/src/custom/constants/index.ts @@ -101,7 +101,7 @@ export const RAW_CODE_LINK = 'https://raw.githubusercontent.com/' + GITHUB_REPOS export const DOCS_LINK = 'https://docs.cow.fi' export const CONTRACTS_CODE_LINK = 'https://github.com/cowprotocol/contracts' export const DISCORD_LINK = 'https://discord.com/invite/cowprotocol' -export const DUNE_DASHBOARD_LINK = 'https://duneanalytics.com/gnosis.protocol/Gnosis-Protocol-V2' +export const DUNE_DASHBOARD_LINK = 'https://dune.com/gnosis.protocol/Gnosis-Protocol-V2' export const TWITTER_LINK = 'https://twitter.com/mevprotection' export const GPAUDIT_LINK = 'https://github.com/cowprotocol/contracts/blob/main/audits/GnosisProtocolV2May2021.pdf' export const FLASHBOYS_LINK = 'https://arxiv.org/abs/1904.05234' diff --git a/src/custom/constants/mainMenu.ts b/src/custom/constants/mainMenu.ts new file mode 100644 index 0000000000..971f0ce8b7 --- /dev/null +++ b/src/custom/constants/mainMenu.ts @@ -0,0 +1,77 @@ +import { Routes } from 'constants/routes' +import { DUNE_DASHBOARD_LINK, CONTRACTS_CODE_LINK, DOCS_LINK, DISCORD_LINK, TWITTER_LINK } from 'constants/index' + +// Assets +import IMAGE_DOCS from 'assets/cow-swap/doc.svg' +import IMAGE_INFO from 'assets/cow-swap/info.svg' +import IMAGE_CODE from 'assets/cow-swap/code.svg' +import IMAGE_DISCORD from 'assets/cow-swap/discord.svg' +import IMAGE_TWITTER from 'assets/cow-swap/twitter.svg' +import IMAGE_PIE from 'assets/cow-swap/pie.svg' +import IMAGE_SLICER from 'assets/cow-swap/ninja-cow.png' +import IMAGE_GAME from 'assets/cow-swap/game.gif' + +export interface MAIN_MENU_TYPE { + title: string + url?: string + externalURL?: boolean + items?: { + sectionTitle?: string + links: { + title?: string + url?: string // If URL is an internal route + externalURL?: boolean // If URL is external + icon?: string // If icon uses a regular tag + iconSVG?: string // If icon is a inline component + action?: string // Special purpose flag for non-regular links + }[] + }[] +} + +export const MAIN_MENU = [ + { title: 'Swap', url: Routes.SWAP }, + { title: 'Account', url: Routes.ACCOUNT }, + { + title: 'FAQ', + items: [ + { + links: [ + { title: 'Overview', url: Routes.FAQ }, + { title: 'Protocol', url: Routes.FAQ_PROTOCOL }, + { title: 'Token', url: Routes.FAQ_TOKEN }, + { title: 'Trading', url: Routes.FAQ_TRADING }, + { title: 'Affiliate', url: Routes.FAQ_AFFILIATE }, + ], + }, + ], + }, + { + title: 'More', + items: [ + { + sectionTitle: 'Overview', + links: [ + { title: 'Documentation', url: DOCS_LINK, externalURL: true, iconSVG: IMAGE_DOCS }, + { title: 'About', url: Routes.ABOUT, iconSVG: IMAGE_INFO }, + { title: 'Statistics', url: DUNE_DASHBOARD_LINK, externalURL: true, iconSVG: IMAGE_PIE }, + { title: 'Contract', url: CONTRACTS_CODE_LINK, externalURL: true, iconSVG: IMAGE_CODE }, + ], + }, + { + sectionTitle: 'Community', + links: [ + { title: 'Discord', url: DISCORD_LINK, externalURL: true, iconSVG: IMAGE_DISCORD }, + { title: 'Twitter', url: TWITTER_LINK, externalURL: true, iconSVG: IMAGE_TWITTER }, + ], + }, + { + sectionTitle: 'Other', + links: [ + { action: 'setColorMode' }, + { title: 'CoW Runner', url: Routes.PLAY_COWRUNNER, icon: IMAGE_GAME }, + { title: 'MEV Slicer', url: Routes.PLAY_MEVSLICER, icon: IMAGE_SLICER }, + ], + }, + ], + }, +] diff --git a/src/custom/constants/routes.ts b/src/custom/constants/routes.ts new file mode 100644 index 0000000000..9931890d86 --- /dev/null +++ b/src/custom/constants/routes.ts @@ -0,0 +1,24 @@ +// ENUM with routes +export enum Routes { + HOME = '/', + SWAP = '/swap', + SWAP_OUTPUT_CURRENCY = '/swap/:outputCurrency', + SEND = '/send', + ACCOUNT = '/account', + ABOUT = '/about', + PRIVACY_POLICY = '/privacy-policy', + COOKIE_POLICY = '/cookie-policy', + TERMS_CONDITIONS = '/terms-and-conditions', + FAQ = '/faq', + FAQ_PROTOCOL = '/faq/protocol', + FAQ_TOKEN = '/faq/protocol', + FAQ_TRADING = '/faq/trading', + FAQ_AFFILIATE = '/faq/affiliate', + PLAY_COWRUNNER = '/play/cow-runner', + PLAY_MEVSLICER = '/play/mev-slicer', + ANYSWAP_AFFECTED = '/anyswap-affected-users', + CHAT = '/chat', + DOCS = '/docs', + STATS = '/stats', + TWITTER = '/twitter', +} diff --git a/src/custom/hooks/useChangeNetworks.ts b/src/custom/hooks/useChangeNetworks.ts index de1d2d40b1..1b5baaf19c 100644 --- a/src/custom/hooks/useChangeNetworks.ts +++ b/src/custom/hooks/useChangeNetworks.ts @@ -9,7 +9,7 @@ import usePrevious from 'hooks/usePrevious' import { addPopup, ApplicationModal } from 'state/application/reducer' import { useAppDispatch } from 'state/hooks' import { replaceURLParam } from 'utils/routes' -import { getChainNameFromId, getParsedChainId } from 'components/Header/NetworkSelector/NetworkSelectorMod' +import { getChainNameFromId, getParsedChainId } from 'components/Header/NetworkSelector' import { useHistory } from 'react-router-dom' type ChangeNetworksParams = Pick, 'account' | 'chainId' | 'library'> diff --git a/src/custom/hooks/useMediaQuery.ts b/src/custom/hooks/useMediaQuery.ts new file mode 100644 index 0000000000..9b7177010c --- /dev/null +++ b/src/custom/hooks/useMediaQuery.ts @@ -0,0 +1,22 @@ +import { useState, useEffect } from 'react' +import { MEDIA_WIDTHS } from 'theme' + +export const useMediaQuery = (query: string) => { + const [matches, setMatches] = useState(false) + + useEffect(() => { + const media = window.matchMedia(query) + if (media.matches !== matches) { + setMatches(media.matches) + } + const listener = () => setMatches(media.matches) + window.addEventListener('resize', listener) + return () => window.removeEventListener('resize', listener) + }, [matches, query]) + + return matches +} + +export const upToSmall = `(max-width: ${MEDIA_WIDTHS.upToSmall}px)` +export const upToMedium = `(max-width: ${MEDIA_WIDTHS.upToMedium}px)` +export const upToLarge = `(max-width: ${MEDIA_WIDTHS.upToLarge}px)` diff --git a/src/custom/pages/About/index.tsx b/src/custom/pages/About/index.tsx index ceacdd92ae..35f06c4b32 100644 --- a/src/custom/pages/About/index.tsx +++ b/src/custom/pages/About/index.tsx @@ -8,6 +8,7 @@ import { MEV_TOTAL, FLASHBOTS_LINK } from 'constants/index' import diagramIMG from 'assets/cow-swap/cowswap-diagram.png' import gaslessIMG from 'assets/cow-swap/gasless.png' import mevIMG from 'assets/cow-swap/mev.png' +import { Routes } from 'constants/routes' const ExternalLink = styled(ExternalLinkTheme)`` @@ -103,7 +104,7 @@ export default function About() {

Do you want to know more?

- Head over to the FAQ + Head over to the FAQ

diff --git a/src/custom/pages/App/AppMod.tsx b/src/custom/pages/App/AppMod.tsx index d447f44465..16cbbb8b37 100644 --- a/src/custom/pages/App/AppMod.tsx +++ b/src/custom/pages/App/AppMod.tsx @@ -121,7 +121,7 @@ export default function App(props?: { children?: ReactNode }) {
- + diff --git a/src/custom/pages/App/index.tsx b/src/custom/pages/App/index.tsx index bab7d64381..81c131c9aa 100644 --- a/src/custom/pages/App/index.tsx +++ b/src/custom/pages/App/index.tsx @@ -3,6 +3,7 @@ import styled from 'styled-components/macro' import { RedirectPathToSwapOnly, RedirectToSwap } from 'pages/Swap/redirects' import { Suspense, lazy } from 'react' import { Redirect, Route, Switch } from 'react-router-dom' +import { Routes } from 'constants/routes' import AnySwapAffectedUsers from 'pages/error/AnySwapAffectedUsers' import * as Sentry from '@sentry/react' @@ -11,6 +12,7 @@ import { version } from '@src/../package.json' import { environmentName } from 'utils/environments' import RedirectAnySwapAffectedUsers from 'pages/error/AnySwapAffectedUsers/RedirectAnySwapAffectedUsers' import { SENTRY_IGNORED_GP_QUOTE_ERRORS } from 'api/gnosisProtocol/errors/QuoteError' +import { DUNE_DASHBOARD_LINK, DOCS_LINK, DISCORD_LINK, TWITTER_LINK } from 'constants/index' const SENTRY_DSN = process.env.REACT_APP_SENTRY_DSN const SENTRY_TRACES_SAMPLE_RATE = process.env.REACT_APP_SENTRY_TRACES_SAMPLE_RATE @@ -48,7 +50,7 @@ if (SENTRY_DSN) { export const Wrapper = styled(AppMod)`` -export const BodyWrapper = styled.div` +export const BodyWrapper = styled.div<{ location: { pathname: string } }>` display: flex; flex-direction: row; width: 100%; @@ -58,14 +60,14 @@ export const BodyWrapper = styled.div` flex: auto; z-index: 1; - ${({ theme }) => theme.mediaWidth.upToMedium` - padding: 0 10px 0; - `} - ${({ theme }) => theme.mediaWidth.upToExtraLarge` padding-top: 5vh; align-items: flex-start; `} + + ${({ theme, location }) => theme.mediaWidth.upToMedium` + padding: ${location.pathname === Routes.SWAP ? '0 0 16px' : '0 16px 16px'}; + `} ` export const LoadingWrapper = styled.div` @@ -96,38 +98,28 @@ export default function App() { - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/custom/pages/Claim/ClaimingStatus.tsx b/src/custom/pages/Claim/ClaimingStatus.tsx index ae8c159c78..fdee5501ae 100644 --- a/src/custom/pages/Claim/ClaimingStatus.tsx +++ b/src/custom/pages/Claim/ClaimingStatus.tsx @@ -30,6 +30,7 @@ import { shortenAddress } from 'utils' import CopyHelper from 'components/Copy' import { ButtonSecondary } from 'components/Button' import { ClaimCommonTypes } from './types' +import { Routes } from 'constants/routes' const COW_TWEET_TEMPLATE = 'I just joined the 🐮 CoWmunity @MEVprotection and claimed my first vCOW tokens! Join me at https://cowswap.exchange/' @@ -133,7 +134,7 @@ export default function ClaimingStatus({ handleChangeAccount }: ClaimNavProps) { {isSelfClaiming && ( - + View vCOW balance diff --git a/src/custom/pages/Faq/AffiliateFaq.tsx b/src/custom/pages/Faq/AffiliateFaq.tsx index 78d7e5fc8c..1360dbe4e9 100644 --- a/src/custom/pages/Faq/AffiliateFaq.tsx +++ b/src/custom/pages/Faq/AffiliateFaq.tsx @@ -1,11 +1,13 @@ import Page, { Content } from 'components/Page' import { LinkScrollable } from 'components/Link' +import { Link } from 'react-router-dom' import { ExternalLinkFaq, Wrapper } from './styled' import { Footer } from '.' import { useToC } from './hooks' import ToC from './ToC' import { FaqMenu } from './Menu' +import { Routes } from 'constants/routes' export default function AffiliateFaq() { const { toc, faqRef } = useToC() @@ -18,11 +20,11 @@ export default function AffiliateFaq() {

Affiliate program

-

What is the Profile page?

+

What is the Account page?

- It is a page where you can see your number of trades and volume that you have done with the wallet you have - connected with. + The account page is where you can see your number of trades and volume, + generated with the wallet you&re connected with.

@@ -97,8 +99,8 @@ export default function AffiliateFaq() { counted.

-

- I shared my referral with a friend, who then also traded. Why do I not see any referral trades in my profile +

+ I shared my referral with a friend, who then also traded. Why do I not see any referral trades in my account page?

@@ -139,10 +141,13 @@ export default function AffiliateFaq() {

- Why do I see more trades and referrals in my profile page than I actually see in the activity list? + Why do I see more trades and referrals in my account page than I actually see in the activity list?

-

The number of trades on the profile page is calculated based on on-chain data.

+

+ The number of trades on the account page is calculated based on on-chain + data. +

We have two publicly facing interfaces where both use the same contracts, which are:

  • diff --git a/src/custom/pages/Faq/Menu.tsx b/src/custom/pages/Faq/Menu.tsx index 121c88987d..3ecd22bde7 100644 --- a/src/custom/pages/Faq/Menu.tsx +++ b/src/custom/pages/Faq/Menu.tsx @@ -1,12 +1,13 @@ import { NavLink } from 'react-router-dom' import { Menu } from './styled' +import { Routes } from 'constants/routes' const LINKS = [ - { title: 'General', url: '/faq' }, - { title: 'Protocol', url: '/faq/protocol' }, - { title: 'Token', url: '/faq/token' }, - { title: 'Trading', url: '/faq/trading' }, - { title: 'Affiliate', url: '/faq/affiliate' }, + { title: 'General', url: Routes.FAQ }, + { title: 'Protocol', url: Routes.FAQ_PROTOCOL }, + { title: 'Token', url: Routes.FAQ_TOKEN }, + { title: 'Trading', url: Routes.FAQ_TRADING }, + { title: 'Affiliate', url: Routes.FAQ_AFFILIATE }, ] export function FaqMenu() { diff --git a/src/custom/pages/Faq/TokenFaq.tsx b/src/custom/pages/Faq/TokenFaq.tsx index f680e19cac..69e8c86667 100644 --- a/src/custom/pages/Faq/TokenFaq.tsx +++ b/src/custom/pages/Faq/TokenFaq.tsx @@ -1,10 +1,12 @@ import Page, { Content } from 'components/Page' import { ExternalLinkFaq, Wrapper } from './styled' +import { Link } from 'react-router-dom' import { Footer } from '.' import { useToC } from './hooks' import ToC from './ToC' import { FaqMenu } from './Menu' +import { Routes } from 'constants/routes' export default function TokenFaq() { const { toc, faqRef } = useToC() @@ -51,9 +53,10 @@ export default function TokenFaq() {

    {' '} - Directly in the CowSwap UI. Simply click on the profile section at the top left of the page, or in the menu - if you are on mobile, and you will be redirected to a page where you can see your total combined balance for - COW and vCOW and convert them instantly. + Directly in the CowSwap UI. Simply click on the account menu item at the + top left of the page (desktop) or in the mobile menu. You then will be redirected to the{' '} + account page where you can see your total COW and/or vCOW balance. You will + then be able to convert your vCOW to COW (if applicable).

    What is the purpose of COW Token?

    diff --git a/src/custom/pages/Faq/index.tsx b/src/custom/pages/Faq/index.tsx index 08bdb5032b..f8b26b9297 100644 --- a/src/custom/pages/Faq/index.tsx +++ b/src/custom/pages/Faq/index.tsx @@ -10,6 +10,7 @@ import { FLASHBOTS_LINK, } from 'constants/index' import Page, { Content } from 'components/Page' +import { Routes } from 'constants/routes' import { ExternalLinkFaq, Wrapper, ButtonNav, FooterWrapper } from './styled' import { FaqMenu } from './Menu' import { StyledInternalLink } from 'theme' @@ -37,7 +38,7 @@ export function Footer() { {' '}

    - We really hope you like CowSwap. If you do, Milk it! + We really hope you like CowSwap. If you do, Milk it! 🥛 @@ -179,7 +180,7 @@ export default function Faq() { protocols or dapps, your use is at your own risk.{' '} Please review our{' '} - + Terms and Conditions . diff --git a/src/custom/pages/Swap/SwapMod.tsx b/src/custom/pages/Swap/SwapMod.tsx index 8fa53d33e0..80206e6fd2 100644 --- a/src/custom/pages/Swap/SwapMod.tsx +++ b/src/custom/pages/Swap/SwapMod.tsx @@ -19,7 +19,7 @@ import ReactGA from 'react-ga' // import { RouteComponentProps } from 'react-router-dom' import { Text } from 'rebass' // import { TradeState } from 'state/routing/types' -import styled, { ThemeContext } from 'styled-components/macro' +import { ThemeContext } from 'styled-components/macro' import AddressInputPanel from 'components/AddressInputPanel' import { ButtonConfirmed /*, ButtonError, ButtonLight, ButtonPrimary*/ } from 'components/Button' @@ -84,11 +84,12 @@ import { useErrorMessage } from 'hooks/useErrorMessageAndModal' import { GpEther } from 'constants/tokens' import { SupportedChainId } from 'constants/chains' import CowSubsidyModal from 'components/CowSubsidyModal' +import { AlertWrapper } from './styleds' // mod -const AlertWrapper = styled.div` - max-width: 460px; - width: 100%; -` +// const AlertWrapper = styled.div` +// max-width: 460px; +// width: 100%; +// ` export default function Swap({ history, location, diff --git a/src/custom/pages/Swap/styleds.tsx b/src/custom/pages/Swap/styleds.tsx index 427d78f998..54a0374f8e 100644 --- a/src/custom/pages/Swap/styleds.tsx +++ b/src/custom/pages/Swap/styleds.tsx @@ -17,4 +17,19 @@ export const StyledAppBody = styled(AppBody)` border: ${({ theme }) => theme.appBody.border}; box-shadow: ${({ theme }) => theme.appBody.boxShadow}; background: ${({ theme }) => theme.bg1}; + + ${({ theme }) => theme.mediaWidth.upToSmall` + border: ${({ theme }) => theme.appBody.borderMobile}; + box-shadow: ${({ theme }) => theme.appBody.boxShadowMobile}; + `}; +` + +export const AlertWrapper = styled.div` + max-width: 460px; + width: 100%; + + ${({ theme }) => theme.mediaWidth.upToSmall` + margin: 26px auto 0; + padding: 0 16px; + `} ` diff --git a/src/custom/theme/baseTheme.tsx b/src/custom/theme/baseTheme.tsx index a1070f2347..76ef947da1 100644 --- a/src/custom/theme/baseTheme.tsx +++ b/src/custom/theme/baseTheme.tsx @@ -150,9 +150,11 @@ export function themeVariables(darkMode: boolean, colorsTheme: Colors) { } `, appBody: { - boxShadow: `4px 4px 0px ${colorsTheme.black}`, + boxShadow: `4px 4px 0 ${colorsTheme.black}`, + boxShadowMobile: `0 4px 0 ${colorsTheme.black}`, borderRadius: '16px', border: `3px solid ${colorsTheme.black}`, + borderMobile: 'none', padding: '12px 6px', maxWidth: { normal: '460px', @@ -280,7 +282,7 @@ export function themeVariables(darkMode: boolean, colorsTheme: Colors) { }, wallet: { color: darkMode ? colorsTheme.text1 : colorsTheme.text1, - background: darkMode ? colorsTheme.bg3 : colorsTheme.bg2, + background: darkMode ? colorsTheme.bg3 : colorsTheme.bg1, }, } } diff --git a/src/custom/theme/styled.d.ts b/src/custom/theme/styled.d.ts index 8ac88aab38..cf33e2fbd8 100644 --- a/src/custom/theme/styled.d.ts +++ b/src/custom/theme/styled.d.ts @@ -77,8 +77,10 @@ declare module 'styled-components' { } appBody: { boxShadow: string + boxShadowMobile: string borderRadius: string border: string + borderMobile: string padding: string maxWidth: { normal: string diff --git a/src/custom/utils/toggleBodyClass.ts b/src/custom/utils/toggleBodyClass.ts new file mode 100644 index 0000000000..1879f8c345 --- /dev/null +++ b/src/custom/utils/toggleBodyClass.ts @@ -0,0 +1,7 @@ +export const toggleBodyClass = (className: string, isAdd: boolean) => { + if (isAdd) { + document.body.classList.add(className) + } else { + document.body.classList.remove(className) + } +}