Skip to content

Commit

Permalink
Display legal disclaimer to first time swappers
Browse files Browse the repository at this point in the history
  • Loading branch information
jmealy committed Apr 29, 2024
1 parent ddcaaf5 commit cd85798
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 86 deletions.
25 changes: 0 additions & 25 deletions src/components/common/BlockedAddress/index.stories.tsx

This file was deleted.

53 changes: 15 additions & 38 deletions src/components/common/BlockedAddress/index.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,28 @@
import type { ReactElement } from 'react'
import { Box, Button, Divider, Paper, Stack, SvgIcon, Typography, useMediaQuery, useTheme } from '@mui/material'
import Link from 'next/link'
import InfoIcon from '@/public/images/notifications/info.svg'
import css from './styles.module.css'
import { useMediaQuery, useTheme } from '@mui/material'
import { shortenAddress } from '@/utils/formatters'
import { useRouter } from 'next/router'
import Disclaimer from '@/components/common/Disclaimer'
import { AppRoutes } from '@/config/routes'

export const BlockedAddress = ({ address }: { address: string }): ReactElement => {
export const BlockedAddress = ({ address }: { address?: string }): ReactElement => {
const theme = useTheme()
const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
const displayAddress = isMobile ? shortenAddress(address) : address
const displayAddress = address && isMobile ? shortenAddress(address) : address
const router = useRouter()

return (
<div className={css.container}>
<Paper sx={{ maxWidth: '500px' }}>
<Stack
padding="var(--space-3)"
gap={2}
display="flex"
alignItems="center"
sx={({ palette }) => ({ borderBottom: `1px solid ${palette.border.light}` })}
>
<Typography color="var(--color-text-Secondary, #A1A3A7)">{displayAddress}</Typography>
const handleAccept = () => {
router.push({ pathname: AppRoutes.home, query: router.query })
}

<Box className={css.iconCircle}>
<SvgIcon component={InfoIcon} inheritViewBox fontSize="medium" />
</Box>
<Typography variant="h3" fontWeight={700}>
Blocked Address
</Typography>
<Typography variant="body2">
This signer address is blocked by the Safe interface, due to being associated with the blocked activities by
the U.S. Department of Treasury in the Specially Designated Nationals (SDN) list.{' '}
</Typography>
<Divider />
</Stack>
<Box display="flex" justifyContent="center" pt={3} pb={2}>
<Link href={{ pathname: AppRoutes.home, query: router.query }}>
<Button variant="contained" size="small" sx={{ px: '16px' }}>
Got it
</Button>
</Link>
</Box>
</Paper>
</div>
return (
<Disclaimer
title="Blocked Address"
subtitle={displayAddress}
content="This signer address is blocked by the Safe interface, due to being associated with the blocked activities by
the U.S. Department of Treasury in the Specially Designated Nationals (SDN) list."
onAccept={handleAccept}
/>
)
}

Expand Down
40 changes: 40 additions & 0 deletions src/components/common/Disclaimer/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { Meta, StoryObj } from '@storybook/react'
import Disclaimer from './index'
import LegalDisclaimerContent from '@/components/common/LegalDisclaimerContent'

const meta = {
component: Disclaimer,
parameters: {
componentSubtitle: 'Renders an information block intended for users whose address is blocked by OFAC',
},
tags: ['autodocs'],
} satisfies Meta<typeof Disclaimer>

export default meta
type Story = StoryObj<typeof meta>

export const BlockedAddress: Story = {
args: {
subtitle: '0xD3a484faEa53313eF85b5916C9302a3E304ae622',
title: 'Blocked Address',
buttonText: 'continue',
content:
'This signer address is blocked by the Safe interface, due to being associated with the blocked activities by the U.S. Department of Treasury in the Specially Designated Nationals (SDN) list.',
onAccept: () => {},
},
parameters: {
design: {
type: 'figma',
url: 'https://www.figma.com/file/VyA38zUPbJ2zflzCIYR6Nu/Swap?node-id=6167%3A14371&mode=dev',
},
},
}

export const LegalDisclaimer: Story = {
args: {
title: 'Legal Disclaimer',
content: <LegalDisclaimerContent withTitle={false} />,
buttonText: 'continue',
onAccept: () => {},
},
}
50 changes: 50 additions & 0 deletions src/components/common/Disclaimer/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { ReactElement, ReactNode } from 'react'
import { Box, Button, Divider, Paper, Stack, SvgIcon, Typography } from '@mui/material'
import InfoIcon from '@/public/images/notifications/info.svg'
import css from './styles.module.css'

export const Disclaimer = ({
title,
subtitle,
buttonText,
content,
onAccept,
}: {
title: string
subtitle?: string
buttonText?: string
content: ReactNode
onAccept: () => void
}): ReactElement => {
return (
<div className={css.container}>
<Paper sx={{ maxWidth: '500px' }}>
<Stack
padding="var(--space-3)"
gap={2}
display="flex"
alignItems="center"
sx={({ palette }) => ({ borderBottom: `1px solid ${palette.border.light}` })}
>
{subtitle && <Typography color="var(--color-text-Secondary, #A1A3A7)">{subtitle}</Typography>}

<Box className={css.iconCircle}>
<SvgIcon component={InfoIcon} inheritViewBox fontSize="medium" />
</Box>
<Typography variant="h3" fontWeight={700}>
{title}
</Typography>
<Typography variant="body2">{content}</Typography>
<Divider />
</Stack>
<Box display="flex" justifyContent="center" pt={3} pb={2}>
<Button variant="contained" size="small" sx={{ px: '16px' }} onClick={onAccept}>
{buttonText || 'Got it'}
</Button>
</Box>
</Paper>
</div>
)
}

export default Disclaimer
14 changes: 14 additions & 0 deletions src/components/common/Disclaimer/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.container {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}

.iconCircle {
color: var(--color-info-main);
border-radius: 50%;
display: flex;
padding: var(--space-1);
background: #d7f6ff;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@ import { Typography } from '@mui/material'

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

const LegalDisclaimer = (): JSX.Element => (
const LegalDisclaimerContent = ({ withTitle = true }: { withTitle?: boolean }): JSX.Element => (
<div className={css.disclaimerContainer}>
<Typography variant="body2" color="text.secondary" mx={8}>
Before starting to use Safe dApps...
</Typography>
<Typography variant="h3" fontWeight={700} my={3}>
Disclaimer
</Typography>
{withTitle && (
<Typography variant="h3" fontWeight={700} my={3}>
Disclaimer
</Typography>
)}
<div className={css.disclaimerInner}>
<Typography mb={4}>
You are now accessing third-party apps, which we do not own, control, maintain or audit. We are not liable for
any loss you may suffer in connection with interacting with the apps, which is at your own risk.
You are now accessing a third-party app, which we do not own, control, maintain or audit. We are not liable for
any loss you may suffer in connection with interacting with the app, which is at your own risk.
</Typography>

<Typography mb={4}>
Expand All @@ -33,4 +32,4 @@ const LegalDisclaimer = (): JSX.Element => (
</div>
)

export default LegalDisclaimer
export default LegalDisclaimerContent
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

.disclaimerContainer p,
.disclaimerContainer h3 {
line-height: 24px;
}

.disclaimerInner p {
text-align: justify;
}
4 changes: 2 additions & 2 deletions src/components/safe-apps/SafeAppsInfoModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import { Grid, LinearProgress } from '@mui/material'

import type { BrowserPermission } from '@/hooks/safe-apps/permissions'
import Slider from './Slider'
import LegalDisclaimer from './LegalDisclaimer'
import AllowedFeaturesList from './AllowedFeaturesList'
import type { AllowedFeatures, AllowedFeatureSelection } from '../types'
import { PermissionStatus } from '../types'
import UnknownAppWarning from './UnknownAppWarning'
import { getOrigin } from '../utils'
import LegalDisclaimerContent from '@/components/common/LegalDisclaimerContent'

type SafeAppsInfoModalProps = {
onCancel: () => void
Expand Down Expand Up @@ -135,7 +135,7 @@ const SafeAppsInfoModal = ({
/>
<Grid container justifyContent="center" alignItems="center" direction="column" textAlign="center" p={3}>
<Slider onSlideChange={handleSlideChange}>
{!isConsentAccepted && <LegalDisclaimer />}
{!isConsentAccepted && <LegalDisclaimerContent />}

{!isPermissionsReviewCompleted && (
<AllowedFeaturesList
Expand Down
9 changes: 0 additions & 9 deletions src/components/safe-apps/SafeAppsInfoModal/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,6 @@
background-size: cover;
}

.disclaimerContainer p,
.disclaimerContainer h3 {
line-height: 24px;
}

.disclaimerInner p {
text-align: justify;
}

.domainIcon {
position: relative;
top: 6px;
Expand Down
22 changes: 22 additions & 0 deletions src/features/swap/useSwapLegalDisclaimer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useCallback } from 'react'
import useLocalStorage from '@/services/local-storage/useLocalStorage'

const SWAPS_CONSENT_STORAGE_KEY = 'swapConsent'

const useSafeAppsInfoModal = (): {
isConsentAccepted: boolean
onAccept: () => void
} => {
const [isConsentAccepted = false, setIsConsentAccepted] = useLocalStorage<boolean>(SWAPS_CONSENT_STORAGE_KEY)

const onAccept = useCallback(() => {
setIsConsentAccepted(true)
}, [setIsConsentAccepted])

return {
isConsentAccepted,
onAccept,
}
}

export default useSafeAppsInfoModal
14 changes: 13 additions & 1 deletion src/pages/swap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ import type { NextPage } from 'next'
import Head from 'next/head'
import { useRouter } from 'next/router'
import SwapWidget from '@/features/swap'
import useSwapLegalDisclaimer from '@/features/swap/useSwapLegalDisclaimer'
import LegalDisclaimerContent from '@/components/common/LegalDisclaimerContent'
import Disclaimer from '@/components/common/Disclaimer'

const Swap: NextPage = () => {
const router = useRouter()
const { token, amount } = router.query
const { isConsentAccepted, onAccept } = useSwapLegalDisclaimer()

let sell = undefined
if (token && amount) {
Expand All @@ -21,7 +25,15 @@ const Swap: NextPage = () => {
</Head>

<main className="swapWrapper">
<SwapWidget sell={sell} />
{isConsentAccepted ? (
<SwapWidget sell={sell} />
) : (
<Disclaimer
title="Legal Disclaimer"
content={<LegalDisclaimerContent withTitle={false} />}
onAccept={onAccept}
/>
)}
</main>
</>
)
Expand Down
3 changes: 2 additions & 1 deletion src/services/ofac/blockedAddressList.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,5 +149,6 @@
"0x530a64c0ce595026a4a556b703644228179e2d57",
"0xfac583c0cf07ea434052c49115a4682172ab6b4f",
"0x961c5be54a2ffc17cf4cb021d863c42dacd47fc1",
"0x983a81ca6fb1e441266d2fbcb7d8e530ac2e05a2"
"0x983a81ca6fb1e441266d2fbcb7d8e530ac2e05a2",
"0xC62CBc9d49ba64EC27a1c27fAA9c7f2ce5D583C8"
]
1 change: 1 addition & 0 deletions src/utils/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const FeatureRoutes = {
}

export const hasFeature = (chain: ChainInfo, feature: FEATURES): boolean => {
if (feature === FEATURES.NATIVE_SWAPS) return true
return (chain.features as string[]).includes(feature)
}

Expand Down

0 comments on commit cd85798

Please sign in to comment.