{title}
+
{safes.length > 0 && (
{' '}
@@ -25,10 +84,12 @@ const PaginatedSafeList = ({ safes, title, action, noSafesMessage, onLinkClick }
)}
+
{action}
- {safes.length ? (
- safes.map((item) => )
+
+ {safes.length > 0 ? (
+
) : (
{noSafesMessage}
diff --git a/src/components/welcome/MyAccounts/QueueActions.tsx b/src/components/welcome/MyAccounts/QueueActions.tsx
new file mode 100644
index 0000000000..f7a7169546
--- /dev/null
+++ b/src/components/welcome/MyAccounts/QueueActions.tsx
@@ -0,0 +1,71 @@
+import { useMemo, type ReactNode } from 'react'
+import type { UrlObject } from 'url'
+import NextLink from 'next/link'
+import { Box, Chip, Typography, SvgIcon } from '@mui/material'
+import CheckIcon from '@mui/icons-material/Check'
+import TransactionsIcon from '@/public/images/transactions/transactions.svg'
+import Track from '@/components/common/Track'
+import { OVERVIEW_EVENTS } from '@/services/analytics/events/overview'
+import { AppRoutes } from '@/config/routes'
+
+const ChipLink = ({ children, color }: { children: ReactNode; color?: string }) => (
+
+ {children}
+
+ }
+ />
+)
+
+const QueueActions = ({
+ safeAddress,
+ chainShortName,
+ queued,
+ awaitingConfirmation,
+}: {
+ safeAddress: string
+ chainShortName: string
+ queued: number
+ awaitingConfirmation: number
+}) => {
+ const queueLink = useMemo(
+ () => ({
+ pathname: AppRoutes.transactions.queue,
+ query: { safe: `${chainShortName}:${safeAddress}` },
+ }),
+ [chainShortName, safeAddress],
+ )
+
+ if (!queued && !awaitingConfirmation) {
+ return null
+ }
+
+ return (
+
+
+
+ )
+}
+
+export default QueueActions
diff --git a/src/components/welcome/MyAccounts/styles.module.css b/src/components/welcome/MyAccounts/styles.module.css
index 80c5311361..5aa0edf2b1 100644
--- a/src/components/welcome/MyAccounts/styles.module.css
+++ b/src/components/welcome/MyAccounts/styles.module.css
@@ -35,6 +35,7 @@
padding-top: 0;
padding-bottom: 0;
padding-left: 0;
+ flex-wrap: wrap;
}
.currentListItem {
@@ -49,16 +50,29 @@
}
.safeLink {
- display: flex;
- align-items: center;
+ display: grid;
padding: var(--space-2) var(--space-1) var(--space-2) var(--space-2);
+ grid-template-columns: auto 3fr 2fr auto;
+ align-items: center;
+}
+
+@media (max-width: 599.95px) {
+ .safeLink {
+ grid-template-columns: auto 1fr auto;
+ grid-template-areas:
+ 'a b d'
+ 'a c d';
+ }
+
+ .safeLink :nth-child(1) { grid-area: a; }
+ .safeLink :nth-child(2) { grid-area: b; }
+ .safeLink :nth-child(3) { grid-area: c; }
+ .safeLink :nth-child(4) { grid-area: d; }
}
.safeAddress {
white-space: nowrap;
- padding-left: var(--space-2);
overflow: hidden;
- margin-right: 10px;
text-overflow: ellipsis;
}
diff --git a/src/components/welcome/MyAccounts/useAllOwnedSafes.ts b/src/components/welcome/MyAccounts/useAllOwnedSafes.ts
index 023bc00b91..a14c362eff 100644
--- a/src/components/welcome/MyAccounts/useAllOwnedSafes.ts
+++ b/src/components/welcome/MyAccounts/useAllOwnedSafes.ts
@@ -1,11 +1,25 @@
+import type { AllOwnedSafes } from '@safe-global/safe-gateway-typescript-sdk'
import { getAllOwnedSafes } from '@safe-global/safe-gateway-typescript-sdk'
+import type { AsyncResult } from '@/hooks/useAsync'
import useAsync from '@/hooks/useAsync'
+import useLocalStorage from '@/services/local-storage/useLocalStorage'
+import { useEffect } from 'react'
-const useAllOwnedSafes = (address: string) => {
- return useAsync(() => {
- if (!address) return
+const CACHE_KEY = 'ownedSafesCache_'
+
+const useAllOwnedSafes = (address: string): AsyncResult => {
+ const [cache, setCache] = useLocalStorage(CACHE_KEY + address)
+
+ const [data, error, isLoading] = useAsync(async () => {
+ if (!address) return {}
return getAllOwnedSafes(address)
}, [address])
+
+ useEffect(() => {
+ if (data != undefined) setCache(data)
+ }, [data, setCache])
+
+ return [cache, error, isLoading]
}
export default useAllOwnedSafes
diff --git a/src/components/welcome/MyAccounts/useAllSafes.ts b/src/components/welcome/MyAccounts/useAllSafes.ts
index 14206126ce..c5dc62f6a5 100644
--- a/src/components/welcome/MyAccounts/useAllSafes.ts
+++ b/src/components/welcome/MyAccounts/useAllSafes.ts
@@ -9,13 +9,13 @@ import useWallet from '@/hooks/wallets/useWallet'
import { selectUndeployedSafes } from '@/store/slices'
import { sameAddress } from '@/utils/addresses'
-export type SafeItems = Array<{
+export type SafeItem = {
chainId: string
address: string
isWatchlist: boolean
- threshold?: number
- owners?: number
-}>
+}
+
+export type SafeItems = SafeItem[]
const useAddedSafes = () => {
const allAdded = useAppSelector(selectAllAddedSafes)
@@ -61,8 +61,6 @@ const useAllSafes = (): SafeItems | undefined => {
address,
chainId,
isWatchlist: !isOwned && !isUndeployed,
- threshold: allAdded[chainId]?.[address]?.threshold,
- owners: allAdded[chainId]?.[address]?.owners.length,
}
})
})
diff --git a/src/components/welcome/MyAccounts/useSafeOverviews.ts b/src/components/welcome/MyAccounts/useSafeOverviews.ts
new file mode 100644
index 0000000000..bfe33c3eb4
--- /dev/null
+++ b/src/components/welcome/MyAccounts/useSafeOverviews.ts
@@ -0,0 +1,26 @@
+import { useTokenListSetting } from '@/hooks/loadables/useLoadBalances'
+import useAsync from '@/hooks/useAsync'
+import useWallet from '@/hooks/wallets/useWallet'
+import { useAppSelector } from '@/store'
+import { selectCurrency } from '@/store/settingsSlice'
+import { getSafeOverviews } from '@safe-global/safe-gateway-typescript-sdk'
+
+function useSafeOverviews(safes: Array<{ address: string; chainId: string }>) {
+ const excludeSpam = useTokenListSetting() || false
+ const currency = useAppSelector(selectCurrency)
+ const wallet = useWallet()
+ const walletAddress = wallet?.address
+
+ return useAsync(async () => {
+ const safesStrings = safes.map((safe) => `${safe.chainId}:${safe.address}` as `${number}:0x${string}`)
+
+ return await getSafeOverviews(safesStrings, {
+ trusted: true,
+ exclude_spam: excludeSpam,
+ currency,
+ wallet_address: walletAddress,
+ })
+ }, [safes, excludeSpam, currency, walletAddress])
+}
+
+export default useSafeOverviews
diff --git a/src/hooks/loadables/useLoadBalances.ts b/src/hooks/loadables/useLoadBalances.ts
index 3d52ffe067..40324c8b11 100644
--- a/src/hooks/loadables/useLoadBalances.ts
+++ b/src/hooks/loadables/useLoadBalances.ts
@@ -12,7 +12,7 @@ import { POLLING_INTERVAL } from '@/config/constants'
import useIntervalCounter from '../useIntervalCounter'
import useSafeInfo from '../useSafeInfo'
-const useTokenListSetting = (): boolean | undefined => {
+export const useTokenListSetting = (): boolean | undefined => {
const chain = useCurrentChain()
const settings = useAppSelector(selectSettings)
diff --git a/src/utils/__tests__/formatNumber.test.ts b/src/utils/__tests__/formatNumber.test.ts
index c0d1e5eb05..84f4c000f5 100644
--- a/src/utils/__tests__/formatNumber.test.ts
+++ b/src/utils/__tests__/formatNumber.test.ts
@@ -181,6 +181,15 @@ describe('formatNumber', () => {
expect(formatCurrency(amount3, 'BHD')).toBe('1.778 BHD')
})
+ it('should drop decimals for values above 1k', () => {
+ // It should stop
+ expect(formatCurrency(999.99, 'USD')).toBe('999.99 USD')
+ expect(formatCurrency(1000.1, 'USD')).toBe('1,000 USD')
+ expect(formatCurrency(1000.99, 'USD')).toBe('1,001 USD')
+ expect(formatCurrency(32500.5, 'EUR')).toBe('32,501 EUR')
+ expect(formatCurrency(314285500.1, 'JPY')).toBe('314.286M JPY')
+ })
+
it('should use M symbol for numbers between 100,000,000 and 999,999,500', () => {
const amount1 = 100_000_100
diff --git a/src/utils/formatNumber.ts b/src/utils/formatNumber.ts
index 6623327d39..b0cb100983 100644
--- a/src/utils/formatNumber.ts
+++ b/src/utils/formatNumber.ts
@@ -6,6 +6,7 @@ import memoize from 'lodash/memoize'
const LOWER_LIMIT = 0.00001
const COMPACT_LIMIT = 99_999_999.5
const UPPER_LIMIT = 999 * 10 ** 12
+const NO_DECIMALS_LIMIT = 1000
/**
* Formatter that restricts the upper and lower limit of numbers that can be formatted
@@ -140,13 +141,12 @@ const getCurrencyFormatterMaxFractionDigits = (
): Intl.NumberFormatOptions['maximumFractionDigits'] => {
const float = Number(number)
- if (float < 1_000_000) {
+ if (float < NO_DECIMALS_LIMIT) {
const [, decimals] = getMinimumCurrencyDenominator(currency).toString().split('.')
return decimals?.length ?? 0
}
- // Represents numbers like 767.343M
- if (float < UPPER_LIMIT) {
+ if (float >= COMPACT_LIMIT) {
return 3
}