Skip to content
Merged
2 changes: 0 additions & 2 deletions src/app/(mobile-ui)/home/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import { PEANUT_WALLET_TOKEN_DECIMALS } from '@/constants/zerodev.consts'
import { PostSignupActionManager } from '@/components/Global/PostSignupActionManager'
import { useWithdrawFlow } from '@/context/WithdrawFlowContext'
import { useClaimBankFlow } from '@/context/ClaimBankFlowContext'
import { useDeviceType } from '@/hooks/useGetDeviceType'
import { useNotifications } from '@/hooks/useNotifications'
import useKycStatus from '@/hooks/useKycStatus'
import { useCardPioneerInfo } from '@/hooks/useCardPioneerInfo'
Expand Down Expand Up @@ -57,7 +56,6 @@ export default function Home() {
const { balance, isFetchingBalance } = useWallet()
const { resetFlow: resetClaimBankFlow } = useClaimBankFlow()
const { resetWithdrawFlow } = useWithdrawFlow()
const { deviceType } = useDeviceType()
const { user } = useUserStore()
const [isBalanceHidden, setIsBalanceHidden] = useState(() => {
const prefs = user ? getUserPreferences(user.user.userId) : undefined
Expand Down
2 changes: 1 addition & 1 deletion src/app/(mobile-ui)/points/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ const PointsPage = () => {
return (
<>
{number}
{suffix && <span className="text-primary-1">{suffix}</span>}
{suffix && <span>{suffix}</span>}
</>
)
})()}{' '}
Expand Down
57 changes: 35 additions & 22 deletions src/app/(mobile-ui)/qr-pay/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1057,25 +1057,25 @@ export default function QRPayPage() {
)
}

// Show maintenance error if provider is disabled
if (isProviderDisabled) {
// Get user-facing payment method name
const paymentMethodName = useMemo(() => {
if (paymentProcessor === 'MANTECA') {
switch (qrType) {
case EQrType.PIX:
return 'PIX'
case EQrType.MERCADO_PAGO:
return 'Mercado Pago'
case EQrType.ARGENTINA_QR3:
return 'QR'
default:
return 'QR'
}
// get user-facing payment method name for maintenance screen
const paymentMethodName = useMemo(() => {
if (paymentProcessor === 'MANTECA') {
switch (qrType) {
case EQrType.PIX:
return 'PIX'
case EQrType.MERCADO_PAGO:
return 'Mercado Pago'
case EQrType.ARGENTINA_QR3:
return 'QR'
default:
return 'QR'
}
return 'SimpleFi'
}, [])
}
return 'SimpleFi'
}, [paymentProcessor, qrType])

// Show maintenance error if provider is disabled
if (isProviderDisabled) {
return (
<div className="my-auto flex h-full w-full flex-col justify-center space-y-4">
<Card className="flex w-full flex-col items-center gap-2 p-4">
Expand Down Expand Up @@ -1267,9 +1267,9 @@ export default function QRPayPage() {

{/* Perk Success Banner - Show after claiming */}
{(perkClaimed || qrPayment?.perk?.claimed) && (
<Card className="flex items-start gap-4 bg-white p-6">
<div className="flex h-16 w-16 flex-shrink-0 items-center justify-center rounded-full bg-yellow-400">
<Image src={STAR_STRAIGHT_ICON} alt="star" width={36} height={36} />
<Card className="flex items-start gap-3 bg-white p-4">
<div className="flex max-w-[15%] flex-shrink-0 items-center justify-center rounded-full bg-yellow-400 p-2">
<Image src={STAR_STRAIGHT_ICON} alt="star" width={28} height={28} />
</div>
<div className="flex flex-col gap-2">
<h2 className="text-2xl font-bold">Peanut got you!</h2>
Expand Down Expand Up @@ -1332,15 +1332,28 @@ export default function QRPayPage() {
WebkitTapHighlightColor: 'transparent',
}}
>
{/* Black progress fill from left to right */}
{/* progress fill from left to right */}
<div
className="absolute inset-0 bg-black transition-all duration-100"
style={{
width: `${holdProgress}%`,
left: 0,
}}
/>
<span className="relative z-10">Claim Peanut Perk Now!</span>
{(() => {
const label = 'Claim Peanut Perk Now!'
return (
<>
<span className="relative z-10">{label}</span>
<span
className="absolute inset-0 z-20 flex items-center justify-center text-white transition-all duration-75"
style={{ clipPath: `inset(0 ${100 - holdProgress}% 0 0)` }}
>
{label}
</span>
</>
)
})()}
</Button>
) : (
<>
Expand Down
6 changes: 2 additions & 4 deletions src/app/(mobile-ui)/withdraw/manteca/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { Icon } from '@/components/Global/Icons/Icon'
import PeanutLoading from '@/components/Global/PeanutLoading'
import { mantecaApi, type WithdrawPriceLock } from '@/services/manteca'
import { useCurrency } from '@/hooks/useCurrency'
import { isTxReverted } from '@/utils/general.utils'
import { loadingStateContext } from '@/context'
import { countryData } from '@/components/AddMoney/consts'
import Image from 'next/image'
Expand All @@ -27,7 +26,6 @@ import {
import ValidatedInput from '@/components/Global/ValidatedInput'
import AmountInput from '@/components/Global/AmountInput'
import { formatUnits, parseUnits } from 'viem'
import type { TransactionReceipt, Hash } from 'viem'
import { PaymentInfoRow } from '@/components/Payment/PaymentInfoRow'
import { useAuth } from '@/context/authContext'
import { useModalsContext } from '@/context/ModalsContext'
Expand Down Expand Up @@ -623,7 +621,7 @@ export default function MantecaWithdrawFlow() {
</div>
<div>
<p className="flex items-center gap-1 text-center text-sm text-gray-600">
<Icon name="arrow-up" size={10} /> You're withdrawing
<Icon name="arrow-up" size={10} /> You're sending
</p>
<p className="text-2xl font-bold">
{currencyCode} {formatNumberForDisplay(currencyAmount, { maxDecimals: 2 })}
Expand Down Expand Up @@ -737,7 +735,7 @@ export default function MantecaWithdrawFlow() {
</div>
<div>
<p className="flex items-center gap-1 text-center text-sm text-gray-600">
<Icon name="arrow-up" size={10} /> You're withdrawing
<Icon name="arrow-up" size={10} /> You're sending
</p>
<p className="text-2xl font-bold">
{currencyCode}{' '}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Global/Badges/StatusBadge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const StatusBadge: React.FC<StatusBadgeProps> = ({ status, className, size = 'sm
case 'cancelled':
return 'bg-error-1 text-error border border-error-2'
case 'refunded':
return 'bg-secondary-4 text-yellow-6 border border-yellow-7'
return 'bg-success-2 text-success-4 border border-success-5'
case 'soon':
case 'custom':
return 'bg-primary-3 text-primary-4'
Expand Down
42 changes: 38 additions & 4 deletions src/components/Global/ExchangeRateWidget/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useDebounce } from '@/hooks/useDebounce'
import { useExchangeRate } from '@/hooks/useExchangeRate'
import Image from 'next/image'
import { useRouter, useSearchParams } from 'next/navigation'
import { type FC, useCallback, useEffect, useMemo } from 'react'
import { type FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Icon, type IconName } from '../Icons/Icon'
import { Button } from '@/components/0_Bruddle/Button'

Expand Down Expand Up @@ -90,6 +90,28 @@ const ExchangeRateWidget: FC<IExchangeRateWidgetProps> = ({ ctaLabel, ctaIcon, c
[updateUrlParams, sourceCurrency]
)

const [isSwapping, setIsSwapping] = useState(false)
const skipNextDebounceSyncRef = useRef(false)

const swapCurrencies = useCallback(() => {
setIsSwapping(true)
skipNextDebounceSyncRef.current = true
const newAmount =
typeof destinationAmount === 'number' && destinationAmount > 0
? Math.round(destinationAmount * 100) / 100
: undefined
updateUrlParams({ from: destinationCurrency, to: sourceCurrency, amount: newAmount })
}, [sourceCurrency, destinationCurrency, destinationAmount, updateUrlParams])

// clear swapping state once exchange rate hook finishes recalculating
useEffect(() => {
if (isSwapping && !isLoading) {
setIsSwapping(false)
}
}, [isSwapping, isLoading])

const showLoading = isLoading || isSwapping

// Enforce USD rule: at least one currency must be USD
useEffect(() => {
if (sourceCurrency !== 'USD' && destinationCurrency !== 'USD') {
Expand All @@ -100,6 +122,10 @@ const ExchangeRateWidget: FC<IExchangeRateWidgetProps> = ({ ctaLabel, ctaIcon, c

// Update URL when source amount changes (only for valid numbers)
useEffect(() => {
if (skipNextDebounceSyncRef.current) {
skipNextDebounceSyncRef.current = false
return
}
if (typeof debouncedSourceAmount === 'number' && debouncedSourceAmount !== urlSourceAmount) {
updateUrlParams({ amount: debouncedSourceAmount })
}
Expand All @@ -125,7 +151,7 @@ const ExchangeRateWidget: FC<IExchangeRateWidgetProps> = ({ ctaLabel, ctaIcon, c
<div className="w-full">
<h2 className="text-left text-sm">You Send</h2>
<div className="btn btn-shadow-primary-4 mt-2 flex w-full items-center justify-center gap-4 bg-white p-4">
{isLoading ? (
{showLoading ? (
<div className="flex w-full items-center">
<div className="h-8 w-40 animate-pulse rounded-full bg-grey-2" />
</div>
Expand Down Expand Up @@ -167,10 +193,18 @@ const ExchangeRateWidget: FC<IExchangeRateWidgetProps> = ({ ctaLabel, ctaIcon, c
</div>
</div>

<button
onClick={swapCurrencies}
className="flex h-8 w-8 items-center justify-center self-center rounded-full hover:bg-grey-4"
aria-label="Swap currencies"
>
<Icon name="arrow-exchange" size={18} className="rotate-90 transition-transform duration-300" />
</button>

<div className="w-full">
<h2 className="text-left text-sm">Recipient Gets</h2>
<div className="btn btn-shadow-primary-4 mt-2 flex w-full items-center justify-center gap-4 bg-white p-4">
{isLoading ? (
{showLoading ? (
<div className="flex w-full items-center">
<div className="h-8 w-40 animate-pulse rounded-full bg-grey-2" />
</div>
Expand Down Expand Up @@ -212,7 +246,7 @@ const ExchangeRateWidget: FC<IExchangeRateWidgetProps> = ({ ctaLabel, ctaIcon, c
</div>

<div className="rounded-full bg-grey-4 px-2 py-[2px] text-xs font-bold text-gray-1">
{isLoading ? (
{showLoading ? (
<div className="mx-auto h-3 w-28 animate-pulse rounded-full bg-grey-2" />
) : isError ? (
<span>Rate currently unavailable</span>
Expand Down
1 change: 0 additions & 1 deletion src/components/Global/Icons/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ import {
VerifiedUserOutlined,
EmojiEventsOutlined,
LockOutlined,
CallSplitRounded,
GroupsRounded,
VpnLockOutlined,
CameraswitchRounded,
Expand Down
2 changes: 1 addition & 1 deletion src/components/Global/PeanutActionDetailsCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export default function PeanutActionDetailsCard({
else title = `${renderRecipient()} sent you`
}
if (transactionType === 'ADD_MONEY' || transactionType === 'ADD_MONEY_BANK_ACCOUNT') title = `You're adding`
if (transactionType === 'WITHDRAW' || transactionType === 'WITHDRAW_BANK_ACCOUNT') title = `You're withdrawing`
if (transactionType === 'WITHDRAW' || transactionType === 'WITHDRAW_BANK_ACCOUNT') title = `You're sending`
if (transactionType === 'CLAIM_LINK_BANK_ACCOUNT') {
if (viewType === 'SUCCESS') {
title = 'You will receive'
Expand Down
2 changes: 1 addition & 1 deletion src/components/Global/StatusPill/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const StatusPill = ({ status }: StatusPillProps) => {
completed: 'border-success-5 bg-success-2 text-success-4',
pending: 'border-yellow-8 bg-secondary-4 text-yellow-6',
cancelled: 'border-error-2 bg-error-1 text-error',
refunded: 'border-yellow-8 bg-secondary-4 text-yellow-6',
refunded: 'border-success-5 bg-success-2 text-success-4',
failed: 'border-error-2 bg-error-1 text-error',
processing: 'border-yellow-8 bg-secondary-4 text-yellow-6',
soon: 'border-yellow-8 bg-secondary-4 text-yellow-6',
Expand Down
Loading
Loading