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

Cowfi fix link #5021

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion apps/cow-fi/components/ArticlesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ interface ArticlesListProps {
articles: Article[]
}

const ARTICLES_PATH = '/learn/articles/'
const ARTICLES_PATH = '/learn/'

export const ArticlesList: React.FC<ArticlesListProps> = ({ articles }) => (
<LinkColumn>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useCallback, useEffect, useMemo, useState } from 'react'

import ICON_HOOK from '@cowprotocol/assets/cow-swap/hook.svg'
import { HookDappWalletCompatibility } from '@cowprotocol/hook-dapp-lib'
import { Command } from '@cowprotocol/types'
import { BannerOrientation, DismissableInlineBanner } from '@cowprotocol/ui'
import { useIsSmartContractWallet } from '@cowprotocol/wallet'
Expand Down Expand Up @@ -33,62 +34,48 @@ interface HookStoreModal {
export function HookRegistryList({ onDismiss, isPreHook, hookToEdit }: HookStoreModal) {
const [selectedDapp, setSelectedDapp] = useState<HookDapp | null>(null)
const [dappDetails, setDappDetails] = useState<HookDapp | null>(null)

const [isAllHooksTab, setIsAllHooksTab] = useState<boolean>(true)
const [searchQuery, setSearchQuery] = useState<string>('')

const isSmartContractWallet = useIsSmartContractWallet()
const walletType = isSmartContractWallet
? HookDappWalletCompatibility.SMART_CONTRACT
: HookDappWalletCompatibility.EOA
const addCustomHookDapp = useAddCustomHookDapp(isPreHook)
const removeCustomHookDapp = useRemoveCustomHookDapp()
const customHookDapps = useCustomHookDapps(isPreHook)
const hookToEditDetails = useHookById(hookToEdit, isPreHook)

// State for Search Input
const [searchQuery, setSearchQuery] = useState<string>('')

// Clear search input handler
const handleClearSearch = useCallback(() => {
setSearchQuery('')
}, [])

const internalHookDapps = useInternalHookDapps(isPreHook)

const currentDapps = useMemo(() => {
return isAllHooksTab ? internalHookDapps.concat(customHookDapps) : customHookDapps
}, [isAllHooksTab, internalHookDapps, customHookDapps])
const currentDapps = useMemo(
() => (isAllHooksTab ? [...internalHookDapps, ...customHookDapps] : customHookDapps),
[isAllHooksTab, internalHookDapps, customHookDapps],
)

// Compute filteredDapps based on searchQuery
const filteredDapps = useMemo(() => {
if (!searchQuery) return currentDapps

const lowerQuery = searchQuery.toLowerCase()

return currentDapps.filter((dapp) => {
const name = dapp.name?.toLowerCase() || ''
const description = dapp.descriptionShort?.toLowerCase() || ''

return name.includes(lowerQuery) || description.includes(lowerQuery)
})
return currentDapps.filter(({ name = '', descriptionShort = '' }) =>
[name, descriptionShort].some((text) => text.toLowerCase().includes(lowerQuery)),
)
}, [currentDapps, searchQuery])

const sortedFilteredDapps = useMemo(() => {
const isCompatible = (dapp: HookDapp) =>
!dapp.conditions?.walletCompatibility || dapp.conditions.walletCompatibility.includes(walletType)
return filteredDapps.sort((a, b) => (isCompatible(a) === isCompatible(b) ? 0 : isCompatible(a) ? -1 : 1))
}, [filteredDapps, isSmartContractWallet])

const customHooksCount = customHookDapps.length
const allHooksCount = internalHookDapps.length + customHooksCount

// Compute title based on selected dapp or details
const title = useMemo(() => {
if (selectedDapp) return selectedDapp.name
if (dappDetails) return 'Hook description'
return 'Hook Store'
}, [selectedDapp, dappDetails])
const title = selectedDapp?.name || (dappDetails ? 'Hook description' : 'Hook Store')

// Handle modal dismiss
const onDismissModal = useCallback(() => {
if (hookToEdit) {
setSelectedDapp(null)
onDismiss()
return
}

if (dappDetails) {
} else if (dappDetails) {
setDappDetails(null)
} else if (selectedDapp) {
setSelectedDapp(null)
Expand All @@ -97,33 +84,27 @@ export function HookRegistryList({ onDismiss, isPreHook, hookToEdit }: HookStore
}
}, [onDismiss, selectedDapp, dappDetails, hookToEdit])

// Handle hookToEditDetails
useEffect(() => {
if (!hookToEditDetails) {
setSelectedDapp(null)
if (hookToEditDetails) {
const foundDapp = findHookDappById(currentDapps, hookToEditDetails)
setSelectedDapp(foundDapp || null)
} else {
setSelectedDapp(findHookDappById(currentDapps, hookToEditDetails) || null)
setSelectedDapp(null)
}
}, [hookToEditDetails, currentDapps])

// Reset dappDetails when tab changes
useEffect(() => {
setDappDetails(null)
}, [isAllHooksTab])

// Handle add custom hook button
const handleAddCustomHook = useCallback(() => {
setIsAllHooksTab(false)
}, [setIsAllHooksTab])
const handleAddCustomHook = () => setIsAllHooksTab(false)
const handleClearSearch = () => setSearchQuery('')

// Determine the message for EmptyList based on the active tab and search query
const emptyListMessage = useMemo(() => {
if (isAllHooksTab) {
return searchQuery ? 'No hooks match your search.' : 'No hooks available.'
} else {
return "You haven't added any custom hooks yet. Add a custom hook to get started."
}
}, [isAllHooksTab, searchQuery])
const emptyListMessage = isAllHooksTab
? searchQuery
? 'No hooks match your search.'
: 'No hooks available.'
: "You haven't added any custom hooks yet. Add a custom hook to get started."

const DappsListContent = (
<>
Expand Down Expand Up @@ -153,13 +134,16 @@ export function HookRegistryList({ onDismiss, isPreHook, hookToEdit }: HookStore
onClear={handleClearSearch}
/>

{filteredDapps.length > 0 ? (
{sortedFilteredDapps.length > 0 ? (
<HookDappsList>
{filteredDapps.map((dapp) => (
{sortedFilteredDapps.map((dapp) => (
<HookListItem
key={isHookDappIframe(dapp) ? dapp.url : dapp.name}
dapp={dapp}
onRemove={isAllHooksTab ? undefined : () => removeCustomHookDapp(dapp as HookDappIframe)}
walletType={
isSmartContractWallet ? HookDappWalletCompatibility.SMART_CONTRACT : HookDappWalletCompatibility.EOA
}
onRemove={!isAllHooksTab ? () => removeCustomHookDapp(dapp as HookDappIframe) : undefined}
onSelect={() => setSelectedDapp(dapp)}
onOpenDetails={() => setDappDetails(dapp)}
/>
Expand Down Expand Up @@ -189,37 +173,29 @@ export function HookRegistryList({ onDismiss, isPreHook, hookToEdit }: HookStore
onAddCustomHook={handleAddCustomHook}
/>
)}
{(() => {
if (selectedDapp) {
return (
<>
<HookDetailHeader dapp={selectedDapp} iconSize={58} gap={12} padding="24px 10px" />
<HookDappContainer
isPreHook={isPreHook}
onDismiss={onDismiss}
dapp={selectedDapp}
hookToEdit={hookToEdit}
/>
</>
)
}

if (dappDetails) {
return <HookDappDetails dapp={dappDetails} onSelect={() => setSelectedDapp(dappDetails)} />
}

return isAllHooksTab ? (
DappsListContent
) : (
<AddCustomHookForm
{selectedDapp ? (
<>
<HookDetailHeader dapp={selectedDapp} iconSize={58} gap={12} padding="24px 10px" walletType={walletType} />
<HookDappContainer
isPreHook={isPreHook}
isSmartContractWallet={isSmartContractWallet}
addHookDapp={addCustomHookDapp}
>
{DappsListContent}
</AddCustomHookForm>
)
})()}
onDismiss={onDismiss}
dapp={selectedDapp}
hookToEdit={hookToEdit}
/>
</>
) : dappDetails ? (
<HookDappDetails dapp={dappDetails} onSelect={() => setSelectedDapp(dappDetails)} walletType={walletType} />
) : isAllHooksTab ? (
DappsListContent
) : (
<AddCustomHookForm
isPreHook={isPreHook}
isSmartContractWallet={isSmartContractWallet}
addHookDapp={addCustomHookDapp}
>
{DappsListContent}
</AddCustomHookForm>
)}
</NewModal>
</Wrapper>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import { HookDetailHeader } from '../HookDetailHeader'
interface HookDappDetailsProps {
dapp: HookDapp
onSelect: Command
walletType: HookDappWalletCompatibility
}

export function HookDappDetails({ dapp, onSelect }: HookDappDetailsProps) {
export function HookDappDetails({ dapp, onSelect, walletType }: HookDappDetailsProps) {
const tags = useMemo(() => {
const { version, website, type, conditions } = dapp
const walletCompatibility = conditions?.walletCompatibility || []
Expand Down Expand Up @@ -60,7 +61,7 @@ export function HookDappDetails({ dapp, onSelect }: HookDappDetailsProps) {

return (
<styled.Wrapper>
<HookDetailHeader dapp={dapp} onSelect={onSelect} />
<HookDetailHeader dapp={dapp} onSelect={onSelect} walletType={walletType} />
<styled.Body>
<p>{dapp.description}</p>
</styled.Body>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,43 @@
import { HookDappWalletCompatibility } from '@cowprotocol/hook-dapp-lib'

import * as styled from './styled'

import { HookDapp } from '../../types/hooks'

interface HookDetailHeaderProps {
dapp: HookDapp
walletType: HookDappWalletCompatibility
onSelect?: () => void
iconSize?: number
gap?: number
padding?: string
}

export function HookDetailHeader({ dapp, onSelect, iconSize, gap, padding }: HookDetailHeaderProps) {
export function HookDetailHeader({ dapp, walletType, onSelect, iconSize, gap, padding }: HookDetailHeaderProps) {
const { name, image, descriptionShort } = dapp

const isCompatible =
!dapp.conditions?.walletCompatibility ||
dapp.conditions.walletCompatibility.includes(
walletType === HookDappWalletCompatibility.EOA
? HookDappWalletCompatibility.EOA
: HookDappWalletCompatibility.SMART_CONTRACT,
)

return (
<styled.Header iconSize={iconSize} gap={gap} padding={padding}>
<img src={image} alt={name} />
<styled.Content>
<h3>{name}</h3>
<styled.Description>{descriptionShort}</styled.Description>
{onSelect && <styled.AddButton onClick={onSelect}>Add</styled.AddButton>}
{onSelect &&
(isCompatible ? (
<styled.AddButton onClick={onSelect}>Add</styled.AddButton>
) : (
<styled.AddButton disabled title="Not compatible with current wallet type">
n/a
</styled.AddButton>
))}
</styled.Content>
</styled.Header>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,20 @@ export const Description = styled.span`
}
`

export const AddButton = styled.button`
background: var(${UI.COLOR_PRIMARY});
color: var(${UI.COLOR_PAPER});
export const AddButton = styled.button<{ disabled?: boolean }>`
background: ${({ disabled }) => `var(${disabled ? UI.COLOR_PRIMARY_OPACITY_10 : UI.COLOR_PRIMARY})`};
color: ${({ disabled }) => `var(${disabled ? UI.COLOR_TEXT_OPACITY_50 : UI.COLOR_PAPER})`};
border: none;
outline: none;
font-weight: 600;
font-size: 16px;
padding: 11px;
border-radius: 21px;
cursor: pointer;
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
transition: background 0.2s ease-in-out;
margin: 16px 0 0;

&:hover {
background: var(${UI.COLOR_PRIMARY_DARKEST});
background: ${({ disabled }) => `var(${disabled ? UI.COLOR_PRIMARY_OPACITY_10 : UI.COLOR_PRIMARY_DARKEST})`};
}
`
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ICON_INFO from '@cowprotocol/assets/cow-swap/info.svg'
import { HookDappWalletCompatibility } from '@cowprotocol/hook-dapp-lib'
import { Command } from '@cowprotocol/types'

import SVG from 'react-inlinesvg'
Expand All @@ -9,12 +10,13 @@ import { HookDapp } from '../../types/hooks'

interface HookListItemProps {
dapp: HookDapp
walletType: HookDappWalletCompatibility
onSelect: Command
onOpenDetails: Command
onRemove?: Command
}

export function HookListItem({ dapp, onSelect, onOpenDetails, onRemove }: HookListItemProps) {
export function HookListItem({ dapp, walletType, onSelect, onOpenDetails, onRemove }: HookListItemProps) {
const { name, descriptionShort, image, version } = dapp

const handleItemClick = (event: React.MouseEvent<HTMLLIElement>) => {
Expand All @@ -25,8 +27,15 @@ export function HookListItem({ dapp, onSelect, onOpenDetails, onRemove }: HookLi
}
}

// If walletCompatibility is not defined, the hook is compatible with any wallet type
const isCompatible =
!dapp.conditions?.walletCompatibility ||
dapp.conditions.walletCompatibility.includes(
walletType === 'EOA' ? HookDappWalletCompatibility.EOA : HookDappWalletCompatibility.SMART_CONTRACT,
)

return (
<styled.HookDappListItem onClick={handleItemClick}>
<styled.HookDappListItem onClick={handleItemClick} isCompatible={isCompatible}>
<img src={image} alt={name} />

<styled.HookDappDetails onClick={onOpenDetails}>
Expand All @@ -37,9 +46,15 @@ export function HookListItem({ dapp, onSelect, onOpenDetails, onRemove }: HookLi
</p>
</styled.HookDappDetails>
<span>
<styled.LinkButton onClick={onSelect} className="link-button">
Add
</styled.LinkButton>
{isCompatible ? (
<styled.LinkButton onClick={onSelect} className="link-button">
Add
</styled.LinkButton>
) : (
<styled.LinkButton disabled title="Not compatible with current wallet type">
n/a
</styled.LinkButton>
)}
{onRemove ? (
<styled.RemoveButton onClick={onRemove} className="remove-button">
Remove
Expand Down
Loading
Loading