diff --git a/.release/.changeset/big-spoons-clean.md b/.release/.changeset/big-spoons-clean.md new file mode 100644 index 00000000..ad2eb2a0 --- /dev/null +++ b/.release/.changeset/big-spoons-clean.md @@ -0,0 +1,5 @@ +--- +"@bnb-chain/canonical-bridge-widget": patch +--- + +Some ui updates & Header chain switch update diff --git a/apps/canonical-bridge-ui/core/locales/en.ts b/apps/canonical-bridge-ui/core/locales/en.ts index 125000c6..2071c5a3 100644 --- a/apps/canonical-bridge-ui/core/locales/en.ts +++ b/apps/canonical-bridge-ui/core/locales/en.ts @@ -87,6 +87,7 @@ export const en = { 'select-modal.destination.incompatible.tooltip': 'The token you’ve selected is incompatible with this network. Please select another token.', + 'wallet.network.switch-network': 'Please switch the network', 'wallet.network.wrong-network': 'Wrong network', 'wallet.error.switch-network': 'An error occurred when attempting to switch the network. Please select another network or try again.', diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useDefaultSelectedInfo.ts b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useDefaultSelectedInfo.ts index 4199d278..29f52dd5 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useDefaultSelectedInfo.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useDefaultSelectedInfo.ts @@ -1,4 +1,5 @@ import { useEffect } from 'react'; +import { useAccount } from 'wagmi'; import { useAppDispatch } from '@/modules/store/StoreProvider'; import { setSendValue } from '@/modules/transfer/action'; @@ -9,6 +10,7 @@ export function useDefaultSelectedInfo() { const { isReady, defaultSelectedInfo } = useAggregator(); const { selectDefault } = useSelection(); const dispatch = useAppDispatch(); + const { chainId } = useAccount(); useEffect(() => { if (isReady) { @@ -16,5 +18,5 @@ export function useDefaultSelectedInfo() { dispatch(setSendValue(defaultSelectedInfo.amount)); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isReady]); + }, [isReady, chainId]); } diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts index 36e622c3..94a34106 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts @@ -11,7 +11,9 @@ import { getTokenBalances } from '@/modules/aggregator/shared/getTokenBalances'; import { sortTokens } from '@/modules/aggregator/shared/sortTokens'; export function useSelection() { - const { getFromChains, getToChains, getTokens, getToToken, adapters } = useAggregator(); + const { chainId } = useAccount(); + const { getFromChains, getToChains, getTokens, getToToken, adapters, defaultSelectedInfo } = + useAggregator(); const fromChain = useAppSelector((state) => state.transfer.fromChain); const toChain = useAppSelector((state) => state.transfer.toChain); @@ -72,6 +74,38 @@ export function useSelection() { }); }; + const selectFromChain = async (tmpFromChain: IBridgeChain) => { + // After selecting fromChain, if toChain becomes incompatible, reselect the first compatible network in toChain list. + const toChains = getToChains({ + fromChainId: tmpFromChain.id, + }); + + const tmpToChain = + toChains.find((c) => isChainOrTokenCompatible(c) && c.id === toChain?.id) ?? + toChains.find((c) => isChainOrTokenCompatible(c) && c.chainType !== 'link'); + + const tmpTokens = await getSortedTokens({ + fromChainId: tmpFromChain.id, + tokens: getTokens({ + fromChainId: tmpFromChain.id, + toChainId: tmpToChain?.id, + }), + }); + + const newToken = + tmpTokens.find( + (t) => + isChainOrTokenCompatible(t) && + t.displaySymbol.toUpperCase() === selectedToken?.displaySymbol.toUpperCase(), + ) ?? tmpTokens.find((t) => isChainOrTokenCompatible(t)); + + updateSelectedInfo({ + tmpToken: newToken, + tmpFromChain, + tmpToChain, + }); + }; + return { async selectDefault({ fromChainId, @@ -87,6 +121,15 @@ export function useSelection() { bridgeTypes.map((item) => [item, { symbol: tokenSymbol }]), ) as any as IBridgeToken; + if (chainId && chainId !== defaultSelectedInfo.fromChainId) { + const fromChains = getFromChains({}); + const chain = fromChains.find((chain) => chain.id === chainId); + if (chain) { + selectFromChain(chain); + return; + } + } + const fromChains = getFromChains({ toChainId, token, @@ -116,38 +159,7 @@ export function useSelection() { token: newToken, }); }, - - async selectFromChain(tmpFromChain: IBridgeChain) { - // After selecting fromChain, if toChain becomes incompatible, reselect the first compatible network in toChain list. - const toChains = getToChains({ - fromChainId: tmpFromChain.id, - }); - const tmpToChain = - toChains.find((c) => isChainOrTokenCompatible(c) && c.id === toChain?.id) ?? - toChains.find((c) => isChainOrTokenCompatible(c) && c.chainType !== 'link'); - - const tmpTokens = await getSortedTokens({ - fromChainId: tmpFromChain.id, - tokens: getTokens({ - fromChainId: tmpFromChain.id, - toChainId: tmpToChain?.id, - }), - }); - - const newToken = - tmpTokens.find( - (t) => - isChainOrTokenCompatible(t) && - t.displaySymbol.toUpperCase() === selectedToken?.displaySymbol.toUpperCase(), - ) ?? tmpTokens.find((t) => isChainOrTokenCompatible(t)); - - updateSelectedInfo({ - tmpToken: newToken, - tmpFromChain, - tmpToChain, - }); - }, - + selectFromChain, async selectToChain(tmpToChain: IBridgeChain) { const fromChainId = fromChain!.id; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/Button/SwitchNetworkButton.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/Button/SwitchNetworkButton.tsx index 4bde5ffe..afd5819e 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/Button/SwitchNetworkButton.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Button/SwitchNetworkButton.tsx @@ -1,10 +1,10 @@ -import { Button, useColorMode, useIntl, useTheme } from '@bnb-chain/space'; +import { Button, useColorMode, useIntl, useTheme, ButtonProps } from '@bnb-chain/space'; import { useAppSelector } from '@/modules/store/StoreProvider'; import { useEvmSwitchChain } from '@/modules/wallet/hooks/useEvmSwitchChain'; import { reportEvent } from '@/core/utils/gtm'; -export const SwitchNetworkButton = () => { +export const SwitchNetworkButton = (props: ButtonProps) => { const { formatMessage } = useIntl(); const fromChain = useAppSelector((state) => state.transfer.fromChain); @@ -35,6 +35,7 @@ export const SwitchNetworkButton = () => { }); } }} + {...props} > {formatMessage({ id: 'transfer.button.switch-network' })} diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/FromSection/index.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/FromSection/index.tsx index c80b11ec..3f143046 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/FromSection/index.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/FromSection/index.tsx @@ -1,12 +1,4 @@ -import { - Flex, - Typography, - useBreakpointValue, - useColorMode, - useDisclosure, - useIntl, - useTheme, -} from '@bnb-chain/space'; +import { Flex, Typography, useColorMode, useDisclosure, useIntl, useTheme } from '@bnb-chain/space'; import { SelectButton } from '@/modules/transfer/components/SelectButton'; import { useAppSelector } from '@/modules/store/StoreProvider'; @@ -16,7 +8,6 @@ import { SourceNetworkModal } from '@/modules/aggregator/components/SelectModal/ export function FromSection() { const { colorMode } = useColorMode(); const { isOpen, onClose, onOpen } = useDisclosure(); - const isBase = useBreakpointValue({ base: true, md: false }) ?? false; const { formatMessage } = useIntl(); const fromChain = useAppSelector((state) => state.transfer.fromChain); @@ -24,18 +15,20 @@ export function FromSection() { return ( - {isBase ? ( - - - {formatMessage({ id: 'from.section.title' })} - - - ) : null} + + + {formatMessage({ id: 'from.section.title' })} + + { const { formatMessage } = useIntl(); const { colorMode } = useColorMode(); const theme = useTheme(); - const isBase = useBreakpointValue({ base: true, md: false }) ?? false; return ( - {!isBase ? ( - <> - - - - {formatMessage({ id: 'from.section.title' })} - - - - - {formatMessage({ id: 'to.section.title' })} - - - {' '} - - ) : null} + + + + {formatMessage({ id: 'from.section.title' })} + + + + + {formatMessage({ id: 'to.section.title' })} + + + {' '} { gap={['16px', '16px', '16px', '12px']} > - + diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/ReceiveInfo/ReceiveLoading.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/ReceiveInfo/ReceiveLoading.tsx index f34820c6..dd20ac3c 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/ReceiveInfo/ReceiveLoading.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/ReceiveInfo/ReceiveLoading.tsx @@ -1,41 +1,45 @@ -import { Flex, Skeleton, SkeletonCircle, useBreakpointValue } from '@bnb-chain/space'; +import { Flex, Skeleton, SkeletonCircle } from '@bnb-chain/space'; export const ReceiveLoading = () => { - const isBase = useBreakpointValue({ base: true, lg: false }) ?? false; - - return !isBase ? ( - - - - - - - - - ) : ( - - - - - - + return ( + <> + - - + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + - - - + ); }; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/ReceiveInfo/index.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/ReceiveInfo/index.tsx index 4317d92c..63c9366f 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/ReceiveInfo/index.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/ReceiveInfo/index.tsx @@ -1,4 +1,12 @@ -import { Box, Flex, useBreakpointValue, useColorMode, useIntl, useTheme } from '@bnb-chain/space'; +import { + Box, + Flex, + useBreakpointValue, + useColorMode, + useIntl, + useTheme, + Collapse, +} from '@bnb-chain/space'; import { useEffect, useMemo } from 'react'; import { useAppDispatch, useAppSelector } from '@/modules/store/StoreProvider'; @@ -116,13 +124,13 @@ export const ReceiveInfo = ({ onOpen }: ReceiveInfoProps) => { const isHideSection = useMemo(() => { // no receive amount and some routes are displayed + if (!Number(sendValue)) return true; if (isGlobalFeeLoading) return false; return ( - !Number(sendValue) || - (!isBase && - estimatedAmount && - !Object.values(estimatedAmount).every((element) => element === undefined) && - !receiveAmt) + !isBase && + estimatedAmount && + !Object.values(estimatedAmount).every((element) => element === undefined) && + !receiveAmt ); }, [sendValue, estimatedAmount, isBase, receiveAmt, isGlobalFeeLoading]); @@ -132,61 +140,65 @@ export const ReceiveInfo = ({ onOpen }: ReceiveInfoProps) => { ); }, [estimatedAmount]); - return !isHideSection ? ( - - - - {formatMessage({ id: 'you.receive.title' })} - - {isBase && !isHideRouteButton ? : null} - - - {debouncedSendValue === sendValue ? ( - receiveAmt && !isGlobalFeeLoading ? ( - <> - {isBase && } - {isBase && } - {bridgeType && ( - - )} - - - - - - - ) : !receiveAmt && !isGlobalFeeLoading ? ( - + return ( + + + + + {formatMessage({ id: 'you.receive.title' })} + + {isBase && !isHideRouteButton ? : null} + + + {debouncedSendValue === sendValue ? ( + receiveAmt && !isGlobalFeeLoading ? ( + <> + {isBase && } + {isBase && } + {bridgeType && ( + + )} + + + + + + + ) : !receiveAmt && !isGlobalFeeLoading ? ( + + ) : ( + + ) ) : ( - ) - ) : ( - - )} + )} + - - ) : null; + + ); }; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/SendInput/InputValidationMessage.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/SendInput/InputValidationMessage.tsx index a89174be..b3068832 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/SendInput/InputValidationMessage.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/SendInput/InputValidationMessage.tsx @@ -1,4 +1,4 @@ -import { Box, useBreakpointValue, useColorMode, useTheme } from '@bnb-chain/space'; +import { Box, useColorMode, useTheme } from '@bnb-chain/space'; import { useEffect, useState } from 'react'; import { useAccount } from 'wagmi'; @@ -13,7 +13,6 @@ export const InputValidationMessage = () => { const { validateInput } = useInputValidation(); const { chain } = useAccount(); const dispatch = useAppDispatch(); - const isBase = useBreakpointValue({ base: true, lg: false }) ?? false; const transferActionInfo = useAppSelector((state) => state.transfer.transferActionInfo); const theme = useTheme(); @@ -65,7 +64,7 @@ export const InputValidationMessage = () => { fontSize={'12px'} fontWeight={400} lineHeight={'16px'} - position={isBase ? 'static' : 'absolute'} + position={{ base: 'static', lg: 'absolute' }} top={`calc(100% + ${'8px'})`} > {balanceInputError ?? error?.text} diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/ToSection/index.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/ToSection/index.tsx index d37568b3..ad33a7c6 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/ToSection/index.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/ToSection/index.tsx @@ -1,12 +1,4 @@ -import { - Flex, - Typography, - theme, - useBreakpointValue, - useColorMode, - useDisclosure, - useIntl, -} from '@bnb-chain/space'; +import { Flex, Typography, theme, useColorMode, useDisclosure, useIntl } from '@bnb-chain/space'; import { SelectButton } from '@/modules/transfer/components/SelectButton'; import { useAppSelector } from '@/modules/store/StoreProvider'; @@ -16,25 +8,26 @@ import { DestinationNetworkModal } from '@/modules/aggregator/components/SelectM export function ToSection() { const { isOpen, onClose, onOpen } = useDisclosure(); const { formatMessage } = useIntl(); - const isBase = useBreakpointValue({ base: true, md: false }) ?? false; const { colorMode } = useColorMode(); const toChain = useAppSelector((state) => state.transfer.toChain); return ( - {isBase ? ( - - - {formatMessage({ id: 'to.section.title' })} - - - ) : null} + + + {formatMessage({ id: 'to.section.title' })} + + - {showRoute && ( + - {!isBase ? ( - - {formatMessage({ id: 'route.title' })} - {!options.length || isGlobalFeeLoading ? ( - - ) : !isBase ? ( - - ) : null} - - ) : null} + + {formatMessage({ id: 'route.title' })} + {!options.length || isGlobalFeeLoading ? ( + + ) : !isBase ? ( + + ) : null} + - )} + {routeContentBottom ? routeContentBottom : null} ); diff --git a/packages/canonical-bridge-widget/src/modules/transfer/index.tsx b/packages/canonical-bridge-widget/src/modules/transfer/index.tsx index 4ad1c877..d96138b4 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/index.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/index.tsx @@ -53,12 +53,12 @@ export function TransferWidget() { {appearance.bridgeTitle && ( {appearance.bridgeTitle} @@ -70,11 +70,12 @@ export function TransferWidget() { - {isBase ? routeContentBottom : null} + {routeContentBottom} - {!isBase ? ( + - ) : ( + + {isBase && ( state.transfer.fromChain); - + const thresholdRef = useRef(false); const theme = useTheme(); const { colorMode } = useColorMode(); const { chain, chainId } = useAccount(); const { formatMessage } = useIntl(); const fromChains = useFromChains(); const { switchChain } = useEvmSwitchChain(); - const bridgeChains = useFromChains(); + useEffect(() => { + thresholdRef.current = true; + setTimeout(() => { + thresholdRef.current = false; + }, 1000); + }, [chainId]); + + const switchDropdown = (onClose: () => void) => ( + + + + + {formatMessage({ id: 'wallet.network.switch-network' })} + + + + + + + {fromChain!.name} + + + + + + + + + ); + if (!chain) { - return null; + if (!chainId || !fromChain) return null; + + return ( + + {({ isOpen, onClose }) => { + return ( + <> + + + + + Unknown + + + + + {switchDropdown(onClose)} + + ); + }} + + ); } - const isWrongNetwork = !!fromChain && fromChain.id !== chain.id; + const isWrongNetwork = !!fromChain && fromChain.id !== chain.id && !thresholdRef.current; const iconUrl = bridgeChains.find((e) => e.id === chain.id)?.icon; return ( - {({ isOpen }) => { + {({ isOpen, onClose }) => { return ( <> - + {isWrongNetwork ? ( + + ) : ( + + )} {chain.name} - {isWrongNetwork && ( - - {formatMessage({ id: 'wallet.network.wrong-network' })} - - )} - - {fromChains.map((item) => { - const isSelected = chainId === item.id; + {isWrongNetwork ? ( + switchDropdown(onClose) + ) : ( + + {fromChains.map((item) => { + const isSelected = chainId === item.id; - return ( - { - if (chainId !== item.id) { - switchChain({ - chainId: item.id, - }); + return ( + - - - {item.name} - - {isSelected && } - - ); - })} - + bg={isSelected ? theme.colors[colorMode].popover.selected : undefined} + onClick={() => { + if (chainId !== item.id) { + switchChain({ + chainId: item.id, + }); + } + }} + > + + + {item.name} + + {isSelected && } + + ); + })} + + )} ); }}