Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: add swaps card to safe apps list #3786

Merged
merged 24 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
49dab7d
feat: add native swap card to storybook
jmealy May 30, 2024
c423b09
make card clickable and handle dismiss
jmealy May 31, 2024
3e1445c
add flag to safe apps card type for native features
jmealy May 31, 2024
77dd4c5
move hook to hooks folder
jmealy May 31, 2024
aac00da
adjust padding
jmealy May 31, 2024
ae9d480
remove test code
jmealy May 31, 2024
11a347c
fix: enable swap app card only on supported networks
compojoom Jun 3, 2024
b1f486e
fix: failing apps test
compojoom Jun 3, 2024
944d1ec
fix lint error and add tags to swaps card
jmealy Jun 4, 2024
38f1106
Merge branch 'dev' into feat/safe-apps-swaps-block
jmealy Jun 4, 2024
af6047a
render the swaps card first
jmealy Jun 10, 2024
7feddc4
Merge branch 'dev' into feat/safe-apps-swaps-block
jmealy Jun 10, 2024
fdb26ea
separate native app promotion from safe app cards
jmealy Jun 10, 2024
f8bf88e
remove swap card hook
jmealy Jun 10, 2024
6ac939b
revert changes to safe apps card map
jmealy Jun 10, 2024
5e5a453
Update src/features/swap/index.tsx
jmealy Jun 10, 2024
0badc49
Update src/pages/apps/index.tsx
jmealy Jun 10, 2024
2d71a13
use localstorage flag within card component and fix storybook
jmealy Jun 10, 2024
69b6427
remove commented code
jmealy Jun 10, 2024
85d543e
revert changes to useCurrentChain
jmealy Jun 10, 2024
19aeebd
remove unused app id
jmealy Jun 10, 2024
aa7f2b9
Merge branch 'dev' of github.com:safe-global/safe-wallet-web into fea…
katspaugh Jun 11, 2024
8da9076
Move SAP banner down
katspaugh Jun 11, 2024
d8f3a18
Adjust banners
katspaugh Jun 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/components/safe-apps/NativeSwapsCard/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Meta, StoryObj } from '@storybook/react'
import NativeSwapsCard from './index'
import { Box } from '@mui/material'

const meta = {
component: NativeSwapsCard,
parameters: {
componentSubtitle: 'Renders an order id with an external link and a copy button',
},

decorators: [
(Story) => {
return (
<Box sx={{ maxWidth: '500px' }}>
<Story />
</Box>
)
},
],
tags: ['autodocs'],
} satisfies Meta<typeof NativeSwapsCard>

export default meta
type Story = StoryObj<typeof meta>

export const Default: Story = {
args: { onDismiss: () => {} },
}
53 changes: 53 additions & 0 deletions src/components/safe-apps/NativeSwapsCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
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'

const NativeSwapsCard = ({ onDismiss }: { onDismiss: () => void }) => {
const router = useRouter()

return (
<Paper className={css.container}>
<CardHeader
className={css.header}
avatar={
<div className={css.iconContainer}>
<SafeAppIconCard src="/images/common/swap.svg" alt="Swap Icon" width={24} height={24} />
</div>
}
/>

<CardContent className={css.content}>
<Typography className={css.title} variant="h5">
Native swaps are here!
</Typography>

<Typography className={css.description} variant="body2" color="text.secondary">
Experience seamless trading with better decoding and security in native swaps.
</Typography>

<Stack direction="row" gap={2} className={css.buttons}>
<Track {...SWAP_EVENTS.OPEN_SWAPS} label={SWAP_LABELS.safeAppsPromoWidget}>
<Link href={{ pathname: AppRoutes.swap, query: { safe: router.query.safe } }} passHref legacyBehavior>
<Button variant="contained" size="small">
Try now
</Button>
</Link>
</Track>
<Button onClick={onDismiss} size="small" variant="text" sx={{ px: '16px' }}>
Don&apos;t show
</Button>
</Stack>
</CardContent>
</Paper>
)
}

export default NativeSwapsCard
49 changes: 49 additions & 0 deletions src/components/safe-apps/NativeSwapsCard/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.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);
}
14 changes: 14 additions & 0 deletions src/components/safe-apps/SafeAppList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ 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'
import useLocalStorage from '@/services/local-storage/useLocalStorage'
import { useHasFeature } from '@/hooks/useChains'
import { FEATURES } from '@/utils/chains'

type SafeAppListProps = {
safeAppsList: SafeAppData[]
Expand All @@ -20,8 +24,11 @@ type SafeAppListProps = {
removeCustomApp?: (safeApp: SafeAppData) => void
title: string
query?: string
isFiltered?: boolean
}

const SWAPS_APP_CARD_STORAGE_KEY = 'showSwapsAppCard'

const SafeAppList = ({
safeAppsList,
safeAppsListLoading,
Expand All @@ -31,9 +38,12 @@ const SafeAppList = ({
removeCustomApp,
title,
query,
isFiltered = false,
}: SafeAppListProps) => {
const { isPreviewDrawerOpen, previewDrawerApp, openPreviewDrawer, closePreviewDrawer } = useSafeAppPreviewDrawer()
const { openedSafeAppIds } = useOpenedSafeApps()
let [isSwapsCardVisible = true, setIsSwapsCardVisible] = useLocalStorage<boolean>(SWAPS_APP_CARD_STORAGE_KEY)
katspaugh marked this conversation as resolved.
Show resolved Hide resolved
const isSwapFeatureEnabled = useHasFeature(FEATURES.NATIVE_SWAPS)
katspaugh marked this conversation as resolved.
Show resolved Hide resolved

const showZeroResultsPlaceholder = query && safeAppsList.length === 0

Expand Down Expand Up @@ -69,6 +79,10 @@ const SafeAppList = ({
</li>
))}

{!isFiltered && isSwapsCardVisible && isSwapFeatureEnabled && (
<NativeSwapsCard onDismiss={() => setIsSwapsCardVisible(false)} />
)}

{/* Flat list filtered by search query */}
{safeAppsList.map((safeApp) => (
<li key={safeApp.id}>
Expand Down
1 change: 1 addition & 0 deletions src/features/swap/config/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const NATIVE_SWAPS_APP_ID = 100_000
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export const NATIVE_SWAPS_APP_ID = 100_000

3 changes: 2 additions & 1 deletion src/features/swap/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { isBlockedAddress } from '@/services/ofac'
import { selectSwapParams, setSwapParams, type SwapState } from './store/swapParamsSlice'
import { setSwapOrder } from '@/store/swapOrderSlice'
import useChainId from '@/hooks/useChainId'
import { NATIVE_SWAPS_APP_ID } from '@/features/swap/config/constants'
katspaugh marked this conversation as resolved.
Show resolved Hide resolved
import { type BaseTransaction } from '@safe-global/safe-apps-sdk'
import { APPROVAL_SIGNATURE_HASH } from '@/components/tx/ApprovalEditor/utils/approvals'
import { id } from 'ethers'
Expand Down Expand Up @@ -94,7 +95,7 @@ const SwapWidget = ({ sell }: Params) => {

const appData: SafeAppData = useMemo(
() => ({
id: 1,
id: NATIVE_SWAPS_APP_ID,
jmealy marked this conversation as resolved.
Show resolved Hide resolved
url: 'https://app.safe.global',
name: SWAP_TITLE,
iconUrl: darkMode ? './images/common/safe-swap-dark.svg' : './images/common/safe-swap.svg',
Expand Down
3 changes: 2 additions & 1 deletion src/pages/apps/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const SafeApps: NextPage = () => {

const nonPinnedApps = useMemo(
() => remoteSafeApps.filter((app) => !pinnedSafeAppIds.has(app.id)),
[remoteSafeApps, pinnedSafeAppIds],
[pinnedSafeAppIds, remoteSafeApps],
jmealy marked this conversation as resolved.
Show resolved Hide resolved
)

// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down Expand Up @@ -73,6 +73,7 @@ const SafeApps: NextPage = () => {
{/* All apps */}
<SafeAppList
title="All apps"
isFiltered={isFiltered}
safeAppsList={isFiltered ? filteredApps : nonPinnedApps}
safeAppsListLoading={remoteSafeAppsLoading}
bookmarkedSafeAppsId={pinnedSafeAppIds}
Expand Down
1 change: 1 addition & 0 deletions src/services/analytics/events/swaps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ export enum SWAP_LABELS {
asset = 'asset',
dashboard_assets = 'dashboard_assets',
promoWidget = 'promoWidget',
safeAppsPromoWidget = 'safeAppsPromoWidget',
}
Loading