Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a0db7e4
reafactor: create reusable country list component and use it for all …
kushagrasarathe Aug 6, 2025
837787a
feat: reusable user accounts components
kushagrasarathe Aug 6, 2025
face7a6
feat: handle different cases based on kyc status for bank claim
kushagrasarathe Aug 7, 2025
3759469
fix: account creation
kushagrasarathe Aug 7, 2025
5715ac9
chore: add docstring to hooks
kushagrasarathe Aug 7, 2025
47c8c23
chore: better comments for bank flow manager
kushagrasarathe Aug 8, 2025
13b8152
Merge branch 'peanut-wallet-dev' into feat/links-v2.1-send
kushagrasarathe Aug 8, 2025
0511374
fix: kyc modal closing after tos acceptance issue
kushagrasarathe Aug 11, 2025
2e01872
fix: remove bank acc caching from withdraw flow
kushagrasarathe Aug 11, 2025
3d02fa3
fix: update confirm claim modal copy
kushagrasarathe Aug 11, 2025
4a34c48
fix: remove bank acc caching from claim flow
kushagrasarathe Aug 11, 2025
7f39669
fix: navheader title
kushagrasarathe Aug 11, 2025
1eafb28
feat: req fulfillment exchange flow
kushagrasarathe Aug 12, 2025
63520a1
fix: header title
kushagrasarathe Aug 12, 2025
0fd4096
feat: req fulfillment using connected external wallet
kushagrasarathe Aug 12, 2025
ac30392
fix: navigation and ui
kushagrasarathe Aug 12, 2025
2e4fc79
fix: file name
kushagrasarathe Aug 12, 2025
c058d46
feat: abstract reusbale components from onramp flow for bank fulfilment
kushagrasarathe Aug 13, 2025
a1d83c2
feat: handle onramp creation for request fulfilment
kushagrasarathe Aug 13, 2025
6c5157e
Merge branch 'peanut-wallet-dev' into feat/links-v2.1-send
kushagrasarathe Aug 13, 2025
dd4fb9d
feat: reusable verification component
kushagrasarathe Aug 15, 2025
8e7c750
feat: handle bank req fulfilment for peanut users
kushagrasarathe Aug 15, 2025
97324c0
Merge branch 'feat/links-v2.1-send' into feat/links-v2.1-req
kushagrasarathe Aug 15, 2025
18dd786
Merge branch 'peanut-wallet-dev' into feat/links-v2.1-req
kushagrasarathe Aug 15, 2025
76f08d6
Merge branch 'peanut-wallet-dev' into feat/links-v2.1-req
kushagrasarathe Aug 15, 2025
8042912
fix: show all supported countries in req/claim bank flow
kushagrasarathe Aug 15, 2025
c4b1a28
feat: show google-pay/apple-pay based on users device
kushagrasarathe Aug 15, 2025
908c6bd
feat: handle bank send link claim hisotry for peanut users
kushagrasarathe Aug 19, 2025
1a51de9
feat: handle history ui changes for request fulfillment using bank ac…
kushagrasarathe Aug 19, 2025
47f2455
Merge branch 'peanut-wallet-dev' into feat/links-v2.1-req
kushagrasarathe Aug 20, 2025
db4b622
fix: resolve pr review comments
kushagrasarathe Aug 20, 2025
de6c16e
fix: exhange rate hook fallback value
kushagrasarathe Aug 20, 2025
037423e
Merge branch 'feat/links-v2.1-req' into feat/links-v2.1-history
kushagrasarathe Aug 20, 2025
f46e7eb
fix: resolve pr comments
kushagrasarathe Aug 20, 2025
a492364
Merge branch 'feat/links-v2.1-req' into feat/links-v2.1-history
kushagrasarathe Aug 20, 2025
4e9a258
fix: review comments
kushagrasarathe Aug 20, 2025
50fc973
Merge branch 'peanut-wallet-dev' into feat/links-v2.1-req
kushagrasarathe Aug 22, 2025
ad4b549
Merge branch 'feat/links-v2.1-req' into feat/links-v2.1-history
kushagrasarathe Aug 22, 2025
9877e88
Merge branch 'peanut-wallet-dev' into feat/links-v2.1-history
kushagrasarathe Aug 22, 2025
b724647
feat: badges updates (#1119)
kushagrasarathe Aug 22, 2025
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
52 changes: 41 additions & 11 deletions src/app/[...recipient]/client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import InitialPaymentView from '@/components/Payment/Views/Initial.payment.view'
import DirectSuccessView from '@/components/Payment/Views/Status.payment.view'
import PintaReqPaySuccessView from '@/components/PintaReqPay/Views/Success.pinta.view'
import PublicProfile from '@/components/Profile/components/PublicProfile'
import { TransactionDetailsReceipt } from '@/components/TransactionDetails/TransactionDetailsDrawer'
import { TransactionDetailsReceipt } from '@/components/TransactionDetails/TransactionDetailsReceipt'
import { TransactionDetails } from '@/components/TransactionDetails/transactionTransformer'
import { useAuth } from '@/context/authContext'
import { useCurrency } from '@/hooks/useCurrency'
import { useTransactionDetailsDrawer } from '@/hooks/useTransactionDetailsDrawer'
import { EHistoryEntryType, EHistoryUserRole } from '@/hooks/useTransactionHistory'
import { useUserInteractions } from '@/hooks/useUserInteractions'
import { EParseUrlError, parsePaymentURL, ParseUrlError } from '@/lib/url-parser/parser'
import { ParsedURL } from '@/lib/url-parser/types/payment'
import { useAppDispatch, usePaymentStore } from '@/redux/hooks'
Expand Down Expand Up @@ -60,6 +61,18 @@ export default function PaymentPage({ recipient, flow = 'request_pay' }: Props)
const [isLinkCancelling, setisLinkCancelling] = useState(false)
const { showExternalWalletFulfilMethods, showRequestFulfilmentBankFlowManager } = useRequestFulfillmentFlow()

// determine if the current user is the recipient of the transaction
const isCurrentUserRecipient = chargeDetails?.requestLink.recipientAccount?.userId === user?.user.userId

// determine the counterparty of the transaction
const payer = chargeDetails?.payments && chargeDetails?.payments.length > 0 ? chargeDetails.payments[0] : null
const counterpartyUserId = isCurrentUserRecipient
? payer?.payerAccount?.userId
: chargeDetails?.requestLink.recipientAccount?.userId

// fetch interactions for the counterparty
const { interactions } = useUserInteractions(counterpartyUserId ? [counterpartyUserId] : [])

const isMountedRef = useRef(true)

const fetchChargeDetails = async () => {
Expand Down Expand Up @@ -280,14 +293,26 @@ export default function PaymentPage({ recipient, flow = 'request_pay' }: Props)

const recipientAccount = chargeDetails.requestLink.recipientAccount
const isCurrentUser = recipientAccount?.userId === user?.user.userId

if (status === 'pending' && !isCurrentUser) {
return null
}

const payerAccount =
chargeDetails.payments && chargeDetails.payments.length > 0 ? chargeDetails.payments[0].payerAccount : null

// determine who the counterparty is.
// if the current user is the recipient of the funds, the counterparty is the one who paid.
// otherwise, the counterparty is the one who will receive the funds.
const counterparty = isCurrentUser ? payerAccount : recipientAccount

const username =
recipientAccount?.user?.username ||
recipientAccount?.identifier ||
chargeDetails.requestLink.recipientAddress
counterparty?.user?.username ||
counterparty?.identifier ||
(isCurrentUser && chargeDetails.payments.length > 0
? chargeDetails.payments[0].payerAddress
: chargeDetails.requestLink.recipientAddress)

const originalUserRole = isCurrentUser ? EHistoryUserRole.RECIPIENT : EHistoryUserRole.SENDER
let details: Partial<TransactionDetails> = {
id: chargeDetails.uuid,
Expand All @@ -312,6 +337,8 @@ export default function PaymentPage({ recipient, flow = 'request_pay' }: Props)
amountDisplay: '$ 0.00',
},
currency: usdAmount ? { amount: usdAmount, code: 'USD' } : undefined,
isVerified: counterparty?.user?.kycStatus === 'approved',
haveSentMoneyToUser: counterparty?.userId ? interactions[counterparty.userId] || false : false,
}

if (isExternalWalletFlow) {
Expand All @@ -326,7 +353,15 @@ export default function PaymentPage({ recipient, flow = 'request_pay' }: Props)
}

return details as TransactionDetails
}, [chargeDetails, user?.user.userId, isExternalWalletFlow, user?.user.username, usdAmount, paymentDetails])
}, [
chargeDetails,
user?.user.userId,
isExternalWalletFlow,
user?.user.username,
usdAmount,
paymentDetails,
interactions,
])

useEffect(() => {
if (!transactionForDrawer) return
Expand Down Expand Up @@ -388,12 +423,7 @@ export default function PaymentPage({ recipient, flow = 'request_pay' }: Props)
}
return (
<div className={twMerge('mx-auto h-full w-full space-y-8 self-start')}>
<PublicProfile
username={username}
isVerified={user?.user.kycStatus === 'approved'}
isLoggedIn={!!user}
onSendClick={handleSendClick}
/>
<PublicProfile username={username} isLoggedIn={!!user} onSendClick={handleSendClick} />
</div>
)
}
Expand Down
2 changes: 2 additions & 0 deletions src/app/actions/onramp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface CreateOnrampGuestParams {
amount: string
country: CountryData
userId: string
chargeId?: string
}

/**
Expand Down Expand Up @@ -84,6 +85,7 @@ export async function createOnrampForGuest(
body: JSON.stringify({
amount: params.amount,
userId: params.userId,
chargeId: params.chargeId,
source: {
currency,
paymentRail,
Expand Down
11 changes: 9 additions & 2 deletions src/components/Claim/Claim.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import { useCallback, useContext, useEffect, useMemo, useState } from 'react'

import { fetchTokenDetails, fetchTokenPrice } from '@/app/actions/tokens'
import { StatusType } from '@/components/Global/Badges/StatusBadge'
import { TransactionDetailsReceipt } from '@/components/TransactionDetails/TransactionDetailsDrawer'
import { TransactionDetailsReceipt } from '@/components/TransactionDetails/TransactionDetailsReceipt'
import { TransactionDetails, REWARD_TOKENS } from '@/components/TransactionDetails/transactionTransformer'
import * as consts from '@/constants'
import { tokenSelectorContext } from '@/context'
import { useAuth } from '@/context/authContext'
import { useTransactionDetailsDrawer } from '@/hooks/useTransactionDetailsDrawer'
import { EHistoryEntryType, EHistoryUserRole } from '@/hooks/useTransactionHistory'
import { useUserInteractions } from '@/hooks/useUserInteractions'
import { useWallet } from '@/hooks/wallet/useWallet'
import * as interfaces from '@/interfaces'
import { ESendLinkStatus, sendLinksApi, type ClaimLinkData } from '@/services/sendLinks'
Expand Down Expand Up @@ -62,6 +63,8 @@ export const Claim = ({}) => {
const { address } = useWallet()
const { user, isFetchingUser } = useAuth()
const [isLinkCancelling, setisLinkCancelling] = useState(false)
const senderId = claimLinkData?.sender.userId
const { interactions } = useUserInteractions(senderId ? [senderId] : [])

const transactionForDrawer: TransactionDetails | null = useMemo(() => {
if (!claimLinkData) return null
Expand Down Expand Up @@ -119,10 +122,14 @@ export const Claim = ({}) => {
peanutFeeDetails: {
amountDisplay: '$ 0.00',
},
isVerified: claimLinkData.sender?.kycStatus === 'approved',
haveSentMoneyToUser: claimLinkData.sender?.userId
? interactions[claimLinkData.sender.userId] || false
: false,
}

return details as TransactionDetails
}, [claimLinkData])
}, [claimLinkData, interactions])

const handleOnNext = () => {
if (step.idx === _consts.CLAIM_SCREEN_FLOW.length - 1) return
Expand Down
97 changes: 0 additions & 97 deletions src/components/Global/Badges/AchievementsBadge.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion src/components/Global/DisplayIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const DisplayIcon: React.FC<DisplayIconProps> = ({
// Fallback to AvatarWithBadge
return (
<div className={`${sizeClass} flex items-center justify-center rounded-full bg-gray-200 ${className}`.trim()}>
<AvatarWithBadge name={fallbackName} size={badgeSize} achievementsBadgeSize={badgeSize} />
<AvatarWithBadge name={fallbackName} size={badgeSize} />
</div>
)
}
Expand Down
3 changes: 3 additions & 0 deletions src/components/Global/Icons/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import { PendingIcon } from './pending'
import { ProcessingIcon } from './processing'
import { FailedIcon } from './failed'
import { ChevronDownIcon } from './chevron-down'
import { DoubleCheckIcon } from './double-check'

// available icon names
export type IconName =
Expand All @@ -72,6 +73,7 @@ export type IconName =
| 'check-circle'
| 'cancel'
| 'download'
| 'double-check'
| 'eye'
| 'eye-slash'
| 'exchange'
Expand Down Expand Up @@ -135,6 +137,7 @@ const iconComponents: Record<IconName, ComponentType<SVGProps<SVGSVGElement>>> =
'chevron-up': ChevronUpIcon,
download: DownloadIcon,
dollar: DollarIcon,
'double-check': DoubleCheckIcon,
eye: EyeIcon,
'eye-slash': EyeSlashIcon,
exchange: ExchangeIcon,
Expand Down
10 changes: 10 additions & 0 deletions src/components/Global/Icons/double-check.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { FC, SVGProps } from 'react'

export const DoubleCheckIcon: FC<SVGProps<SVGSVGElement>> = (props) => (
<svg width="15" height="9" viewBox="0 0 15 9" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<path
d="M10.3514 1.12054C10.1078 0.876949 9.71434 0.876949 9.47075 1.12054L5.94811 4.64318L6.82877 5.52384L10.3514 1.99495C10.5888 1.75761 10.5888 1.35788 10.3514 1.12054ZM12.9996 1.11429L6.82877 7.28516L4.65523 5.11786C4.41164 4.87427 4.01815 4.87427 3.77457 5.11786C3.53098 5.36145 3.53098 5.75493 3.77457 5.99852L6.38532 8.60927C6.6289 8.85286 7.02239 8.85286 7.26598 8.60927L13.8803 2.0012C14.1239 1.75761 14.1239 1.36412 13.8803 1.12054H13.8741C13.6367 0.870703 13.2432 0.870703 12.9996 1.11429ZM0.245678 6.00477L2.85643 8.61552C3.10002 8.8591 3.4935 8.8591 3.73709 8.61552L4.1743 8.17831L1.12634 5.11786C0.882752 4.87427 0.489265 4.87427 0.245678 5.11786C0.00209156 5.36145 0.00209156 5.76118 0.245678 6.00477Z"
fill="currentColor"
/>
</svg>
)
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const NetworkButton: React.FC<NetworkButtonProps> = ({
onError={() => setChainImageError(true)}
/>
) : (
<AvatarWithBadge size="extra-small" name={chainName} achievementsBadgeSize="extra-small" />
<AvatarWithBadge size="extra-small" name={chainName} />
)}
</div>
<span className="text-sm font-medium">{isSearch ? 'more' : chainName}</span>
Expand Down
36 changes: 36 additions & 0 deletions src/components/Home/HomeHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { KycStatusItem } from '../Kyc/KycStatusItem'
import { isKycStatusItem, KycHistoryEntry } from '@/hooks/useKycFlow'
import { KYCStatus } from '@/utils'
import { useWallet } from '@/hooks/wallet/useWallet'
import { useUserInteractions } from '@/hooks/useUserInteractions'

/**
* component to display a preview of the most recent transactions on the home page.
Expand Down Expand Up @@ -61,6 +62,26 @@ const HomeHistory = ({ isPublic = false, username }: { isPublic?: boolean; usern
// Combine fetched history with real-time updates
const [combinedEntries, setCombinedEntries] = useState<Array<any>>([])

// get all the user ids from the combined entries to check for interactions
const userIds = useMemo(() => {
if (!combinedEntries.length) return []
return Array.from(
new Set(
combinedEntries
.map((entry) => {
if (isKycStatusItem(entry)) return null
if (entry.userRole === 'SENDER') return entry.recipientAccount.userId
if (entry.userRole === 'RECIPIENT') return entry.senderAccount?.userId
return null
})
.filter((userId) => userId) as string[]
)
)
}, [combinedEntries])

// fetch the interaction status for the user ids
const { interactions } = useUserInteractions(userIds)

useEffect(() => {
if (historyData?.entries) {
// Start with the fetched entries
Expand Down Expand Up @@ -195,6 +216,12 @@ const HomeHistory = ({ isPublic = false, username }: { isPublic?: boolean; usern

// determine card position for styling (first, middle, last, single)
const position = getCardPosition(index, pendingRequests.length)
const haveSentMoneyToUser =
item.userRole === 'SENDER'
? interactions[item.recipientAccount.userId]
: item.senderAccount?.userId
? interactions[item.senderAccount.userId]
: false

return (
<TransactionCard
Expand All @@ -207,6 +234,7 @@ const HomeHistory = ({ isPublic = false, username }: { isPublic?: boolean; usern
transaction={transactionDetails}
position={position}
isPending={true}
haveSentMoneyToUser={haveSentMoneyToUser}
/>
)
})}
Expand Down Expand Up @@ -243,6 +271,13 @@ const HomeHistory = ({ isPublic = false, username }: { isPublic?: boolean; usern

// determine card position for styling (first, middle, last, single)

const haveSentMoneyToUser =
item.userRole === 'SENDER'
? interactions[item.recipientAccount.userId]
: item.senderAccount?.userId
? interactions[item.senderAccount.userId]
: false

return (
<TransactionCard
key={item.uuid}
Expand All @@ -253,6 +288,7 @@ const HomeHistory = ({ isPublic = false, username }: { isPublic?: boolean; usern
initials={transactionDetails.initials}
transaction={transactionDetails}
position={position}
haveSentMoneyToUser={haveSentMoneyToUser}
/>
)
})}
Expand Down
Loading
Loading