diff --git a/package.json b/package.json
index 987f4e10f0..e574b66d06 100644
--- a/package.json
+++ b/package.json
@@ -89,7 +89,7 @@
"@gnosis.pm/safe-core-sdk": "^2.0.0",
"@gnosis.pm/safe-deployments": "^1.8.0",
"@gnosis.pm/safe-react-components": "^1.1.2",
- "@gnosis.pm/safe-react-gateway-sdk": "^2.10.2",
+ "@gnosis.pm/safe-react-gateway-sdk": "^2.10.3",
"@gnosis.pm/safe-web3-lib": "^1.0.0",
"@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.0",
diff --git a/src/components/Dashboard/FeaturedApps/FeaturedApps.tsx b/src/components/Dashboard/FeaturedApps/FeaturedApps.tsx
new file mode 100644
index 0000000000..0f3e4ede25
--- /dev/null
+++ b/src/components/Dashboard/FeaturedApps/FeaturedApps.tsx
@@ -0,0 +1,64 @@
+import { useAppList } from 'src/routes/safe/components/Apps/hooks/appList/useAppList'
+import { Text } from '@gnosis.pm/safe-react-components'
+import { Link } from 'react-router-dom'
+import { getSafeAppUrl, SafeRouteParams } from 'src/routes/routes'
+import { useSelector } from 'react-redux'
+import { currentSafe } from 'src/logic/safe/store/selectors'
+import { getShortName } from 'src/config'
+import { ReactElement, useMemo } from 'react'
+import Row from 'src/components/layout/Row'
+import Col from 'src/components/layout/Col'
+import styled from 'styled-components'
+
+const FEATURED_APPS_TAGS = 'dashboard-widgets'
+
+const StyledImage = styled.img`
+ max-width: 64px;
+ max-height: 64px;
+`
+
+const StyledLink = styled(Link)`
+ margin-top: 10px;
+ text-decoration: none;
+`
+
+const StyledRow = styled(Row)`
+ gap: 24px;
+ flex-wrap: inherit;
+`
+
+export const FeaturedApps = (): ReactElement => {
+ const { allApps } = useAppList()
+ const { address } = useSelector(currentSafe) ?? {}
+ const featuredApps = useMemo(() => allApps.filter((app) => app.tags?.includes(FEATURED_APPS_TAGS)), [allApps])
+
+ const routesSlug: SafeRouteParams = {
+ shortName: getShortName(),
+ safeAddress: address,
+ }
+
+ return (
+ <>
+ {featuredApps.map((app) => {
+ const appRoute = getSafeAppUrl(app.url, routesSlug)
+ return (
+
+
+
+
+
+
+ {app.description}
+
+
+
+ Use {app.name}
+
+
+
+
+ )
+ })}
+ >
+ )
+}
diff --git a/src/routes/Home/index.tsx b/src/routes/Home/index.tsx
index 8df655ffe8..d61b2cbb15 100644
--- a/src/routes/Home/index.tsx
+++ b/src/routes/Home/index.tsx
@@ -8,6 +8,7 @@ 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;
@@ -36,7 +37,8 @@ function Home(): ReactElement {
- Owned Safes
+ Safe Apps
+
diff --git a/src/routes/routes.ts b/src/routes/routes.ts
index 71e53c971d..56a12545ed 100644
--- a/src/routes/routes.ts
+++ b/src/routes/routes.ts
@@ -126,3 +126,7 @@ export const generatePrefixedAddressRoutes = (params: SafeRouteParams): typeof S
{} as typeof STANDARD_SAFE_ROUTES,
)
}
+
+export const getSafeAppUrl = (appUrl: string, routesSlug: SafeRouteParams): string => {
+ return generateSafeRoute(SAFE_ROUTES.APPS, routesSlug) + `?appUrl=${appUrl}`
+}
diff --git a/src/routes/safe/components/Apps/components/AppsList.test.tsx b/src/routes/safe/components/Apps/components/AppsList.test.tsx
index 8b3975cc8e..24b2227ad7 100644
--- a/src/routes/safe/components/Apps/components/AppsList.test.tsx
+++ b/src/routes/safe/components/Apps/components/AppsList.test.tsx
@@ -39,6 +39,7 @@ beforeEach(() => {
accessControl: {
type: safeAppsGatewaySDK.SafeAppAccessPolicyTypes.NoRestrictions,
},
+ tags: [],
}),
)
@@ -55,6 +56,7 @@ beforeEach(() => {
accessControl: {
type: safeAppsGatewaySDK.SafeAppAccessPolicyTypes.NoRestrictions,
},
+ tags: [],
},
{
id: 3,
@@ -69,6 +71,7 @@ beforeEach(() => {
type: safeAppsGatewaySDK.SafeAppAccessPolicyTypes.DomainAllowlist,
value: ['https://gnosis-safe.io'],
},
+ tags: [],
},
{
id: 14,
@@ -81,6 +84,7 @@ beforeEach(() => {
accessControl: {
type: safeAppsGatewaySDK.SafeAppAccessPolicyTypes.NoRestrictions,
},
+ tags: [],
},
{
id: 24,
@@ -94,6 +98,7 @@ beforeEach(() => {
type: safeAppsGatewaySDK.SafeAppAccessPolicyTypes.DomainAllowlist,
value: ['https://gnosis-safe.io'],
},
+ tags: [],
},
]),
)
diff --git a/src/routes/safe/components/Apps/utils.ts b/src/routes/safe/components/Apps/utils.ts
index e0618d2844..975915021e 100644
--- a/src/routes/safe/components/Apps/utils.ts
+++ b/src/routes/safe/components/Apps/utils.ts
@@ -62,6 +62,7 @@ export const getEmptySafeApp = (url = ''): SafeApp => {
accessControl: {
type: SafeAppAccessPolicyTypes.NoRestrictions,
},
+ tags: [],
}
}
diff --git a/yarn.lock b/yarn.lock
index 2877c36864..c7fdcadff3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1887,10 +1887,10 @@
dependencies:
cross-fetch "^3.1.5"
-"@gnosis.pm/safe-react-gateway-sdk@^2.10.2":
- version "2.10.2"
- resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-react-gateway-sdk/-/safe-react-gateway-sdk-2.10.2.tgz#25d01ea5c947bc2701535c6cdf059b380276e0f5"
- integrity sha512-9o0JiA3zS5s1fVQa61DB1gBdFgyqA9QxW3y8lTYzX/lWNbfpm2psonyLjBJkETp7RoMFSp30pM4wgPXXT9bjzQ==
+"@gnosis.pm/safe-react-gateway-sdk@^2.10.3":
+ version "2.10.3"
+ resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-react-gateway-sdk/-/safe-react-gateway-sdk-2.10.3.tgz#4537442a78eb0508c483aabcac19296335a77ac3"
+ integrity sha512-ukaLACozdJQb2YGSAZgBUkF4CT9iKVjpnKFCKUnGGghXqp+Yyn9jpdcfFK0VYQJ6ZSwAm40tHtQaN3K9817Bcg==
dependencies:
cross-fetch "^3.1.5"