diff --git a/package.json b/package.json index c5f91a71b..f2a55c3cf 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "js-cookie": "^3.0.5", "jsqr": "^1.4.0", "next": "16.0.10", + "nuqs": "^2.8.6", "pulltorefreshjs": "^0.1.22", "react": "^19.2.1", "react-dom": "^19.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 70dc59441..ec4587d8a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -134,6 +134,9 @@ importers: next: specifier: 16.0.10 version: 16.0.10(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + nuqs: + specifier: ^2.8.6 + version: 2.8.6(next@16.0.10(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1) pulltorefreshjs: specifier: ^0.1.22 version: 0.1.22 @@ -5908,6 +5911,27 @@ packages: nullthrows@1.1.1: resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} + nuqs@2.8.6: + resolution: {integrity: sha512-aRxeX68b4ULmhio8AADL2be1FWDy0EPqaByPvIYWrA7Pm07UjlrICp/VPlSnXJNAG0+3MQwv3OporO2sOXMVGA==} + peerDependencies: + '@remix-run/react': '>=2' + '@tanstack/react-router': ^1 + next: '>=14.2.0' + react: '>=18.2.0 || ^19.0.0-0' + react-router: ^5 || ^6 || ^7 + react-router-dom: ^5 || ^6 || ^7 + peerDependenciesMeta: + '@remix-run/react': + optional: true + '@tanstack/react-router': + optional: true + next: + optional: true + react-router: + optional: true + react-router-dom: + optional: true + nwsapi@2.2.21: resolution: {integrity: sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==} @@ -15623,6 +15647,13 @@ snapshots: nullthrows@1.1.1: {} + nuqs@2.8.6(next@16.0.10(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1): + dependencies: + '@standard-schema/spec': 1.0.0 + react: 19.2.1 + optionalDependencies: + next: 16.0.10(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + nwsapi@2.2.21: {} ob1@0.83.2: diff --git a/src/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx b/src/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx index 448cbb6e3..6374e3224 100644 --- a/src/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx +++ b/src/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx @@ -15,7 +15,7 @@ export default function AddMoneyRegionalMethodPage() { MantecaSupportedExchanges[countryDetails?.id as keyof typeof MantecaSupportedExchanges] && method === 'manteca' ) { - return + return } return null } diff --git a/src/app/(mobile-ui)/add-money/[country]/bank/page.tsx b/src/app/(mobile-ui)/add-money/[country]/bank/page.tsx index 1006dc888..8ce1ff0e2 100644 --- a/src/app/(mobile-ui)/add-money/[country]/bank/page.tsx +++ b/src/app/(mobile-ui)/add-money/[country]/bank/page.tsx @@ -25,25 +25,40 @@ import { getCurrencyConfig, getCurrencySymbol, getMinimumAmount } from '@/utils/ import { OnrampConfirmationModal } from '@/components/AddMoney/components/OnrampConfirmationModal' import { InitiateBridgeKYCModal } from '@/components/Kyc/InitiateBridgeKYCModal' import InfoCard from '@/components/Global/InfoCard' +import { useQueryStates, parseAsString, parseAsStringEnum } from 'nuqs' -type AddStep = 'inputAmount' | 'kyc' | 'loading' | 'collectUserDetails' | 'showDetails' +// Step type for URL state +type BridgeBankStep = 'inputAmount' | 'kyc' | 'collectUserDetails' | 'showDetails' export default function OnrampBankPage() { const router = useRouter() const params = useParams() - const [step, setStep] = useState('loading') - const [rawTokenAmount, setRawTokenAmount] = useState('') + + // URL state - persisted in query params + // Example: /add-money/mexico/bank?step=inputAmount&amount=500 + const [urlState, setUrlState] = useQueryStates( + { + step: parseAsStringEnum(['inputAmount', 'kyc', 'collectUserDetails', 'showDetails']), + amount: parseAsString, + }, + { history: 'push' } + ) + + // Amount from URL + const rawTokenAmount = urlState.amount ?? '' + + // Local UI state (not URL-appropriate - transient) const [showWarningModal, setShowWarningModal] = useState(false) const [isRiskAccepted, setIsRiskAccepted] = useState(false) - const [isKycModalOpen, setIsKycModalOpen] = useState(false) const [liveKycStatus, setLiveKycStatus] = useState(undefined) - const { amountToOnramp: amountFromContext, setAmountToOnramp, setError, error, setOnrampData } = useOnrampFlow() - const formRef = useRef<{ handleSubmit: () => void }>(null) const [isUpdatingUser, setIsUpdatingUser] = useState(false) const [userUpdateError, setUserUpdateError] = useState(null) const [isUserDetailsFormValid, setIsUserDetailsFormValid] = useState(false) + const { setError, error, setOnrampData, onrampData } = useOnrampFlow() + const formRef = useRef<{ handleSubmit: () => void }>(null) + const { balance } = useWallet() const { user, fetchUser } = useAuth() const { createOnramp, isLoading: isCreatingOnramp, error: onrampError } = useCreateOnramp() @@ -82,32 +97,30 @@ export default function OnrampBankPage() { return getMinimumAmount(selectedCountry.id) }, [selectedCountry?.id]) + // Determine initial step based on KYC status (only when URL has no step) useEffect(() => { - if (user === null) return // wait for user to be fetched - if (step === 'loading') { - const currentKycStatus = liveKycStatus || user?.user.bridgeKycStatus - const isUserKycVerified = currentKycStatus === 'approved' + // If URL already has a step, respect it (allows deep linking) + if (urlState.step) return - if (!isUserKycVerified) { - setStep('collectUserDetails') - } else { - setStep('inputAmount') - if (amountFromContext && !rawTokenAmount) { - setRawTokenAmount(amountFromContext) - } - } + // Wait for user to be fetched before determining initial step + if (user === null) return + + const currentKycStatus = liveKycStatus || user?.user.bridgeKycStatus + const isUserKycVerified = currentKycStatus === 'approved' + + if (!isUserKycVerified) { + setUrlState({ step: 'collectUserDetails' }) + } else { + setUrlState({ step: 'inputAmount' }) } - }, [liveKycStatus, user, step, amountFromContext, rawTokenAmount]) + }, [liveKycStatus, user, urlState.step, setUrlState]) // Handle KYC completion useEffect(() => { - if (step === 'kyc' && liveKycStatus === 'approved') { - setStep('inputAmount') - if (amountFromContext && !rawTokenAmount) { - setRawTokenAmount(amountFromContext) - } + if (urlState.step === 'kyc' && liveKycStatus === 'approved') { + setUrlState({ step: 'inputAmount' }) } - }, [liveKycStatus, step, amountFromContext, rawTokenAmount]) + }, [liveKycStatus, urlState.step, setUrlState]) const validateAmount = useCallback( (amountStr: string): boolean => { @@ -130,22 +143,23 @@ export default function OnrampBankPage() { [setError, minimumAmount] ) + // Handle amount change - sync to URL state const handleTokenAmountChange = useCallback( (value: string | undefined) => { - setRawTokenAmount(value || '') + const newAmount = value || null // null removes from URL + setUrlState({ amount: newAmount }) }, - [setRawTokenAmount] + [setUrlState] ) + // Validate amount when it changes useEffect(() => { if (rawTokenAmount === '') { - if (!amountFromContext) { - setError({ showError: false, errorMessage: '' }) - } + setError({ showError: false, errorMessage: '' }) } else { validateAmount(rawTokenAmount) } - }, [rawTokenAmount, validateAmount, setError, amountFromContext]) + }, [rawTokenAmount, validateAmount, setError]) const handleAmountContinue = () => { if (validateAmount(rawTokenAmount)) { @@ -161,7 +175,6 @@ export default function OnrampBankPage() { }) return } - setAmountToOnramp(rawTokenAmount) setShowWarningModal(false) setIsRiskAccepted(false) try { @@ -172,7 +185,7 @@ export default function OnrampBankPage() { setOnrampData(onrampDataResponse) if (onrampDataResponse.transferId) { - setStep('showDetails') + setUrlState({ step: 'showDetails' }) } else { setError({ showError: true, @@ -195,13 +208,9 @@ export default function OnrampBankPage() { setIsRiskAccepted(false) } - const handleKycModalOpen = () => { - setIsKycModalOpen(true) - } - const handleKycSuccess = () => { setIsKycModalOpen(false) - setStep('inputAmount') + setUrlState({ step: 'inputAmount' }) } const handleKycModalClose = () => { @@ -222,7 +231,7 @@ export default function OnrampBankPage() { throw new Error(result.error) } await fetchUser() - setStep('kyc') + setUrlState({ step: 'kyc' }) } catch (error: any) { setUserUpdateError(error.message) return { error: error.message } @@ -240,24 +249,29 @@ export default function OnrampBankPage() { } } - const [firstName, ...lastNameParts] = (user?.user.fullName ?? '').split(' ') - const lastName = lastNameParts.join(' ') - const initialUserDetails: Partial = useMemo( () => ({ fullName: user?.user.fullName ?? '', email: user?.user.email ?? '', }), - [user?.user.fullName, user?.user.email, firstName, lastName] + [user?.user.fullName, user?.user.email] ) useEffect(() => { - if (step === 'kyc') { + if (urlState.step === 'kyc') { setIsKycModalOpen(true) } - }, [step]) + }, [urlState.step]) - if (step === 'loading') { + // Redirect to inputAmount if showDetails is accessed without required data (deep link / back navigation) + useEffect(() => { + if (urlState.step === 'showDetails' && !onrampData?.transferId) { + setUrlState({ step: 'inputAmount' }) + } + }, [urlState.step, onrampData?.transferId, setUrlState]) + + // Show loading while user is being fetched and no step in URL yet + if (!urlState.step && user === null) { return } @@ -270,7 +284,12 @@ export default function OnrampBankPage() { ) } - if (step === 'collectUserDetails') { + // Still determining initial step + if (!urlState.step) { + return + } + + if (urlState.step === 'collectUserDetails') { return (
@@ -299,7 +318,7 @@ export default function OnrampBankPage() { ) } - if (step === 'kyc') { + if (urlState.step === 'kyc') { return (
+ } return } - if (step === 'inputAmount') { + if (urlState.step === 'inputAmount') { return (
diff --git a/src/app/ClientProviders.tsx b/src/app/ClientProviders.tsx index 0f2e846fb..a6d4959cc 100644 --- a/src/app/ClientProviders.tsx +++ b/src/app/ClientProviders.tsx @@ -12,19 +12,22 @@ import { TranslationSafeWrapper } from '@/components/Global/TranslationSafeWrapp import { PeanutProvider } from '@/config' import { ContextProvider } from '@/context' import { FooterVisibilityProvider } from '@/context/footerVisibility' +import { NuqsAdapter } from 'nuqs/adapters/next/app' export function ClientProviders({ children }: { children: React.ReactNode }) { return ( - - - - - - - {children} - - - - + + + + + + + + {children} + + + + + ) } diff --git a/src/components/AddMoney/components/AddMoneyBankDetails.tsx b/src/components/AddMoney/components/AddMoneyBankDetails.tsx index c4eae89c5..d944274c1 100644 --- a/src/components/AddMoney/components/AddMoneyBankDetails.tsx +++ b/src/components/AddMoney/components/AddMoneyBankDetails.tsx @@ -17,6 +17,7 @@ import InfoCard from '@/components/Global/InfoCard' import CopyToClipboard from '@/components/Global/CopyToClipboard' import { Button } from '@/components/0_Bruddle/Button' import { useExchangeRate } from '@/hooks/useExchangeRate' +import { useQueryState, parseAsString } from 'nuqs' interface IAddMoneyBankDetails { flow?: 'add-money' | 'request-fulfillment' @@ -25,6 +26,9 @@ interface IAddMoneyBankDetails { export default function AddMoneyBankDetails({ flow = 'add-money' }: IAddMoneyBankDetails) { const isAddMoneyFlow = flow === 'add-money' + // URL state - read amount from URL query params + const [amountFromUrl] = useQueryState('amount', parseAsString) + // contexts const onrampContext = useOnrampFlow() const { @@ -72,10 +76,9 @@ export default function AddMoneyBankDetails({ flow = 'add-money' }: IAddMoneyBan enabled: true, }) - // data from contexts based on flow - const amount = isAddMoneyFlow - ? onrampContext.amountToOnramp - : requestFulfilmentOnrampData?.depositInstructions?.amount + // data from URL state (add-money flow) or context (request-fulfillment flow) + // For add-money flow, amount is now in URL state via nuqs + const amount = isAddMoneyFlow ? (amountFromUrl ?? '') : requestFulfilmentOnrampData?.depositInstructions?.amount const onrampData = isAddMoneyFlow ? onrampContext.onrampData : requestFulfilmentOnrampData const currencySymbolBasedOnCountry = useMemo(() => { diff --git a/src/components/AddMoney/components/InputAmountStep.tsx b/src/components/AddMoney/components/InputAmountStep.tsx index f91c2a20d..e58cec3bc 100644 --- a/src/components/AddMoney/components/InputAmountStep.tsx +++ b/src/components/AddMoney/components/InputAmountStep.tsx @@ -14,11 +14,13 @@ interface InputAmountStepProps { onSubmit: () => void isLoading: boolean tokenAmount: string - setTokenAmount: React.Dispatch> + setTokenAmount: ((value: string) => void) | React.Dispatch> error: string | null setCurrencyAmount: (amount: string | undefined) => void currencyData?: ICurrency setCurrentDenomination?: (denomination: string) => void + initialDenomination?: string + setDisplayedAmount?: (value: string) => void } const InputAmountStep = ({ @@ -30,6 +32,8 @@ const InputAmountStep = ({ currencyData, setCurrencyAmount, setCurrentDenomination, + initialDenomination, + setDisplayedAmount, }: InputAmountStepProps) => { const router = useRouter() @@ -45,8 +49,10 @@ const InputAmountStep = ({ = ({ source }) => { +const MantecaAddMoney: FC = () => { const params = useParams() const router = useRouter() - const [step, setStep] = useState('inputAmount') + const queryClient = useQueryClient() + + // URL state - persisted in query params + // Example: /add-money/argentina/manteca?step=inputAmount&amount=100¤cy=ARS + // The `amount` is stored in whatever denomination `currency` specifies + const [urlState, setUrlState] = useQueryStates( + { + step: parseAsStringEnum(['inputAmount', 'depositDetails']), + amount: parseAsString, + currency: parseAsStringEnum(['USD', 'ARS', 'BRL', 'MXN', 'EUR']), + }, + { history: 'push' } + ) + + // Derive state from URL (with defaults) + const step: MantecaStep = urlState.step ?? 'inputAmount' + // Amount from URL - this is in the denomination specified by `currency` + const displayedAmount = urlState.amount ?? '' + const currentDenomination = urlState.currency ?? 'USD' + + // Local UI state for tracking both amounts (needed for API call and validation) + const [usdAmount, setUsdAmount] = useState('') + const [localCurrencyAmount, setLocalCurrencyAmount] = useState('') + + // Other local UI state (not URL-appropriate - transient or API responses) const [isCreatingDeposit, setIsCreatingDeposit] = useState(false) - const [currencyAmount, setCurrencyAmount] = useState() - const [currentDenomination, setCurrentDenomination] = useState('USD') - const [usdAmount, setUsdAmount] = useState('') const [error, setError] = useState(null) const [depositDetails, setDepositDetails] = useState() const [isKycModalOpen, setIsKycModalOpen] = useState(false) - const queryClient = useQueryClient() const selectedCountryPath = params.country as string const selectedCountry = useMemo(() => { @@ -58,6 +79,7 @@ const MantecaAddMoney: FC = ({ source }) => { }, }) + // Validate USD amount (for min/max checks which are in USD) useEffect(() => { if (!usdAmount || usdAmount === '0.00') { setError(null) @@ -73,11 +95,12 @@ const MantecaAddMoney: FC = ({ source }) => { } }, [usdAmount]) + // Invalidate transactions query when entering deposit details step useEffect(() => { if (step === 'depositDetails') { queryClient.invalidateQueries({ queryKey: [TRANSACTIONS] }) } - }, [step]) + }, [step, queryClient]) const handleKycCancel = () => { setIsKycModalOpen(false) @@ -86,6 +109,33 @@ const MantecaAddMoney: FC = ({ source }) => { } } + // Handle displayed amount change - save to URL + // This is called by AmountInput with the currently DISPLAYED value + const handleDisplayedAmountChange = useCallback( + (value: string) => { + setUrlState({ amount: value || null }) // null removes from URL + }, + [setUrlState] + ) + + // Handle local currency amount change (primary in AmountInput) + const handleLocalCurrencyAmountChange = useCallback((value: string | undefined) => { + setLocalCurrencyAmount(value ?? '') + }, []) + + // Handle USD amount change (secondary in AmountInput) + const handleUsdAmountChange = useCallback((value: string) => { + setUsdAmount(value) + }, []) + + // Handle currency denomination change - sync to URL state + const handleDenominationChange = useCallback( + (value: string) => { + setUrlState({ currency: value as CurrencyDenomination }) + }, + [setUrlState] + ) + const handleAmountSubmit = useCallback(async () => { if (!selectedCountry?.currency) return if (isCreatingDeposit) return @@ -105,7 +155,8 @@ const MantecaAddMoney: FC = ({ source }) => { setError(null) setIsCreatingDeposit(true) const isUsdDenominated = currentDenomination === 'USD' - const amount = isUsdDenominated ? usdAmount : currencyAmount + // Use the displayed amount for the API call + const amount = displayedAmount const depositData = await mantecaApi.deposit({ amount: amount!, isUsdDenominated, @@ -116,14 +167,15 @@ const MantecaAddMoney: FC = ({ source }) => { return } setDepositDetails(depositData.data) - setStep('depositDetails') + // Update URL state to show deposit details step + setUrlState({ step: 'depositDetails' }) } catch (error) { console.log(error) setError(error instanceof Error ? error.message : String(error)) } finally { setIsCreatingDeposit(false) } - }, [currentDenomination, selectedCountry, usdAmount, currencyAmount]) + }, [currentDenomination, selectedCountry, displayedAmount, isMantecaKycRequired, isCreatingDeposit, setUrlState]) // handle verification modal opening useEffect(() => { @@ -132,20 +184,29 @@ const MantecaAddMoney: FC = ({ source }) => { } }, [isMantecaKycRequired]) + // Redirect to inputAmount if depositDetails is accessed without required data (deep link / back navigation) + useEffect(() => { + if (step === 'depositDetails' && !depositDetails) { + setUrlState({ step: 'inputAmount' }) + } + }, [step, depositDetails, setUrlState]) + if (!selectedCountry) return null if (step === 'inputAmount') { return ( <> {isKycModalOpen && ( = ({ source }) => { ) } - if (step === 'depositDetails' && depositDetails) { - return ( - - ) + if (step === 'depositDetails') { + // Show nothing while useEffect redirects if data is missing + if (!depositDetails) { + return null + } + return } return null diff --git a/src/components/AddMoney/components/MantecaDepositShareDetails.tsx b/src/components/AddMoney/components/MantecaDepositShareDetails.tsx index d2302b5a0..6f1e2d4af 100644 --- a/src/components/AddMoney/components/MantecaDepositShareDetails.tsx +++ b/src/components/AddMoney/components/MantecaDepositShareDetails.tsx @@ -19,14 +19,10 @@ import { shortenStringLong, formatCurrency } from '@/utils/general.utils' const MantecaDepositShareDetails = ({ depositDetails, - source, currencyAmount, - onBack, }: { depositDetails: MantecaDepositResponseData - source: 'bank' | 'regionalMethod' currencyAmount?: string | undefined - onBack?: () => void }) => { const router = useRouter() const params = useParams() @@ -86,7 +82,7 @@ const MantecaDepositShareDetails = ({ return (
- router.back())} /> +
{/* Amount Display Card */} diff --git a/src/components/AddMoney/views/RhinoDeposit.view.tsx b/src/components/AddMoney/views/RhinoDeposit.view.tsx index b30fe87b6..7b2ef4b7b 100644 --- a/src/components/AddMoney/views/RhinoDeposit.view.tsx +++ b/src/components/AddMoney/views/RhinoDeposit.view.tsx @@ -58,7 +58,8 @@ const RhinoDepositView = ({ return rhinoApi.getDepositAddressStatus(depositAddressData.depositAddress as string) }, enabled: !!depositAddressData?.depositAddress && isDelayComplete, // Add some delay to start polling after the deposit address is created - refetchInterval: (query) => (query.state.data?.status === 'completed' ? false : 5000), + refetchInterval: (query: { state: { data?: { status?: string } } }) => + query.state.data?.status === 'completed' ? false : 5000, }) const { containerRef, truncatedAddress } = useAutoTruncatedAddress(depositAddressData?.depositAddress ?? '') diff --git a/src/components/Global/AmountInput/index.tsx b/src/components/Global/AmountInput/index.tsx index 65b373ac2..315de6a65 100644 --- a/src/components/Global/AmountInput/index.tsx +++ b/src/components/Global/AmountInput/index.tsx @@ -13,9 +13,11 @@ const DECIMAL_SCALE = 18 // Max expected decimal places for any denomination interface AmountInputProps { className?: string initialAmount?: string + initialDenomination?: string onSubmit?: () => void setPrimaryAmount: (value: string) => void setSecondaryAmount?: (value: string) => void + setDisplayedAmount?: (value: string) => void onBlur?: () => void disabled?: boolean primaryDenomination?: { symbol: string; price: number; decimals: number } @@ -36,9 +38,11 @@ interface AmountInputProps { const AmountInput = ({ className, initialAmount, + initialDenomination, onSubmit, setPrimaryAmount, setSecondaryAmount, + setDisplayedAmount, onBlur, disabled, primaryDenomination = { symbol: '$', price: 1, decimals: 2 }, @@ -64,7 +68,19 @@ const AmountInput = ({ // Store display value for input field (what user sees when typing) const [displayValue, setDisplayValue] = useState(initialAmount || '') const [exactValue, setExactValue] = useState(Number(initialAmount || '') * 10 ** DECIMAL_SCALE) - const [displaySymbol, setDisplaySymbol] = useState(primaryDenomination.symbol) + // Use initialDenomination if provided and valid, otherwise default to primaryDenomination + const [displaySymbol, setDisplaySymbol] = useState(() => { + if (initialDenomination) { + // Check if initialDenomination matches primary or secondary + if (initialDenomination === primaryDenomination.symbol) { + return primaryDenomination.symbol + } + if (secondaryDenomination && initialDenomination === secondaryDenomination.symbol) { + return secondaryDenomination.symbol + } + } + return primaryDenomination.symbol + }) // sync displayValue with initialAmount changes (e.g. when charge is fetched) useEffect(() => { @@ -120,6 +136,9 @@ const AmountInput = ({ const rawDisplayValue = displayValue.replace(/,/g, '') const rawAlternativeValue = alternativeDisplayValue.replace(/,/g, '') + // Always call setDisplayedAmount with the currently displayed value + setDisplayedAmount?.(rawDisplayValue) + if (isPrimaryDenomination) { setPrimaryAmount(rawDisplayValue) setSecondaryAmount?.(rawAlternativeValue) diff --git a/src/context/OnrampFlowContext.tsx b/src/context/OnrampFlowContext.tsx index 1b3dd91b6..26b51f737 100644 --- a/src/context/OnrampFlowContext.tsx +++ b/src/context/OnrampFlowContext.tsx @@ -2,8 +2,6 @@ import React, { createContext, type ReactNode, useCallback, useContext, useMemo, useState } from 'react' -export type OnrampView = 'INITIAL' | 'SELECT_METHOD' - export interface InitialViewErrorState { showError: boolean errorMessage: string @@ -28,11 +26,20 @@ export interface IOnrampData { } } +/** + * OnrampFlowContext - Manages transient state for the add-money (onramp) flow. + * + * NOTE: Step and amount are now managed via URL query parameters using nuqs. + * See the useQueryStates usage in: + * - src/app/(mobile-ui)/add-money/[country]/bank/page.tsx + * - src/components/AddMoney/components/MantecaAddMoney.tsx + * + * This context only manages: + * - `error` - Transient error state for form validation + * - `fromBankSelected` - Flag for navigation + * - `onrampData` - API response data (not appropriate for URL) + */ interface OnrampFlowContextType { - amountToOnramp: string - setAmountToOnramp: (amount: string) => void - currentView: OnrampView - setCurrentView: (view: OnrampView) => void error: InitialViewErrorState setError: (error: InitialViewErrorState) => void fromBankSelected: boolean @@ -45,18 +52,17 @@ interface OnrampFlowContextType { const OnrampFlowContext = createContext(undefined) export const OnrampFlowContextProvider: React.FC<{ children: ReactNode }> = ({ children }) => { - const [amountToOnramp, setAmountToOnramp] = useState('') - const [currentView, setCurrentView] = useState('INITIAL') + // Transient UI state - not appropriate for URL const [error, setError] = useState({ showError: false, errorMessage: '', }) const [fromBankSelected, setFromBankSelected] = useState(false) + + // API response data - not appropriate for URL const [onrampData, setOnrampData] = useState(null) const resetOnrampFlow = useCallback(() => { - setAmountToOnramp('') - setCurrentView('INITIAL') setError({ showError: false, errorMessage: '', @@ -67,10 +73,6 @@ export const OnrampFlowContextProvider: React.FC<{ children: ReactNode }> = ({ c const value = useMemo( () => ({ - amountToOnramp, - setAmountToOnramp, - currentView, - setCurrentView, error, setError, fromBankSelected, @@ -79,7 +81,7 @@ export const OnrampFlowContextProvider: React.FC<{ children: ReactNode }> = ({ c setOnrampData, resetOnrampFlow, }), - [amountToOnramp, currentView, error, fromBankSelected, onrampData, resetOnrampFlow] + [error, fromBankSelected, onrampData, resetOnrampFlow] ) return {children}