Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix gap above navbar on scroll with uk banner #7671

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 63 additions & 11 deletions apps/web/src/components/NavBar/UkBanner.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -66,17 +72,63 @@ export const bannerText = t`
recommendation, invitation or inducement to deal in cryptoassets.
`

const BannerContainer = 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<boolean>(true)
const [notDismissed, dismissBanner] = useUkBannerState()

const originCountry = useAppSelector((state: AppState) => state.user.originCountry)

useEffect(() => {
const scrollListener = () => {
if (window.scrollY > 0) setShow(false)
else setShow(true)
}
window.addEventListener('scroll', throttle(scrollListener, 200))
return () => window.removeEventListener('scroll', throttle(scrollListener, 200))
}, [])

return {
renderUkBanner: Boolean(originCountry) && originCountry === 'GB' && show && notDismissed,
dismissBanner,
}
}

export function UkBanner() {
const { renderUkBanner, dismissBanner } = useRenderUkBanner()
const openDisclaimer = useOpenModal(ApplicationModal.UK_DISCLAIMER)

return (
<BannerWrapper>
<BannerTextWrapper lineHeight="24px">{t`UK disclaimer:` + ' ' + bannerText}</BannerTextWrapper>
<ReadMoreWrapper>
<ThemedText.BodySecondary lineHeight="24px" color="accent1" onClick={openDisclaimer}>
<Trans>Read more</Trans>
</ThemedText.BodySecondary>
</ReadMoreWrapper>
</BannerWrapper>
<BannerContainer show={renderUkBanner}>
<BannerWrapper>
<BannerTextWrapper lineHeight="24px">{`${t`UK disclaimer:`} ${bannerText}`}</BannerTextWrapper>
<ReadMoreWrapper>
<ThemedText.BodySecondary lineHeight="24px" color="accent1" onClick={openDisclaimer}>
<Trans>Read more</Trans>
</ThemedText.BodySecondary>
</ReadMoreWrapper>
</BannerWrapper>
<Flex alignContent="center" justifyContent="center" width="2%">
<CloseIcon onClick={dismissBanner} width={18} height={18} />
</Flex>
</BannerContainer>
)
}
40 changes: 19 additions & 21 deletions apps/web/src/pages/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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'
Expand All @@ -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;
Expand All @@ -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`
Expand All @@ -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}`};
Expand All @@ -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(() => {
Expand Down Expand Up @@ -165,7 +171,7 @@ export default function App() {
}}
>
<UserPropertyUpdater />
{renderUkBanner && <UkBanner />}
<UkBanner />
<Header />
<ResetPageScrollEffect />
<Body />
Expand All @@ -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 (
<BodyWrapper bannerIsVisible={renderUkBanner}>
Expand Down Expand Up @@ -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 (
<HeaderWrapper transparent={isHeaderTransparent} bannerIsVisible={renderUkBanner} scrollY={scrollY}>
Expand Down
17 changes: 17 additions & 0 deletions apps/web/src/state/application/atoms.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import dayjs from 'dayjs'
import { atom, useAtom } from 'jotai'
import { atomWithStorage, createJSONStorage } from 'jotai/utils'

// Note:
Expand All @@ -10,3 +12,18 @@ import { atomWithStorage, createJSONStorage } from 'jotai/utils'
const storage = createJSONStorage(() => sessionStorage)

export const shouldDisableNFTRoutesAtom = atomWithStorage('shouldDisableNFTRoutes', false, storage)

const UKBannerAtom = atomWithStorage<number>('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)
}