Skip to content

Commit

Permalink
feat: dynamic campaign promotions (#202)
Browse files Browse the repository at this point in the history
  • Loading branch information
schmanu authored Sep 19, 2024
1 parent 9c57b1a commit 8d175c8
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 187 deletions.
10 changes: 0 additions & 10 deletions src/components/Claim/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ import SafeToken from '@/public/images/token.svg'
import css from './styles.module.css'
import { useRouter } from 'next/router'
import { AppRoutes } from '@/config/routes'
import { canRedeemSep5Airdrop } from '@/utils/airdrop'
import { Sep5InfoBox } from '../Sep5InfoBox'
import { formatAmount } from '@/utils/formatters'
import { ClaimCard } from '../ClaimCard'
import { InfoAlert } from '../InfoAlert'
Expand Down Expand Up @@ -78,8 +76,6 @@ const ClaimOverview = (): ReactElement => {
// Allocation, vesting and voting power
const { data: allocation } = useSafeTokenAllocation()

const canRedeemSep5 = canRedeemSep5Airdrop(allocation)

const { ecosystemVesting, investorVesting } = getVestingTypes(allocation?.vestingData ?? [])

const { sep5, user, ecosystem, investor, total } = useTaggedAllocations()
Expand Down Expand Up @@ -213,12 +209,6 @@ const ClaimOverview = (): ReactElement => {
</Typography>
</Box>

{canRedeemSep5 && (
<Grid item xs={12}>
<Sep5InfoBox />
</Grid>
)}

<Grid item container gap={3} flexWrap="nowrap" xs={12} mb={1}>
<Grid item xs={6}>
<TextField
Expand Down
27 changes: 2 additions & 25 deletions src/components/DashboardWidgets/ClaimingWidget.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BigNumber } from 'ethers'
import { formatEther } from 'ethers/lib/utils'
import { useSafeAppsSDK } from '@safe-global/safe-apps-react-sdk'
import { Box, Button, Typography, Link, Skeleton, Card, IconButton, Grid, Tooltip } from '@mui/material'
import { Box, Button, Typography, Link, Skeleton, Card, IconButton, Grid } from '@mui/material'
import ModeEditOutlinedIcon from '@mui/icons-material/ModeEditOutlined'
import CheckSharpIcon from '@mui/icons-material/CheckSharp'
import { useState } from 'react'
Expand All @@ -10,15 +10,12 @@ import type { ReactElement } from 'react'

import { ExternalLink } from '@/components/ExternalLink'
import SafeToken from '@/public/images/token.svg'
import { DISCORD_URL, FORUM_URL, SEP5_EXPIRATION_DATE } from '@/config/constants'
import { DISCORD_URL, FORUM_URL } from '@/config/constants'
import { getGovernanceAppSafeAppUrl } from '@/utils/safe-apps'
import { useDelegate } from '@/hooks/useDelegate'
import { useSafeTokenAllocation } from '@/hooks/useSafeTokenAllocation'
import { SelectedDelegate } from '@/components/SelectedDelegate'
import { formatAmount } from '@/utils/formatters'
import { Sep5DeadlineChip } from '@/components/Sep5DeadlineChip'
import { TypographyChip } from '@/components/TypographyChip'
import { canRedeemSep5Airdrop } from '@/utils/airdrop'

import css from './styles.module.css'

Expand Down Expand Up @@ -82,7 +79,6 @@ const VotingPowerWidget = (): ReactElement => {
const { safe } = useSafeAppsSDK()
const delegate = useDelegate()
const { data: allocation } = useSafeTokenAllocation()
const canRedeemSep5 = canRedeemSep5Airdrop(allocation)

const totalClaimed = allocation?.vestingData.reduce((acc, { amountClaimed }) => {
return acc.add(amountClaimed)
Expand All @@ -96,23 +92,6 @@ const VotingPowerWidget = (): ReactElement => {

return (
<Grid container p={1} height={1}>
{canRedeemSep5 && (
<Grid item xs={12} display="flex" justifyContent="space-between" mb="auto">
<TypographyChip fontWeight={700} px={1}>
New allocation
</TypographyChip>

<Tooltip
title={`You qualify for a new SAFE allocation! Ensure you execute at least one claim before ${SEP5_EXPIRATION_DATE}`}
arrow
placement="top"
>
<span>
<Sep5DeadlineChip px={1} />
</span>
</Tooltip>
</Grid>
)}
<Grid item xs={12} display="flex" flexDirection="column" alignItems="center" mt={4}>
<Typography variant="subtitle2" color="primary.light" textAlign="center">
Your voting power
Expand Down Expand Up @@ -164,7 +143,6 @@ const VotingPowerWidget = (): ReactElement => {

export const ClaimingWidget = (): ReactElement => {
const { data: allocation, isLoading } = useSafeTokenAllocation()
const canRedeemSep5 = canRedeemSep5Airdrop(allocation)

if (isLoading) {
return (
Expand All @@ -186,7 +164,6 @@ export const ClaimingWidget = (): ReactElement => {
sx={{
minWidth: WIDGET_WIDTH,
maxWidth: WIDGET_WIDTH,
border: canRedeemSep5 ? ({ palette }) => `1px solid ${palette.primary.main}` : undefined,
}}
>
<>{allocation?.votingPower.eq(0) ? <CtaWidget /> : <VotingPowerWidget />}</>
Expand Down
159 changes: 80 additions & 79 deletions src/components/Points/CampaignPromo.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,45 @@
import { Box, LinearProgress, Stack, SvgIcon, Typography } from '@mui/material'
import { Box, LinearProgress, Stack, Typography } from '@mui/material'
import PaperContainer from '../PaperContainer'
import MorhoIcon from '@/public/images/morpho.svg'
import SpotlightIcon from '@/public/images/spotlight.svg'

import css from './styles.module.css'
import { MORPHO_CAMPAIGN_IDS } from '@/config/constants'
import { useChainId } from '@/hooks/useChainId'
import { useCampaignInfo } from '@/hooks/useCampaigns'
import { useCampaignsPaginated } from '@/hooks/useCampaigns'
import { getRelativeTime } from '@/utils/date'
import { getSafeAppUrl } from '@/utils/safe-apps'
import { useAddress } from '@/hooks/useAddress'
import { useMemo } from 'react'
import { ExternalLink } from '../ExternalLink'
import { formatAmount } from '@/utils/formatters'

export const CampaignPromo = () => {
const chainId = useChainId()
const address = useAddress()
const morphoCampaignId = MORPHO_CAMPAIGN_IDS[chainId]
const { data: campaignsPage } = useCampaignsPaginated()

const morphoCampaign = useCampaignInfo(morphoCampaignId)
// We only check the first page as it contains the most up-to-date campaigns
const promotedCampaign = useMemo(
() => campaignsPage?.find((campaign) => campaign.isPromoted && new Date(campaign.endDate).getTime() > Date.now()),
[campaignsPage],
)

const safeAppUrl = useMemo(
() => (address ? getSafeAppUrl(chainId, address, 'https://safe-app.morpho.org/') : ''),
[chainId, address],
() =>
address && promotedCampaign?.safeAppUrl
? getSafeAppUrl(chainId, address, promotedCampaign?.safeAppUrl ?? '')
: undefined,
[address, chainId, promotedCampaign?.safeAppUrl],
)

if (!morphoCampaign) {
if (!promotedCampaign) {
return null
}

const hasStarted = new Date(morphoCampaign.startDate).getTime() <= Date.now()
const hasEnded = new Date(morphoCampaign.endDate).getTime() <= Date.now()
const hasStarted = new Date(promotedCampaign.startDate).getTime() <= Date.now()
const hasEnded = new Date(promotedCampaign.endDate).getTime() <= Date.now()

const progress =
Math.max(Date.now() - new Date(morphoCampaign.startDate).getTime(), 0) /
(new Date(morphoCampaign.endDate).getTime() - new Date(morphoCampaign.startDate).getTime())
Math.max(Date.now() - new Date(promotedCampaign.startDate).getTime(), 0) /
(new Date(promotedCampaign.endDate).getTime() - new Date(promotedCampaign.startDate).getTime())

if (hasEnded) {
return null
Expand All @@ -45,7 +50,7 @@ export const CampaignPromo = () => {
<LinearProgress
className={css.progressBar}
variant="determinate"
value={progress}
value={progress * 100}
sx={{
mt: '-40px',
ml: '-32px',
Expand All @@ -54,78 +59,74 @@ export const CampaignPromo = () => {
/>
<Stack alignItems="center">
<Typography variant="body2" color="text.secondary" mb={2}>
{morphoCampaign
{promotedCampaign
? !hasStarted
? `Starts ${getRelativeTime(new Date(morphoCampaign.startDate))}`
: `Ends ${getRelativeTime(new Date(morphoCampaign.endDate))}`
? `Starts ${getRelativeTime(new Date(promotedCampaign.startDate))}`
: `Ends ${getRelativeTime(new Date(promotedCampaign.endDate))}`
: null}
</Typography>
<Box position="relative">
<SvgIcon
component={SpotlightIcon}
inheritViewBox
sx={{
position: 'absolute',
top: 0,
left: '-21px',
width: '42px',
height: '56px',
}}
/>
<SvgIcon
component={MorhoIcon}
inheritViewBox
sx={{
width: '148px',
height: '100px',
}}
/>
<SvgIcon
component={SpotlightIcon}
inheritViewBox
sx={{
position: 'absolute',
top: 0,
right: '-21px',
width: '42px',
height: '56px',
transform: 'rotateY(180deg)',
}}
/>
{promotedCampaign.iconUrl ? (
<Box position="relative">
{promotedCampaign.iconUrl && (
<img
alt={'Campaign icon'}
src={promotedCampaign.iconUrl}
style={{ maxWidth: '176px', maxHeight: '80px', borderRadius: '8px' }}
/>
)}
</Box>
) : (
<Typography variant="h1" fontWeight={700} maxWidth="80%" className={css.gradientText} mb={2}>
{promotedCampaign.name}
</Typography>
)}
<Box className={css.prizePoolBox}>
<Typography variant="overline" color="text.secondary" fontWeight={700}>
Prize pool
</Typography>
{promotedCampaign.rewardValue ? (
<Typography mt={1} variant="h4" fontSize="27px" fontWeight={700}>
{formatAmount(Number(promotedCampaign.rewardValue), 0)}{' '}
<span style={{ color: '#817EE9' }}>{promotedCampaign.rewardText}</span>
</Typography>
) : (
<Typography mt={1} variant="h4" fontSize="27px" fontWeight={700}>
{promotedCampaign.rewardText}
</Typography>
)}

<Typography variant="body2">& Safe points</Typography>
</Box>

<Typography mt={5} variant="overline" color="text.secondary">
Prize pool
</Typography>
<Typography mt={1} variant="h4" fontSize="27px" fontWeight={700}>
600,000 <span style={{ color: '#2470FF' }}>MORPHO</span>
</Typography>
<Typography variant="body2">& Safe points</Typography>
<Typography variant="body2" color="text.secondary" mt={5}>
<ExternalLink href="https://dub.sh/morphocampaign" isPartner>
Learn more
</ExternalLink>{' '}
and participate in campaign:
Participate in campaign:
</Typography>
<Stack direction="row" alignItems="center" spacing={2} mt={2}>
<ExternalLink variant="button" color="primary" href={safeAppUrl} target="_blank" isPartner>
Safe App
</ExternalLink>
<Typography>or</Typography>
<ExternalLink
isPartner
href="https://app.morpho.org/"
target="_blank"
variant="button"
sx={{
color: '#2470FF',
'&:hover': {
backgroundColor: ({ palette }) => `rgba(36, 112, 255, ${palette.action.hoverOpacity})`,
},
}}
>
Morpho
</ExternalLink>
{safeAppUrl && (
<>
<ExternalLink variant="button" color="primary" href={safeAppUrl} target="_blank" isPartner>
Safe App
</ExternalLink>
<Typography>or</Typography>
</>
)}

{promotedCampaign.partnerUrl && (
<ExternalLink
isPartner
href={promotedCampaign.partnerUrl}
target="_blank"
variant="button"
sx={{
color: '#FFF',
'&:hover': {
backgroundColor: ({ palette }) => `rgba(255, 255, 255, ${palette.action.hoverOpacity})`,
},
}}
>
{promotedCampaign.name}
</ExternalLink>
)}
</Stack>
</Stack>
</PaperContainer>
Expand Down
12 changes: 12 additions & 0 deletions src/components/Points/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,15 @@
.progressBar {
background: none;
}

.prizePoolBox {
border-radius: 6px;
border: 1px solid var(--mui-palette-border-light);
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
margin-top: 32px;
padding: 16px 0px;
}
16 changes: 0 additions & 16 deletions src/components/Sep5DeadlineChip/index.tsx

This file was deleted.

44 changes: 0 additions & 44 deletions src/components/Sep5InfoBox/index.tsx

This file was deleted.

Loading

0 comments on commit 8d175c8

Please sign in to comment.