diff --git a/packages/widget/src/components/ChainSelect/ChainSelect.tsx b/packages/widget/src/components/ChainSelect/ChainSelect.tsx index 0b1e5caf9..a45f7b1bc 100644 --- a/packages/widget/src/components/ChainSelect/ChainSelect.tsx +++ b/packages/widget/src/components/ChainSelect/ChainSelect.tsx @@ -11,6 +11,7 @@ import { } from '../../stores/chains/createChainOrderStore.js' import type { FormTypeProps } from '../../stores/form/types.js' import { FormKeyHelper } from '../../stores/form/types.js' +import { useFieldActions } from '../../stores/form/useFieldActions.js' import { useFieldValues } from '../../stores/form/useFieldValues.js' import { navigationRoutes } from '../../utils/navigationRoutes.js' import { AllChainsAvatar } from '../Chains/AllChainsAvatar.js' @@ -29,7 +30,7 @@ export const ChainSelect = memo(({ formType }: FormTypeProps) => { const isMobile = useMediaQuery((theme: Theme) => theme.breakpoints.down(theme.breakpoints.values.xs) ) - + const { setFieldValue } = useFieldActions() const { chainOrder, chains, @@ -73,7 +74,10 @@ export const ChainSelect = memo(({ formType }: FormTypeProps) => { const selectAllNetworks = useCallback(() => { setIsAllNetworks(true, formType) - }, [setIsAllNetworks, formType]) + // Reset the chain and token fields when selecting all networks + setFieldValue(FormKeyHelper.getChainKey(formType), '', { isTouched: true }) + setFieldValue(FormKeyHelper.getTokenKey(formType), '', { isTouched: true }) + }, [setIsAllNetworks, formType, setFieldValue]) const chainsToHide = chains?.length === maxChainsToShow diff --git a/packages/widget/src/components/Chains/VirtualizedChainList.tsx b/packages/widget/src/components/Chains/VirtualizedChainList.tsx index 7f28f24ce..33f0d21c2 100644 --- a/packages/widget/src/components/Chains/VirtualizedChainList.tsx +++ b/packages/widget/src/components/Chains/VirtualizedChainList.tsx @@ -4,7 +4,8 @@ import type { RefObject } from 'react' import { useCallback, useEffect, useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' import { useChainOrderStore } from '../../stores/chains/ChainOrderStore.js' -import type { FormType } from '../../stores/form/types.js' +import { FormKeyHelper, type FormType } from '../../stores/form/types.js' +import { useFieldActions } from '../../stores/form/useFieldActions.js' import { AllChainsAvatar } from './AllChainsAvatar.js' import { List, @@ -52,7 +53,7 @@ export const VirtualizedChainList = ({ state.setIsAllNetworks, state[`${formType}ShowAllNetworks`], ]) - + const { setFieldValue } = useFieldActions() const onPin = useCallback( (chainId: number) => { setPinnedChain(chainId) @@ -161,7 +162,10 @@ export const VirtualizedChainList = ({ const selectAllNetworks = useCallback(() => { setIsAllNetworks(true, formType) - }, [setIsAllNetworks, formType]) + // Reset the chain and token fields when selecting all networks + setFieldValue(FormKeyHelper.getChainKey(formType), '', { isTouched: true }) + setFieldValue(FormKeyHelper.getTokenKey(formType), '', { isTouched: true }) + }, [setIsAllNetworks, formType, setFieldValue]) return ( (null) const { chains } = useChains() const { setFieldValue, getFieldValues } = useFieldActions() @@ -31,16 +38,8 @@ export function ChainOrderStoreProvider({ useExternalWalletProvider() if (!storeRef.current) { - const [initialFromChain, initialToChain] = getFieldValues( - 'fromChain', - 'toChain' - ) storeRef.current = createChainOrderStore({ ...props, - initialIsAllNetworks: { - from: !initialFromChain, - to: !initialToChain, - }, }) } @@ -81,9 +80,17 @@ export function ChainOrderStoreProvider({ filteredChains.length > 1 && !hiddenUI?.includes(HiddenUI.AllNetworks) && !isSwapTo - if (!showAllNetworks) { - storeRef.current?.getState().setIsAllNetworks(false, key) - } + + // Initialize the isAllNetworks with true if the tab is shown, + // there is no config chain value and no url chain value + const urlValues = getDefaultValuesFromQueryString({ buildUrl }) + const urlChainValue = + key === 'from' ? urlValues.fromChain : urlValues.toChain + const configChainValue = + key === 'from' ? fromChainConfig : toChainConfig + const initialIsAllNetworks = + showAllNetworks && !configChainValue && !urlChainValue + storeRef.current?.getState().setIsAllNetworks(initialIsAllNetworks, key) storeRef.current?.getState().setShowAllNetworks(showAllNetworks, key) // If swap only, set the to chain to the from chain @@ -97,12 +104,6 @@ export function ChainOrderStoreProvider({ return } - // If no chain is selected (e.g., removed from URL params) and - // showAllNetworks is enabled, reset isAllNetworks to true - if (showAllNetworks) { - storeRef.current?.getState().setIsAllNetworks(true, key) - } - const firstAllowedPinnedChain = storeRef.current ?.getState() .pinnedChains?.find((chainId) => @@ -130,6 +131,9 @@ export function ChainOrderStoreProvider({ subvariantOptions?.wide?.disableChainSidebar, hiddenUI, swapOnly, + fromChainConfig, + toChainConfig, + buildUrl, ]) return ( diff --git a/packages/widget/src/stores/chains/createChainOrderStore.ts b/packages/widget/src/stores/chains/createChainOrderStore.ts index 54efa4a1d..8d0897ef4 100644 --- a/packages/widget/src/stores/chains/createChainOrderStore.ts +++ b/packages/widget/src/stores/chains/createChainOrderStore.ts @@ -17,23 +17,13 @@ const defaultChainState = { to: [], } -interface CreateChainOrderStoreProps extends PersistStoreProps { - initialIsAllNetworks?: { - from?: boolean - to?: boolean - } -} - -export const createChainOrderStore = ({ - namePrefix, - initialIsAllNetworks, -}: CreateChainOrderStoreProps) => +export const createChainOrderStore = ({ namePrefix }: PersistStoreProps) => create()( persist( (set, get) => ({ chainOrder: defaultChainState, - fromIsAllNetworks: initialIsAllNetworks?.from ?? true, - toIsAllNetworks: initialIsAllNetworks?.to ?? true, + fromIsAllNetworks: true, + toIsAllNetworks: true, fromShowAllNetworks: true, toShowAllNetworks: true, availableChains: defaultChainState, @@ -125,13 +115,9 @@ export const createChainOrderStore = ({ }), { name: `${namePrefix || 'li.fi'}-widget-chains-order`, - version: 2, + version: 4, partialize: (state) => ({ chainOrder: state.chainOrder, - fromIsAllNetworks: state.fromIsAllNetworks, - toIsAllNetworks: state.toIsAllNetworks, - fromShowAllNetworks: state.fromShowAllNetworks, - toShowAllNetworks: state.toShowAllNetworks, pinnedChains: state.pinnedChains, }), } diff --git a/packages/widget/src/stores/form/FormStore.tsx b/packages/widget/src/stores/form/FormStore.tsx index 8a828a9f1..a21685e54 100644 --- a/packages/widget/src/stores/form/FormStore.tsx +++ b/packages/widget/src/stores/form/FormStore.tsx @@ -110,9 +110,10 @@ export const FormStoreProvider: React.FC = ({ ) if (!storeRef.current) { - const queryDefaults = buildUrl - ? getDefaultValuesFromQueryString({ includeToAddress: false }) - : {} + const queryDefaults = getDefaultValuesFromQueryString({ + buildUrl, + includeToAddress: false, + }) storeRef.current = createFormStore( initialiseDefaultValues({ ...reactiveFormValues, diff --git a/packages/widget/src/stores/form/URLSearchParamsBuilder.tsx b/packages/widget/src/stores/form/URLSearchParamsBuilder.tsx index 10197bc84..69cdefc7a 100644 --- a/packages/widget/src/stores/form/URLSearchParamsBuilder.tsx +++ b/packages/widget/src/stores/form/URLSearchParamsBuilder.tsx @@ -1,6 +1,7 @@ import { useLocation } from '@tanstack/react-router' import { useEffect } from 'react' import { useAddressValidation } from '../../hooks/useAddressValidation.js' +import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js' import { useSendToWalletActions } from '../../stores/settings/useSendToWalletStore.js' import { useBookmarkActions } from '../bookmarks/useBookmarkActions.js' import type { FormFieldNames } from '../form/types.js' @@ -25,7 +26,7 @@ export const URLSearchParamsBuilder = () => { const { setSendToWallet } = useSendToWalletActions() const { setSelectedBookmark, addRecentWallet } = useBookmarkActions() const { validateAddress } = useAddressValidation() - + const { buildUrl } = useWidgetConfig() // Using these methods as trying to use the touchedFields and values above // often has a lag that can effect the widgets initialisation sequence // and accidentally cause values to be wiped from the query string @@ -34,7 +35,7 @@ export const URLSearchParamsBuilder = () => { useEffect(() => { // get the initial values from the querystring - const formValues = getDefaultValuesFromQueryString() + const formValues = getDefaultValuesFromQueryString({ buildUrl }) const { toAddress, ...initialFormValues } = formValues /** @@ -73,6 +74,7 @@ export const URLSearchParamsBuilder = () => { validateAddress, setSelectedBookmark, addRecentWallet, + buildUrl, ]) // biome-ignore lint/correctness/useExhaustiveDependencies: run only when pathname changes diff --git a/packages/widget/src/stores/form/getDefaultValuesFromQueryString.ts b/packages/widget/src/stores/form/getDefaultValuesFromQueryString.ts index c206e8606..5e1991ecd 100644 --- a/packages/widget/src/stores/form/getDefaultValuesFromQueryString.ts +++ b/packages/widget/src/stores/form/getDefaultValuesFromQueryString.ts @@ -3,15 +3,21 @@ import type { DefaultValues } from './types.js' interface GetDefaultValuesFromQueryStringOptions { includeToAddress?: boolean + buildUrl?: boolean } export const getDefaultValuesFromQueryString = ({ + buildUrl = false, includeToAddress = true, -}: GetDefaultValuesFromQueryStringOptions = {}): Partial => { +}: GetDefaultValuesFromQueryStringOptions): Partial => { if (typeof window === 'undefined') { return {} } + if (!buildUrl) { + return {} + } + const searchParams = Object.fromEntries( new URLSearchParams(window.location.search) )