From 4b6534fe464bf4468adefce426cea21a213ad95b Mon Sep 17 00:00:00 2001 From: Majorfi Date: Tue, 17 Oct 2023 12:54:23 +0200 Subject: [PATCH] feat: use search query for vault list --- .eslintrc.js | 5 +- apps/vaults/Wrapper.tsx | 2 +- apps/vaults/components/ListHero.tsx | 6 +-- apps/vaults/contexts/useAppSettings.tsx | 18 ++++++- apps/vaults/hooks/useFilteredVaults.ts | 7 --- apps/vaults/hooks/useSolverChainCoin.ts | 2 +- apps/vaults/hooks/useSolverCowswap.ts | 2 +- .../hooks/useSolverInternalMigration.ts | 2 +- apps/vaults/hooks/useSolverOptimismBooster.ts | 2 +- apps/vaults/hooks/useSolverPartnerContract.ts | 2 +- apps/vaults/hooks/useSolverPortals.ts | 2 +- apps/vaults/hooks/useSolverVanilla.ts | 2 +- apps/vaults/hooks/useSolverWido.ts | 2 +- package.json | 1 + pages/vaults/index.tsx | 54 ++++++++++++++++--- yarn.lock | 12 +++++ 16 files changed, 90 insertions(+), 31 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index e96fd59a3..6a676e882 100755 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -26,8 +26,9 @@ module.exports = { } ], 'react-hooks/exhaustive-deps': [ - 'warn', { - 'additionalHooks': '^useAsync$' + 'warn', + { + additionalHooks: '^useAsync$' } ] } diff --git a/apps/vaults/Wrapper.tsx b/apps/vaults/Wrapper.tsx index 2d18033f2..06d777250 100755 --- a/apps/vaults/Wrapper.tsx +++ b/apps/vaults/Wrapper.tsx @@ -20,7 +20,7 @@ export function Wrapper({children, router}: {children: ReactElement; router: Nex void; set_selectedChains: (chains: string) => void; - set_searchValue: (searchValue: string) => void; + onSearch: (searchValue: string) => void; }; -export function ListHero({categories, set_categories, searchValue, selectedChains, set_searchValue, set_selectedChains}: TListHero): ReactElement { +export function ListHero({categories, set_categories, searchValue, selectedChains, onSearch, set_selectedChains}: TListHero): ReactElement { const chainsFromJSON = useMemo((): number[] => JSON.parse(selectedChains || '[]') as number[], [selectedChains]); const categoriesFromJSON = useMemo((): string[] => JSON.parse(categories || '[]') as string[], [categories]); @@ -143,7 +143,7 @@ export function ListHero({categories, set_categories, searchValue, selectedChain className={'md:w-full'} searchPlaceholder={'YFI Vault'} searchValue={searchValue} - set_searchValue={set_searchValue} + set_searchValue={onSearch} /> diff --git a/apps/vaults/contexts/useAppSettings.tsx b/apps/vaults/contexts/useAppSettings.tsx index bc050ae44..3b3382b70 100755 --- a/apps/vaults/contexts/useAppSettings.tsx +++ b/apps/vaults/contexts/useAppSettings.tsx @@ -34,8 +34,11 @@ const defaultProps: TAppSettingsContext = { const AppSettingsContext = createContext(defaultProps); export const AppSettingsContextApp = memo(function AppSettingsContextApp({children}: {children: ReactElement}): ReactElement { - const [category, set_category] = useSessionStorage('yearn.fi/vaults-categories@0.0.1', ALL_CATEGORIES); + /** + * @deprecated Use use-query-params instead + */ const [searchValue, set_searchValue] = useSessionStorage('yearn.fi/vaults-search@0.0.1', ''); + const [category, set_category] = useSessionStorage('yearn.fi/vaults-categories@0.0.1', ALL_CATEGORIES); const [selectedChains, set_selectedChains] = useSessionStorage('yearn.fi/selected-chains@0.0.1', ALL_CHAINS); const [shouldHideDust, set_shouldHideDust] = useLocalStorage('yearn.fi/should-hide-dust@0.0.1', false); const [shouldHideLowTVLVaults, set_shouldHideLowTVLVaults] = useLocalStorage('yearn.fi/hide-low-tvl@0.0.1', false); @@ -56,7 +59,18 @@ export const AppSettingsContextApp = memo(function AppSettingsContextApp({childr set_searchValue, set_selectedChains }), - [shouldHideDust, shouldHideLowTVLVaults, category, searchValue, set_category, set_searchValue, set_shouldHideDust, set_shouldHideLowTVLVaults] + [ + shouldHideDust, + shouldHideLowTVLVaults, + category, + selectedChains, + searchValue, + set_category, + set_searchValue, + set_selectedChains, + set_shouldHideDust, + set_shouldHideLowTVLVaults + ] ); return {children}; diff --git a/apps/vaults/hooks/useFilteredVaults.ts b/apps/vaults/hooks/useFilteredVaults.ts index 9bf124ca1..d687b6f2c 100644 --- a/apps/vaults/hooks/useFilteredVaults.ts +++ b/apps/vaults/hooks/useFilteredVaults.ts @@ -117,13 +117,6 @@ export function useVaultFilter(): {activeVaults: TYDaemonVault[]; retiredVaults: //remove duplicates _vaultList = _vaultList.filter((vault, index, self): boolean => index === self.findIndex((v): boolean => v.address === vault.address)); - for (const vault of _vaultList) { - if (vault.apr.forwardAPR.netAPR === 0) { - // console.log(`DebtRatio for vault ${vault.address} - ${vault.name}: 0`); - } - } - // console.log('-------------------') - return _vaultList; }, [categoriesFromJSON, curveVaults, balancerVaults, velodromeVaults, aerodromeVaults, boostedVaults, stablesVaults, cryptoVaults, holdingsVaults]); diff --git a/apps/vaults/hooks/useSolverChainCoin.ts b/apps/vaults/hooks/useSolverChainCoin.ts index 75246c945..567af063a 100644 --- a/apps/vaults/hooks/useSolverChainCoin.ts +++ b/apps/vaults/hooks/useSolverChainCoin.ts @@ -49,7 +49,7 @@ export function useSolverChainCoin(): TSolverContext { **************************************************************************/ const onRetrieveAllowance = useCallback( async (shouldForceRefetch?: boolean): Promise => { - if (!request?.current) { + if (!request?.current || !provider) { return toNormalizedBN(0); } diff --git a/apps/vaults/hooks/useSolverCowswap.ts b/apps/vaults/hooks/useSolverCowswap.ts index 7c3eebd05..079353aaf 100644 --- a/apps/vaults/hooks/useSolverCowswap.ts +++ b/apps/vaults/hooks/useSolverCowswap.ts @@ -275,7 +275,7 @@ export function useSolverCowswap(): TSolverContext { **************************************************************************/ const onRetrieveAllowance = useCallback( async (shouldForceRefetch?: boolean): Promise => { - if (!request?.current || request.current.chainID !== 1) { + if (!request?.current || request.current.chainID !== 1 || !provider) { return toNormalizedBN(0); } assert(request.current, 'Request is not defined'); diff --git a/apps/vaults/hooks/useSolverInternalMigration.ts b/apps/vaults/hooks/useSolverInternalMigration.ts index 67dd776f2..70622bdcf 100644 --- a/apps/vaults/hooks/useSolverInternalMigration.ts +++ b/apps/vaults/hooks/useSolverInternalMigration.ts @@ -60,7 +60,7 @@ export function useSolverInternalMigration(): TSolverContext { **************************************************************************/ const onRetrieveAllowance = useCallback( async (shouldForceRefetch?: boolean): Promise => { - if (!request?.current) { + if (!request?.current || !provider) { return toNormalizedBN(0); } diff --git a/apps/vaults/hooks/useSolverOptimismBooster.ts b/apps/vaults/hooks/useSolverOptimismBooster.ts index 724548376..ba6dd18f3 100644 --- a/apps/vaults/hooks/useSolverOptimismBooster.ts +++ b/apps/vaults/hooks/useSolverOptimismBooster.ts @@ -46,7 +46,7 @@ export function useSolverOptimismBooster(): TSolverContext { **************************************************************************/ const onRetrieveAllowance = useCallback( async (shouldForceRefetch?: boolean): Promise => { - if (!request?.current) { + if (!request?.current || !provider) { return toNormalizedBN(0); } diff --git a/apps/vaults/hooks/useSolverPartnerContract.ts b/apps/vaults/hooks/useSolverPartnerContract.ts index 1eaeb6eb3..884d0331a 100644 --- a/apps/vaults/hooks/useSolverPartnerContract.ts +++ b/apps/vaults/hooks/useSolverPartnerContract.ts @@ -48,7 +48,7 @@ export function useSolverPartnerContract(): TSolverContext { **************************************************************************/ const onRetrieveAllowance = useCallback( async (shouldForceRefetch?: boolean): Promise => { - if (!request?.current) { + if (!request?.current || !provider) { return toNormalizedBN(0); } diff --git a/apps/vaults/hooks/useSolverPortals.ts b/apps/vaults/hooks/useSolverPortals.ts index c32b557ba..f2ca97049 100644 --- a/apps/vaults/hooks/useSolverPortals.ts +++ b/apps/vaults/hooks/useSolverPortals.ts @@ -282,7 +282,7 @@ export function useSolverPortals(): TSolverContext { **************************************************************************/ const onApprove = useCallback( async (amount = MAX_UINT_256, txStatusSetter: React.Dispatch>, onSuccess: () => Promise): Promise => { - if (!request.current || isSolverDisabled(request.current.chainID)[Solver.enum.Portals]) { + if (!request.current || isSolverDisabled(request.current.chainID)[Solver.enum.Portals] || !provider) { return; } assert(request.current, 'Request is not set'); diff --git a/apps/vaults/hooks/useSolverVanilla.ts b/apps/vaults/hooks/useSolverVanilla.ts index fb598bf59..ef68a3794 100644 --- a/apps/vaults/hooks/useSolverVanilla.ts +++ b/apps/vaults/hooks/useSolverVanilla.ts @@ -45,7 +45,7 @@ export function useSolverVanilla(): TSolverContext { **************************************************************************/ const onRetrieveAllowance = useCallback( async (shouldForceRefetch?: boolean): Promise => { - if (!request?.current) { + if (!request?.current || !provider) { return toNormalizedBN(0); } diff --git a/apps/vaults/hooks/useSolverWido.ts b/apps/vaults/hooks/useSolverWido.ts index e26c90486..af96c3051 100644 --- a/apps/vaults/hooks/useSolverWido.ts +++ b/apps/vaults/hooks/useSolverWido.ts @@ -182,7 +182,7 @@ export function useSolverWido(): TSolverContext { **************************************************************************/ const onRetrieveAllowance = useCallback( async (shouldForceRefetch?: boolean): Promise => { - if (!latestQuote?.current || !request?.current || isSolverDisabled(request.current.chainID)[Solver.enum.Wido]) { + if (!latestQuote?.current || !request?.current || isSolverDisabled(request.current.chainID)[Solver.enum.Wido] || !provider) { return toNormalizedBN(0); } diff --git a/package.json b/package.json index b56281cd2..586b92b4d 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "tailwindcss": "^3.3.3", "ts-loader": "^9.4.4", "typescript": "^5.2.2", + "use-query-params": "^2.2.1", "vitest": "^0.34.6", "webpack": "^5.88.2" }, diff --git a/pages/vaults/index.tsx b/pages/vaults/index.tsx index 7af56f439..09e4bda78 100644 --- a/pages/vaults/index.tsx +++ b/pages/vaults/index.tsx @@ -1,4 +1,5 @@ -import {Fragment, useCallback, useEffect, useMemo} from 'react'; +import {Fragment, useCallback, useEffect, useMemo, useState} from 'react'; +import {QueryParamProvider, StringParam, useQueryParams} from 'use-query-params'; import {motion, useSpring, useTransform} from 'framer-motion'; import {VaultListOptions} from '@vaults/components/list/VaultListOptions'; import {VaultsListEmpty} from '@vaults/components/list/VaultsListEmpty'; @@ -20,6 +21,7 @@ import {isZero} from '@yearn-finance/web-lib/utils/isZero'; import {ListHead} from '@common/components/ListHead'; import {useWallet} from '@common/contexts/useWallet'; import {useYearn} from '@common/contexts/useYearn'; +import {NextQueryParamAdapter} from '@common/utils/QueryParamsProvider'; import type {NextRouter} from 'next/router'; import type {ReactElement, ReactNode} from 'react'; @@ -95,21 +97,53 @@ function Index(): ReactElement { sortBy: TPossibleSortBy; sortDirection: TSortDirection; }>('yVaultsSorting', {sortBy: 'featuringScore', sortDirection: 'desc'}); - const {category, searchValue, selectedChains, set_category, set_searchValue, set_selectedChains} = useAppSettings(); + const {category, selectedChains, set_category, set_selectedChains} = useAppSettings(); const chainsFromJSON = useMemo((): number[] => JSON.parse(selectedChains || '[]') as number[], [selectedChains]); const categoriesFromJSON = useMemo((): string[] => JSON.parse(category || '[]') as string[], [category]); const {activeVaults, migratableVaults, retiredVaults} = useVaultFilter(); + const [searchParam, set_searchParam] = useQueryParams({search: StringParam}); + const [search, set_search] = useState(searchParam?.search); + + /** 🔵 - Yearn ********************************************************************************* + ** This useEffect hook is used to synchronize the search state with the query parameter + ** It checks if the search state and the search query parameter are the same, if they are, + ** it does nothing. + ** If the search state is undefined and the search query parameter is not, it sets the search + ** state to the value of the search query parameter. + ** If the search state is not undefined, it updates the search query parameter to match the + ** search state. + ** If the search state is undefined, it removes the search query parameter. + *********************************************************************************************/ + useEffect((): void => { + // If the search state and the search query parameter are the same, do nothing + if (searchParam.search === search) { + return; + } + // If the search state is undefined and the search query parameter is not, set the search + // state to the value of the search query parameter + if (search === undefined && searchParam.search !== undefined) { + set_search(searchParam.search); + return; + } + // If the search state is not undefined, update the search query parameter to match + // the search state + if (!search) { + set_searchParam({}, 'push'); + } else { + set_searchParam({search: search}, 'push'); + } + }, [searchParam, search, set_searchParam]); /* 🔵 - Yearn Finance ************************************************************************** ** Then, on the activeVaults list, we apply the search filter. The search filter is ** implemented as a simple string.includes() on the vault name. **********************************************************************************************/ const searchedVaultsToDisplay = useMemo((): TYDaemonVault[] => { - if (searchValue === '') { + if (!search) { return activeVaults; } return activeVaults.filter((vault: TYDaemonVault): boolean => { - const lowercaseSearch = searchValue.toLowerCase(); + const lowercaseSearch = search.toLowerCase(); return ( vault.name.toLowerCase().startsWith(lowercaseSearch) || vault.symbol.toLowerCase().startsWith(lowercaseSearch) || @@ -119,7 +153,7 @@ function Index(): ReactElement { vault.token.address.toLowerCase().startsWith(lowercaseSearch) ); }); - }, [activeVaults, searchValue]); + }, [activeVaults, search]); /* 🔵 - Yearn Finance ************************************************************************** ** Then, once we have reduced the list of vaults to display, we can sort them. The sorting @@ -193,10 +227,10 @@ function Index(): ReactElement { set_search(value)} /> 0}> @@ -253,7 +287,11 @@ function Index(): ReactElement { } Index.getLayout = function getLayout(page: ReactElement, router: NextRouter): ReactElement { - return {page}; + return ( + + {page} + + ); }; export default Index; diff --git a/yarn.lock b/yarn.lock index aa86e6403..67f15373b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8629,6 +8629,11 @@ serialize-javascript@^6.0.1: dependencies: randombytes "^2.1.0" +serialize-query-params@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/serialize-query-params/-/serialize-query-params-2.0.2.tgz#598a3fb9e13f4ea1c1992fbd20231aa16b31db81" + integrity sha512-1chMo1dST4pFA9RDXAtF0Rbjaut4is7bzFbI1Z26IuMub68pNCILku85aYmeFhvnY//BXUPUhoRMjYcsT93J/Q== + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -9696,6 +9701,13 @@ use-callback-ref@^1.3.0: dependencies: tslib "^2.0.0" +use-query-params@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/use-query-params/-/use-query-params-2.2.1.tgz#c558ab70706f319112fbccabf6867b9f904e947d" + integrity sha512-i6alcyLB8w9i3ZK3caNftdb+UnbfBRNPDnc89CNQWkGRmDrm/gfydHvMBfVsQJRq3NoHOM2dt/ceBWG2397v1Q== + dependencies: + serialize-query-params "^2.0.2" + use-sidecar@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.2.tgz#2f43126ba2d7d7e117aa5855e5d8f0276dfe73c2"