From 5f0272d0ed406b74731baee6be1a223d27fd18bd Mon Sep 17 00:00:00 2001 From: BingBong Date: Wed, 13 Mar 2024 00:04:48 +0000 Subject: [PATCH 1/4] fix: fix gap above navbar on scroll with uk banner --- apps/web/src/components/NavBar/UkBanner.tsx | 74 ++++++++++++++++++--- apps/web/src/pages/App.tsx | 47 ++++++------- apps/web/src/state/application/atoms.ts | 17 +++++ 3 files changed, 104 insertions(+), 34 deletions(-) diff --git a/apps/web/src/components/NavBar/UkBanner.tsx b/apps/web/src/components/NavBar/UkBanner.tsx index 76bcbce2aea..b704160dcfb 100644 --- a/apps/web/src/components/NavBar/UkBanner.tsx +++ b/apps/web/src/components/NavBar/UkBanner.tsx @@ -1,16 +1,22 @@ import { t, Trans } from '@lingui/macro' +import throttle from 'lodash/throttle' +import { useEffect, useState } from 'react' import { useOpenModal } from 'state/application/hooks' import { ApplicationModal } from 'state/application/reducer' import styled from 'styled-components' -import { ButtonText, ThemedText } from 'theme/components' +import { ButtonText, CloseIcon, ThemedText } from 'theme/components' import { Z_INDEX } from 'theme/zIndex' +import { useUkBannerState } from 'state/application/atoms' +import { useAppSelector } from 'state/hooks' +import { AppState } from 'state/reducer' +import { Flex } from 'ui/src' + export const UK_BANNER_HEIGHT = 65 export const UK_BANNER_HEIGHT_MD = 113 export const UK_BANNER_HEIGHT_SM = 137 const BannerWrapper = styled.div` - position: relative; display: flex; background-color: ${({ theme }) => theme.surface1}; padding: 20px; @@ -19,7 +25,7 @@ const BannerWrapper = styled.div` box-sizing: border-box; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; - + width: 98%; @media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { flex-direction: column; } @@ -66,17 +72,63 @@ export const bannerText = t` recommendation, invitation or inducement to deal in cryptoassets. ` +const BannerConatner = styled.div<{ + show: boolean +}>` + display: flex; + max-height: ${({ show }) => (show ? `${UK_BANNER_HEIGHT}px` : '0px')}; + overflow: hidden; + width: 100%; + transition: max-height 0.35s ease-in-out; + position: relative; + + @media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { + max-height: ${({ show }) => (show ? `${UK_BANNER_HEIGHT_MD}px` : '0px')}; + } + + @media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { + max-height: ${({ show }) => (show ? `${UK_BANNER_HEIGHT_SM}px` : '0px')}; + } +` + +export const useRenderUkBanner = () => { + const [show, setShow] = useState(true) + const [notDismissed, dismissBanner] = useUkBannerState() + + const originCountry = useAppSelector((state: AppState) => state.user.originCountry) + + useEffect(() => { + const scrollListener = () => { + if (window.scrollY > 0) setShow(false) + if (window.scrollY <= 5) setShow(true) + } + window.addEventListener('scroll', throttle(scrollListener, 100)) + return () => window.removeEventListener('scroll', throttle(scrollListener, 100)) + }, []) + + return { + renderUkBanner: Boolean(originCountry) && originCountry === 'GB' && show && notDismissed, + dismissBanner, + } +} + export function UkBanner() { + const { renderUkBanner, dismissBanner } = useRenderUkBanner() const openDisclaimer = useOpenModal(ApplicationModal.UK_DISCLAIMER) return ( - - {t`UK disclaimer:` + ' ' + bannerText} - - - Read more - - - + + + {`${t`UK disclaimer:`} ${bannerText}`} + + + Read more + + + + + + + ) } diff --git a/apps/web/src/pages/App.tsx b/apps/web/src/pages/App.tsx index ffc026cd2d5..0764bf0b037 100644 --- a/apps/web/src/pages/App.tsx +++ b/apps/web/src/pages/App.tsx @@ -4,7 +4,13 @@ import { getDeviceId, sendAnalyticsEvent, sendInitializationEvent, Trace, user } import ErrorBoundary from 'components/ErrorBoundary' import Loader from 'components/Icons/LoadingSpinner' import NavBar, { PageTabs } from 'components/NavBar' -import { UK_BANNER_HEIGHT, UK_BANNER_HEIGHT_MD, UK_BANNER_HEIGHT_SM, UkBanner } from 'components/NavBar/UkBanner' +import { + UK_BANNER_HEIGHT, + UK_BANNER_HEIGHT_MD, + UK_BANNER_HEIGHT_SM, + UkBanner, + useRenderUkBanner, +} from 'components/NavBar/UkBanner' import { useFeatureFlagsIsLoaded, useFeatureFlagURLOverrides } from 'featureFlags' import { useAtom } from 'jotai' import { useBag } from 'nft/hooks/useBag' @@ -13,7 +19,6 @@ import { Helmet } from 'react-helmet' import { Navigate, Route, Routes, useLocation, useSearchParams } from 'react-router-dom' import { shouldDisableNFTRoutesAtom } from 'state/application/atoms' import { useAppSelector } from 'state/hooks' -import { AppState } from 'state/reducer' import { useRouterPreference } from 'state/user/hooks' import { StatsigProvider, StatsigUser } from 'statsig-react' import styled from 'styled-components' @@ -37,6 +42,7 @@ const BodyWrapper = styled.div<{ bannerIsVisible?: boolean }>` flex-direction: column; position: relative; width: 100%; + min-height: calc(100vh - ${({ bannerIsVisible }) => (bannerIsVisible ? UK_BANNER_HEIGHT : 0)}px); padding: ${({ theme }) => theme.navHeight}px 0px 5rem 0px; align-items: center; @@ -49,6 +55,7 @@ const BodyWrapper = styled.div<{ bannerIsVisible?: boolean }>` @media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { min-height: calc(100vh - ${({ bannerIsVisible }) => (bannerIsVisible ? UK_BANNER_HEIGHT_SM : 0)}px); } + transition: top 0.35s ease-out; ` const MobileBottomBar = styled.div` @@ -72,7 +79,11 @@ const MobileBottomBar = styled.div` } ` -const HeaderWrapper = styled.div<{ transparent?: boolean; bannerIsVisible?: boolean; scrollY: number }>` +const HeaderWrapper = styled.div<{ + transparent?: boolean + bannerIsVisible?: boolean + scrollY: number +}>` ${flexRowNoWrap}; background-color: ${({ theme, transparent }) => !transparent && theme.surface1}; border-bottom: ${({ theme, transparent }) => !transparent && `1px solid ${theme.surface3}`}; @@ -89,20 +100,15 @@ const HeaderWrapper = styled.div<{ transparent?: boolean; bannerIsVisible?: bool @media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { top: ${({ bannerIsVisible }) => (bannerIsVisible ? Math.max(UK_BANNER_HEIGHT_SM - scrollY, 0) : 0)}px; } + transition: top 0.35s ease-out; ` -const useRenderUkBanner = () => { - const originCountry = useAppSelector((state: AppState) => state.user.originCountry) - return Boolean(originCountry) && originCountry === 'GB' -} - export default function App() { const [, setShouldDisableNFTRoutes] = useAtom(shouldDisableNFTRoutesAtom) const location = useLocation() const { pathname } = location const currentPage = getCurrentPageFromLocation(pathname) - const renderUkBanner = useRenderUkBanner() const [searchParams] = useSearchParams() useEffect(() => { @@ -147,7 +153,7 @@ export default function App() { {/* This is where *static* page titles are injected into the tag. If you want to set a page title based on data that's dynamic or not available on first render, - you can set it later in the page component itself, since react-helmet prefers the most recently rendered title. + you can set it later in the page component itself, since react-helmet-async prefers the most recently rendered title. */} {findRouteByPath(pathname)?.getTitle(pathname) ?? 'Uniswap Interface'} @@ -165,7 +171,7 @@ export default function App() { }} > - {renderUkBanner && } +
@@ -181,7 +187,7 @@ export default function App() { const Body = memo(function Body() { const isLoaded = useFeatureFlagsIsLoaded() const routerConfig = useRouterConfig() - const renderUkBanner = useRenderUkBanner() + const { renderUkBanner } = useRenderUkBanner() return ( @@ -235,18 +241,10 @@ const ResetPageScrollEffect = memo(function ResetPageScrollEffect() { }) const Header = memo(function Header() { - const [isScrolledDown, setIsScrolledDown] = useState(false) const isBagExpanded = useBag((state) => state.bagExpanded) - const isHeaderTransparent = !isScrolledDown && !isBagExpanded - const renderUkBanner = useRenderUkBanner() + const { renderUkBanner } = useRenderUkBanner() - useEffect(() => { - const scrollListener = () => { - setIsScrolledDown(window.scrollY > 0) - } - window.addEventListener('scroll', scrollListener) - return () => window.removeEventListener('scroll', scrollListener) - }, []) + const isHeaderTransparent = useMemo(() => Boolean(window.scrollY > 0 && !isBagExpanded), [isBagExpanded]) return ( @@ -280,7 +278,10 @@ function UserPropertyUpdater() { const sendWebVital = (metric: string) => ({ delta }: Metric) => - sendAnalyticsEvent(SharedEventName.WEB_VITALS, { ...pageLoadProperties, [metric]: delta }) + sendAnalyticsEvent(SharedEventName.WEB_VITALS, { + ...pageLoadProperties, + [metric]: delta, + }) getCLS(sendWebVital('cumulative_layout_shift')) getFCP(sendWebVital('first_contentful_paint_ms')) getFID(sendWebVital('first_input_delay_ms')) diff --git a/apps/web/src/state/application/atoms.ts b/apps/web/src/state/application/atoms.ts index ae271c9cf8b..c1d5a63f29b 100644 --- a/apps/web/src/state/application/atoms.ts +++ b/apps/web/src/state/application/atoms.ts @@ -1,3 +1,5 @@ +import dayjs from 'dayjs' +import { atom, useAtom } from 'jotai' import { atomWithStorage, createJSONStorage } from 'jotai/utils' // Note: @@ -10,3 +12,18 @@ import { atomWithStorage, createJSONStorage } from 'jotai/utils' const storage = createJSONStorage(() => sessionStorage) export const shouldDisableNFTRoutesAtom = atomWithStorage('shouldDisableNFTRoutes', false, storage) + +const UKBannerAtom = atomWithStorage('uni:uk-banner', 0) + +const hideUKBannerAtom = atom( + (get) => { + const now = dayjs() + const last = dayjs(get(UKBannerAtom)) + return now.diff(last, 'days', true) >= 5 // option to not totally disable banner forever + }, + (_, set) => set(UKBannerAtom, Date.now()) +) + +export function useUkBannerState() { + return useAtom(hideUKBannerAtom) +} From c943f20d0eb3f231a15c442859045c0369dc66ee Mon Sep 17 00:00:00 2001 From: BingBong Date: Wed, 13 Mar 2024 00:39:54 +0000 Subject: [PATCH 2/4] chore: undoing needless changes --- apps/web/src/pages/App.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/apps/web/src/pages/App.tsx b/apps/web/src/pages/App.tsx index 0764bf0b037..e0847401d6f 100644 --- a/apps/web/src/pages/App.tsx +++ b/apps/web/src/pages/App.tsx @@ -153,7 +153,7 @@ export default function App() { {/* This is where *static* page titles are injected into the tag. If you want to set a page title based on data that's dynamic or not available on first render, - you can set it later in the page component itself, since react-helmet-async prefers the most recently rendered title. + you can set it later in the page component itself, since react-helmet prefers the most recently rendered title. */} {findRouteByPath(pathname)?.getTitle(pathname) ?? 'Uniswap Interface'} @@ -278,10 +278,7 @@ function UserPropertyUpdater() { const sendWebVital = (metric: string) => ({ delta }: Metric) => - sendAnalyticsEvent(SharedEventName.WEB_VITALS, { - ...pageLoadProperties, - [metric]: delta, - }) + sendAnalyticsEvent(SharedEventName.WEB_VITALS, { ...pageLoadProperties, [metric]: delta }) getCLS(sendWebVital('cumulative_layout_shift')) getFCP(sendWebVital('first_contentful_paint_ms')) getFID(sendWebVital('first_input_delay_ms')) From 22b4c5fe8a57e5ec83d11946e85c6a2c8dc292ad Mon Sep 17 00:00:00 2001 From: BingBong Date: Wed, 13 Mar 2024 00:48:17 +0000 Subject: [PATCH 3/4] change throttle timeout --- apps/web/src/components/NavBar/UkBanner.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/NavBar/UkBanner.tsx b/apps/web/src/components/NavBar/UkBanner.tsx index b704160dcfb..9590e461a0b 100644 --- a/apps/web/src/components/NavBar/UkBanner.tsx +++ b/apps/web/src/components/NavBar/UkBanner.tsx @@ -102,8 +102,8 @@ export const useRenderUkBanner = () => { if (window.scrollY > 0) setShow(false) if (window.scrollY <= 5) setShow(true) } - window.addEventListener('scroll', throttle(scrollListener, 100)) - return () => window.removeEventListener('scroll', throttle(scrollListener, 100)) + window.addEventListener('scroll', throttle(scrollListener, 200)) + return () => window.removeEventListener('scroll', throttle(scrollListener, 200)) }, []) return { From b9af45d9514714b56ef16a81ca02e82989fd0a51 Mon Sep 17 00:00:00 2001 From: BingBong Date: Wed, 13 Mar 2024 16:00:41 +0000 Subject: [PATCH 4/4] chore: emit else clause --- apps/web/src/components/NavBar/UkBanner.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/web/src/components/NavBar/UkBanner.tsx b/apps/web/src/components/NavBar/UkBanner.tsx index 9590e461a0b..d9e12bc9288 100644 --- a/apps/web/src/components/NavBar/UkBanner.tsx +++ b/apps/web/src/components/NavBar/UkBanner.tsx @@ -72,7 +72,7 @@ export const bannerText = t` recommendation, invitation or inducement to deal in cryptoassets. ` -const BannerConatner = styled.div<{ +const BannerContainer = styled.div<{ show: boolean }>` display: flex; @@ -100,7 +100,7 @@ export const useRenderUkBanner = () => { useEffect(() => { const scrollListener = () => { if (window.scrollY > 0) setShow(false) - if (window.scrollY <= 5) setShow(true) + else setShow(true) } window.addEventListener('scroll', throttle(scrollListener, 200)) return () => window.removeEventListener('scroll', throttle(scrollListener, 200)) @@ -117,7 +117,7 @@ export function UkBanner() { const openDisclaimer = useOpenModal(ApplicationModal.UK_DISCLAIMER) return ( - + {`${t`UK disclaimer:`} ${bannerText}`} @@ -129,6 +129,6 @@ export function UkBanner() { - + ) }