()
const queuedTxsToDisplay: Transaction[] = useMemo(() => {
if (!queueTxns) return []
+ const allQueuedTransactions = Object.values(queueTxns.next).concat(Object.values(queueTxns.queued))
+ setTotalQueuedTxs(allQueuedTransactions.length)
+
return (
- Object.values(queueTxns.next)
- .concat(Object.values(queueTxns.queued))
+ allQueuedTransactions
// take the first (i.e. newest) tx in a group of txns with the same nonce
.map((group: Transaction[]) => group[0])
.slice(0, size)
)
}, [queueTxns, size])
- if (!queueTxns) {
- return (
-
+ const LoadingState = useMemo(
+ () => (
+
{Array.from(Array(size).keys()).map((key) => (
))}
-
- )
- }
+
+ ),
+ [size],
+ )
- if (!queuedTxsToDisplay?.length) {
- return This Safe has no queued transactions
+ const ResultState = useMemo(
+ () => (
+
+ {queuedTxsToDisplay?.map((transaction) => (
+
+ ))}
+
+ ),
+ [queuedTxsToDisplay, url],
+ )
+
+ const getWidgetBody = () => {
+ if (!queueTxns) return LoadingState
+ if (!queuedTxsToDisplay?.length) return EmptyState
+ return ResultState
}
- const { shortName } = getChainById(chainId)
- const url = generateSafeRoute(SAFE_ROUTES.TRANSACTIONS_QUEUE, { safeAddress: address, shortName })
return (
-
- {queuedTxsToDisplay?.map((transaction) => (
-
- ))}
-
+
+
+ Transactions to Sign{totalQueuedTxs ? ` (${totalQueuedTxs})` : ''}
+
+ View All
+
+
+
+ {getWidgetBody()}
+
)
}
diff --git a/src/components/Dashboard/SafeApps/Card.tsx b/src/components/Dashboard/SafeApps/Card.tsx
index 425b82fb5c..bbdb1445c7 100644
--- a/src/components/Dashboard/SafeApps/Card.tsx
+++ b/src/components/Dashboard/SafeApps/Card.tsx
@@ -1,40 +1,40 @@
import { ReactElement, useCallback, useEffect, useState } from 'react'
import styled from 'styled-components'
-import { Text, Title } from '@gnosis.pm/safe-react-components'
+import { Text } from '@gnosis.pm/safe-react-components'
import { Bookmark, BookmarkBorder } from '@material-ui/icons'
-import { IconButton } from '@material-ui/core'
+import { Box, IconButton } from '@material-ui/core'
import { Link, generatePath } from 'react-router-dom'
-import { GENERIC_APPS_ROUTE } from 'src/routes/routes'
-const StyledLink = styled(Link)`
- text-decoration: none;
- color: black;
-`
+import { GENERIC_APPS_ROUTE } from 'src/routes/routes'
+import { md, lg } from 'src/theme/variables'
-export const CARD_WIDTH = 260
export const CARD_HEIGHT = 200
export const CARD_PADDING = 24
-const StyledCard = styled.div`
+const StyledLink = styled(Link)`
+ display: block;
+ text-decoration: none;
+ color: black;
+ height: 100%;
position: relative;
- width: ${CARD_WIDTH}px;
- height: ${CARD_HEIGHT}px;
background-color: white;
border-radius: 8px;
padding: ${CARD_PADDING}px;
+ box-sizing: border-box;
`
const StyledLogo = styled.img`
display: block;
width: auto;
height: 60px;
+ margin-bottom: ${md};
`
const IconBtn = styled(IconButton)`
&.MuiButtonBase-root {
position: absolute;
- top: 10px;
- right: 10px;
+ top: ${lg};
+ right: ${lg};
z-index: 10;
padding: 5px;
}
@@ -77,18 +77,19 @@ const Card = (props: CardProps): ReactElement => {
return (
-
-
-
- {props.name}
+
-
- {props.description}
+
+
+ {props.name}
+
+
+
+ {props.description}
+
- {/* Bookmark button */}
- {localPinned ? : }
-
+ {localPinned ? : }
)
}
diff --git a/src/components/Dashboard/SafeApps/Grid.tsx b/src/components/Dashboard/SafeApps/Grid.tsx
index 8d1f0c1bc9..e43b4c146e 100644
--- a/src/components/Dashboard/SafeApps/Grid.tsx
+++ b/src/components/Dashboard/SafeApps/Grid.tsx
@@ -3,20 +3,18 @@ import styled from 'styled-components'
import { Button } from '@gnosis.pm/safe-react-components'
import { generatePath, Link } from 'react-router-dom'
import Skeleton from '@material-ui/lab/Skeleton/Skeleton'
+import { Grid } from '@material-ui/core'
+import { sampleSize } from 'lodash'
+import { screenSm, screenMd } from 'src/theme/variables'
import { useAppList } from 'src/routes/safe/components/Apps/hooks/appList/useAppList'
import { GENERIC_APPS_ROUTE } from 'src/routes/routes'
-import Card, { CARD_HEIGHT, CARD_PADDING, CARD_WIDTH } from 'src/components/Dashboard/SafeApps/Card'
+import Card, { CARD_HEIGHT, CARD_PADDING } from 'src/components/Dashboard/SafeApps/Card'
import ExploreIcon from 'src/assets/icons/explore.svg'
import { SafeApp } from 'src/routes/safe/components/Apps/types'
import { getAppsUsageData, rankTrackedSafeApps } from 'src/routes/safe/components/Apps/trackAppUsageCount'
-
-const StyledGrid = styled.div`
- display: flex;
- align-items: center;
- gap: 20px;
- flex-wrap: wrap;
-`
+import { FEATURED_APPS_TAG } from 'src/components/Dashboard/FeaturedApps/FeaturedApps'
+import { WidgetTitle, WidgetBody, WidgetContainer } from 'src/components/Dashboard/styled'
const SkeletonWrapper = styled.div`
border-radius: 8px;
@@ -24,8 +22,6 @@ const SkeletonWrapper = styled.div`
`
const StyledExplorerButton = styled.div`
- width: 260px;
- height: 200px;
background-color: white;
border-radius: 8px;
padding: 24px;
@@ -45,36 +41,29 @@ const StyledLink = styled(Link)`
}
`
-// Transactions Builder && Wallet connect
-const featuredAppsId = ['29', '11']
-
-const getRandomApps = (nonRankedApps: SafeApp[], size: number) => {
- const randomIndexes: string[] = []
- for (let i = 1; randomIndexes.length < size; i++) {
- const randomAppIndex = Math.floor(Math.random() * nonRankedApps.length).toString()
- const randomAppId = nonRankedApps[randomAppIndex].id
+const StyledGrid = styled.div`
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ grid-template-rows: repeat(2, 1fr);
+ gap: 24px;
- // Do not repeat random apps or featured apps
- if (!randomIndexes.includes(randomAppIndex) && !featuredAppsId.includes(randomAppId)) {
- randomIndexes.push(randomAppIndex)
- }
+ @media (max-width: ${screenMd}px) {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
}
- const randomSafeApps: SafeApp[] = []
- randomIndexes.forEach((index) => {
- randomSafeApps.push(nonRankedApps[index])
- })
-
- return randomSafeApps
-}
+ @media (max-width: ${screenSm}px) {
+ grid-template-columns: repeat(1, minmax(0, 1fr));
+ }
+`
-const Grid = ({ size = 6 }: { size?: number }): ReactElement => {
+const SafeAppsGrid = ({ size = 6 }: { size?: number }): ReactElement => {
const { allApps, pinnedSafeApps, togglePin, isLoading } = useAppList()
const displayedApps = useMemo(() => {
if (!allApps.length) return []
const trackData = getAppsUsageData()
const rankedSafeAppIds = rankTrackedSafeApps(trackData)
+ const featuredSafeAppIds = allApps.filter((app) => app.tags?.includes(FEATURED_APPS_TAG)).map((app) => app.id)
const topRankedSafeApps: SafeApp[] = []
rankedSafeAppIds.forEach((id) => {
@@ -82,9 +71,12 @@ const Grid = ({ size = 6 }: { size?: number }): ReactElement => {
if (sortedApp) topRankedSafeApps.push(sortedApp)
})
- const nonRankedApps = allApps.filter((app) => !rankedSafeAppIds.includes(app.id))
- // Get random apps that are not ranked
- const randomApps = getRandomApps(nonRankedApps, size - 1 - rankedSafeAppIds.length)
+ const nonRankedApps = allApps.filter(
+ (app) => !rankedSafeAppIds.includes(app.id) && !featuredSafeAppIds.includes(app.id),
+ )
+
+ // Get random apps that are not ranked and not featured
+ const randomApps = sampleSize(nonRankedApps, size - 1 - topRankedSafeApps.length)
// Display size - 1 in order to always display the "Explore Safe Apps" card
return topRankedSafeApps.concat(randomApps).slice(0, size - 1)
@@ -92,18 +84,27 @@ const Grid = ({ size = 6 }: { size?: number }): ReactElement => {
const path = generatePath(GENERIC_APPS_ROUTE)
- return (
-
-
Safe Apps
- {isLoading ? (
-
- {Array.from(Array(size).keys()).map((key) => (
-
-
+ const LoadingState = useMemo(
+ () => (
+
+ {Array.from(Array(size).keys()).map((key) => (
+
+
+
- ))}
-
- ) : (
+
+ ))}
+
+ ),
+ [size],
+ )
+
+ if (isLoading) return LoadingState
+
+ return (
+
+ Explore our DApp Ecosystem
+
{displayedApps.map((safeApp) => (
{
- )}
-
+
+
)
}
-export default Grid
+export default SafeAppsGrid
diff --git a/src/components/Dashboard/styled.tsx b/src/components/Dashboard/styled.tsx
new file mode 100644
index 0000000000..427a91b14a
--- /dev/null
+++ b/src/components/Dashboard/styled.tsx
@@ -0,0 +1,39 @@
+import { lg, black500, extraLargeFontSize, largeFontSize } from 'src/theme/variables'
+import styled from 'styled-components'
+
+export const WidgetContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+`
+
+export const DashboardTitle = styled.h1`
+ color: ${black500};
+ font-size: ${extraLargeFontSize};
+ margin-top: 0;
+`
+
+export const WidgetTitle = styled.h2`
+ color: ${black500};
+ font-size: ${largeFontSize};
+ margin-top: 0;
+`
+
+export const WidgetBody = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ height: 100%;
+`
+
+export const Card = styled.div`
+ background: #fff;
+ padding: ${lg};
+ border-radius: 8px;
+ flex-grow: 1;
+ position: relative;
+
+ & > h2 {
+ margin-top: 0;
+ }
+`
diff --git a/src/components/NetworkLabel/NetworkLabel.tsx b/src/components/NetworkLabel/NetworkLabel.tsx
index 903272dc85..8930c4eb81 100644
--- a/src/components/NetworkLabel/NetworkLabel.tsx
+++ b/src/components/NetworkLabel/NetworkLabel.tsx
@@ -42,7 +42,7 @@ const StyledLabel = styled.span`
color: ${({ textColor }) => textColor ?? fontColor};
cursor: ${({ onClick }) => (onClick ? 'pointer' : 'inherit')};
text-align: center;
- border-radius: 3px;
+ border-radius: 4px;
text-transform: capitalize;
flex-grow: ${({ flexGrow }) => (flexGrow ? 1 : 'initial')};
`
diff --git a/src/routes/Home/index.tsx b/src/routes/Home/index.tsx
index d61b2cbb15..0ba0190203 100644
--- a/src/routes/Home/index.tsx
+++ b/src/routes/Home/index.tsx
@@ -1,55 +1,34 @@
import { ReactElement } from 'react'
-import styled from 'styled-components'
import Page from 'src/components/layout/Page'
import PendingTxsList from 'src/components/Dashboard/PendingTxs/PendingTxsList'
-import CreateSafeWidget from 'src/components/Dashboard/CreateSafe'
import Overview from 'src/components/Dashboard/Overview/Overview'
-import { lg } from 'src/theme/variables'
import SafeAppsGrid from 'src/components/Dashboard/SafeApps/Grid'
-import Row from 'src/components/layout/Row'
import { FeaturedApps } from 'src/components/Dashboard/FeaturedApps/FeaturedApps'
-
-const Card = styled.div`
- background: #fff;
- padding: ${lg};
- border-radius: 8px;
- flex: 1;
- margin: 10px;
-
- & > h2 {
- margin-top: 0;
- }
-`
+import { Grid } from '@material-ui/core'
function Home(): ReactElement {
return (
-
-
+
+
-
+
-
-
-
-
+
-
-
- Safe Apps
+
-
+
-
- Transactions to Sign
-
-
-
+
+
+
-
-
-
+
+
+
+
)
}
diff --git a/src/routes/routes.ts b/src/routes/routes.ts
index 56a12545ed..6c421fa08f 100644
--- a/src/routes/routes.ts
+++ b/src/routes/routes.ts
@@ -50,7 +50,7 @@ export const LOAD_SAFE_ROUTE = generatePath(LOAD_SPECIFIC_SAFE_ROUTE) // By prov
export const SAFE_ROUTES = {
DASHBOARD: `${ADDRESSED_ROUTE}/home`,
ASSETS_BALANCES: `${ADDRESSED_ROUTE}/balances`, // [SAFE_SECTION_SLUG] === 'balances'
- ASSETS_BALANCES_COLLECTIBLES: `${ADDRESSED_ROUTE}/balances/collectibles`, // [SAFE_SUBSECTION_SLUG] === 'collectibles'
+ ASSETS_BALANCES_COLLECTIBLES: `${ADDRESSED_ROUTE}/balances/nfts`, // [SAFE_SUBSECTION_SLUG] === 'nfts'
LEGACY_COLLECTIBLES: `${ADDRESSED_ROUTE}/balances/collectibles`,
TRANSACTIONS: `${ADDRESSED_ROUTE}/transactions`,
TRANSACTIONS_HISTORY: `${ADDRESSED_ROUTE}/transactions/history`,
diff --git a/src/routes/safe/container/index.tsx b/src/routes/safe/container/index.tsx
index 37007a6e09..ccf9612ee5 100644
--- a/src/routes/safe/container/index.tsx
+++ b/src/routes/safe/container/index.tsx
@@ -101,7 +101,6 @@ const Container = (): React.ReactElement => {
)}
/>
- wrapInSuspense()} />