Skip to content

Commit

Permalink
Polish Safenet UI for Security Checks (#4486)
Browse files Browse the repository at this point in the history
## What it solves

Designs for the Safenet UI included changes to the security check UI elements; this PR implements those styling changes.

## How this PR fixes it

A new `GradientBoxSafenet` UI element was created that wraps an inner element in a box. This was done because implementing gradients on borders is kind of annoying, and so having a component to abstract this away was nice.

UI was changed in both the TX details view in the queue, but also for the signing and execution flow.

## Screenshots

### Transaction Details

![image](https://github.com/user-attachments/assets/3e38cb68-1bde-48a6-a2e5-324e84af6af6)

### Sign and Execute Flow

![image](https://github.com/user-attachments/assets/554ea7d6-c626-4550-b924-268764f9dad7)

### Verification Link

![image](https://github.com/user-attachments/assets/5dc3f587-8540-45d7-a6f1-efb4549fa330)
  • Loading branch information
nlordell authored Nov 6, 2024
1 parent 5979f3f commit 0a8a8ce
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 51 deletions.
58 changes: 58 additions & 0 deletions src/components/common/GradientBoxSafenet/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Box, SvgIcon, Typography } from '@mui/material'
import SafenetIcon from '@/public/images/safenet.svg'
import { type CSSProperties, type ReactNode } from 'react'

const GradientBoxSafenet = ({
heading,
children,
className,
style,
}: {
heading?: string
children?: ReactNode
className?: string
style?: CSSProperties
}) => {
return (
<Box
className={className}
style={{
background: 'linear-gradient(90deg, #32f970 0%, #eed509 100%)',
borderRadius: 'calc(var(--space-1) - 1px)',
display: 'flex',
alignItems: 'stretch',
flexDirection: 'column',
justifyContent: 'space-between',
...style,
}}
>
<Box
style={{
color: 'var(--color-static-main)',
padding: 'calc(var(--space-1) / 2) var(--space-1)',
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<SvgIcon component={SafenetIcon} inheritViewBox fontSize="small" />
<Typography variant="h5" fontSize="small">
{heading ?? 'Safenet'}
</Typography>
</Box>
<Box
className="GradientBoxSafenet-content"
style={{
background: 'var(--color-background-paper)',
borderRadius: '0 0 var(--space-1) var(--space-1)',
margin: '1px',
}}
>
{children}
</Box>
</Box>
)
}

export default GradientBoxSafenet
25 changes: 14 additions & 11 deletions src/components/transactions/TxDetails/Summary/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { calculateSafeTransactionHash } from '@safe-global/protocol-kit/dist/src
import useSafeInfo from '@/hooks/useSafeInfo'
import { SafeTxHashDataRow } from './SafeTxHashDataRow'
import { SafenetTxSimulation } from '@/components/tx/security/safenet'
import GradientBoxSafenet from '@/components/common/GradientBoxSafenet'

interface Props {
txDetails: TransactionDetails
Expand Down Expand Up @@ -76,17 +77,19 @@ const Summary = ({ txDetails, defaultExpanded = false, hideDecodedData = false }

<Box mt={1}>
<TxDataRow title="Safenet Simulation:">
<SafenetTxSimulation
safe={safe.address.value}
chainId={safe.chainId}
safeTx={{
data: safeTxData!,
signatures: new Map(),
getSignature: () => undefined,
addSignature: () => {},
encodedSignatures: () => '',
}}
/>
<GradientBoxSafenet heading="Powered by Safenet" className={css.safenetGradientRow}>
<SafenetTxSimulation
safe={safe.address.value}
chainId={safe.chainId}
safeTx={{
data: safeTxData!,
signatures: new Map(),
getSignature: () => undefined,
addSignature: () => {},
encodedSignatures: () => '',
}}
/>
</GradientBoxSafenet>
</TxDataRow>
</Box>

Expand Down
17 changes: 10 additions & 7 deletions src/components/tx/SignOrExecuteForm/SafenetTxChecks.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Typography } from '@mui/material'
import { type SafeTransaction } from '@safe-global/safe-core-sdk-types'
import { type ReactElement } from 'react'
import GradientBoxSafenet from '@/components/common/GradientBoxSafenet'
import { SafenetTxSimulation } from '@/components/tx/security/safenet'
import TxCard from '@/components/tx-flow/common/TxCard'
import useIsSafenetEnabled from '@/hooks/useIsSafenetEnabled'
import { Typography } from '@mui/material'
import useChainId from '@/hooks/useChainId'
import useSafeAddress from '@/hooks/useSafeAddress'
import type { SafeTransaction } from '@safe-global/safe-core-sdk-types'
import css from './styles.module.css'

const SafenetTxChecks = ({ safeTx }: { safeTx: SafeTransaction }): ReactElement | null => {
const safe = useSafeAddress()
Expand All @@ -17,11 +19,12 @@ const SafenetTxChecks = ({ safeTx }: { safeTx: SafeTransaction }): ReactElement
}

return (
<TxCard>
<Typography variant="h5">Safenet checks</Typography>

<SafenetTxSimulation safe={safe} chainId={chainId} safeTx={safeTx} />
</TxCard>
<GradientBoxSafenet heading="Powered by Safenet" className={css.safenetGradientCard}>
<TxCard>
<Typography variant="h5">Safenet checks</Typography>
<SafenetTxSimulation safe={safe} chainId={chainId} safeTx={safeTx} />
</TxCard>
</GradientBoxSafenet>
)
}

Expand Down
8 changes: 8 additions & 0 deletions src/components/tx/SignOrExecuteForm/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,11 @@
border-radius: 4px;
padding: 2px 8px;
}

.safenetGradientCard {
margin-bottom: var(--space-2);
}

.safenetGradientCard > div:last-child > div {
margin-bottom: 0;
}
75 changes: 42 additions & 33 deletions src/components/tx/security/safenet/index.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
import {
CircularProgress,
Link,
List,
ListItem,
ListItemIcon,
ListItemText,
Paper,
SvgIcon,
Typography,
} from '@mui/material'
import { Button, CircularProgress, List, ListItem, ListItemText, Paper, SvgIcon, Typography } from '@mui/material'
import type { SafeTransaction } from '@safe-global/safe-core-sdk-types'
import useDecodeTx from '@/hooks/useDecodeTx'
import CheckIcon from '@/public/images/common/check.svg'
import CloseIcon from '@/public/images/common/close.svg'
import LinkIcon from '@/public/images/common/link.svg'
import type { SafenetSimulationResponse } from '@/store/safenet'
import { useLazySimulateSafenetTxQuery } from '@/store/safenet'
import { hashTypedData } from '@/utils/web3'
import { useEffect, type ReactElement } from 'react'
import css from './styles.module.css'
import { hashTypedData } from '@/utils/web3'
import { Loop } from '@mui/icons-material'

export type SafenetTxSimulationProps = {
safe: string
Expand Down Expand Up @@ -80,6 +70,43 @@ function _getSafeTxHash({ safe, chainId, safeTx }: Required<SafenetTxSimulationP
})
}

const StatusAction = ({ status, link }: { status: string; link?: string }): ReactElement => {
if (status === 'success') {
return (
<div>
<SvgIcon
component={CheckIcon}
inheritViewBox
fontSize="small"
color="success"
className={css.safenetCheckIcon}
/>
<span className={css.labelSuccess}>No issues found</span>
</div>
)
} else if (status === 'pending' && link) {
return (
<Button
variant="outlined"
size="small"
href={link}
target="_blank"
sx={{ width: '100%', py: 0.5 }}
startIcon={<SvgIcon component={LinkIcon} inheritViewBox fontSize="small" />}
>
Verify recipient
</Button>
)
} else {
return (
<div>
<SvgIcon component={CloseIcon} inheritViewBox fontSize="small" color="error" className={css.safenetCheckIcon} />
<span className={css.labelFailure}>Failure</span>
</div>
)
}
}

const SafenetTxTxSimulationSummary = ({ simulation }: { simulation: SafenetSimulationResponse }): ReactElement => {
if (simulation.results.length === 0) {
return <Typography>No Safenet checks enabled...</Typography>
Expand All @@ -97,26 +124,8 @@ const SafenetTxTxSimulationSummary = ({ simulation }: { simulation: SafenetSimul

<List>
{guarantees.map(({ display, status, link }) => (
<ListItem key={display}>
<ListItemIcon>
{status === 'success' && (
<SvgIcon component={CheckIcon} inheritViewBox fontSize="small" color="success" />
)}
{status === 'failure' && <SvgIcon component={CloseIcon} inheritViewBox fontSize="small" color="error" />}
{status === 'pending' && <SvgIcon component={Loop} inheritViewBox fontSize="small" />}
</ListItemIcon>
<ListItemText>
<div>{display}</div>
{status === 'pending' && link && (
<div className={css.pending}>
Share this{' '}
<Link href={link} target="_blank">
link
</Link>{' '}
to the recipient to confirm the transfer
</div>
)}
</ListItemText>
<ListItem key={display} secondaryAction={<StatusAction status={status} link={link} />}>
<ListItemText>{display}</ListItemText>
</ListItem>
))}
</List>
Expand Down
15 changes: 15 additions & 0 deletions src/components/tx/security/safenet/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,18 @@
.errorSummary {
padding: calc(var(--space-1) * 2) calc(var(--space-2) * 2) var(--space-1) calc(var(--space-2) * 2);
}

.labelSuccess {
color: var(--color-success-main);
font-size: 0.9rem;
}

.labelFailure {
color: var(--color-error-main);
font-size: 0.9rem;
}

.safenetCheckIcon {
vertical-align: middle;
margin: 0 4px 2px 0;
}

0 comments on commit 0a8a8ce

Please sign in to comment.