Skip to content

Commit

Permalink
feat: refactor badge component and experimental icon (#5102)
Browse files Browse the repository at this point in the history
* feat: refactor badge component and experimental icon

* feat: refactor link color

* feat: style and refactor cow shed banner and widget

* feat: refactor trade container styles
  • Loading branch information
fairlighteth authored Nov 19, 2024
1 parent 29ab5c4 commit f642ce5
Show file tree
Hide file tree
Showing 20 changed files with 402 additions and 187 deletions.
18 changes: 13 additions & 5 deletions apps/cowswap-frontend/src/common/constants/routes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import EXPERIMENT_ICON from '@cowprotocol/assets/cow-swap/experiment.svg'
import { isInjectedWidget } from '@cowprotocol/common-utils'
import { BadgeTypes } from '@cowprotocol/ui'

export const TRADE_WIDGET_PREFIX = isInjectedWidget() ? '/widget' : ''

Expand Down Expand Up @@ -39,29 +41,35 @@ export const Routes = {
export type RoutesKeys = keyof typeof Routes
export type RoutesValues = (typeof Routes)[RoutesKeys]

export const MENU_ITEMS: {
export interface IMenuItem {
route: RoutesValues
label: string
fullLabel?: string
description: string
badge?: string
}[] = [
badgeImage?: string
badgeType?: (typeof BadgeTypes)[keyof typeof BadgeTypes]
}

export const MENU_ITEMS: IMenuItem[] = [
{ route: Routes.SWAP, label: 'Swap', description: 'Trade tokens' },
{ route: Routes.LIMIT_ORDER, label: 'Limit', fullLabel: 'Limit order', description: 'Set your own price' },
{ route: Routes.ADVANCED_ORDERS, label: 'TWAP', description: 'Place orders with a time-weighted average price' },
]

export const HOOKS_STORE_MENU_ITEM = {
export const HOOKS_STORE_MENU_ITEM: IMenuItem = {
route: Routes.HOOKS,
label: 'Hooks',
description: 'Powerful tool to generate pre/post interaction for CoW Protocol',
badge: '🧪',
badgeImage: EXPERIMENT_ICON,
badgeType: BadgeTypes.INFORMATION,
}

export const YIELD_MENU_ITEM = {
export const YIELD_MENU_ITEM: IMenuItem = {
route: Routes.YIELD,
label: 'Yield',
fullLabel: 'Yield',
description: 'Provide liquidity',
badge: 'New',
badgeType: BadgeTypes.ALERT,
}
4 changes: 2 additions & 2 deletions apps/cowswap-frontend/src/common/hooks/useMenuItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { isLocal } from '@cowprotocol/common-utils'

import { useHooksEnabled } from 'legacy/state/user/hooks'

import { HOOKS_STORE_MENU_ITEM, MENU_ITEMS, YIELD_MENU_ITEM } from '../constants/routes'
import { HOOKS_STORE_MENU_ITEM, MENU_ITEMS, IMenuItem, YIELD_MENU_ITEM } from '../constants/routes'

export function useMenuItems() {
export function useMenuItems(): IMenuItem[] {
const isHooksEnabled = useHooksEnabled()
const { isYieldEnabled } = useFeatureFlags()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useFeatureFlags } from '@cowprotocol/common-hooks'
import { isInjectedWidget } from '@cowprotocol/common-utils'
import { Color, Footer, GlobalCoWDAOStyles, Media, MenuBar, CowSwapTheme } from '@cowprotocol/ui'

import SVG from 'react-inlinesvg'
import { NavLink } from 'react-router-dom'
import { ThemeProvider } from 'theme'

Expand Down Expand Up @@ -79,7 +80,13 @@ export function App() {
children: menuItems.map((item) => {
const href = parameterizeTradeRoute(tradeContext, item.route, true)

return { href, label: item.label, description: item.description, badge: item.badge }
return {
href,
label: item.label,
description: item.description,
badge: item.badgeImage ? <SVG src={item.badgeImage} width={10} height={10} /> : item.badge,
badgeType: item.badgeType,
}
}),
},
...NAV_ITEMS,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MenuItem, ProductVariant } from '@cowprotocol/ui'
import { BadgeTypes, MenuItem, ProductVariant } from '@cowprotocol/ui'

import AppziButton from 'legacy/components/AppziButton'
import { Version } from 'legacy/components/Version'
Expand Down Expand Up @@ -42,6 +42,7 @@ export const NAV_ITEMS: MenuItem[] = [
{
label: 'More',
badge: 'New',
badgeType: BadgeTypes.ALERT,
children: [
{
href: 'https://cow.fi/cow-protocol',
Expand All @@ -52,6 +53,7 @@ export const NAV_ITEMS: MenuItem[] = [
href: 'https://cow.fi/cow-amm',
label: 'CoW AMM',
badge: 'New',
badgeType: BadgeTypes.ALERT,
external: true,
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const HooksTopActions = styled.div`
background: var(${UI.COLOR_PAPER_DARKER});
`

export const RescueFundsToggle = styled.button`
export const RecoverFundsToggle = styled.button`
background: transparent;
font-size: inherit;
color: inherit;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { atom, useAtom } from 'jotai'
import { useCallback, useEffect, useState } from 'react'

import IMG_ICON_MINUS from '@cowprotocol/assets/images/icon-minus.svg'
import IMG_ICON_PLUS from '@cowprotocol/assets/images/icon-plus.svg'
import { useNativeTokenBalance } from '@cowprotocol/balances-and-allowances'
import { getCurrencyAddress, getEtherscanLink, getIsNativeToken } from '@cowprotocol/common-utils'
import { Command } from '@cowprotocol/types'
import { BannerOrientation, ButtonPrimary, ExternalLink, InlineBanner, Loader, TokenAmount } from '@cowprotocol/ui'
import { ButtonPrimary, ExternalLink, Loader, TokenAmount } from '@cowprotocol/ui'
import { useWalletInfo } from '@cowprotocol/wallet'
import { Currency, CurrencyAmount } from '@uniswap/sdk-core'

import ms from 'ms.macro'
import SVG from 'react-inlinesvg'
import useSWR from 'swr'

import { useErrorModal } from 'legacy/hooks/useErrorMessageAndModal'
Expand All @@ -25,15 +28,81 @@ import { useTokenContract } from 'common/hooks/useContract'
import { CurrencySelectButton } from 'common/pure/CurrencySelectButton'
import { NewModal } from 'common/pure/NewModal'

import { Content, ProxyInfo, Wrapper, Title } from './styled'
import { useRescueFundsFromProxy } from './useRescueFundsFromProxy'
import { Content, FAQItem, FAQWrapper, ProxyInfo, Title, Wrapper } from './styled'
import { useRecoverFundsFromProxy } from './useRecoverFundsFromProxy'

const BALANCE_UPDATE_INTERVAL = ms`5s`
const BALANCE_SWR_CFG = { refreshInterval: BALANCE_UPDATE_INTERVAL, revalidateOnFocus: true }

const selectedCurrencyAtom = atom<Currency | undefined>(undefined)

export function RescueFundsFromProxy({ onDismiss }: { onDismiss: Command }) {
function FAQ({ explorerLink }: { explorerLink: string | undefined }) {
const [openItems, setOpenItems] = useState<Record<number, boolean>>({})

const handleToggle = (index: number) => (e: React.MouseEvent) => {
e.preventDefault()
setOpenItems((prev) => ({ ...prev, [index]: !prev[index] }))
}

const FAQ_DATA = [
{
question: 'What is CoW Shed?',
answer: (
<>
<ExternalLink href="https://github.com/cowdao-grants/cow-shed">CoW Shed</ExternalLink> is a helper contract
that enhances user experience inside CoW Swap for features like{' '}
<ExternalLink href="https://docs.cow.fi/cow-protocol/reference/core/intents/hooks">CoW Hooks</ExternalLink>
.
<br />
<br />
This contract is deployed only once per account. This account becomes the only owner. CoW Shed will act as an
intermediary account who will do the trading on your behalf.
<br />
<br />
Because this contract holds the funds temporarily, it's possible the funds are stuck in some edge cases. This
tool will help you recover your funds.
</>
),
},
{
question: 'How do I recover my funds from CoW Shed?',
answer: (
<>
<ol>
<li>
{explorerLink ? (
<ExternalLink href={explorerLink}>Check in the block explorer</ExternalLink>
) : (
'Check in block explorer'
)}{' '}
if your own CoW Shed has any token
</li>
<li>Select the token you want to recover from CoW Shed</li>
<li>Recover!</li>
</ol>
</>
),
},
]

return (
<FAQWrapper>
{FAQ_DATA.map((faq, index) => (
<FAQItem key={index} open={openItems[index]}>
<summary onClick={handleToggle(index)}>
{faq.question}
<i>
<SVG src={openItems[index] ? IMG_ICON_MINUS : IMG_ICON_PLUS} />
</i>
</summary>
{openItems[index] && <div>{faq.answer}</div>}
</FAQItem>
))}
</FAQWrapper>
)
}

export function RecoverFundsFromProxy({ onDismiss }: { onDismiss: Command }) {
const [selectedCurrency, setSelectedCurrency] = useAtom(selectedCurrencyAtom)
const [tokenBalance, setTokenBalance] = useState<CurrencyAmount<Currency> | null>(null)

Expand All @@ -55,10 +124,10 @@ export function RescueFundsFromProxy({ onDismiss }: { onDismiss: Command }) {
}, [updateSelectTokenWidget, onDismiss])

const {
callback: rescueFundsCallback,
callback: recoverFundsCallback,
isTxSigningInProgress,
proxyAddress,
} = useRescueFundsFromProxy(selectedTokenAddress, tokenBalance, isNativeToken)
} = useRecoverFundsFromProxy(selectedTokenAddress, tokenBalance, isNativeToken)

const { isLoading: isErc20BalanceLoading } = useSWR(
!isNativeToken && erc20Contract && proxyAddress && selectedCurrency
Expand All @@ -85,18 +154,18 @@ export function RescueFundsFromProxy({ onDismiss }: { onDismiss: Command }) {

const isBalanceLoading = isErc20BalanceLoading || isNativeBalanceLoading

const rescueFunds = useCallback(async () => {
const recoverFunds = useCallback(async () => {
try {
const txHash = await rescueFundsCallback()
const txHash = await recoverFundsCallback()

if (txHash) {
addTransaction({ hash: txHash, summary: 'Rescue funds from CoW Shed Proxy' })
addTransaction({ hash: txHash, summary: 'Recover funds from CoW Shed Proxy' })
}
} catch (e) {
console.error(e)
handleSetError(e.message || e.toString())
}
}, [rescueFundsCallback, addTransaction, handleSetError])
}, [recoverFundsCallback, addTransaction, handleSetError])

const onCurrencySelectClick = useCallback(() => {
onSelectToken(selectedTokenAddress, undefined, undefined, setSelectedCurrency)
Expand All @@ -117,55 +186,24 @@ export function RescueFundsFromProxy({ onDismiss }: { onDismiss: Command }) {
<SelectTokenWidget />
{!isSelectTokenWidgetOpen && (
<>
<p>
<ExternalLink href="https://github.com/cowdao-grants/cow-shed">CoW Shed</ExternalLink> is a helper
contract that enhances user experience inside CoW Swap for features like{' '}
<ExternalLink href="https://docs.cow.fi/cow-protocol/reference/core/intents/hooks">
CoW Hooks
</ExternalLink>
.
</p>

<p>
This contract is deployed only once per account. This account becomes the only owner. CoW Shed will act as
an intermediary account who will do the trading on your behalf.
</p>

<Title>Rescue funds</Title>
<p>
Because this contract holds the funds temporarily, it's possible the funds are stuck in some edge cases.
This tool will help you recover your funds.
</p>
<InlineBanner orientation={BannerOrientation.Horizontal}>
<strong>How do I unstuck my funds in CoW Shed?</strong>
<ol>
<li>
{explorerLink ? (
<ExternalLink href={explorerLink}>Check in the block explorer</ExternalLink>
) : (
'Check in block explorer'
)}{' '}
if your own CoW Shed has any token
</li>
<li>Select the token you want to withdraw from CoW Shed</li>
<li>Withdraw!</li>
</ol>
</InlineBanner>
<ProxyInfo>
<h4>Proxy account:</h4>
{explorerLink && (
<ExternalLink href={explorerLink}>
<span>{proxyAddress}</span>
</ExternalLink>
)}
</ProxyInfo>
<Content>
<Title>Recover funds</Title>

<ProxyInfo>
<h4>Proxy account:</h4>
{explorerLink && (
<ExternalLink href={explorerLink}>
<span>{proxyAddress}</span>
</ExternalLink>
)}
</ProxyInfo>

<CurrencySelectButton currency={selectedCurrency} loading={false} onClick={onCurrencySelectClick} />

{selectedTokenAddress ? (
<>
<p>
Balance to be rescued:
Balance to be recovered:
<br />
{tokenBalance ? (
<b>
Expand All @@ -175,14 +213,21 @@ export function RescueFundsFromProxy({ onDismiss }: { onDismiss: Command }) {
<Loader />
) : null}
</p>
<ButtonPrimary onClick={rescueFunds} disabled={!hasBalance || isTxSigningInProgress}>
{isTxSigningInProgress ? <Loader /> : hasBalance ? 'Rescue funds' : 'No funds to rescue'}
<ButtonPrimary onClick={recoverFunds} disabled={!hasBalance || isTxSigningInProgress}>
{isTxSigningInProgress ? (
<Loader />
) : hasBalance ? (
'Recover funds'
) : (
<span className="noFunds">No funds to recover</span>
)}
</ButtonPrimary>
</>
) : (
<div></div>
)}
</Content>
<FAQ explorerLink={explorerLink} />
</>
)}
</NewModal>
Expand Down
Loading

0 comments on commit f642ce5

Please sign in to comment.