From fa37a99bf5606c48d95d666c31007a8e5fbff201 Mon Sep 17 00:00:00 2001 From: katspaugh Date: Mon, 18 Sep 2023 17:23:37 +0200 Subject: [PATCH] Feat: actionable pending txs on the Dashboard --- cypress/e2e/pages/dashboard.pages.js | 2 +- .../PendingTxs/PendingTxListItem.tsx | 18 +++- .../dashboard/PendingTxs/PendingTxsList.tsx | 102 ++++++++---------- .../dashboard/PendingTxs/styles.module.css | 17 +++ src/components/dashboard/index.tsx | 2 +- .../transactions/ExecuteTxButton/index.tsx | 1 + .../transactions/SignTxButton/index.tsx | 1 + 7 files changed, 84 insertions(+), 59 deletions(-) diff --git a/cypress/e2e/pages/dashboard.pages.js b/cypress/e2e/pages/dashboard.pages.js index a2d90a3984..e6a3f216cb 100644 --- a/cypress/e2e/pages/dashboard.pages.js +++ b/cypress/e2e/pages/dashboard.pages.js @@ -1,7 +1,7 @@ import * as constants from '../../support/constants' const connectAndTransactStr = 'Connect & transact' -const transactionQueueStr = 'Transaction queue' +const transactionQueueStr = 'Pending transactions' const noTransactionStr = 'This Safe has no queued transactions' const overviewStr = 'Overview' const viewAssetsStr = 'View assets' diff --git a/src/components/dashboard/PendingTxs/PendingTxListItem.tsx b/src/components/dashboard/PendingTxs/PendingTxListItem.tsx index a700f8e9b3..e19e78db45 100644 --- a/src/components/dashboard/PendingTxs/PendingTxListItem.tsx +++ b/src/components/dashboard/PendingTxs/PendingTxListItem.tsx @@ -5,12 +5,16 @@ import { useMemo } from 'react' import ChevronRight from '@mui/icons-material/ChevronRight' import type { TransactionSummary } from '@safe-global/safe-gateway-typescript-sdk' import { Box, SvgIcon, Typography } from '@mui/material' -import { isMultisigExecutionInfo } from '@/utils/transaction-guards' +import { isExecutable, isMultisigExecutionInfo, isSignableBy } from '@/utils/transaction-guards' import TxInfo from '@/components/transactions/TxInfo' import TxType from '@/components/transactions/TxType' import css from './styles.module.css' import OwnersIcon from '@/public/images/common/owners.svg' import { AppRoutes } from '@/config/routes' +import useSafeInfo from '@/hooks/useSafeInfo' +import useWallet from '@/hooks/wallets/useWallet' +import SignTxButton from '@/components/transactions/SignTxButton' +import ExecuteTxButton from '@/components/transactions/ExecuteTxButton' type PendingTxType = { transaction: TransactionSummary @@ -19,6 +23,10 @@ type PendingTxType = { const PendingTx = ({ transaction }: PendingTxType): ReactElement => { const router = useRouter() const { id } = transaction + const { safe } = useSafeInfo() + const wallet = useWallet() + const signable = wallet ? isSignableBy(transaction, wallet.address) : false + const executable = wallet ? isExecutable(transaction, wallet?.address, safe) : false const url = useMemo( () => ({ @@ -55,7 +63,13 @@ const PendingTx = ({ transaction }: PendingTxType): ReactElement => { )} - + {executable ? ( + + ) : signable ? ( + + ) : ( + + )} ) diff --git a/src/components/dashboard/PendingTxs/PendingTxsList.tsx b/src/components/dashboard/PendingTxs/PendingTxsList.tsx index 28cf208d0f..0f9c2d2627 100644 --- a/src/components/dashboard/PendingTxs/PendingTxsList.tsx +++ b/src/components/dashboard/PendingTxs/PendingTxsList.tsx @@ -2,7 +2,6 @@ import type { ReactElement } from 'react' import { useMemo } from 'react' import { useRouter } from 'next/router' import { getLatestTransactions } from '@/utils/tx-list' -import styled from '@emotion/styled' import { Box, Skeleton, Typography } from '@mui/material' import { Card, ViewAllLink, WidgetBody, WidgetContainer } from '../styled' import PendingTxListItem from './PendingTxListItem' @@ -10,23 +9,12 @@ import useTxQueue from '@/hooks/useTxQueue' import { AppRoutes } from '@/config/routes' import NoTransactionsIcon from '@/public/images/transactions/no-transactions.svg' import { getQueuedTransactionCount } from '@/utils/transactions' +import css from './styles.module.css' +import { isSignableBy, isExecutable } from '@/utils/transaction-guards' +import useWallet from '@/hooks/wallets/useWallet' +import useSafeInfo from '@/hooks/useSafeInfo' -const SkeletonWrapper = styled.div` - border-radius: 8px; - overflow: hidden; -` - -const StyledList = styled.div` - display: flex; - flex-direction: column; - gap: var(--space-1); - width: 100%; -` - -const StyledWidgetTitle = styled.div` - display: flex; - justify-content: space-between; -` +const MAX_TXS = 4 const EmptyState = () => { return ( @@ -42,11 +30,31 @@ const EmptyState = () => { ) } -const PendingTxsList = ({ size = 4 }: { size?: number }): ReactElement | null => { +const LoadingState = () => ( +
+ {Array.from(Array(MAX_TXS).keys()).map((key) => ( + + ))} +
+) + +const PendingTxsList = (): ReactElement | null => { const { page, loading } = useTxQueue() + const { safe } = useSafeInfo() + const wallet = useWallet() const queuedTxns = useMemo(() => getLatestTransactions(page?.results), [page?.results]) - const queuedTxsToDisplay = queuedTxns.slice(0, size) - const totalQueuedTxs = getQueuedTransactionCount(page) + + const actionable = useMemo(() => { + return wallet + ? queuedTxns.filter( + (tx) => isSignableBy(tx.transaction, wallet.address) || isExecutable(tx.transaction, wallet.address, safe), + ) + : queuedTxns + }, [wallet, queuedTxns, safe]) + + const txs = actionable.length ? actionable : queuedTxns + const txsToDisplay = txs.slice(0, MAX_TXS) + const txsCount = txs.length const router = useRouter() const queueUrl = useMemo( @@ -57,45 +65,29 @@ const PendingTxsList = ({ size = 4 }: { size?: number }): ReactElement | null => [router], ) - const LoadingState = useMemo( - () => ( - - {Array.from(Array(size).keys()).map((key) => ( - - - - ))} - - ), - [size], - ) - - const ResultState = useMemo( - () => ( - - {queuedTxsToDisplay.map((transaction) => ( - - ))} - - ), - [queuedTxsToDisplay], - ) - - const getWidgetBody = () => { - if (loading) return LoadingState - if (!queuedTxsToDisplay.length) return - return ResultState - } - return ( - +
- Transaction queue {totalQueuedTxs ? ` (${totalQueuedTxs})` : ''} + Pending transaction{txsCount > 1 ? 's' : ''} - {queuedTxns.length > 0 && } - - {getWidgetBody()} + + {queuedTxns.length > 0 && } +
+ + + {loading ? ( + + ) : queuedTxns.length ? ( +
+ {txsToDisplay.map((tx) => ( + + ))} +
+ ) : ( + + )} +
) } diff --git a/src/components/dashboard/PendingTxs/styles.module.css b/src/components/dashboard/PendingTxs/styles.module.css index e62e170746..ad2a6fe17e 100644 --- a/src/components/dashboard/PendingTxs/styles.module.css +++ b/src/components/dashboard/PendingTxs/styles.module.css @@ -15,6 +15,23 @@ border-color: var(--color-secondary-light); } +.list { + display: flex; + flex-direction: column; + gap: var(--space-1); + width: 100%; +} + +.skeleton { + border-radius: 8px; + overflow: hidden; +} + +.title { + display: flex; + justify-content: space-between; +} + .confirmationsCount { display: flex; align-items: center; diff --git a/src/components/dashboard/index.tsx b/src/components/dashboard/index.tsx index 713abbe444..497aed2b7e 100644 --- a/src/components/dashboard/index.tsx +++ b/src/components/dashboard/index.tsx @@ -25,7 +25,7 @@ const Dashboard = (): ReactElement => { - + diff --git a/src/components/transactions/ExecuteTxButton/index.tsx b/src/components/transactions/ExecuteTxButton/index.tsx index 1cc4d8e60a..1791c43394 100644 --- a/src/components/transactions/ExecuteTxButton/index.tsx +++ b/src/components/transactions/ExecuteTxButton/index.tsx @@ -38,6 +38,7 @@ const ExecuteTxButton = ({ const onClick = (e: SyntheticEvent) => { e.stopPropagation() + e.preventDefault() setTxFlow(, undefined, false) } diff --git a/src/components/transactions/SignTxButton/index.tsx b/src/components/transactions/SignTxButton/index.tsx index fbd35d7901..f05705ddf4 100644 --- a/src/components/transactions/SignTxButton/index.tsx +++ b/src/components/transactions/SignTxButton/index.tsx @@ -35,6 +35,7 @@ const SignTxButton = ({ const onClick = (e: SyntheticEvent) => { e.stopPropagation() + e.preventDefault() setTxFlow(, undefined, false) }