diff --git a/src/components/dashboard/ActivityRewardsSection/index.tsx b/src/components/dashboard/ActivityRewardsSection/index.tsx
index e64132e8db..ece466568c 100644
--- a/src/components/dashboard/ActivityRewardsSection/index.tsx
+++ b/src/components/dashboard/ActivityRewardsSection/index.tsx
@@ -51,7 +51,7 @@ const ActivityRewardsSection = () => {
}
return (
-
+ <>
{
-
+ >
)
}
diff --git a/src/components/dashboard/ActivityRewardsSection/styles.module.css b/src/components/dashboard/ActivityRewardsSection/styles.module.css
index 7b62eaec1e..92c6ac1639 100644
--- a/src/components/dashboard/ActivityRewardsSection/styles.module.css
+++ b/src/components/dashboard/ActivityRewardsSection/styles.module.css
@@ -67,7 +67,7 @@
.links {
display: flex;
- flex-wrap: wrap;
+ flex-wrap: nowrap;
align-items: center;
margin-top: var(--space-3);
text-wrap: nowrap;
diff --git a/src/components/dashboard/index.tsx b/src/components/dashboard/index.tsx
index a03adb52d9..50e27e0f27 100644
--- a/src/components/dashboard/index.tsx
+++ b/src/components/dashboard/index.tsx
@@ -18,9 +18,9 @@ import ActivityRewardsSection from '@/components/dashboard/ActivityRewardsSectio
import { useHasFeature } from '@/hooks/useChains'
import { FEATURES } from '@/utils/chains'
import css from './styles.module.css'
+import SwapWidget from '@/features/swap/components/SwapWidget'
const RecoveryHeader = dynamic(() => import('@/features/recovery/components/RecoveryHeader'))
-const SwapWidget = dynamic(() => import('@/features/swap/components/SwapWidget'))
const Dashboard = (): ReactElement => {
const router = useRouter()
@@ -44,13 +44,17 @@ const Dashboard = (): ReactElement => {
-
-
-
-
{safe.deployed && (
<>
-
+
+
+
+
+
+
+
+
+
diff --git a/src/components/safe-apps/NativeSwapsCard/index.stories.tsx b/src/components/safe-apps/NativeSwapsCard/index.stories.tsx
new file mode 100644
index 0000000000..9781c14934
--- /dev/null
+++ b/src/components/safe-apps/NativeSwapsCard/index.stories.tsx
@@ -0,0 +1,31 @@
+import type { Meta, StoryObj } from '@storybook/react'
+import NativeSwapsCard from './index'
+import { Box } from '@mui/material'
+import { StoreDecorator } from '@/stories/storeDecorator'
+
+const meta = {
+ component: NativeSwapsCard,
+ parameters: {
+ componentSubtitle: 'Renders a promo card for native swaps',
+ },
+
+ decorators: [
+ (Story) => {
+ return (
+
+
+
+
+
+ )
+ },
+ ],
+ tags: ['autodocs'],
+} satisfies Meta
+
+export default meta
+type Story = StoryObj
+
+export const Default: Story = {
+ args: {},
+}
diff --git a/src/components/safe-apps/NativeSwapsCard/index.tsx b/src/components/safe-apps/NativeSwapsCard/index.tsx
new file mode 100644
index 0000000000..25f029ae0d
--- /dev/null
+++ b/src/components/safe-apps/NativeSwapsCard/index.tsx
@@ -0,0 +1,61 @@
+import CardHeader from '@mui/material/CardHeader'
+import CardContent from '@mui/material/CardContent'
+import Typography from '@mui/material/Typography'
+import { Button, Paper, Stack } from '@mui/material'
+import SafeAppIconCard from '../SafeAppIconCard'
+import css from './styles.module.css'
+import { SWAP_EVENTS, SWAP_LABELS } from '@/services/analytics/events/swaps'
+import Track from '@/components/common/Track'
+import Link from 'next/link'
+import { AppRoutes } from '@/config/routes'
+import { useRouter } from 'next/router'
+import useLocalStorage from '@/services/local-storage/useLocalStorage'
+import { useHasFeature } from '@/hooks/useChains'
+import { FEATURES } from '@/utils/chains'
+
+const SWAPS_APP_CARD_STORAGE_KEY = 'showSwapsAppCard'
+
+const NativeSwapsCard = () => {
+ const router = useRouter()
+ const isSwapFeatureEnabled = useHasFeature(FEATURES.NATIVE_SWAPS)
+ const [isSwapsCardVisible = true, setIsSwapsCardVisible] = useLocalStorage(SWAPS_APP_CARD_STORAGE_KEY)
+ if (!isSwapFeatureEnabled || !isSwapsCardVisible) return null
+
+ return (
+
+
+
+
+ }
+ />
+
+
+
+ Native swaps are here!
+
+
+
+ Experience seamless trading with better decoding and security in native swaps.
+
+
+
+
+
+
+
+
+ )
+}
+
+export default NativeSwapsCard
diff --git a/src/components/safe-apps/NativeSwapsCard/styles.module.css b/src/components/safe-apps/NativeSwapsCard/styles.module.css
new file mode 100644
index 0000000000..68b767d1a5
--- /dev/null
+++ b/src/components/safe-apps/NativeSwapsCard/styles.module.css
@@ -0,0 +1,50 @@
+.container {
+ transition: background-color 0.3s ease-in-out, border 0.3s ease-in-out;
+ border: 1px solid transparent;
+ height: 100%;
+}
+
+.container:hover {
+ background-color: var(--color-background-light);
+ border: 1px solid var(--color-secondary-light);
+}
+
+.header {
+ padding: var(--space-3) var(--space-2) var(--space-1) var(--space-2);
+}
+
+.content {
+ padding: var(--space-2);
+}
+
+.iconContainer {
+ position: relative;
+ background: var(--color-secondary-light);
+ border-radius: 50%;
+ display: flex;
+ padding: var(--space-1);
+}
+
+.title {
+ line-height: 175%;
+ margin: 0;
+
+ flex-grow: 1;
+
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.description {
+ /* Truncate Safe App Description (3 lines) */
+ display: -webkit-box;
+ -webkit-line-clamp: 3;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+}
+
+.buttons {
+ padding-top: var(--space-2);
+ white-space: nowrap;
+}
diff --git a/src/components/safe-apps/SafeAppList/index.tsx b/src/components/safe-apps/SafeAppList/index.tsx
index c9a7daf84e..1f57227405 100644
--- a/src/components/safe-apps/SafeAppList/index.tsx
+++ b/src/components/safe-apps/SafeAppList/index.tsx
@@ -10,6 +10,7 @@ import useSafeAppPreviewDrawer from '@/hooks/safe-apps/useSafeAppPreviewDrawer'
import css from './styles.module.css'
import { Skeleton } from '@mui/material'
import { useOpenedSafeApps } from '@/hooks/safe-apps/useOpenedSafeApps'
+import NativeSwapsCard from '@/components/safe-apps/NativeSwapsCard'
type SafeAppListProps = {
safeAppsList: SafeAppData[]
@@ -20,6 +21,7 @@ type SafeAppListProps = {
removeCustomApp?: (safeApp: SafeAppData) => void
title: string
query?: string
+ isFiltered?: boolean
}
const SafeAppList = ({
@@ -31,6 +33,7 @@ const SafeAppList = ({
removeCustomApp,
title,
query,
+ isFiltered = false,
}: SafeAppListProps) => {
const { isPreviewDrawerOpen, previewDrawerApp, openPreviewDrawer, closePreviewDrawer } = useSafeAppPreviewDrawer()
const { openedSafeAppIds } = useOpenedSafeApps()
@@ -69,6 +72,8 @@ const SafeAppList = ({
))}
+ {!isFiltered && }
+
{/* Flat list filtered by search query */}
{safeAppsList.map((safeApp) => (
diff --git a/src/features/swap/components/SwapWidget/index.tsx b/src/features/swap/components/SwapWidget/index.tsx
index e306a38854..7e7d8eb206 100644
--- a/src/features/swap/components/SwapWidget/index.tsx
+++ b/src/features/swap/components/SwapWidget/index.tsx
@@ -14,10 +14,10 @@ import Track from '@/components/common/Track'
import { useHasFeature } from '@/hooks/useChains'
import { FEATURES } from '@/utils/chains'
-const SWAP_PROMO_WIDGET_IS_HIDDEN = 'SWAP_PROMO_WIDGET_IS_HIDDEN'
+const SWAPS_PROMO_WIDGET_IS_HIDDEN = 'swapsPromoWidgetIsHidden'
function SwapWidget(): ReactElement | null {
- const [isHidden = false, setIsHidden] = useLocalStorage(SWAP_PROMO_WIDGET_IS_HIDDEN)
+ const [isHidden = false, setIsHidden] = useLocalStorage(SWAPS_PROMO_WIDGET_IS_HIDDEN)
const isSwapFeatureEnabled = useHasFeature(FEATURES.NATIVE_SWAPS)
const onClick = useCallback(() => {
@@ -36,7 +36,7 @@ function SwapWidget(): ReactElement | null {
-
+
Introducing native swaps
@@ -44,11 +44,14 @@ function SwapWidget(): ReactElement | null {
-
+
Experience our native swaps, powered by CoW Protocol! Trade seamlessly and efficiently with decoded
transactions that are easy to understand.
+
+
+
-
-
-
+
+
+
diff --git a/src/features/swap/components/SwapWidget/styles.module.css b/src/features/swap/components/SwapWidget/styles.module.css
index c6fb8c2608..a276f52fba 100644
--- a/src/features/swap/components/SwapWidget/styles.module.css
+++ b/src/features/swap/components/SwapWidget/styles.module.css
@@ -10,6 +10,7 @@
.grid {
display: flex;
+ flex-wrap: nowrap;
height: inherit;
gap: var(--space-3);
}
@@ -27,31 +28,39 @@
margin-right: var(--space-1);
}
-.imageContainer {
- display: flex;
- align-items: flex-end;
-}
-
.buttonContainer {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
gap: var(--space-2);
+ white-space: nowrap;
+ position: relative;
+ z-index: 2;
}
-@media (max-width: 599.95px) {
- .imageContainer {
- width: 100%;
- justify-content: flex-end;
- }
+.imageContainer {
+ position: relative;
+ z-index: 1;
+}
- .buttonContainer {
- gap: 0;
- justify-content: space-between;
- }
+.imageContainer img {
+ display: block;
+ position: absolute;
+ z-index: 0;
+ right: 0;
+ bottom: 0;
+ max-width: 500px;
+}
+@media (max-width: 599.95px) {
.wrapper {
padding: var(--space-3);
}
}
+
+@media (max-width: 970px) {
+ .imageContainer {
+ display: none;
+ }
+}
diff --git a/src/pages/apps/index.tsx b/src/pages/apps/index.tsx
index 4839e57974..ea2f82b7a1 100644
--- a/src/pages/apps/index.tsx
+++ b/src/pages/apps/index.tsx
@@ -73,6 +73,7 @@ const SafeApps: NextPage = () => {
{/* All apps */}