diff --git a/cypress/e2e/pages/dashboard.pages.js b/cypress/e2e/pages/dashboard.pages.js
index e8a10fffb3..d862ab987c 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 2ae589338d..1ac2855c73 100644
--- a/src/components/dashboard/PendingTxs/PendingTxListItem.tsx
+++ b/src/components/dashboard/PendingTxs/PendingTxListItem.tsx
@@ -2,16 +2,20 @@ import NextLink from 'next/link'
import { useRouter } from 'next/router'
import type { ReactElement } from 'react'
import { useMemo } from 'react'
+import { TransactionInfoType } from '@safe-global/safe-gateway-typescript-sdk'
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 { TransactionInfoType } from '@safe-global/safe-gateway-typescript-sdk'
+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
@@ -20,6 +24,10 @@ type PendingTxType = {
const PendingTx = ({ transaction }: PendingTxType): ReactElement => {
const router = useRouter()
const { id } = transaction
+ const { safe } = useSafeInfo()
+ const wallet = useWallet()
+ const canSign = wallet ? isSignableBy(transaction, wallet.address) : false
+ const canExecute = wallet ? isExecutable(transaction, wallet?.address, safe) : false
const url = useMemo(
() => ({
@@ -60,7 +68,13 @@ const PendingTx = ({ transaction }: PendingTxType): ReactElement => {
)}
-
+ {canExecute ? (
+
+ ) : canSign ? (
+
+ ) : (
+
+ )}
)
diff --git a/src/components/dashboard/PendingTxs/PendingTxsList.tsx b/src/components/dashboard/PendingTxs/PendingTxsList.tsx
index 28cf208d0f..263b5a726b 100644
--- a/src/components/dashboard/PendingTxs/PendingTxsList.tsx
+++ b/src/components/dashboard/PendingTxs/PendingTxsList.tsx
@@ -2,31 +2,18 @@ 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'
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,60 +29,63 @@ 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 router = useRouter()
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 router = useRouter()
+
+ const actionableTxs = useMemo(() => {
+ return wallet
+ ? queuedTxns.filter(
+ (tx) => isSignableBy(tx.transaction, wallet.address) || isExecutable(tx.transaction, wallet.address, safe),
+ )
+ : queuedTxns
+ }, [wallet, queuedTxns, safe])
+
+ const txs = actionableTxs.length ? actionableTxs : queuedTxns
+ const txsToDisplay = txs.slice(0, MAX_TXS)
const queueUrl = useMemo(
() => ({
pathname: AppRoutes.transactions.queue,
query: { safe: router.query.safe },
}),
- [router],
+ [router.query.safe],
)
- 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 transactions
+
{queuedTxns.length > 0 && }
-
- {getWidgetBody()}
+
+
+
+ {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/BatchExecuteButton/index.tsx b/src/components/transactions/BatchExecuteButton/index.tsx
index a9daefb3a8..98365d7525 100644
--- a/src/components/transactions/BatchExecuteButton/index.tsx
+++ b/src/components/transactions/BatchExecuteButton/index.tsx
@@ -60,7 +60,7 @@ const BatchExecuteButton = () => {
disabled={isDisabled}
onClick={handleOpenModal}
>
- Bulk execute{isBatchable && ` ${batchableTransactions.length} txs`}
+ Bulk execute{isBatchable && ` ${batchableTransactions.length} transactions`}
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)
}