Skip to content

Commit e390e74

Browse files
Contentful navigation (#3885)
* config file in prerequisites * move product hub to prerequisites * featured earn products * featured multiply products * top borrow products * top tokens * tokens and use cases * custom modules * remove comments
1 parent 6f873dd commit e390e74

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+966
-1192
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
1-
import { useProductHubData } from 'features/productHub/hooks/useProductHubData'
2-
import type { ConfigResponseType, PreloadAppDataContext } from 'helpers/config'
1+
import type { PreloadAppDataContext } from 'helpers/config'
32
import { configCacheTime, getLocalAppConfig, saveConfigToLocalStorage } from 'helpers/config'
43
import { LendingProtocol } from 'lendingProtocols'
54
import type { PropsWithChildren } from 'react'
65
import React, { useContext, useEffect, useState } from 'react'
76

8-
const configFetcher = () =>
9-
fetch(`/api/config`, {
10-
method: 'GET',
11-
headers: {
12-
'Content-Type': 'application/json',
13-
},
14-
})
15-
167
export const preloadAppDataContext = React.createContext<PreloadAppDataContext | undefined>(
178
undefined,
189
)
@@ -28,49 +19,47 @@ export function usePreloadAppDataContext(): PreloadAppDataContext {
2819
}
2920

3021
export function PreloadAppDataContextProvider({ children }: PropsWithChildren<{}>) {
31-
const [context, setContext] = useState<PreloadAppDataContext | undefined>(undefined)
22+
const [context, setContext] = useState<PreloadAppDataContext>()
3223
const { AjnaSafetySwitch, MorphoSafetySwitch } = getLocalAppConfig('features')
3324

34-
const [config, setConfig] = useState<ConfigResponseType | undefined>(undefined)
35-
const { data: productHub } = useProductHubData({
36-
protocols: [
37-
...(AjnaSafetySwitch ? [] : [LendingProtocol.Ajna]),
38-
LendingProtocol.AaveV2,
39-
LendingProtocol.AaveV3,
40-
LendingProtocol.Maker,
41-
...(MorphoSafetySwitch ? [] : [LendingProtocol.MorphoBlue]),
42-
LendingProtocol.SparkV3,
43-
].filter((p) => p) as LendingProtocol[],
44-
})
45-
4625
useEffect(() => {
4726
const setup = async () => {
4827
const fetchConfig = async () => {
4928
try {
50-
const response = await configFetcher()
51-
const configResponse = (await response.json()) as ConfigResponseType
52-
if (!configResponse || configResponse.error) {
53-
throw new Error(`Error fetching preload app data: ${configResponse.error}`)
54-
}
55-
setConfig(configResponse)
56-
saveConfigToLocalStorage(configResponse)
29+
const response = await fetch(
30+
`/api/prerequisites?protocols=${[
31+
...(AjnaSafetySwitch ? [] : [LendingProtocol.Ajna]),
32+
LendingProtocol.AaveV2,
33+
LendingProtocol.AaveV3,
34+
LendingProtocol.Maker,
35+
...(MorphoSafetySwitch ? [] : [LendingProtocol.MorphoBlue]),
36+
LendingProtocol.SparkV3,
37+
]}`,
38+
{
39+
method: 'GET',
40+
headers: {
41+
'Content-Type': 'application/json',
42+
},
43+
},
44+
)
45+
const data = (await response.json()) as PreloadAppDataContext
46+
47+
if (!data) throw new Error('Error fetching preload app data')
48+
49+
setContext(data)
50+
saveConfigToLocalStorage(data.config)
5751
} catch (error) {
5852
console.error(`Error fetching preload app data context: ${error}`)
5953
}
6054
}
6155
await fetchConfig()
56+
6257
setInterval(fetchConfig, 1000 * configCacheTime.frontend)
6358
}
6459
setup().catch((error) => {
6560
console.error(`Error setting up preload app data context: ${error}`)
6661
})
6762
}, [])
6863

69-
useEffect(() => {
70-
if (config && productHub) {
71-
setContext({ config, productHub })
72-
}
73-
}, [config, productHub])
74-
7564
return <preloadAppDataContext.Provider value={context}>{children}</preloadAppDataContext.Provider>
7665
}

components/navigation/Navigation.types.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import type { IconProps } from 'components/Icon.types'
22
import type { ReactNode } from 'react'
33

4-
export interface NavigationMenuPanelAsset {
5-
token: string
6-
link: string
7-
}
4+
export type NavigationModule = 'swap' | 'bridge'
85

96
export interface NavigationMenuPanelLink {
107
icon: IconProps['icon']
@@ -97,11 +94,11 @@ export interface NavigationMenuPanelListItem {
9794
hoverColor?: string
9895
icon?: NavigationMenuPanelIcon
9996
list?: NavigationMenuPanelList
97+
navigationModule?: NavigationModule
10098
promoted?: boolean
10199
tags?: NavigationMenuPanelListTags
102100
title: ReactNode
103101
url?: string
104-
callback?: () => void
105102
}
106103
export interface NavigationMenuPanelType {
107104
label: string

components/navigation/NavigationMenuDropdownContentList.tsx

+17-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { NavigationModuleBridge } from 'components/navigation/content/NavigationModuleBridge'
2+
import { NavigationModuleSwap } from 'components/navigation/content/NavigationModuleSwap'
13
import type { NavigationMenuPanelList } from 'components/navigation/Navigation.types'
24
import { NavigationMenuDropdownContentListItem } from 'components/navigation/NavigationMenuDropdownContentListItem'
35
import { WithArrow } from 'components/WithArrow'
@@ -65,7 +67,7 @@ export function NavigationMenuDropdownContentList({
6567
p: 0,
6668
}}
6769
>
68-
{items.map(({ callback, hoverColor, url, ...item }, i) => (
70+
{items.map(({ hoverColor, url, navigationModule, ...item }, i) => (
6971
<Box
7072
key={i}
7173
as="li"
@@ -77,7 +79,7 @@ export function NavigationMenuDropdownContentList({
7779
cursor: 'default',
7880
...itemHoverEffect,
7981
}),
80-
...((url || onClick || callback) && {
82+
...((url || onClick || navigationModule) && {
8183
cursor: 'pointer',
8284
'&:hover': itemHoverEffect,
8385
}),
@@ -94,8 +96,19 @@ export function NavigationMenuDropdownContentList({
9496
<NavigationMenuDropdownContentListItem hoverColor={hoverColor} {...item} />
9597
</Link>
9698
) : (
97-
<Box sx={{ ...itemInnerPadding }} onClick={callback}>
98-
<NavigationMenuDropdownContentListItem hoverColor={hoverColor} {...item} />
99+
<Box sx={{ ...itemInnerPadding }}>
100+
{navigationModule ? (
101+
<>
102+
{
103+
{
104+
swap: <NavigationModuleSwap />,
105+
bridge: <NavigationModuleBridge />,
106+
}[navigationModule]
107+
}
108+
</>
109+
) : (
110+
<NavigationMenuDropdownContentListItem hoverColor={hoverColor} {...item} />
111+
)}
99112
</Box>
100113
)}
101114
</Box>

components/navigation/NavigationMenuDropdownContentListItem.tsx

+13-4
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import { Box, Flex, Heading, Text } from 'theme-ui'
77

88
import type { NavigationMenuPanelList } from './Navigation.types'
99

10-
type NavigationMenuDropdownContentListItemProps =
11-
NavigationMenuPanelList['items'] extends readonly (infer ElementType)[] ? ElementType : never
10+
type NavigationMenuDropdownContentListItemProps = {
11+
onClick?: () => void
12+
} & (NavigationMenuPanelList['items'] extends readonly (infer ElementType)[] ? ElementType : never)
1213

1314
export function NavigationMenuDropdownContentListItem({
1415
description,
1516
hoverColor,
1617
icon,
18+
onClick,
1719
promoted,
1820
tags,
1921
title,
@@ -29,7 +31,10 @@ export function NavigationMenuDropdownContentListItem({
2931
}
3032

3133
return (
32-
<Flex sx={{ alignItems: 'center', columnGap: '12px', lineHeight: '24px' }}>
34+
<Flex
35+
sx={{ alignItems: 'center', columnGap: '12px', lineHeight: '24px' }}
36+
{...(onClick && { onClick })}
37+
>
3338
{icon && icon.position === 'global' && <NavigationMenuDropdownContentIcon {...icon} />}
3439
<Box>
3540
<Flex sx={{ alignItems: 'center', columnGap: 2 }}>
@@ -66,7 +71,11 @@ export function NavigationMenuDropdownContentListItem({
6671
variant="paragraph4"
6772
sx={{ mt: 1, color: 'neutral80', em: { color: 'primary100', fontStyle: 'normal' } }}
6873
>
69-
{description}
74+
{typeof description === 'string' ? (
75+
<span dangerouslySetInnerHTML={{ __html: description.replace(/\n/giu, '<br />') }} />
76+
) : (
77+
description
78+
)}
7079
</Text>
7180
)}
7281
{tags && (
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { BaseNetworkNames, networksByName } from 'blockchain/networks'
2+
import { NavigationMenuDropdownContentListItem } from 'components/navigation/NavigationMenuDropdownContentListItem'
3+
import type { SwapWidgetChangeAction } from 'features/swapWidget/SwapWidgetChange'
4+
import { SWAP_WIDGET_CHANGE_SUBJECT } from 'features/swapWidget/SwapWidgetChange'
5+
import { useConnection } from 'features/web3OnBoard/useConnection'
6+
import { uiChanges } from 'helpers/uiChanges'
7+
import { useTranslation } from 'next-i18next'
8+
import type { FC } from 'react'
9+
import React from 'react'
10+
import { bridge } from 'theme/icons'
11+
import { Box, Flex, Image } from 'theme-ui'
12+
13+
export const NavigationModuleBridge: FC = () => {
14+
const { t } = useTranslation()
15+
16+
const { connect } = useConnection()
17+
18+
return (
19+
<NavigationMenuDropdownContentListItem
20+
title={t('nav.bridge')}
21+
description={
22+
<>
23+
{t('nav.bridge-description')}
24+
<Flex
25+
as="ul"
26+
sx={{
27+
mt: '14px',
28+
ml: 0,
29+
p: 0,
30+
listStyle: 'none',
31+
columnGap: '14px',
32+
}}
33+
>
34+
{[BaseNetworkNames.Ethereum, BaseNetworkNames.Optimism, BaseNetworkNames.Arbitrum].map(
35+
(network) => (
36+
<Box as="li" key={network}>
37+
<Image
38+
src={networksByName[network].icon}
39+
width={20}
40+
sx={{ verticalAlign: 'bottom' }}
41+
/>
42+
</Box>
43+
),
44+
)}
45+
</Flex>
46+
</>
47+
}
48+
icon={{
49+
position: 'global',
50+
icon: bridge,
51+
}}
52+
onClick={() => {
53+
connect()
54+
uiChanges.publish<SwapWidgetChangeAction>(SWAP_WIDGET_CHANGE_SUBJECT, {
55+
type: 'open',
56+
variant: 'bridge',
57+
})
58+
}}
59+
/>
60+
)
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { NavigationMenuDropdownContentListItem } from 'components/navigation/NavigationMenuDropdownContentListItem'
2+
import type { SwapWidgetChangeAction } from 'features/swapWidget/SwapWidgetChange'
3+
import { SWAP_WIDGET_CHANGE_SUBJECT } from 'features/swapWidget/SwapWidgetChange'
4+
import { useConnection } from 'features/web3OnBoard/useConnection'
5+
import { uiChanges } from 'helpers/uiChanges'
6+
import { useTranslation } from 'next-i18next'
7+
import type { FC } from 'react'
8+
import React from 'react'
9+
import { exchange } from 'theme/icons'
10+
11+
export const NavigationModuleSwap: FC = () => {
12+
const { t } = useTranslation()
13+
14+
const { connect } = useConnection()
15+
16+
return (
17+
<NavigationMenuDropdownContentListItem
18+
title={t('nav.swap')}
19+
description={t('nav.swap-description')}
20+
icon={{
21+
position: 'global',
22+
icon: exchange,
23+
}}
24+
onClick={() => {
25+
connect()
26+
uiChanges.publish<SwapWidgetChangeAction>(SWAP_WIDGET_CHANGE_SUBJECT, {
27+
type: 'open',
28+
variant: 'swap',
29+
})
30+
}}
31+
/>
32+
)
33+
}

contentful/api.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import getConfig from 'next/config'
22

3-
export async function fetchGraphQL<T>(query: string, preview = false): Promise<T> {
3+
export async function fetchContentfulGraphQL<T>(query: string, preview = false): Promise<T> {
44
const accessToken =
55
getConfig()?.publicRuntimeConfig?.contentfulAccessToken || process.env.CONTENTFUL_ACCESS_TOKEN
66
const previewAccessToken =

contentful/queries/getLandingPageBySlug.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { fetchGraphQL } from 'contentful/api'
1+
import { fetchContentfulGraphQL } from 'contentful/api'
22
import type { LandingPageRawResponse } from 'contentful/types'
33

44
export async function getLandingPageBySlug(slug: string, preview: boolean) {
5-
return await fetchGraphQL<LandingPageRawResponse>(
5+
return await fetchContentfulGraphQL<LandingPageRawResponse>(
66
`
77
{
88
landingPageCollection(

contentful/queries/getLandingPageContentByIds.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { fetchGraphQL } from 'contentful/api'
1+
import { fetchContentfulGraphQL } from 'contentful/api'
22
import { landingPageContentsChunkSize } from 'contentful/constants'
33
import type { EntryRawResponse } from 'contentful/types'
44
import { splitArrayToSameSizeChunks } from 'helpers/splitArrayToSameSizeChunks'
@@ -8,7 +8,7 @@ export const getLandingPageContentByIds = async (collectionIds: string[], previe
88
await Promise.all(
99
splitArrayToSameSizeChunks(collectionIds, landingPageContentsChunkSize).map(
1010
(collectionIdsChunk) =>
11-
fetchGraphQL<EntryRawResponse>(
11+
fetchContentfulGraphQL<EntryRawResponse>(
1212
`
1313
{
1414
entryCollection(

features/aave/services/risk-slider-config.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { RiskRatio } from '@oasisdex/dma-library'
22
import BigNumber from 'bignumber.js'
33
import type { AdjustRiskViewConfig } from 'features/aave/components'
4-
import type { ConfigResponseType } from 'helpers/config'
54
import { getLocalAppConfig } from 'helpers/config'
65
import { formatCryptoBalance, formatPercent } from 'helpers/formatters/format'
76
import React from 'react'
7+
import type { AppConfigType } from 'types/config'
88

9-
const getRiskRatio = (type: keyof ConfigResponseType['parameters']['aaveLike']['riskRatios']) => {
9+
const getRiskRatio = (type: keyof AppConfigType['parameters']['aaveLike']['riskRatios']) => {
1010
const { aaveLike } = getLocalAppConfig('parameters')
1111
return new BigNumber(aaveLike?.riskRatios?.[type] ?? 5) // 5 as fallback, optional for the tests to pass
1212
}

0 commit comments

Comments
 (0)