Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add batch execute button and tx highlighting #352

Merged
merged 11 commits into from
Aug 16, 2022
24 changes: 24 additions & 0 deletions src/components/common/CustomTooltip/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { styled } from '@mui/material/styles'
import Tooltip, { tooltipClasses, TooltipProps } from '@mui/material/Tooltip'

const CustomTooltip = styled(({ className, ...props }: TooltipProps) => (
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only extracted this so it can be reused

<Tooltip {...props} classes={{ popper: className }} />
))(({ theme }) => ({
[`& .${tooltipClasses.tooltip}`]: {
color: theme.palette.common.black,
backgroundColor: theme.palette.common.white,
borderRadius: '8px',
boxShadow: '1px 2px 10px rgba(40, 54, 61, 0.18)',
fontSize: '14px',
padding: '16px',
lineHeight: 'normal',
},
[`& .${tooltipClasses.arrow}`]: {
'&::before': {
backgroundColor: theme.palette.common.white,
boxShadow: '1px 2px 10px rgba(40, 54, 61, 0.18)',
},
},
}))

export default CustomTooltip
2 changes: 1 addition & 1 deletion src/components/common/PaginatedTxns/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const PaginatedTxns = ({ useTxns }: { useTxns: typeof useTxHistory | typeof useT
) : null

return (
<Box mb={3}>
<Box mb={3} position="relative">
{loading ? (
<CircularProgress size={40} sx={{ marginTop: 2 }} />
) : error ? (
Expand Down
2 changes: 1 addition & 1 deletion src/components/create-safe/status/useSafeCreation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe('useSafeCreation', () => {
owners: [],
saltNonce: 123,
chainId: '4',
safeAddress: '0x10',
address: '0x10',
txHash: '0x123',
},
jest.fn,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createContext, ReactElement, ReactNode, useState } from 'react'

export const BatchExecuteHoverContext = createContext<{
activeHover: string[]
setActiveHover: (activeHover: string[]) => void
}>({
activeHover: [],
setActiveHover: () => {},
})

// Used for highlighting transactions that will be included when executing them as a batch
export const BatchExecuteHoverProvider = ({ children }: { children: ReactNode }): ReactElement => {
const [activeHover, setActiveHover] = useState<string[]>([])

return (
<BatchExecuteHoverContext.Provider value={{ activeHover, setActiveHover }}>
{children}
</BatchExecuteHoverContext.Provider>
)
}
52 changes: 52 additions & 0 deletions src/components/transactions/BatchExecuteButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useCallback, useContext } from 'react'
import { Button } from '@mui/material'
import css from './styles.module.css'
import { BatchExecuteHoverContext } from '@/components/transactions/BatchExecuteButton/BatchExecuteHoverProvider'
import { Transaction, TransactionListItem } from '@gnosis.pm/safe-react-gateway-sdk'
import { useAppSelector } from '@/store'
import { selectPendingTxs } from '@/store/pendingTxsSlice'
import CustomTooltip from '@/components/common/CustomTooltip'
import useBatchedTxs from '@/hooks/useBatchedTxs'

const BatchExecuteButton = ({ items }: { items: (TransactionListItem | Transaction[])[] }) => {
const pendingTxs = useAppSelector(selectPendingTxs)
const hoverContext = useContext(BatchExecuteHoverContext)
const batchableTransactions = useBatchedTxs(items)

const isBatchable = batchableTransactions.length > 1
const hasPendingTx = batchableTransactions.some((tx) => pendingTxs[tx.transaction.id])
const isDisabled = !isBatchable || hasPendingTx

const handleOnMouseEnter = useCallback(() => {
hoverContext.setActiveHover(batchableTransactions.map((tx) => tx.transaction.id))
}, [batchableTransactions, hoverContext])

const handleOnMouseLeave = useCallback(() => {
hoverContext.setActiveHover([])
}, [hoverContext])

return (
<CustomTooltip
placement="top-start"
arrow
title={
isDisabled
? 'Batch execution is only available for transactions that have been fully signed and are strictly sequential in Safe nonce.'
: 'All transactions highlighted in light green will be included in the batch execution.'
}
>
<Button
onMouseEnter={handleOnMouseEnter}
onMouseLeave={handleOnMouseLeave}
className={css.button}
variant="contained"
size="small"
disabled={isDisabled}
>
Execute Batch {isBatchable && ` (${batchableTransactions.length})`}
</Button>
</CustomTooltip>
)
}

export default BatchExecuteButton
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.button {
position: absolute;
right: 0;
top: -50px;
}
28 changes: 20 additions & 8 deletions src/components/transactions/TxList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import {
} from '@/utils/transaction-guards'
import GroupedTxListItems from '@/components/transactions/GroupedTxListItems'
import css from './styles.module.css'
import BatchExecuteButton from '@/components/transactions/BatchExecuteButton'
import { BatchExecuteHoverProvider } from '@/components/transactions/BatchExecuteButton/BatchExecuteHoverProvider'
import { useRouter } from 'next/router'
import { AppRoutes } from '@/config/routes'

type TxListProps = {
items: TransactionListPage['results']
Expand All @@ -24,6 +28,7 @@ export const TxListGrid = ({ children }: { children: (ReactElement | null)[] }):
}

const TxList = ({ items }: TxListProps): ReactElement => {
const router = useRouter()
// Ensure list always starts with a date label
const list = useMemo(() => {
const firstDateLabelIndex = items.findIndex(isDateLabel)
Expand Down Expand Up @@ -59,16 +64,23 @@ const TxList = ({ items }: TxListProps): ReactElement => {
}, [])
}, [list])

const isQueue = router.pathname === AppRoutes.safe.transactions.queue

return (
<TxListGrid>
{listWithGroupedItems.map((item, index) => {
if (Array.isArray(item)) {
return <GroupedTxListItems key={index} groupedListItems={item} />
}
<>
<BatchExecuteHoverProvider>
{isQueue && <BatchExecuteButton items={listWithGroupedItems} />}
<TxListGrid>
{listWithGroupedItems.map((item, index) => {
if (Array.isArray(item)) {
return <GroupedTxListItems key={index} groupedListItems={item} />
}

return <TxListItem key={index} item={item} />
})}
</TxListGrid>
return <TxListItem key={index} item={item} />
})}
</TxListGrid>
</BatchExecuteHoverProvider>
</>
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import TxSummary from '@/components/transactions/TxSummary'
import TxDetails from '@/components/transactions/TxDetails'
import CreateTxInfo from '@/components/transactions/SafeCreationTx'
import { isCreationTxInfo } from '@/utils/transaction-guards'
import { useContext } from 'react'
import { BatchExecuteHoverContext } from '@/components/transactions/BatchExecuteButton/BatchExecuteHoverProvider'
import css from './styles.module.css'

interface ExpandableTransactionItemProps {
isGrouped?: boolean
Expand All @@ -13,6 +16,9 @@ interface ExpandableTransactionItemProps {
}

export const ExpandableTransactionItem = ({ isGrouped = false, item, txDetails }: ExpandableTransactionItemProps) => {
const hoverContext = useContext(BatchExecuteHoverContext)
const isActive = hoverContext.activeHover.includes(item.transaction.id)

return (
<Accordion
disableGutters
Expand All @@ -22,6 +28,7 @@ export const ExpandableTransactionItem = ({ isGrouped = false, item, txDetails }
}}
elevation={0}
defaultExpanded={!!txDetails}
className={isActive ? css.active : undefined}
>
<AccordionSummary expandIcon={<ExpandMoreIcon />} sx={{ justifyContent: 'flex-start', overflowX: 'auto' }}>
<TxSummary item={item} isGrouped={isGrouped} />
Expand Down
7 changes: 7 additions & 0 deletions src/components/transactions/TxListItem/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.active {
border-color: var(--color-primary-light);
}

.active :global .MuiAccordionSummary-root {
background-color: var(--color-primary-background);
}
25 changes: 3 additions & 22 deletions src/components/transactions/Warning/index.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,12 @@
import { ReactElement } from 'react'
import { Alert, Link } from '@mui/material'
import Tooltip, { tooltipClasses, type TooltipProps } from '@mui/material/Tooltip'
import { styled } from '@mui/material/styles'
import { tooltipClasses } from '@mui/material/Tooltip'
import css from './styles.module.css'
import CustomTooltip from '@/components/common/CustomTooltip'

const UNEXPECTED_DELEGATE_ARTICLE =
'https://help.gnosis-safe.io/en/articles/6302452-why-do-i-see-an-unexpected-delegate-call-warning-in-my-transaction'

const CustomTooltip = styled(({ className, ...props }: TooltipProps) => (
<Tooltip {...props} classes={{ popper: className }} />
))(({ theme }) => ({
[`& .${tooltipClasses.tooltip}`]: {
color: theme.palette.common.black,
backgroundColor: theme.palette.common.white,
borderRadius: '8px',
boxShadow: '1px 2px 10px rgba(40, 54, 61, 0.18)',
fontSize: '14px',
padding: '16px',
lineHeight: 'normal',
},
[`& .${tooltipClasses.arrow}`]: {
'&::before': {
backgroundColor: theme.palette.common.white,
boxShadow: '1px 2px 10px rgba(40, 54, 61, 0.18)',
},
},
}))

export const DelegateCallWarning = ({ showWarning }: { showWarning: boolean }): ReactElement => (
<CustomTooltip
sx={{
Expand Down Expand Up @@ -55,6 +35,7 @@ export const DelegateCallWarning = ({ showWarning }: { showWarning: boolean }):
sx={({ palette }) => ({
color: showWarning ? palette.warning.dark : palette.success.main,
backgroundColor: `${showWarning ? palette.warning.light : palette.success.background}`,
border: 0,
borderLeft: `3px solid ${showWarning ? palette.warning.dark : palette.success.main}`,

'&.MuiAlert-standardInfo .MuiAlert-icon': {
Expand Down
Loading