-
Notifications
You must be signed in to change notification settings - Fork 13
feat: standalone receipts without drawer + share receipt #885
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
kushagrasarathe
commented
Jun 2, 2025
- contributes to TASK-11685 : standalone receipt page without a drawer for direct send and claimed send links
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
## Walkthrough
This update refactors transaction detail display logic by separating the drawer container and receipt content into distinct components. It updates transaction link generation for direct sends, modifies metadata titles, and replaces usage of the old drawer component with the new receipt component in relevant modules. Internal state and rendering logic are reorganized accordingly.
## Changes
| Files/Groups | Change Summary |
|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| src/app/(mobile-ui)/history/layout.tsx | Changed page metadata title from "Transaction History | Peanut" to "History | Peanut". |
| src/app/[...recipient]/client.tsx, src/components/Claim/Claim.tsx | Replaced `TransactionDetailsDrawer` with `TransactionDetailsReceipt`. Updated logic for resolving usernames, user roles, and drawer state. Adjusted rendering to use the new receipt component and removed old drawer usage. Imports reorganized in Claim.tsx. |
| src/components/TransactionDetails/TransactionDetailsDrawer.tsx | Refactored to separate the drawer and receipt into `TransactionDetailsDrawer` and new `TransactionDetailsReceipt` components. Introduced dynamic height calculation for the drawer, reorganized async actions and conditional rendering, and modularized transaction actions (share, cancel, pay, reject). Added export for `TransactionDetailsReceipt`. |
| src/hooks/useTransactionHistory.ts | Added construction of a `link` URL for `'DIRECT_SEND'` transaction entries, forming a shareable link with recipient and charge ID. No other logic changed. |
## Possibly related PRs
- peanutprotocol/peanut-ui#858: Refactors `TransactionDetailsDrawer` to delegate receipt rendering to `TransactionDetailsReceipt` and adds attachment download link rendering inside the receipt, closely related to the modularization and UI changes in this PR.
## Suggested reviewers
- jjramirezn ✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (1)
src/components/TransactionDetails/TransactionDetailsDrawer.tsx (1)
80-446
: Consider breaking down this large component for better maintainability.The
TransactionDetailsReceipt
component handles many responsibilities. Consider:
- Extracting action handlers (cancel, pay, reject) into separate functions or custom hooks
- Creating sub-components for different transaction states (pending requests, sent links, etc.)
- Moving complex conditional logic to helper functions
This would improve readability and testability.
🧰 Tools
🪛 Biome (1.9.4)
[error] 226-236: Avoid using unnecessary Fragment.
A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment(lint/complexity/noUselessFragments)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/app/(mobile-ui)/history/layout.tsx
(1 hunks)src/app/[...recipient]/client.tsx
(4 hunks)src/components/Claim/Claim.tsx
(2 hunks)src/components/TransactionDetails/TransactionDetailsDrawer.tsx
(5 hunks)src/hooks/useTransactionHistory.ts
(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/hooks/useTransactionHistory.ts (1)
src/constants/general.consts.ts (1)
BASE_URL
(43-43)
🪛 Biome (1.9.4)
src/components/TransactionDetails/TransactionDetailsDrawer.tsx
[error] 226-236: Avoid using unnecessary Fragment.
A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment
(lint/complexity/noUselessFragments)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Deploy-Preview
🔇 Additional comments (10)
src/app/(mobile-ui)/history/layout.tsx (1)
6-6
: LGTM! Clean title simplification.The shortened title "History | Peanut" is more concise while maintaining clarity. This change aligns well with the overall UI improvements in this PR.
src/hooks/useTransactionHistory.ts (1)
170-170
: Excellent consistency with existing link generation pattern.The link generation for
DIRECT_SEND
transactions follows the exact same pattern asREQUEST
transactions (line 165), using proper fallback logic and consistent URL structure. This supports the PR's goal of enabling shareable direct send links.src/components/Claim/Claim.tsx (2)
3-22
: Clean import reorganization.The import consolidation removes duplicates and groups related imports together, improving code organization.
278-278
: Proper component replacement aligns with architectural refactor.The replacement of
TransactionDetailsDrawer
withTransactionDetailsReceipt
correctly implements the separation of drawer container from receipt content. The new component receives the transaction data directly without needing drawer-specific props.src/app/[...recipient]/client.tsx (4)
11-11
: Import updated to match architectural refactor.Correctly imports
TransactionDetailsReceipt
instead ofTransactionDetailsDrawer
, aligning with the separation of drawer container and receipt content.
258-262
: Robust username resolution with proper fallbacks.The enhanced fallback chain ensures username resolution works correctly:
- First tries
recipientAccount.user.username
- Falls back to
recipientAccount.identifier
- Finally uses
chargeDetails.requestLink.recipientAddress
The
originalUserRole
logic correctly determines user role based on whether current user is the recipient.
274-276
: Improved specificity for link transaction detection.The updated
isLinkTransaction
logic is more precise - it only sets totrue
when the original user role is sender AND the current user matches. This prevents incorrect link transaction flags and provides better context for the receipt component.
402-403
: Clean conditional rendering with new receipt component.The replacement of
TransactionDetailsDrawer
withTransactionDetailsReceipt
in the conditional rendering maintains the same logic flow while using the new modular component architecture. The transaction data is passed directly as a prop.src/components/TransactionDetails/TransactionDetailsDrawer.tsx (2)
35-78
: Well-structured drawer container component.The separation of drawer container logic from receipt content is a good architectural decision. The dynamic height calculation with constraints provides a better user experience.
80-90
: Excellent refactoring to support standalone receipts.The separation of
TransactionDetailsReceipt
from the drawer container successfully achieves the PR objective of enabling standalone receipt display. The component can now be used independently while maintaining backward compatibility through the optional props.
sendLinksApi | ||
.claim(user!.user.username!, transaction.extraDataForDrawer!.link!) | ||
.then(() => { | ||
// Claiming takes time, so we need to invalidate both transaction query types | ||
setTimeout(() => { | ||
fetchBalance() | ||
queryClient | ||
.invalidateQueries({ | ||
queryKey: [TRANSACTIONS], | ||
}) | ||
.then(() => { | ||
setIsLoading(false) | ||
onClose() | ||
}) | ||
}, 3000) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Replace setTimeout with proper async handling.
Using a fixed 3-second delay is unreliable for waiting on async operations. Consider implementing proper promise chaining or polling to check when the claim operation is actually complete.
- sendLinksApi
- .claim(user!.user.username!, transaction.extraDataForDrawer!.link!)
- .then(() => {
- // Claiming takes time, so we need to invalidate both transaction query types
- setTimeout(() => {
- fetchBalance()
- queryClient
- .invalidateQueries({
- queryKey: [TRANSACTIONS],
- })
- .then(() => {
- setIsLoading(false)
- onClose()
- })
- }, 3000)
- })
+ sendLinksApi
+ .claim(user!.user.username!, transaction.extraDataForDrawer!.link!)
+ .then(async () => {
+ // Wait for claim to be processed
+ await fetchBalance()
+ await queryClient.invalidateQueries({
+ queryKey: [TRANSACTIONS],
+ })
+ setIsLoading(false)
+ onClose()
+ })
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
sendLinksApi | |
.claim(user!.user.username!, transaction.extraDataForDrawer!.link!) | |
.then(() => { | |
// Claiming takes time, so we need to invalidate both transaction query types | |
setTimeout(() => { | |
fetchBalance() | |
queryClient | |
.invalidateQueries({ | |
queryKey: [TRANSACTIONS], | |
}) | |
.then(() => { | |
setIsLoading(false) | |
onClose() | |
}) | |
}, 3000) | |
sendLinksApi | |
.claim(user!.user.username!, transaction.extraDataForDrawer!.link!) | |
.then(async () => { | |
// Wait for claim to be processed | |
await fetchBalance() | |
await queryClient.invalidateQueries({ | |
queryKey: [TRANSACTIONS], | |
}) | |
setIsLoading(false) | |
onClose() | |
}) |
🤖 Prompt for AI Agents
In src/components/TransactionDetails/TransactionDetailsDrawer.tsx around lines
309 to 323, the code uses a fixed 3-second setTimeout to wait after the claim
API call, which is unreliable. Replace the setTimeout with proper async handling
by chaining promises or awaiting the completion of the claim operation and
subsequent fetchBalance and queryClient.invalidateQueries calls. Ensure that
setIsLoading(false) and onClose() are only called after all async operations
have fully completed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
src/components/TransactionDetails/TransactionDetailsDrawer.tsx (2)
225-235
: Remove unnecessary Fragment wrapper.This is a duplicate of a previous review comment. The Fragment is redundant as it contains only one conditional block.
- <> - {transaction.cancelledDate && ( - <PaymentInfoRow - label="Cancelled" - value={formatDate(transaction.cancelledDate as Date)} - hideBottomBorder={ - !transaction.fee && !transaction.memo && !transaction.attachmentUrl - } - /> - )} - </> + {transaction.cancelledDate && ( + <PaymentInfoRow + label="Cancelled" + value={formatDate(transaction.cancelledDate as Date)} + hideBottomBorder={ + !transaction.fee && !transaction.memo && !transaction.attachmentUrl + } + /> + )}🧰 Tools
🪛 Biome (1.9.4)
[error] 225-235: Avoid using unnecessary Fragment.
A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment(lint/complexity/noUselessFragments)
308-328
: Replace setTimeout with proper async handling.This is a duplicate of a previous review comment. Using a fixed 3-second delay is unreliable for waiting on async operations. The claim operation should use proper promise chaining.
- .then(() => { - // Claiming takes time, so we need to invalidate both transaction query types - setTimeout(() => { - fetchBalance() - queryClient - .invalidateQueries({ - queryKey: [TRANSACTIONS], - }) - .then(() => { - setIsLoading(false) - onClose() - }) - }, 3000) - }) + .then(async () => { + // Wait for claim to be processed + await fetchBalance() + await queryClient.invalidateQueries({ + queryKey: [TRANSACTIONS], + }) + setIsLoading(false) + onClose() + })
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/TransactionDetails/TransactionDetailsDrawer.tsx
(5 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
src/components/TransactionDetails/TransactionDetailsDrawer.tsx
[error] 225-235: Avoid using unnecessary Fragment.
A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment
(lint/complexity/noUselessFragments)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Deploy-Preview
🔇 Additional comments (5)
src/components/TransactionDetails/TransactionDetailsDrawer.tsx (5)
38-55
: LGTM: Clean refactor with proper state management.The introduction of internal loading state management and the memoized close handler improves the component's encapsulation and performance. The dynamic height calculation using the custom hook is a good approach for responsive UI.
70-76
: Good separation of concerns.Delegating the content rendering to
TransactionDetailsReceipt
while keeping the drawer container logic separate is excellent architectural design. This makes the components more reusable and testable.
386-429
: LGTM: Comprehensive requestee action handling.The conditional rendering for pay and reject buttons when the user is a pending requestee is well-implemented. The pay action correctly redirects to the transaction link, and the reject action follows the proper async pattern.
303-304
: Good defensive programming with required props.The conditional checks for
setIsLoading && onClose
before rendering action buttons ensure the component gracefully handles cases where these props are not provided. This makes the component more robust when used in different contexts.Also applies to: 353-353, 386-386
123-128
:❓ Verification inconclusive
Verify the share receipt logic.
The
shouldShowShareReceipt
logic excludes pending transactions and specific directions, but includes transactions withtxHash
and non-receive/non-request_sent directions. Please verify this logic aligns with the business requirements for when users should be able to share receipts.
🏁 Script executed:
#!/bin/bash # Description: Search for other usages of share receipt functionality to understand the expected behavior # Expected: Find patterns or documentation about when receipts should be shareable # Search for share receipt related code rg -A 3 -B 3 "share.*receipt|receipt.*share" --type tsx --type ts # Search for transaction direction patterns rg -A 2 -B 2 "direction.*receive|direction.*request_sent" --type tsx --type tsLength of output: 204
🏁 Script executed:
#!/bin/bash # Revised search using file globs for .tsx and .ts to avoid unrecognized file type errors # 1. Search for share receipt related code snippets across the codebase rg -g '*.tsx' -g '*.ts' -A3 -B3 "share.*receipt|receipt.*share" # 2. Search for transaction direction checks to see how 'receive' and 'request_sent' are used elsewhere rg -g '*.tsx' -g '*.ts' -A2 -B2 "direction.*receive|direction.*request_sent" # 3. Locate where shouldShowShareReceipt is referenced in TransactionDetailsDrawer rg "shouldShowShareReceipt" -n src/components/TransactionDetails/TransactionDetailsDrawer.tsxLength of output: 3719
Please confirm share-receipt eligibility against business requirements
I reviewed the code and found thatshouldShowShareReceipt
only returnstrue
when:
transaction.txHash
is presenttransaction.direction
is neither'receive'
nor'request_sent'
- no pending states (
isPendingSentLink
,isPendingRequester
,isPendingRequestee
)No other references to “receipt” or “share receipt” were found in the codebase.
• File:src/components/TransactionDetails/TransactionDetailsDrawer.tsx
(lines 123–128)
• UI hook:{shouldShowShareReceipt && transaction.extraDataForDrawer?.link && ( … )}
Since there’s no in-code documentation or usage examples, please verify this matches the product spec for when users should be able to share receipts.
<Button | ||
icon="cancel" | ||
iconContainerClassName="border border-black w-4 h-4 mr-1 rounded-full" | ||
iconClassName="p-1" | ||
onClick={() => { | ||
setIsLoading(true) | ||
chargesApi | ||
.cancel(transaction.id) | ||
.then(() => { | ||
queryClient | ||
.invalidateQueries({ | ||
queryKey: [TRANSACTIONS], | ||
}) | ||
.then(() => { | ||
setIsLoading(false) | ||
onClose() | ||
}) | ||
}) | ||
.catch((error) => { | ||
captureException(error) | ||
console.error('Error canceling charge:', error) | ||
setIsLoading(false) | ||
}) | ||
}} | ||
variant={'primary-soft'} | ||
shadowSize="4" | ||
className="flex w-full items-center gap-1" | ||
> | ||
Cancel request | ||
</Button> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Inconsistent async handling pattern.
The cancel request button properly chains promises without setTimeout, but the cancel link button (lines 308-328) uses setTimeout. Consider unifying the async handling approach for consistency.
The current implementation correctly handles promise chaining:
.then(() => {
queryClient
.invalidateQueries({
queryKey: [TRANSACTIONS],
})
.then(() => {
setIsLoading(false)
onClose()
})
})
This pattern should be applied to the cancel link functionality as well.
🤖 Prompt for AI Agents
In src/components/TransactionDetails/TransactionDetailsDrawer.tsx around lines
308 to 328, the cancel link button uses setTimeout for async handling, which is
inconsistent with the cancel request button's proper promise chaining seen in
lines 354 to 383. Refactor the cancel link button's async code to remove
setTimeout and instead chain promises directly, following the pattern used in
the cancel request button: call chargesApi.cancel, then invalidate queries with
queryClient.invalidateQueries, and finally update loading state and close the
drawer in the then callbacks.