From 4ae1d80bd03a08b5cd63ed6be49fbca2c93b3c05 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Thu, 16 Oct 2025 14:07:16 -0700 Subject: [PATCH] Add a loading state for rampLastCryptoSelection --- src/components/scenes/RampCreateScene.tsx | 39 +++++++--------- src/hooks/useRampLastCryptoSelection.ts | 56 +++++++++++++++++++++++ yarn.lock | 46 ++----------------- 3 files changed, 77 insertions(+), 64 deletions(-) create mode 100644 src/hooks/useRampLastCryptoSelection.ts diff --git a/src/components/scenes/RampCreateScene.tsx b/src/components/scenes/RampCreateScene.tsx index 32389d25fee..8be5202ddd8 100644 --- a/src/components/scenes/RampCreateScene.tsx +++ b/src/components/scenes/RampCreateScene.tsx @@ -15,6 +15,7 @@ import { import { FLAG_LOGO_URL } from '../../constants/CdnConstants' import { COUNTRY_CODES, FIAT_COUNTRY } from '../../constants/CountryConstants' import { useHandler } from '../../hooks/useHandler' +import { useRampLastCryptoSelection } from '../../hooks/useRampLastCryptoSelection' import { useRampPlugins } from '../../hooks/useRampPlugins' import { useRampQuotes } from '../../hooks/useRampQuotes' import { @@ -95,9 +96,6 @@ export const RampCreateScene: React.FC = (props: Props) => { const rampLastFiatCurrencyCode = useSelector( state => state.ui.settings.rampLastFiatCurrencyCode ) - const rampLastCryptoSelection = useSelector( - state => state.ui.settings.rampLastCryptoSelection - ) // State for trade form const [userInput, setUserInput] = useState('') @@ -112,23 +110,12 @@ export const RampCreateScene: React.FC = (props: Props) => { const defaultFiat = useSelector(state => getDefaultFiat(state)) const selectedFiatCurrencyCode = rampLastFiatCurrencyCode ?? defaultFiat - const persistedCryptoSelection = React.useMemo< - WalletListWalletResult | undefined - >(() => { - if ( - rampLastCryptoSelection == null || - currencyWallets[rampLastCryptoSelection.walletId] == null - ) { - return undefined - } - return { - type: 'wallet', - walletId: rampLastCryptoSelection.walletId, - tokenId: rampLastCryptoSelection.tokenId - } - }, [currencyWallets, rampLastCryptoSelection]) + const { + selection: rampLastCryptoSelection, + isLoading: isLoadingPersistedCryptoSelection + } = useRampLastCryptoSelection() - const selectedCrypto = forcedWalletResult ?? persistedCryptoSelection + const selectedCrypto = forcedWalletResult ?? rampLastCryptoSelection const [selectedWallet, selectedCryptoCurrencyCode] = selectedCrypto != null @@ -783,7 +770,8 @@ export const RampCreateScene: React.FC = (props: Props) => { {/* Bottom Input (Crypto by design) */} - {selectedCryptoCurrencyCode == null ? ( + {selectedCryptoCurrencyCode == null && + !isLoadingPersistedCryptoSelection ? ( = (props: Props) => { ) : ( <> - {selectedCrypto == null || selectedWallet == null ? null : ( + {isLoadingPersistedCryptoSelection ? ( + + ) : selectedCrypto == null || + selectedWallet == null ? null : ( = (props: Props) => { maxDecimals={6} returnKeyType="done" showSpinner={isFetchingQuotes && lastUsedInput === 'fiat'} - disabled={isMaxAmount || cryptoInputDisabled} + disabled={ + isLoadingPersistedCryptoSelection || + isMaxAmount || + cryptoInputDisabled + } expand /> diff --git a/src/hooks/useRampLastCryptoSelection.ts b/src/hooks/useRampLastCryptoSelection.ts new file mode 100644 index 00000000000..50557ffa666 --- /dev/null +++ b/src/hooks/useRampLastCryptoSelection.ts @@ -0,0 +1,56 @@ +import { useQuery } from '@tanstack/react-query' + +import type { WalletListWalletResult } from '../components/modals/WalletListModal' +import { useSelector } from '../types/reactRedux' +import { useWatch } from './useWatch' + +interface UseRampsPersistedCryptoSelectionResult { + selection?: WalletListWalletResult + isLoading: boolean +} + +export const useRampLastCryptoSelection = + (): UseRampsPersistedCryptoSelectionResult => { + const account = useSelector(state => state.core.account) + const currencyWallets = useWatch(account, 'currencyWallets') + + const rampLastCryptoSelection = useSelector( + state => state.ui.settings.rampLastCryptoSelection + ) + + const { data: allWalletsReady = false } = useQuery({ + queryKey: ['waitForAllWallets', account?.id], + queryFn: async () => { + if (account == null) return false + await account.waitForAllWallets() + return true + }, + enabled: account != null, + staleTime: Infinity, + gcTime: 300000, + refetchOnWindowFocus: false + }) + + if (rampLastCryptoSelection == null) { + return { selection: undefined, isLoading: false } + } + + const { walletId, tokenId } = rampLastCryptoSelection + + if (currencyWallets[walletId] == null) { + // Before we know wallet readiness, keep loading state true + if (!allWalletsReady) return { selection: undefined, isLoading: true } + // Otherwise we know there is not wallet for the selection (invalid + // selection state). + return { selection: undefined, isLoading: false } + } + + return { + selection: { + type: 'wallet', + walletId, + tokenId + }, + isLoading: false + } + } diff --git a/yarn.lock b/yarn.lock index d8d71dc6d9b..a97cbb86b4e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1063,20 +1063,7 @@ "@babel/parser" "^7.27.2" "@babel/types" "^7.27.1" -"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.0.tgz#518aa113359b062042379e333db18380b537e34b" - integrity sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg== - dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.28.0" - "@babel/helper-globals" "^7.28.0" - "@babel/parser" "^7.28.0" - "@babel/template" "^7.27.2" - "@babel/types" "^7.28.0" - debug "^4.3.1" - -"@babel/traverse@^7.25.3", "@babel/traverse@^7.27.1", "@babel/traverse@^7.27.3", "@babel/traverse@^7.28.0", "@babel/traverse@^7.7.0": +"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3", "@babel/traverse@^7.25.3", "@babel/traverse@^7.27.1", "@babel/traverse@^7.27.3", "@babel/traverse@^7.28.0", "@babel/traverse@^7.7.0": version "7.28.0" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.0.tgz#518aa113359b062042379e333db18380b537e34b" integrity sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg== @@ -17410,16 +17397,7 @@ string-length@^4.0.2: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -17519,7 +17497,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -17533,13 +17511,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.0, strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -19229,7 +19200,7 @@ wordwrapjs@^4.0.0: reduce-flatten "^2.0.0" typical "^5.2.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -19247,15 +19218,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"