Skip to content

Commit

Permalink
Contentful navigation (#3885)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
piotrkonowrocki authored May 14, 2024
1 parent 6f873dd commit e390e74
Show file tree
Hide file tree
Showing 56 changed files with 966 additions and 1,192 deletions.
61 changes: 25 additions & 36 deletions components/context/PreloadAppDataContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
import { useProductHubData } from 'features/productHub/hooks/useProductHubData'
import type { ConfigResponseType, PreloadAppDataContext } from 'helpers/config'
import type { PreloadAppDataContext } from 'helpers/config'
import { configCacheTime, getLocalAppConfig, saveConfigToLocalStorage } from 'helpers/config'
import { LendingProtocol } from 'lendingProtocols'
import type { PropsWithChildren } from 'react'
import React, { useContext, useEffect, useState } from 'react'

const configFetcher = () =>
fetch(`/api/config`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
})

export const preloadAppDataContext = React.createContext<PreloadAppDataContext | undefined>(
undefined,
)
Expand All @@ -28,49 +19,47 @@ export function usePreloadAppDataContext(): PreloadAppDataContext {
}

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

const [config, setConfig] = useState<ConfigResponseType | undefined>(undefined)
const { data: productHub } = useProductHubData({
protocols: [
...(AjnaSafetySwitch ? [] : [LendingProtocol.Ajna]),
LendingProtocol.AaveV2,
LendingProtocol.AaveV3,
LendingProtocol.Maker,
...(MorphoSafetySwitch ? [] : [LendingProtocol.MorphoBlue]),
LendingProtocol.SparkV3,
].filter((p) => p) as LendingProtocol[],
})

useEffect(() => {
const setup = async () => {
const fetchConfig = async () => {
try {
const response = await configFetcher()
const configResponse = (await response.json()) as ConfigResponseType
if (!configResponse || configResponse.error) {
throw new Error(`Error fetching preload app data: ${configResponse.error}`)
}
setConfig(configResponse)
saveConfigToLocalStorage(configResponse)
const response = await fetch(
`/api/prerequisites?protocols=${[
...(AjnaSafetySwitch ? [] : [LendingProtocol.Ajna]),
LendingProtocol.AaveV2,
LendingProtocol.AaveV3,
LendingProtocol.Maker,
...(MorphoSafetySwitch ? [] : [LendingProtocol.MorphoBlue]),
LendingProtocol.SparkV3,
]}`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
},
)
const data = (await response.json()) as PreloadAppDataContext

if (!data) throw new Error('Error fetching preload app data')

setContext(data)
saveConfigToLocalStorage(data.config)
} catch (error) {
console.error(`Error fetching preload app data context: ${error}`)
}
}
await fetchConfig()

setInterval(fetchConfig, 1000 * configCacheTime.frontend)
}
setup().catch((error) => {
console.error(`Error setting up preload app data context: ${error}`)
})
}, [])

useEffect(() => {
if (config && productHub) {
setContext({ config, productHub })
}
}, [config, productHub])

return <preloadAppDataContext.Provider value={context}>{children}</preloadAppDataContext.Provider>
}
7 changes: 2 additions & 5 deletions components/navigation/Navigation.types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import type { IconProps } from 'components/Icon.types'
import type { ReactNode } from 'react'

export interface NavigationMenuPanelAsset {
token: string
link: string
}
export type NavigationModule = 'swap' | 'bridge'

export interface NavigationMenuPanelLink {
icon: IconProps['icon']
Expand Down Expand Up @@ -97,11 +94,11 @@ export interface NavigationMenuPanelListItem {
hoverColor?: string
icon?: NavigationMenuPanelIcon
list?: NavigationMenuPanelList
navigationModule?: NavigationModule
promoted?: boolean
tags?: NavigationMenuPanelListTags
title: ReactNode
url?: string
callback?: () => void
}
export interface NavigationMenuPanelType {
label: string
Expand Down
21 changes: 17 additions & 4 deletions components/navigation/NavigationMenuDropdownContentList.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { NavigationModuleBridge } from 'components/navigation/content/NavigationModuleBridge'
import { NavigationModuleSwap } from 'components/navigation/content/NavigationModuleSwap'
import type { NavigationMenuPanelList } from 'components/navigation/Navigation.types'
import { NavigationMenuDropdownContentListItem } from 'components/navigation/NavigationMenuDropdownContentListItem'
import { WithArrow } from 'components/WithArrow'
Expand Down Expand Up @@ -65,7 +67,7 @@ export function NavigationMenuDropdownContentList({
p: 0,
}}
>
{items.map(({ callback, hoverColor, url, ...item }, i) => (
{items.map(({ hoverColor, url, navigationModule, ...item }, i) => (
<Box
key={i}
as="li"
Expand All @@ -77,7 +79,7 @@ export function NavigationMenuDropdownContentList({
cursor: 'default',
...itemHoverEffect,
}),
...((url || onClick || callback) && {
...((url || onClick || navigationModule) && {
cursor: 'pointer',
'&:hover': itemHoverEffect,
}),
Expand All @@ -94,8 +96,19 @@ export function NavigationMenuDropdownContentList({
<NavigationMenuDropdownContentListItem hoverColor={hoverColor} {...item} />
</Link>
) : (
<Box sx={{ ...itemInnerPadding }} onClick={callback}>
<NavigationMenuDropdownContentListItem hoverColor={hoverColor} {...item} />
<Box sx={{ ...itemInnerPadding }}>
{navigationModule ? (
<>
{
{
swap: <NavigationModuleSwap />,
bridge: <NavigationModuleBridge />,
}[navigationModule]
}
</>
) : (
<NavigationMenuDropdownContentListItem hoverColor={hoverColor} {...item} />
)}
</Box>
)}
</Box>
Expand Down
17 changes: 13 additions & 4 deletions components/navigation/NavigationMenuDropdownContentListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import { Box, Flex, Heading, Text } from 'theme-ui'

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

type NavigationMenuDropdownContentListItemProps =
NavigationMenuPanelList['items'] extends readonly (infer ElementType)[] ? ElementType : never
type NavigationMenuDropdownContentListItemProps = {
onClick?: () => void
} & (NavigationMenuPanelList['items'] extends readonly (infer ElementType)[] ? ElementType : never)

export function NavigationMenuDropdownContentListItem({
description,
hoverColor,
icon,
onClick,
promoted,
tags,
title,
Expand All @@ -29,7 +31,10 @@ export function NavigationMenuDropdownContentListItem({
}

return (
<Flex sx={{ alignItems: 'center', columnGap: '12px', lineHeight: '24px' }}>
<Flex
sx={{ alignItems: 'center', columnGap: '12px', lineHeight: '24px' }}
{...(onClick && { onClick })}
>
{icon && icon.position === 'global' && <NavigationMenuDropdownContentIcon {...icon} />}
<Box>
<Flex sx={{ alignItems: 'center', columnGap: 2 }}>
Expand Down Expand Up @@ -66,7 +71,11 @@ export function NavigationMenuDropdownContentListItem({
variant="paragraph4"
sx={{ mt: 1, color: 'neutral80', em: { color: 'primary100', fontStyle: 'normal' } }}
>
{description}
{typeof description === 'string' ? (
<span dangerouslySetInnerHTML={{ __html: description.replace(/\n/giu, '<br />') }} />
) : (
description
)}
</Text>
)}
{tags && (
Expand Down
61 changes: 61 additions & 0 deletions components/navigation/content/NavigationModuleBridge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { BaseNetworkNames, networksByName } from 'blockchain/networks'
import { NavigationMenuDropdownContentListItem } from 'components/navigation/NavigationMenuDropdownContentListItem'
import type { SwapWidgetChangeAction } from 'features/swapWidget/SwapWidgetChange'
import { SWAP_WIDGET_CHANGE_SUBJECT } from 'features/swapWidget/SwapWidgetChange'
import { useConnection } from 'features/web3OnBoard/useConnection'
import { uiChanges } from 'helpers/uiChanges'
import { useTranslation } from 'next-i18next'
import type { FC } from 'react'
import React from 'react'
import { bridge } from 'theme/icons'
import { Box, Flex, Image } from 'theme-ui'

export const NavigationModuleBridge: FC = () => {
const { t } = useTranslation()

const { connect } = useConnection()

return (
<NavigationMenuDropdownContentListItem
title={t('nav.bridge')}
description={
<>
{t('nav.bridge-description')}
<Flex
as="ul"
sx={{
mt: '14px',
ml: 0,
p: 0,
listStyle: 'none',
columnGap: '14px',
}}
>
{[BaseNetworkNames.Ethereum, BaseNetworkNames.Optimism, BaseNetworkNames.Arbitrum].map(
(network) => (
<Box as="li" key={network}>
<Image
src={networksByName[network].icon}
width={20}
sx={{ verticalAlign: 'bottom' }}
/>
</Box>
),
)}
</Flex>
</>
}
icon={{
position: 'global',
icon: bridge,
}}
onClick={() => {
connect()
uiChanges.publish<SwapWidgetChangeAction>(SWAP_WIDGET_CHANGE_SUBJECT, {
type: 'open',
variant: 'bridge',
})
}}
/>
)
}
33 changes: 33 additions & 0 deletions components/navigation/content/NavigationModuleSwap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { NavigationMenuDropdownContentListItem } from 'components/navigation/NavigationMenuDropdownContentListItem'
import type { SwapWidgetChangeAction } from 'features/swapWidget/SwapWidgetChange'
import { SWAP_WIDGET_CHANGE_SUBJECT } from 'features/swapWidget/SwapWidgetChange'
import { useConnection } from 'features/web3OnBoard/useConnection'
import { uiChanges } from 'helpers/uiChanges'
import { useTranslation } from 'next-i18next'
import type { FC } from 'react'
import React from 'react'
import { exchange } from 'theme/icons'

export const NavigationModuleSwap: FC = () => {
const { t } = useTranslation()

const { connect } = useConnection()

return (
<NavigationMenuDropdownContentListItem
title={t('nav.swap')}
description={t('nav.swap-description')}
icon={{
position: 'global',
icon: exchange,
}}
onClick={() => {
connect()
uiChanges.publish<SwapWidgetChangeAction>(SWAP_WIDGET_CHANGE_SUBJECT, {
type: 'open',
variant: 'swap',
})
}}
/>
)
}
2 changes: 1 addition & 1 deletion contentful/api.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import getConfig from 'next/config'

export async function fetchGraphQL<T>(query: string, preview = false): Promise<T> {
export async function fetchContentfulGraphQL<T>(query: string, preview = false): Promise<T> {
const accessToken =
getConfig()?.publicRuntimeConfig?.contentfulAccessToken || process.env.CONTENTFUL_ACCESS_TOKEN
const previewAccessToken =
Expand Down
4 changes: 2 additions & 2 deletions contentful/queries/getLandingPageBySlug.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { fetchGraphQL } from 'contentful/api'
import { fetchContentfulGraphQL } from 'contentful/api'
import type { LandingPageRawResponse } from 'contentful/types'

export async function getLandingPageBySlug(slug: string, preview: boolean) {
return await fetchGraphQL<LandingPageRawResponse>(
return await fetchContentfulGraphQL<LandingPageRawResponse>(
`
{
landingPageCollection(
Expand Down
4 changes: 2 additions & 2 deletions contentful/queries/getLandingPageContentByIds.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fetchGraphQL } from 'contentful/api'
import { fetchContentfulGraphQL } from 'contentful/api'
import { landingPageContentsChunkSize } from 'contentful/constants'
import type { EntryRawResponse } from 'contentful/types'
import { splitArrayToSameSizeChunks } from 'helpers/splitArrayToSameSizeChunks'
Expand All @@ -8,7 +8,7 @@ export const getLandingPageContentByIds = async (collectionIds: string[], previe
await Promise.all(
splitArrayToSameSizeChunks(collectionIds, landingPageContentsChunkSize).map(
(collectionIdsChunk) =>
fetchGraphQL<EntryRawResponse>(
fetchContentfulGraphQL<EntryRawResponse>(
`
{
entryCollection(
Expand Down
4 changes: 2 additions & 2 deletions features/aave/services/risk-slider-config.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { RiskRatio } from '@oasisdex/dma-library'
import BigNumber from 'bignumber.js'
import type { AdjustRiskViewConfig } from 'features/aave/components'
import type { ConfigResponseType } from 'helpers/config'
import { getLocalAppConfig } from 'helpers/config'
import { formatCryptoBalance, formatPercent } from 'helpers/formatters/format'
import React from 'react'
import type { AppConfigType } from 'types/config'

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

0 comments on commit e390e74

Please sign in to comment.