Skip to content

Commit

Permalink
feat: coinbase cb1 campaign (#11243)
Browse files Browse the repository at this point in the history
<!--
Before opening a pull request, please read the [contributing
guidelines](https://github.com/pancakeswap/pancake-frontend/blob/develop/CONTRIBUTING.md)
first
-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces a new feature for the `Cb1Membership`, enhancing user
engagement by displaying a modal to eligible users. It also updates
translations and adds constants related to this feature.

### Detailed summary
- Added `LS_CB1` constant to `apps/web/src/config/constants/index.ts`.
- Introduced `Cb1Membership` component in
`apps/web/src/components/Cb1/Cb1Membership.tsx`.
- Implemented logic for showing the membership modal based on user
eligibility.
- Updated translations in
`packages/localization/src/config/translations.json`.
- Refactored imports in `apps/web/src/pages/_app.tsx` for new hooks and
components.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your
question}`

<!-- end pr-codex -->
  • Loading branch information
chef-ryan authored Mar 3, 2025
1 parent 19a0bc7 commit 8d1ab26
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 3 deletions.
75 changes: 75 additions & 0 deletions apps/web/src/components/Cb1/Cb1Membership.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { useTranslation } from '@pancakeswap/localization'
import { Button, Modal, ModalV2, Text, useMatchBreakpoints } from '@pancakeswap/uikit'
import { ASSET_CDN } from 'config/constants/endpoints'
import { useRouter } from 'next/router'
import { Suspense, useEffect, useState } from 'react'
import styled from 'styled-components'
import { useShowCb1Popup } from './useCb1Membership'

export const Cb1Membership = () => {
return (
<Suspense fallback={null}>
<Cb1Inner />
</Suspense>
)
}

const Cb1Inner = () => {
const { t } = useTranslation()
const showCb1 = useShowCb1Popup()
const [close, setClose] = useState(showCb1)
const { isMobile } = useMatchBreakpoints()
const router = useRouter()

useEffect(() => {
if (!showCb1) {
setClose(false)
}
}, [showCb1])
return (
<ModalV2
isOpen={showCb1 && !close}
closeOnOverlayClick
onDismiss={() => {
setClose(true)
}}
>
<Modal title={t('You are Eligible!')} width={isMobile ? '100%' : '336px'}>
<Cb1Image src={`${ASSET_CDN}/web/promotions/cb1.png`} />
<Text
style={{
marginTop: '24px',
}}
>
$8.453 {t('to be earned!')}
</Text>
<Text
style={{
marginTop: '24px',
}}
>
<b>Coinbase One </b>
{t(
'members who trade on PancakeSwap on Base, BNB, or Arbitrum are eligible to earn a portion of $8453 airdropped to their wallet biweekly! Must trade a minimum of $100 to qualify.',
)}
</Text>
<Button
style={{
marginTop: '24px',
}}
onClick={() => {
router.replace('/')
setClose(true)
}}
>
{t('Trade now to participate!')}
</Button>
</Modal>
</ModalV2>
)
}

const Cb1Image = styled.img`
width: 100%;
height: auto;
`
68 changes: 68 additions & 0 deletions apps/web/src/components/Cb1/useCb1Membership.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { getChainName } from '@pancakeswap/chains'
import { LS_CB1 } from 'config/constants'
import useAccountActiveChain from 'hooks/useAccountActiveChain'
import { useEffect, useState } from 'react'

interface CB1State {
expired: number
}

const BASE_URI = 'https://attestation-api.pancakeswap.com'

const EXPIRE = 1000 * 24 * 3600

async function getCb1Membership(chain: string, address: string) {
const resp = await fetch(`${BASE_URI}/api/attestation/base?userAddress=${address}`)
const json = await resp.json()
const attested = Boolean(json?.qualified)
return attested
}

function updateExpire(address: string) {
const cb1State: CB1State = {
expired: Date.now() + EXPIRE,
}
localStorage.setItem(`${LS_CB1}-${address}`, JSON.stringify(cb1State))
}

async function showCb1Popup(chain?: string, address?: string) {
if (!address || !chain) {
return false
}
if (!['base', 'bsc', 'arb'].includes(chain)) {
return false
}

const lsItem = localStorage.getItem(`${LS_CB1}-${address}`)
if (lsItem) {
const cb1State: CB1State = JSON.parse(lsItem)
const shouldShow = Date.now() > cb1State.expired
if (shouldShow) {
updateExpire(address)
}
return shouldShow
}
const attested = await getCb1Membership(chain, address)
if (attested) {
updateExpire(address)
}
return attested
}

export const useShowCb1Popup = () => {
const { account, chainId } = useAccountActiveChain()
const [showCb1, setShowCb1] = useState(false)

const chainName = getChainName(chainId)

useEffect(() => {
const load = async () => {
const show = await showCb1Popup(chainName, account)
requestAnimationFrame(() => setShowCb1(show))
}

load()
}, [account, chainName])

return showCb1
}
2 changes: 2 additions & 0 deletions apps/web/src/config/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@ export const QUERY_SETTINGS_WITHOUT_INTERVAL_REFETCH = {
}

export const PERSIST_CHAIN_KEY = 'persistChain'

export const LS_CB1 = 'cb1-state'
6 changes: 4 additions & 2 deletions apps/web/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,18 @@ import { V4CakeIcon } from 'views/Home/components/V4CakeIcon'
import { AdPanel } from 'components/AdPanel'
import { layoutDesktopAdIgnoredPages, layoutMobileAdIgnoredPages } from 'components/AdPanel/config'
import { shouldRenderOnPages } from 'components/AdPanel/renderConditions'
import { Cb1Membership } from 'components/Cb1/Cb1Membership'
import { ZKSyncAirdropModalWithAutoPopup } from 'components/ClaimZksyncAirdropModal'
import { useDataDogRUM } from 'hooks/useDataDogRUM'
import { useLoadExperimentalFeatures } from 'hooks/useExperimentalFeatureEnabled'
import useInitNotificationsClient from 'hooks/useInitNotificationsClient'
import useOptionsSunsetNotification from 'hooks/useOptionsSunsetNotification'
import { useVercelFeatureFlagOverrides } from 'hooks/useVercelToolbar'
import { useWalletConnectRouterSync } from 'hooks/useWalletConnectRouterSync'
import { useWeb3WalletView } from 'hooks/useWeb3WalletView'
import { useInitGlobalWorker } from 'hooks/useWorker'
import { persistor, useStore } from 'state'
import { usePollBlockNumber } from 'state/block/hooks'
import { useWalletConnectRouterSync } from 'hooks/useWalletConnectRouterSync'
import useOptionsSunsetNotification from 'hooks/useOptionsSunsetNotification'
import { Blocklist, Updaters } from '..'
import { SEO } from '../../next-seo.config'
import Providers from '../Providers'
Expand Down Expand Up @@ -203,6 +204,7 @@ const App = ({ Component, pageProps }: AppPropsWithLayout) => {
<AffiliateSunsetModal />
<SimpleStakingSunsetModal />
<VercelToolbar />
<Cb1Membership />
</ProductionErrorBoundary>
)
}
Expand Down
6 changes: 5 additions & 1 deletion packages/localization/src/config/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -3729,5 +3729,9 @@
"Trade Perpetual v2 to Win $10,000.": "Trade Perpetual v2 to Win $10,000.",
"Trade $TST, $PEPE, and More on Perpetual V2 to Win $10,000.": "Trade $TST, $PEPE, and More on Perpetual V2 to Win $10,000.",
"Deleted": "Deleted",
"PancakeSwap Options will officially be retired on March 11th, 2025, at 08:00 UTC, please withdraw liquidity by the deadline or migrate to PancakeSwap V3 pools.": "PancakeSwap Options will officially be retired on March 11th, 2025, at 08:00 UTC, please withdraw liquidity by the deadline or migrate to PancakeSwap V3 pools."
"PancakeSwap Options will officially be retired on March 11th, 2025, at 08:00 UTC, please withdraw liquidity by the deadline or migrate to PancakeSwap V3 pools.": "PancakeSwap Options will officially be retired on March 11th, 2025, at 08:00 UTC, please withdraw liquidity by the deadline or migrate to PancakeSwap V3 pools.",
"You are Eligible!": "You are Eligible!",
"to be earned!": "to be earned!",
"members who trade on PancakeSwap on Base, BNB, or Arbitrum are eligible to earn a portion of $8453 airdropped to their wallet biweekly! Must trade a minimum of $100 to qualify.": "members who trade on PancakeSwap on Base, BNB, or Arbitrum are eligible to earn a portion of $8453 airdropped to their wallet biweekly! Must trade a minimum of $100 to qualify.",
"Trade now to participate!": "Trade now to participate!"
}

0 comments on commit 8d1ab26

Please sign in to comment.