Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9ccbdc5e2 | ||
|
|
e36b72eb82 | ||
|
|
b173d00dae | ||
|
|
c3b02d7c8d | ||
|
|
bff177d3f1 |
1
CODEOWNERS
Normal file
1
CODEOWNERS
Normal file
@ -0,0 +1 @@
|
|||||||
|
* @uniswap/web-admins
|
||||||
@ -39,4 +39,30 @@ describe('Landing Page', () => {
|
|||||||
cy.get(getTestSelector('pool-nav-link')).last().click()
|
cy.get(getTestSelector('pool-nav-link')).last().click()
|
||||||
cy.url().should('include', '/pools')
|
cy.url().should('include', '/pools')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('does not render uk compliance banner in US', () => {
|
||||||
|
cy.visit('/swap')
|
||||||
|
cy.contains('UK disclaimer').should('not.exist')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders uk compliance banner in uk', () => {
|
||||||
|
cy.intercept('https://api.uniswap.org/v1/amplitude-proxy', (req) => {
|
||||||
|
const requestBody = JSON.stringify(req.body)
|
||||||
|
const byteSize = new Blob([requestBody]).size
|
||||||
|
req.alias = 'amplitude'
|
||||||
|
req.reply(
|
||||||
|
JSON.stringify({
|
||||||
|
code: 200,
|
||||||
|
server_upload_time: Date.now(),
|
||||||
|
payload_size_bytes: byteSize,
|
||||||
|
events_ingested: req.body.events.length,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
'origin-country': 'GB',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
cy.visit('/swap')
|
||||||
|
cy.contains('UK disclaimer')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -11,7 +11,7 @@ export const onRequest: PagesFunction = async ({ request, next }) => {
|
|||||||
}
|
}
|
||||||
const res = next()
|
const res = next()
|
||||||
try {
|
try {
|
||||||
return new HTMLRewriter().on('head', new MetaTagInjector(data)).transform(await res)
|
return new HTMLRewriter().on('head', new MetaTagInjector(data, request)).transform(await res)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,12 +6,15 @@ test('should append meta tag to element', () => {
|
|||||||
} as unknown as Element
|
} as unknown as Element
|
||||||
const property = 'property'
|
const property = 'property'
|
||||||
const content = 'content'
|
const content = 'content'
|
||||||
const injector = new MetaTagInjector({
|
const injector = new MetaTagInjector(
|
||||||
title: 'test',
|
{
|
||||||
url: 'testUrl',
|
title: 'test',
|
||||||
image: 'testImage',
|
url: 'testUrl',
|
||||||
description: 'testDescription',
|
image: 'testImage',
|
||||||
})
|
description: 'testDescription',
|
||||||
|
},
|
||||||
|
new Request('http://localhost')
|
||||||
|
)
|
||||||
injector.append(element, property, content)
|
injector.append(element, property, content)
|
||||||
expect(element.append).toHaveBeenCalledWith(`<meta property="${property}" content="${content}"/>`, { html: true })
|
expect(element.append).toHaveBeenCalledWith(`<meta property="${property}" content="${content}"/>`, { html: true })
|
||||||
|
|
||||||
@ -36,3 +39,22 @@ test('should append meta tag to element', () => {
|
|||||||
|
|
||||||
expect(element.append).toHaveBeenCalledTimes(13)
|
expect(element.append).toHaveBeenCalledTimes(13)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should pass through header blocked paths', () => {
|
||||||
|
const element = {
|
||||||
|
append: jest.fn(),
|
||||||
|
} as unknown as Element
|
||||||
|
const request = new Request('http://localhost')
|
||||||
|
request.headers.set('x-blocked-paths', '/')
|
||||||
|
const injector = new MetaTagInjector(
|
||||||
|
{
|
||||||
|
title: 'test',
|
||||||
|
url: 'testUrl',
|
||||||
|
image: 'testImage',
|
||||||
|
description: 'testDescription',
|
||||||
|
},
|
||||||
|
request
|
||||||
|
)
|
||||||
|
injector.element(element)
|
||||||
|
expect(element.append).toHaveBeenCalledWith(`<meta property="x:blocked-paths" content="/"/>`, { html: true })
|
||||||
|
})
|
||||||
|
|||||||
@ -10,7 +10,7 @@ type MetaTagInjectorInput = {
|
|||||||
* to inject meta tags into the <head> of an HTML document.
|
* to inject meta tags into the <head> of an HTML document.
|
||||||
*/
|
*/
|
||||||
export class MetaTagInjector implements HTMLRewriterElementContentHandlers {
|
export class MetaTagInjector implements HTMLRewriterElementContentHandlers {
|
||||||
constructor(private input: MetaTagInjectorInput) {}
|
constructor(private input: MetaTagInjectorInput, private request: Request) {}
|
||||||
|
|
||||||
append(element: Element, property: string, content: string) {
|
append(element: Element, property: string, content: string) {
|
||||||
element.append(`<meta property="${property}" content="${content}"/>`, { html: true })
|
element.append(`<meta property="${property}" content="${content}"/>`, { html: true })
|
||||||
@ -38,5 +38,10 @@ export class MetaTagInjector implements HTMLRewriterElementContentHandlers {
|
|||||||
this.append(element, 'twitter:image', this.input.image)
|
this.append(element, 'twitter:image', this.input.image)
|
||||||
this.append(element, 'twitter:image:alt', this.input.title)
|
this.append(element, 'twitter:image:alt', this.input.title)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const blockedPaths = this.request.headers.get('x-blocked-paths')
|
||||||
|
if (blockedPaths) {
|
||||||
|
this.append(element, 'x:blocked-paths', blockedPaths)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ export const onRequest: PagesFunction = async ({ params, request, next }) => {
|
|||||||
const { index } = params
|
const { index } = params
|
||||||
const collectionAddress = index[0]?.toString()
|
const collectionAddress = index[0]?.toString()
|
||||||
const tokenId = index[1]?.toString()
|
const tokenId = index[1]?.toString()
|
||||||
return getMetadataRequest(res, request.url, () => getAsset(collectionAddress, tokenId, request.url))
|
return getMetadataRequest(res, request, () => getAsset(collectionAddress, tokenId, request.url))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ export const onRequest: PagesFunction = async ({ params, request, next }) => {
|
|||||||
try {
|
try {
|
||||||
const { index } = params
|
const { index } = params
|
||||||
const collectionAddress = index?.toString()
|
const collectionAddress = index?.toString()
|
||||||
return getMetadataRequest(res, request.url, () => getCollection(collectionAddress, request.url))
|
return getMetadataRequest(res, request, () => getCollection(collectionAddress, request.url))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ export const onRequest: PagesFunction = async ({ params, request, next }) => {
|
|||||||
if (!tokenAddress) {
|
if (!tokenAddress) {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
return getMetadataRequest(res, request.url, () => getToken(networkName, tokenAddress, request.url))
|
return getMetadataRequest(res, request, () => getToken(networkName, tokenAddress, request.url))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,13 +4,13 @@ import { Data } from './cache'
|
|||||||
|
|
||||||
export async function getMetadataRequest(
|
export async function getMetadataRequest(
|
||||||
res: Promise<Response>,
|
res: Promise<Response>,
|
||||||
url: string,
|
request: Request,
|
||||||
getData: () => Promise<Data | undefined>
|
getData: () => Promise<Data | undefined>
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const cachedData = await getRequest(url, getData, (data): data is Data => true)
|
const cachedData = await getRequest(request.url, getData, (data): data is Data => true)
|
||||||
if (cachedData) {
|
if (cachedData) {
|
||||||
return new HTMLRewriter().on('head', new MetaTagInjector(cachedData)).transform(await res)
|
return new HTMLRewriter().on('head', new MetaTagInjector(cachedData, request)).transform(await res)
|
||||||
} else {
|
} else {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
82
src/components/NavBar/UkBanner.tsx
Normal file
82
src/components/NavBar/UkBanner.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { t, Trans } from '@lingui/macro'
|
||||||
|
import { useOpenModal } from 'state/application/hooks'
|
||||||
|
import { ApplicationModal } from 'state/application/reducer'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import { ButtonText, ThemedText } from 'theme/components'
|
||||||
|
import { Z_INDEX } from 'theme/zIndex'
|
||||||
|
|
||||||
|
export const UK_BANNER_HEIGHT = 64
|
||||||
|
export const UK_BANNER_HEIGHT_MD = 112
|
||||||
|
export const UK_BANNER_HEIGHT_SM = 136
|
||||||
|
|
||||||
|
const BannerWrapper = styled.div`
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
background-color: ${({ theme }) => theme.surface1};
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 1px solid ${({ theme }) => theme.surface3};
|
||||||
|
z-index: ${Z_INDEX.fixed};
|
||||||
|
box-sizing: border-box;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
|
||||||
|
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const BannerTextWrapper = styled(ThemedText.BodySecondary)`
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) {
|
||||||
|
@supports (-webkit-line-clamp: 2) {
|
||||||
|
white-space: initial;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) {
|
||||||
|
@supports (-webkit-line-clamp: 3) {
|
||||||
|
white-space: initial;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const ReadMoreWrapper = styled(ButtonText)`
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: max-content;
|
||||||
|
|
||||||
|
:focus {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const bannerText = t`
|
||||||
|
This web application is provided as a tool for users to interact with the Uniswap Protocol on
|
||||||
|
their own initiative, with no endorsement or recommendation of cryptocurrency trading activities. In doing so,
|
||||||
|
Uniswap is not recommending that users or potential users engage in cryptoasset trading activity, and users or
|
||||||
|
potential users of the web application should not regard this webpage or its contents as involving any form of
|
||||||
|
recommendation, invitation or inducement to deal in cryptoassets.
|
||||||
|
`
|
||||||
|
|
||||||
|
export function UkBanner() {
|
||||||
|
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>
|
||||||
|
)
|
||||||
|
}
|
||||||
62
src/components/NavBar/UkDisclaimerModal.tsx
Normal file
62
src/components/NavBar/UkDisclaimerModal.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { Trans } from '@lingui/macro'
|
||||||
|
import { ButtonEmphasis, ButtonSize, ThemeButton } from 'components/Button'
|
||||||
|
import Column from 'components/Column'
|
||||||
|
import Modal from 'components/Modal'
|
||||||
|
import { X } from 'react-feather'
|
||||||
|
import { useCloseModal, useModalIsOpen } from 'state/application/hooks'
|
||||||
|
import { ApplicationModal } from 'state/application/reducer'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import { ButtonText, ThemedText } from 'theme/components'
|
||||||
|
|
||||||
|
import { bannerText } from './UkBanner'
|
||||||
|
|
||||||
|
const Wrapper = styled(Column)`
|
||||||
|
padding: 8px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ButtonContainer = styled(Column)`
|
||||||
|
padding: 8px 12px 4px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const CloseIconWrapper = styled(ButtonText)`
|
||||||
|
display: flex;
|
||||||
|
color: ${({ theme }) => theme.neutral1};
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: 8px 0px 4px;
|
||||||
|
|
||||||
|
:focus {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const StyledThemeButton = styled(ThemeButton)`
|
||||||
|
width: 100%;
|
||||||
|
`
|
||||||
|
|
||||||
|
export function UkDisclaimerModal() {
|
||||||
|
const isOpen = useModalIsOpen(ApplicationModal.UK_DISCLAIMER)
|
||||||
|
const closeModal = useCloseModal()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} onDismiss={closeModal}>
|
||||||
|
<Wrapper gap="md">
|
||||||
|
<CloseIconWrapper onClick={() => closeModal()}>
|
||||||
|
<X size={24} />
|
||||||
|
</CloseIconWrapper>
|
||||||
|
<Column gap="sm">
|
||||||
|
<ThemedText.HeadlineLarge padding="0px 8px" fontSize="24px" lineHeight="32px">
|
||||||
|
<Trans>Disclaimer for UK residents</Trans>
|
||||||
|
</ThemedText.HeadlineLarge>
|
||||||
|
<ThemedText.BodyPrimary padding="8px 8px 12px" lineHeight="24px">
|
||||||
|
{bannerText}
|
||||||
|
</ThemedText.BodyPrimary>
|
||||||
|
</Column>
|
||||||
|
<ButtonContainer gap="md">
|
||||||
|
<StyledThemeButton size={ButtonSize.large} emphasis={ButtonEmphasis.medium} onClick={() => closeModal()}>
|
||||||
|
<Trans>Dismiss</Trans>
|
||||||
|
</StyledThemeButton>
|
||||||
|
</ButtonContainer>
|
||||||
|
</Wrapper>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ import BaseAnnouncementBanner from 'components/Banner/BaseAnnouncementBanner'
|
|||||||
import AddressClaimModal from 'components/claim/AddressClaimModal'
|
import AddressClaimModal from 'components/claim/AddressClaimModal'
|
||||||
import ConnectedAccountBlocked from 'components/ConnectedAccountBlocked'
|
import ConnectedAccountBlocked from 'components/ConnectedAccountBlocked'
|
||||||
import FiatOnrampModal from 'components/FiatOnrampModal'
|
import FiatOnrampModal from 'components/FiatOnrampModal'
|
||||||
|
import { UkDisclaimerModal } from 'components/NavBar/UkDisclaimerModal'
|
||||||
import useAccountRiskCheck from 'hooks/useAccountRiskCheck'
|
import useAccountRiskCheck from 'hooks/useAccountRiskCheck'
|
||||||
import Bag from 'nft/components/bag/Bag'
|
import Bag from 'nft/components/bag/Bag'
|
||||||
import TransactionCompleteModal from 'nft/components/collection/TransactionCompleteModal'
|
import TransactionCompleteModal from 'nft/components/collection/TransactionCompleteModal'
|
||||||
@ -31,6 +32,7 @@ export default function TopLevelModals() {
|
|||||||
<TransactionCompleteModal />
|
<TransactionCompleteModal />
|
||||||
<AirdropModal />
|
<AirdropModal />
|
||||||
<FiatOnrampModal />
|
<FiatOnrampModal />
|
||||||
|
<UkDisclaimerModal />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -90,7 +90,7 @@ export function useUniswapXSwapCallback({
|
|||||||
const { domain, types, values } = updatedOrder.permitData()
|
const { domain, types, values } = updatedOrder.permitData()
|
||||||
|
|
||||||
const signature = await signTypedData(provider.getSigner(account), domain, types, values)
|
const signature = await signTypedData(provider.getSigner(account), domain, types, values)
|
||||||
if (startTime < Math.floor(Date.now() / 1000)) {
|
if (deadline < Math.floor(Date.now() / 1000)) {
|
||||||
throw new SignatureExpiredError()
|
throw new SignatureExpiredError()
|
||||||
}
|
}
|
||||||
return { signature, updatedOrder }
|
return { signature, updatedOrder }
|
||||||
|
|||||||
3678
src/locales/af-ZA.po
Normal file
3678
src/locales/af-ZA.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/ar-SA.po
Normal file
3678
src/locales/ar-SA.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/ca-ES.po
Normal file
3678
src/locales/ca-ES.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/cs-CZ.po
Normal file
3678
src/locales/cs-CZ.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/da-DK.po
Normal file
3678
src/locales/da-DK.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/de-DE.po
Normal file
3678
src/locales/de-DE.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/el-GR.po
Normal file
3678
src/locales/el-GR.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/es-ES.po
Normal file
3678
src/locales/es-ES.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/fi-FI.po
Normal file
3678
src/locales/fi-FI.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/fr-FR.po
Normal file
3678
src/locales/fr-FR.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/he-IL.po
Normal file
3678
src/locales/he-IL.po
Normal file
File diff suppressed because it is too large
Load Diff
3679
src/locales/hu-HU.po
Normal file
3679
src/locales/hu-HU.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/id-ID.po
Normal file
3678
src/locales/id-ID.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/it-IT.po
Normal file
3678
src/locales/it-IT.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/ja-JP.po
Normal file
3678
src/locales/ja-JP.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/ko-KR.po
Normal file
3678
src/locales/ko-KR.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/nl-NL.po
Normal file
3678
src/locales/nl-NL.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/no-NO.po
Normal file
3678
src/locales/no-NO.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/pl-PL.po
Normal file
3678
src/locales/pl-PL.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/pt-BR.po
Normal file
3678
src/locales/pt-BR.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/pt-PT.po
Normal file
3678
src/locales/pt-PT.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/ro-RO.po
Normal file
3678
src/locales/ro-RO.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/ru-RU.po
Normal file
3678
src/locales/ru-RU.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/sl-SI.po
Normal file
3678
src/locales/sl-SI.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/sr-SP.po
Normal file
3678
src/locales/sr-SP.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/sv-SE.po
Normal file
3678
src/locales/sv-SE.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/sw-TZ.po
Normal file
3678
src/locales/sw-TZ.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/th-TH.po
Normal file
3678
src/locales/th-TH.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/tr-TR.po
Normal file
3678
src/locales/tr-TR.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/uk-UA.po
Normal file
3678
src/locales/uk-UA.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/vi-VN.po
Normal file
3678
src/locales/vi-VN.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/zh-CN.po
Normal file
3678
src/locales/zh-CN.po
Normal file
File diff suppressed because it is too large
Load Diff
3678
src/locales/zh-TW.po
Normal file
3678
src/locales/zh-TW.po
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,14 +4,19 @@ import { getDeviceId, sendAnalyticsEvent, sendInitializationEvent, Trace, user }
|
|||||||
import ErrorBoundary from 'components/ErrorBoundary'
|
import ErrorBoundary from 'components/ErrorBoundary'
|
||||||
import Loader from 'components/Icons/LoadingSpinner'
|
import Loader from 'components/Icons/LoadingSpinner'
|
||||||
import NavBar, { PageTabs } from 'components/NavBar'
|
import NavBar, { PageTabs } from 'components/NavBar'
|
||||||
|
import { UK_BANNER_HEIGHT, UK_BANNER_HEIGHT_MD, UK_BANNER_HEIGHT_SM, UkBanner } from 'components/NavBar/UkBanner'
|
||||||
import { useFeatureFlagsIsLoaded } from 'featureFlags'
|
import { useFeatureFlagsIsLoaded } from 'featureFlags'
|
||||||
import { useInfoPoolPageEnabled } from 'featureFlags/flags/infoPoolPage'
|
import { useInfoPoolPageEnabled } from 'featureFlags/flags/infoPoolPage'
|
||||||
|
import { useUniswapXDefaultEnabled } from 'featureFlags/flags/uniswapXDefault'
|
||||||
import { useAtom } from 'jotai'
|
import { useAtom } from 'jotai'
|
||||||
import { useBag } from 'nft/hooks/useBag'
|
import { useBag } from 'nft/hooks/useBag'
|
||||||
import { lazy, Suspense, useEffect, useLayoutEffect, useMemo, useState } from 'react'
|
import { lazy, Suspense, useEffect, useLayoutEffect, useMemo, useState } from 'react'
|
||||||
import { Navigate, Route, Routes, useLocation, useSearchParams } from 'react-router-dom'
|
import { Navigate, Route, Routes, useLocation, useSearchParams } from 'react-router-dom'
|
||||||
import { shouldDisableNFTRoutesAtom } from 'state/application/atoms'
|
import { shouldDisableNFTRoutesAtom } from 'state/application/atoms'
|
||||||
import { useRouterPreference } from 'state/user/hooks'
|
import { useAppSelector } from 'state/hooks'
|
||||||
|
import { AppState } from 'state/reducer'
|
||||||
|
import { RouterPreference } from 'state/routing/types'
|
||||||
|
import { useRouterPreference, useUserOptedOutOfUniswapX } from 'state/user/hooks'
|
||||||
import { StatsigProvider, StatsigUser } from 'statsig-react'
|
import { StatsigProvider, StatsigUser } from 'statsig-react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { SpinnerSVG } from 'theme/components'
|
import { SpinnerSVG } from 'theme/components'
|
||||||
@ -82,15 +87,23 @@ const MobileBottomBar = styled.div`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const HeaderWrapper = styled.div<{ transparent?: boolean }>`
|
const HeaderWrapper = styled.div<{ transparent?: boolean; bannerIsVisible?: boolean; scrollY: number }>`
|
||||||
${flexRowNoWrap};
|
${flexRowNoWrap};
|
||||||
background-color: ${({ theme, transparent }) => !transparent && theme.surface1};
|
background-color: ${({ theme, transparent }) => !transparent && theme.surface1};
|
||||||
border-bottom: ${({ theme, transparent }) => !transparent && `1px solid ${theme.surface3}`};
|
border-bottom: ${({ theme, transparent }) => !transparent && `1px solid ${theme.surface3}`};
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: ${({ bannerIsVisible }) => (bannerIsVisible ? Math.max(UK_BANNER_HEIGHT - scrollY, 0) : 0)}px;
|
||||||
z-index: ${Z_INDEX.dropdown};
|
z-index: ${Z_INDEX.dropdown};
|
||||||
|
|
||||||
|
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) {
|
||||||
|
top: ${({ bannerIsVisible }) => (bannerIsVisible ? Math.max(UK_BANNER_HEIGHT_MD - scrollY, 0) : 0)}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) {
|
||||||
|
top: ${({ bannerIsVisible }) => (bannerIsVisible ? Math.max(UK_BANNER_HEIGHT_SM - scrollY, 0) : 0)}px;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
// this is the same svg defined in assets/images/blue-loader.svg
|
// this is the same svg defined in assets/images/blue-loader.svg
|
||||||
@ -117,12 +130,18 @@ export default function App() {
|
|||||||
const currentPage = getCurrentPageFromLocation(pathname)
|
const currentPage = getCurrentPageFromLocation(pathname)
|
||||||
const isDarkMode = useIsDarkMode()
|
const isDarkMode = useIsDarkMode()
|
||||||
const [routerPreference] = useRouterPreference()
|
const [routerPreference] = useRouterPreference()
|
||||||
const [scrolledState, setScrolledState] = useState(false)
|
|
||||||
const infoPoolPageEnabled = useInfoPoolPageEnabled()
|
const infoPoolPageEnabled = useInfoPoolPageEnabled()
|
||||||
|
const [scrollY, setScrollY] = useState(0)
|
||||||
|
const scrolledState = scrollY > 0
|
||||||
|
const isUniswapXDefaultEnabled = useUniswapXDefaultEnabled()
|
||||||
|
const userOptedOutOfUniswapX = useUserOptedOutOfUniswapX()
|
||||||
|
|
||||||
|
const originCountry = useAppSelector((state: AppState) => state.user.originCountry)
|
||||||
|
const renderUkBannner = Boolean(originCountry) && originCountry === 'GB'
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.scrollTo(0, 0)
|
window.scrollTo(0, 0)
|
||||||
setScrolledState(false)
|
setScrollY(0)
|
||||||
}, [pathname])
|
}, [pathname])
|
||||||
|
|
||||||
const [searchParams] = useSearchParams()
|
const [searchParams] = useSearchParams()
|
||||||
@ -165,12 +184,25 @@ export default function App() {
|
|||||||
}, [isDarkMode])
|
}, [isDarkMode])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// If we're not in the transition period to UniswapX opt-out, set the router preference to whatever is specified.
|
||||||
|
if (!isUniswapXDefaultEnabled) {
|
||||||
|
user.set(CustomUserProperties.ROUTER_PREFERENCE, routerPreference)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the transition period, override the stored API preference to UniswapX if the user hasn't opted out.
|
||||||
|
if (routerPreference === RouterPreference.API && !userOptedOutOfUniswapX) {
|
||||||
|
user.set(CustomUserProperties.ROUTER_PREFERENCE, RouterPreference.X)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, the user has opted out or their preference is UniswapX/client, so set the preference to whatever is specified.
|
||||||
user.set(CustomUserProperties.ROUTER_PREFERENCE, routerPreference)
|
user.set(CustomUserProperties.ROUTER_PREFERENCE, routerPreference)
|
||||||
}, [routerPreference])
|
}, [routerPreference, isUniswapXDefaultEnabled, userOptedOutOfUniswapX])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const scrollListener = () => {
|
const scrollListener = () => {
|
||||||
setScrolledState(window.scrollY > 0)
|
setScrollY(window.scrollY)
|
||||||
}
|
}
|
||||||
window.addEventListener('scroll', scrollListener)
|
window.addEventListener('scroll', scrollListener)
|
||||||
return () => window.removeEventListener('scroll', scrollListener)
|
return () => window.removeEventListener('scroll', scrollListener)
|
||||||
@ -200,6 +232,12 @@ export default function App() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const blockedPaths = document.querySelector('meta[property="x:blocked-paths"]')?.getAttribute('content')?.split(',')
|
||||||
|
const shouldBlockPath = blockedPaths?.includes(pathname) ?? false
|
||||||
|
if (shouldBlockPath && pathname !== '/swap') {
|
||||||
|
return <Navigate to="/swap" replace />
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<DarkModeQueryParamReader />
|
<DarkModeQueryParamReader />
|
||||||
@ -214,7 +252,8 @@ export default function App() {
|
|||||||
api: process.env.REACT_APP_STATSIG_PROXY_URL,
|
api: process.env.REACT_APP_STATSIG_PROXY_URL,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<HeaderWrapper transparent={isHeaderTransparent}>
|
{renderUkBannner && <UkBanner />}
|
||||||
|
<HeaderWrapper transparent={isHeaderTransparent} bannerIsVisible={renderUkBannner} scrollY={scrollY}>
|
||||||
<NavBar blur={isHeaderTransparent} />
|
<NavBar blur={isHeaderTransparent} />
|
||||||
</HeaderWrapper>
|
</HeaderWrapper>
|
||||||
<BodyWrapper>
|
<BodyWrapper>
|
||||||
|
|||||||
@ -43,6 +43,7 @@ export enum ApplicationModal {
|
|||||||
TAX_SERVICE,
|
TAX_SERVICE,
|
||||||
TIME_SELECTOR,
|
TIME_SELECTOR,
|
||||||
VOTE,
|
VOTE,
|
||||||
|
UK_DISCLAIMER,
|
||||||
UNISWAP_NFT_AIRDROP_CLAIM,
|
UNISWAP_NFT_AIRDROP_CLAIM,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user