Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { useWallet } from '@/hooks/wallet/useWallet'
import { AccountType, type Account } from '@/interfaces'
import { formatIban, shortenStringLong, isTxReverted } from '@/utils/general.utils'
import { useParams, useRouter } from 'next/navigation'
import { useEffect, useMemo, useState } from 'react'
import { useEffect, useState } from 'react'
import DirectSuccessView from '@/components/Payment/Views/Status.payment.view'
import { ErrorHandler, getBridgeChainName } from '@/utils'
import { getOfframpCurrencyConfig } from '@/utils/bridge.utils'
Expand All @@ -24,6 +24,7 @@ import countryCurrencyMappings from '@/constants/countryCurrencyMapping'
import { useQuery } from '@tanstack/react-query'
import { pointsApi } from '@/services/points'
import { PointsAction } from '@/services/services.types'
import { useSearchParams } from 'next/navigation'

type View = 'INITIAL' | 'SUCCESS'

Expand All @@ -32,11 +33,16 @@ export default function WithdrawBankPage() {
const { user, fetchUser } = useAuth()
const { address, sendMoney } = useWallet()
const router = useRouter()
const searchParams = useSearchParams()
const [isLoading, setIsLoading] = useState(false)
const [view, setView] = useState<View>('INITIAL')
const params = useParams()
const country = params.country as string

// check if we came from send flow - using method param to detect (only bank goes through this page)
const methodParam = searchParams.get('method')
const fromSendFlow = methodParam === 'bank'

const nonEuroCurrency = countryCurrencyMappings.find(
(currency) =>
country.toLowerCase() === currency.country.toLowerCase() ||
Expand Down Expand Up @@ -222,7 +228,7 @@ export default function WithdrawBankPage() {
return (
<div className="flex min-h-[inherit] w-full flex-col justify-start gap-8 self-start">
<NavHeader
title="Withdraw"
title={fromSendFlow ? 'Send' : 'Withdraw'}
icon={view === 'SUCCESS' ? 'cancel' : undefined}
onPrev={view === 'SUCCESS' ? () => router.push('/home') : () => router.back()}
/>
Expand Down
87 changes: 66 additions & 21 deletions src/app/(mobile-ui)/withdraw/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,23 @@ import { useWallet } from '@/hooks/wallet/useWallet'
import { tokenSelectorContext } from '@/context/tokenSelector.context'
import { formatAmount } from '@/utils'
import { getCountryFromAccount } from '@/utils/bridge.utils'
import { useRouter } from 'next/navigation'
import { useRouter, useSearchParams } from 'next/navigation'
import { useCallback, useEffect, useMemo, useState, useRef, useContext } from 'react'
import { formatUnits } from 'viem'

type WithdrawStep = 'inputAmount' | 'selectMethod'

export default function WithdrawPage() {
const router = useRouter()
const searchParams = useSearchParams()
const { selectedTokenData } = useContext(tokenSelectorContext)

// check if coming from send flow based on method query param
const methodParam = searchParams.get('method')
const isFromSendFlow = !!(methodParam && ['bank', 'crypto'].includes(methodParam))
const isCryptoFromSend = methodParam === 'crypto' && isFromSendFlow
const isBankFromSend = methodParam === 'bank' && isFromSendFlow

const {
amountToWithdraw: amountFromContext,
setAmountToWithdraw,
Expand All @@ -33,10 +40,25 @@ export default function WithdrawPage() {
setShowAllWithdrawMethods,
} = useWithdrawFlow()

const initialStep: WithdrawStep = selectedMethod ? 'inputAmount' : 'selectMethod'
// only go to input amount if method is selected OR if it's crypto from send (bank needs method selection first)
const initialStep: WithdrawStep = selectedMethod || isCryptoFromSend ? 'inputAmount' : 'selectMethod'

const [step, setStep] = useState<WithdrawStep>(initialStep)

// automatically set crypto method when coming from send flow with method=crypto
useEffect(() => {
if (isCryptoFromSend && !selectedMethod) {
setSelectedMethod({
type: 'crypto',
title: 'Crypto',
countryPath: undefined,
})
} else if (isBankFromSend && !selectedMethod) {
// for bank from send flow, prefer showing saved accounts first
setShowAllWithdrawMethods(false)
}
}, [isCryptoFromSend, isBankFromSend, selectedMethod, setSelectedMethod, setShowAllWithdrawMethods])

// flag to know if user has manually entered something
const userTypedRef = useRef<boolean>(false)

Expand Down Expand Up @@ -68,20 +90,20 @@ export default function WithdrawPage() {
}, [setError, amountFromContext])

useEffect(() => {
if (selectedMethod) {
if (selectedMethod || isCryptoFromSend) {
setStep('inputAmount')
if (amountFromContext && !rawTokenAmount) {
setRawTokenAmount(amountFromContext)
}
} else {
} else if (!selectedMethod) {
setStep('selectMethod')
// clear the raw token amount when switching back to method selection
if (step !== 'selectMethod') {
setRawTokenAmount('')
setTokenInputKey((k) => k + 1)
}
}
}, [selectedMethod, amountFromContext, step, rawTokenAmount])
}, [selectedMethod, isCryptoFromSend, amountFromContext, step, rawTokenAmount])

useEffect(() => {
// If amount is available (i.e) user clicked back from select method view, show all methods
Expand Down Expand Up @@ -116,7 +138,7 @@ export default function WithdrawPage() {
// determine message
let message = ''
if (usdEquivalent < 1) {
message = 'Minimum withdrawal is 1.'
message = isFromSendFlow ? 'Minimum send amount is $1.' : 'Minimum withdrawal is $1.'
} else if (amount > maxDecimalAmount) {
message = 'Amount exceeds your wallet balance.'
} else {
Expand All @@ -125,7 +147,7 @@ export default function WithdrawPage() {
setError({ showError: true, errorMessage: message })
return false
},
[maxDecimalAmount, setError, selectedTokenData?.price]
[maxDecimalAmount, setError, selectedTokenData?.price, isFromSendFlow]
)

const handleTokenAmountChange = useCallback(
Expand Down Expand Up @@ -180,25 +202,35 @@ export default function WithdrawPage() {
setUsdAmount(usdVal.toString())

// Route based on selected method type
// preserve method param if coming from send flow
const methodQueryParam = isFromSendFlow ? `method=${methodParam}` : ''

if (selectedBankAccount) {
const country = getCountryFromAccount(selectedBankAccount)
if (country) {
router.push(`/withdraw/${country.path}/bank`)
const queryParams = isFromSendFlow ? `?${methodQueryParam}` : ''
router.push(`/withdraw/${country.path}/bank${queryParams}`)
} else {
throw new Error('Failed to get country from bank account')
}
} else if (selectedMethod.type === 'crypto') {
router.push('/withdraw/crypto')
const queryParams = isFromSendFlow ? `?${methodQueryParam}` : ''
router.push(`/withdraw/crypto${queryParams}`)
} else if (selectedMethod.type === 'manteca') {
// Route directly to Manteca with method and country params
const methodParam = selectedMethod.title?.toLowerCase().replace(/\s+/g, '-') || 'bank-transfer'
router.push(`/withdraw/manteca?method=${methodParam}&country=${selectedMethod.countryPath}`)
const mantecaMethodParam = selectedMethod.title?.toLowerCase().replace(/\s+/g, '-') || 'bank-transfer'
const additionalParams = isFromSendFlow ? `&${methodQueryParam}` : ''
router.push(
`/withdraw/manteca?method=${mantecaMethodParam}&country=${selectedMethod.countryPath}${additionalParams}`
)
} else if (selectedMethod.type === 'bridge' && selectedMethod.countryPath) {
// Bridge countries go to country page for bank account form
router.push(`/withdraw/${selectedMethod.countryPath}`)
const queryParams = isFromSendFlow ? `?${methodQueryParam}` : ''
router.push(`/withdraw/${selectedMethod.countryPath}${queryParams}`)
} else if (selectedMethod.countryPath) {
// Other countries go to their country pages
router.push(`/withdraw/${selectedMethod.countryPath}`)
const queryParams = isFromSendFlow ? `?${methodQueryParam}` : ''
router.push(`/withdraw/${selectedMethod.countryPath}${queryParams}`)
}
}
}
Expand All @@ -221,16 +253,24 @@ export default function WithdrawPage() {
return (
<div className="flex min-h-[inherit] flex-col justify-start space-y-8">
<NavHeader
title="Withdraw"
title={isFromSendFlow ? 'Send' : 'Withdraw'}
onPrev={() => {
// Go back to method selection
setSelectedMethod(null)
setStep('selectMethod')
// if crypto from send, go back to send page
if (isCryptoFromSend) {
setSelectedMethod(null)
router.push('/send')
} else {
// otherwise go back to method selection
setSelectedMethod(null)
setStep('selectMethod')
}
}}
/>
<div className="my-auto flex flex-grow flex-col justify-center gap-4 md:my-0">
<div className="space-y-1">
<div className="text-xl font-bold">Amount to withdraw</div>
<div className="text-xl font-bold">
{isFromSendFlow ? 'Amount to send' : 'Amount to withdraw'}
</div>
</div>
<TokenAmountInput
key={tokenInputKey} // force re-render to clear any internal state
Expand Down Expand Up @@ -258,10 +298,15 @@ export default function WithdrawPage() {
return (
<AddWithdrawRouterView
flow="withdraw"
pageTitle="Withdraw"
mainHeading="How would you like to withdraw?"
pageTitle={isBankFromSend ? 'Send' : 'Withdraw'}
mainHeading={isBankFromSend ? 'How would you like to send?' : 'How would you like to withdraw?'}
onBackClick={() => {
router.push('/home')
// if bank from send flow, go back to send page
if (isBankFromSend) {
router.push('/send')
} else {
router.push('/home')
}
}}
/>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React from 'react'
import { twMerge } from 'tailwind-merge'
import { Button } from '../0_Bruddle'

interface SearchResultCardProps {
interface ActionListCardProps {
title: string | React.ReactNode
description?: string
leftIcon?: React.ReactNode
Expand All @@ -16,7 +16,7 @@ interface SearchResultCardProps {
descriptionClassName?: string
}

export const SearchResultCard = ({
export const ActionListCard = ({
title,
description,
leftIcon,
Expand All @@ -26,7 +26,7 @@ export const SearchResultCard = ({
rightContent,
isDisabled = false,
descriptionClassName,
}: SearchResultCardProps) => {
}: ActionListCardProps) => {
const handleCardClick = () => {
onClick()
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/AddMoney/components/CryptoSourceListCard.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use client'
import AvatarWithBadge from '@/components/Profile/AvatarWithBadge'
import { SearchResultCard } from '@/components/SearchUsers/SearchResultCard'
import Image, { type StaticImageData } from 'next/image'
import { twMerge } from 'tailwind-merge'
import { type CryptoSource } from '../consts'
import { ActionListCard } from '@/components/ActionListCard'

interface CryptoSourceListCardProps {
sources: CryptoSource[]
Expand All @@ -22,7 +22,7 @@ export const CryptoSourceListCard = ({ sources, onItemClick }: CryptoSourceListC
return (
<div className="flex flex-col">
{sources.map((source, index) => (
<SearchResultCard
<ActionListCard
key={source.id}
title={source.name}
className="px-4 py-3"
Expand Down
4 changes: 2 additions & 2 deletions src/components/AddMoney/components/DepositMethodList.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use client'
import { type CardPosition } from '@/components/Global/Card'
import AvatarWithBadge from '@/components/Profile/AvatarWithBadge'
import { SearchResultCard } from '@/components/SearchUsers/SearchResultCard'
import Image from 'next/image'
import { twMerge } from 'tailwind-merge'
import { ALL_COUNTRIES_ALPHA3_TO_ALPHA2 } from '../consts'
import { ActionListCard } from '@/components/ActionListCard'

export interface DepositMethod {
type: 'crypto' | 'country'
Expand Down Expand Up @@ -60,7 +60,7 @@ export const DepositMethodList = ({ methods, onItemClick, isAllMethodsView = fal
const countryCodeForFlag = twoLetterCountryCode.toLowerCase() ?? ''

return (
<SearchResultCard
<ActionListCard
key={`${method.type}-${method.id}`}
title={method.title}
description={method.description || method.currency}
Expand Down
4 changes: 2 additions & 2 deletions src/components/AddMoney/views/TokenSelection.view.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import StatusBadge from '@/components/Global/Badges/StatusBadge'
import { getCardPosition } from '@/components/Global/Card'
import NavHeader from '@/components/Global/NavHeader'
import { SearchResultCard } from '@/components/SearchUsers/SearchResultCard'
import { PEANUT_WALLET_TOKEN_SYMBOL } from '@/constants'
import Image from 'next/image'
import React from 'react'
import { type CryptoToken, DEPOSIT_CRYPTO_TOKENS } from '../consts'
import { ActionListCard } from '@/components/ActionListCard'

interface TokenSelectionViewProps {
headerTitle?: string
Expand All @@ -25,7 +25,7 @@ const TokenSelectionView: React.FC<TokenSelectionViewProps> = ({ headerTitle, on
const isDisabled = token.symbol.toLowerCase() !== PEANUT_WALLET_TOKEN_SYMBOL.toLowerCase()

return (
<SearchResultCard
<ActionListCard
isDisabled={isDisabled}
key={token.id}
title={token.symbol}
Expand Down
Loading
Loading