diff --git a/features/withdrawals/request/form/bunker-info.tsx b/features/withdrawals/request/form/bunker-info.tsx index 1c191ee70..ef1b61122 100644 --- a/features/withdrawals/request/form/bunker-info.tsx +++ b/features/withdrawals/request/form/bunker-info.tsx @@ -1,14 +1,18 @@ -import Link from 'next/link'; - +import { useInpageNavigation } from 'providers/inpage-navigation'; import { InfoBoxStyled } from 'features/withdrawals/shared'; export const BunkerInfo = () => { + const { navigateInpageAnchor } = useInpageNavigation(); + return ( Lido protocol is in "Bunker mode". The withdrawal requests are slowed down until the consequences of the incident that caused "Bunker mode" are not resolved. For more details,{' '} - see here. + + see here + + . ); }; diff --git a/features/withdrawals/request/form/options/lido-option.tsx b/features/withdrawals/request/form/options/lido-option.tsx index a7163a1d7..8ae9219ad 100644 --- a/features/withdrawals/request/form/options/lido-option.tsx +++ b/features/withdrawals/request/form/options/lido-option.tsx @@ -1,4 +1,3 @@ -import Link from 'next/link'; import { useWatch } from 'react-hook-form'; import { formatEther } from '@ethersproject/units'; @@ -6,6 +5,7 @@ import { Tooltip, Question } from '@lidofinance/lido-ui'; import { TOKENS } from '@lido-sdk/constants'; import { useEthAmountByStethWsteth } from 'features/withdrawals/hooks'; +import { useInpageNavigation } from 'providers/inpage-navigation'; import { RequestFormInputType } from 'features/withdrawals/request/request-form-context'; import { @@ -21,6 +21,8 @@ import { } from './styles'; const TooltipWithdrawalAmount = () => { + const { navigateInpageAnchor } = useInpageNavigation(); + return ( { <> The final amount of claimable ETH can differ
For more info, please read{' '} - - - + { + trackMatomoEvent( + MATOMO_CLICK_EVENTS_TYPES.withdrawalFAQtooltipEthAmount, + ); + navigateInpageAnchor(e); + }} + > + FAQ + } > diff --git a/features/withdrawals/request/wallet/wallet-queue-tooltip.tsx b/features/withdrawals/request/wallet/wallet-queue-tooltip.tsx index 1ee5c1ed3..944de18dc 100644 --- a/features/withdrawals/request/wallet/wallet-queue-tooltip.tsx +++ b/features/withdrawals/request/wallet/wallet-queue-tooltip.tsx @@ -1,5 +1,4 @@ import { Question, Tooltip } from '@lidofinance/lido-ui'; -import Link from 'next/link'; import { FormatToken } from 'shared/formatters'; import { useWaitingTime } from 'features/withdrawals/hooks'; @@ -10,10 +9,12 @@ import { } from 'config/trackMatomoEvent'; import { QueueInfoStyled, DataTableRowStyled } from './styles'; import { useRequestFormData } from '../request-form-context'; +import { useInpageNavigation } from 'providers/inpage-navigation'; export const WalletQueueTooltip = () => { const waitingTime = useWaitingTime(''); const { unfinalizedStETH } = useRequestFormData(); + const { navigateInpageAnchor } = useInpageNavigation(); const queueInfo = ( @@ -38,19 +39,18 @@ export const WalletQueueTooltip = () => { <> The withdrawal request time depends on the mode, overall amount of stETH in queue and{' '} - - - + { + trackMatomoEvent( + MATOMO_CLICK_EVENTS_TYPES.withdrawalOtherFactorsTooltipMode, + ); + navigateInpageAnchor(e); + }} + > + other factors + .{queueInfo} ); diff --git a/features/withdrawals/withdrawals-faq/list/bunker-mode-reasons.tsx b/features/withdrawals/withdrawals-faq/list/bunker-mode-reasons.tsx index 0c8bb5947..0a5d0b357 100644 --- a/features/withdrawals/withdrawals-faq/list/bunker-mode-reasons.tsx +++ b/features/withdrawals/withdrawals-faq/list/bunker-mode-reasons.tsx @@ -1,8 +1,8 @@ -import { Accordion } from '@lidofinance/lido-ui'; +import { AccordionNavigatable } from 'shared/components/accordion-navigatable'; export const BunkerModeReasons: React.FC = () => { return ( - @@ -22,6 +22,6 @@ export const BunkerModeReasons: React.FC = () => { the end of it. - + ); }; diff --git a/features/withdrawals/withdrawals-faq/list/claimable-amount-difference.tsx b/features/withdrawals/withdrawals-faq/list/claimable-amount-difference.tsx index cda9f8c36..05787dcd7 100644 --- a/features/withdrawals/withdrawals-faq/list/claimable-amount-difference.tsx +++ b/features/withdrawals/withdrawals-faq/list/claimable-amount-difference.tsx @@ -1,4 +1,4 @@ -import { Accordion } from '@lidofinance/lido-ui'; +import { AccordionNavigatable } from 'shared/components/accordion-navigatable'; type ClaimableAmountDifferenceProps = { title: string; @@ -8,12 +8,12 @@ export const ClaimableAmountDifference: React.FC< ClaimableAmountDifferenceProps > = ({ title }) => { return ( - +

The amount you can claim may differ from your initial request due to a slashing occurrence and penalties. For these reasons, the total claimable reward amount could be reduced.

-
+ ); }; diff --git a/features/withdrawals/withdrawals-faq/list/withdrawal-period-circumstances.tsx b/features/withdrawals/withdrawals-faq/list/withdrawal-period-circumstances.tsx index da5813f00..8624d97a6 100644 --- a/features/withdrawals/withdrawals-faq/list/withdrawal-period-circumstances.tsx +++ b/features/withdrawals/withdrawals-faq/list/withdrawal-period-circumstances.tsx @@ -1,8 +1,8 @@ -import { Accordion } from '@lidofinance/lido-ui'; +import { AccordionNavigatable } from 'shared/components/accordion-navigatable'; export const WithdrawalPeriodCircumstances: React.FC = () => { return ( - @@ -12,6 +12,6 @@ export const WithdrawalPeriodCircumstances: React.FC = () => {
  • Exit queue on the Beacon chain.
  • Demand for staking and unstaking.
  • -
    + ); }; diff --git a/package.json b/package.json index 7bb66b578..f4a84b286 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "@lidofinance/api-rpc": "^0.28.0", "@lidofinance/eth-api-providers": "^0.28.0", "@lidofinance/eth-providers": "^0.28.0", - "@lidofinance/lido-ui": "^3.18.2", + "@lidofinance/lido-ui": "^3.20.1", "@lidofinance/next-api-wrapper": "^0.28.0", "@lidofinance/next-ip-rate-limit": "^0.28.0", "@lidofinance/next-pages": "^0.28.0", diff --git a/providers/index.tsx b/providers/index.tsx index 8d250854c..ec8d2c5cb 100644 --- a/providers/index.tsx +++ b/providers/index.tsx @@ -5,6 +5,7 @@ import { GlobalStyle } from 'styles'; import { AppFlagProvider } from './app-flag'; import { ClientConfigProvider } from './client-config'; import { IPFSInfoBoxStatusesProvider } from './ipfs-info-box-statuses'; +import { InpageNavigationProvider } from './inpage-navigation'; import ModalProvider from './modals'; import Web3Provider from './web3'; @@ -17,7 +18,9 @@ export const Providers: FC = ({ children }) => ( - {children} + + {children} + diff --git a/providers/inpage-navigation.tsx b/providers/inpage-navigation.tsx new file mode 100644 index 000000000..c2be00598 --- /dev/null +++ b/providers/inpage-navigation.tsx @@ -0,0 +1,103 @@ +import { + FC, + PropsWithChildren, + createContext, + useContext, + useState, + useMemo, + useCallback, + useEffect, +} from 'react'; +import invariant from 'tiny-invariant'; +import { dynamics } from 'config'; +import { useRouter } from 'next/router'; + +export type InpageNavigationContextValue = { + hashNav: string; + navigateInpageAnchor: (e: React.MouseEvent) => void; + resetInpageAnchor: () => void; + resetSpecificAnchor: (hash: string) => void; +}; + +const InpageNavigationContext = + createContext(null); +InpageNavigationContext.displayName = 'InpageNavigationContext'; + +// IPFS-compatible hash-based in-page navigation +export const InpageNavigationProvider: FC = ({ + children, +}) => { + const { asPath } = useRouter(); + const [hashNav, setHash] = useState(''); + + useEffect(() => { + if (dynamics.ipfsMode) return; // Hash is reserved in ipfs mode, ignored here + const hash = asPath.split('#')[1]; + setHash(hash); + }, [asPath]); + + const navigateInpageAnchor = useCallback( + (e: React.MouseEvent) => { + const href = e.currentTarget.getAttribute('href'); + if (!href) return; + const hash = href.split('#')[1]; + e.preventDefault(); + + // Remember the hash + setHash(hash); + + // Perform animated scroll + document.getElementById(hash)?.scrollIntoView({ + behavior: 'smooth', + }); + + // Change the hash for non-ipfs ui, without scrolling the page + // We have done animated scroll already on next step + if (!dynamics.ipfsMode) { + history.pushState({}, '', `#${hash}`); + } + }, + [], + ); + + const resetInpageAnchor = useCallback(() => { + setHash(''); + if (!dynamics.ipfsMode) { + const hashTrimmed = asPath.split('#')[0]; + history.pushState({}, '', hashTrimmed); + } + }, [asPath]); + + const resetSpecificAnchor = useCallback( + (hash: string) => { + if (hash !== hashNav) return; + resetInpageAnchor(); + }, + [resetInpageAnchor, hashNav], + ); + + const value = useMemo( + () => ({ + hashNav, + navigateInpageAnchor, + resetInpageAnchor, + resetSpecificAnchor, + }), + [hashNav, navigateInpageAnchor, resetInpageAnchor, resetSpecificAnchor], + ); + + return ( + + {children} + + ); +}; + +export const useInpageNavigation = () => { + const value = useContext(InpageNavigationContext); + invariant( + value !== null, + 'useInpageNavigation was used used outside of InpageNavigationProvider', + ); + return value; +}; diff --git a/shared/components/accordion-navigatable/accordion-navigatable.tsx b/shared/components/accordion-navigatable/accordion-navigatable.tsx new file mode 100644 index 000000000..cf0741f6e --- /dev/null +++ b/shared/components/accordion-navigatable/accordion-navigatable.tsx @@ -0,0 +1,25 @@ +import { Accordion } from '@lidofinance/lido-ui'; +import { useInpageNavigation } from 'providers/inpage-navigation'; +import { useCallback } from 'react'; + +type AccordionNavigatableProps = React.ComponentProps & { + id: string; +}; + +export const AccordionNavigatable = (props: AccordionNavigatableProps) => { + const { onCollapse, id } = props; + const { hashNav, resetSpecificAnchor } = useInpageNavigation(); + + const handleCollapse = useCallback(() => { + resetSpecificAnchor(id); + onCollapse?.(); + }, [resetSpecificAnchor, id, onCollapse]); + + return ( + + ); +}; diff --git a/shared/components/accordion-navigatable/index.ts b/shared/components/accordion-navigatable/index.ts new file mode 100644 index 000000000..5e2caee5f --- /dev/null +++ b/shared/components/accordion-navigatable/index.ts @@ -0,0 +1 @@ +export * from './accordion-navigatable'; diff --git a/yarn.lock b/yarn.lock index b91328dba..75b2e2d2f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2224,7 +2224,7 @@ resolved "https://registry.yarnpkg.com/@lidofinance/eth-providers/-/eth-providers-0.28.0.tgz#069dfcd66f97418298646abd2742e8e997c6fbbb" integrity sha512-oH2nU7jVPkn1mii56vp3gBFZNFR/LysxiKBUwV2freFoizQHNLeWWjU1b8a+l1I020LzbxHcatOkV6z9ur5YfA== -"@lidofinance/lido-ui@^3.18.0", "@lidofinance/lido-ui@^3.18.2": +"@lidofinance/lido-ui@^3.18.0": version "3.18.2" resolved "https://registry.yarnpkg.com/@lidofinance/lido-ui/-/lido-ui-3.18.2.tgz#7eaa79270b9f23129d776ebe2a083f4329fe9ec6" integrity sha512-/MXmkHmliyQ5S7/TQW87XMYLd7May8R+rZ7JHbiZzaLEzUwixlLyFvWpmtX2CKJ4JL13KKWke/06gbNG/8wC5A== @@ -2238,6 +2238,20 @@ ua-parser-js "^1.0.35" use-callback-ref "1.2.5" +"@lidofinance/lido-ui@^3.20.1": + version "3.20.1" + resolved "https://registry.yarnpkg.com/@lidofinance/lido-ui/-/lido-ui-3.20.1.tgz#43034832a0eb1c1efd7e7f526300908516c3df11" + integrity sha512-ZSv3JBWbX28/IZTXuu9Na1sTtNymXE6izEVAmSBK/LShQny1YbkXJAfkkFYcHAhm+bY04kHWGfXrjNNXPgI9hA== + dependencies: + "@styled-system/should-forward-prop" "5.1.5" + react-collapsed "3.0.2" + react-jazzicon "^1.0.4" + react-toastify "7.0.4" + react-transition-group "4" + styled-system "5.1.5" + ua-parser-js "^1.0.35" + use-callback-ref "1.2.5" + "@lidofinance/next-api-wrapper@^0.28.0", "@lidofinance/next-api-wrapper@~0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@lidofinance/next-api-wrapper/-/next-api-wrapper-0.28.0.tgz#76fc32dafdbb1188d0a0ca5afd23bdf13e14d35d"