[TASK-15701] Fix: guest invite link flow#1311
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds pre-claim and pre-payment invite-accept flows that refresh user state, tightens and centralizes redirect validation/handling (new useLogin hook and getValidRedirectUrl), hardens string trimming, narrows waitlist gating on public paths, and updates a few UI/modal behaviors and invite navigation. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/components/Claim/Link/Initial.view.tsx (1)
169-187: Consider extracting invite acceptance logic to reduce duplication.The pre-claim invite acceptance logic is correctly implemented with proper error handling and background user refresh. However, this logic is very similar to the
handleAcceptInvitefunction insrc/components/Payment/PaymentForm/index.tsx(lines 322-337). Both construct the invite code, callinvitesApi.acceptInvitewithEInviteType.PAYMENT_LINK, triggerfetchUser()in the background, and handle errors similarly.Consider extracting this logic into a shared utility function or custom hook (e.g.,
useInviteAcceptance) to eliminate duplication and ensure consistent invite handling across both flows:// hooks/useInviteAcceptance.ts export const useInviteAcceptance = () => { const { fetchUser } = useAuth() const acceptInvite = async (username: string): Promise<{ success: boolean; error?: string }> => { try { const inviteCode = `${username}INVITESYOU` await invitesApi.acceptInvite(inviteCode, EInviteType.PAYMENT_LINK) fetchUser() // Background refresh return { success: true } } catch (error) { Sentry.captureException(error) console.error('Failed to accept invite', error) return { success: false, error: 'Something went wrong. Please try again or contact support.' } } } return { acceptInvite } }Then use it in both components:
const { acceptInvite } = useInviteAcceptance() // In claim flow if (!user?.user.hasAppAccess) { const result = await acceptInvite(claimLinkData.sender.username) if (!result.success) { setErrorState({ showError: true, errorMessage: result.error! }) setLoadingState('Idle') return } } // In payment flow const result = await acceptInvite(recipient?.identifier) if (!result.success) { setInviteError(true) return }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/app/(mobile-ui)/layout.tsx(1 hunks)src/components/Claim/Link/Initial.view.tsx(3 hunks)src/components/Common/ActionList.tsx(1 hunks)src/components/Invites/InvitesPage.tsx(2 hunks)src/components/Payment/PaymentForm/index.tsx(6 hunks)src/hooks/useZeroDev.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-29T18:34:33.596Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1251
File: src/components/Invites/JoinWaitlistPage.tsx:41-55
Timestamp: 2025-09-29T18:34:33.596Z
Learning: In the JoinWaitlistPage component, after successfully accepting an invite via invitesApi.acceptInvite(), calling fetchUser() is sufficient to update the user state and automatically display the app. No manual navigation to /home or other pages is required since the user is already on the home page and the app will be displayed once user.hasAppAccess is updated.
Applied to files:
src/app/(mobile-ui)/layout.tsxsrc/components/Claim/Link/Initial.view.tsx
🧬 Code graph analysis (2)
src/components/Claim/Link/Initial.view.tsx (3)
src/context/authContext.tsx (1)
useAuth(191-197)src/services/invites.ts (1)
invitesApi(7-83)src/redux/slices/send-flow-slice.ts (1)
setErrorState(89-91)
src/components/Payment/PaymentForm/index.tsx (2)
src/context/authContext.tsx (1)
useAuth(191-197)src/services/invites.ts (1)
invitesApi(7-83)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Deploy-Preview
🔇 Additional comments (6)
src/components/Invites/InvitesPage.tsx (1)
21-21: LGTM!The redirect_uri extraction and conditional navigation logic is implemented correctly. The URI is properly encoded before being passed to the setup page, which prevents injection issues and ensures the redirect context is preserved through the signup flow.
Also applies to: 42-47
src/components/Common/ActionList.tsx (1)
207-207: LGTM!The updated navigation correctly constructs the invite URL with both the invite code and the redirect_uri. This ensures that users who accept invites through payment links can return to the original flow context after signup.
src/hooks/useZeroDev.ts (1)
64-64: LGTM!The added safety guard prevents potential runtime errors when
userInviteCodeisnullorundefined. This defensive check ensurestrim()is only called on valid string values.src/app/(mobile-ui)/layout.tsx (1)
96-96: LGTM!The added
!isPublicPathcondition correctly prevents the waitlist page from being shown on public paths like claim and invite pages. This ensures that users can access these public flows even without app access, which is essential for the guest invite functionality.Based on learnings.
src/components/Payment/PaymentForm/index.tsx (2)
96-97: LGTM with a note on duplication.The invite acceptance flow is correctly integrated into the payment process with proper error handling and state management. The logic accepts the invite before proceeding to add money when the user lacks app access, and
fetchUser()is called in the background to refresh user state.Note: As mentioned in the review for
src/components/Claim/Link/Initial.view.tsx, consider extracting the invite acceptance logic into a shared utility to reduce duplication between this component and the claim flow component.Also applies to: 322-337, 340-348
687-687: LGTM!The button loading state correctly reflects both
isAcceptingInviteandisProcessing, providing accurate feedback during invite acceptance and payment processing.
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/components/Invites/InvitesPage.tsx(3 hunks)src/components/Setup/Views/JoinWaitlist.tsx(3 hunks)src/hooks/useLogin.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/components/Invites/InvitesPage.tsx (2)
src/context/authContext.tsx (1)
useAuth(191-197)src/hooks/useLogin.tsx (1)
useLogin(7-40)
src/hooks/useLogin.tsx (3)
src/context/authContext.tsx (1)
useAuth(191-197)src/hooks/useZeroDev.ts (1)
useZeroDev(36-187)src/utils/general.utils.ts (2)
getFromLocalStorage(112-134)sanitizeRedirectURL(1217-1229)
src/components/Setup/Views/JoinWaitlist.tsx (1)
src/hooks/useLogin.tsx (1)
useLogin(7-40)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Deploy-Preview
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/Profile/index.tsx (1)
21-21: Unused state:isKycApprovedModalOpenis no longer set.With the removal of the conditional logic at line 78, this state variable is never set to
true, making it and the associated modal (lines 134-148) effectively dead code.If the conditional logic is restored, this state will be used again. Otherwise, consider removing both the state and the unused modal.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/components/Common/ActionList.tsx(2 hunks)src/components/Profile/index.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/Common/ActionList.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Deploy-Preview
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/components/Global/GuestLoginCta/index.tsx(1 hunks)src/components/Setup/Views/SetupPasskey.tsx(1 hunks)src/components/Setup/Views/Welcome.tsx(1 hunks)src/hooks/useLogin.tsx(1 hunks)src/utils/general.utils.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/components/Setup/Views/Welcome.tsx (1)
src/utils/general.utils.ts (1)
sanitizeRedirectURL(1217-1237)
src/hooks/useLogin.tsx (3)
src/context/authContext.tsx (1)
useAuth(191-197)src/hooks/useZeroDev.ts (1)
useZeroDev(36-187)src/utils/general.utils.ts (2)
getFromLocalStorage(112-134)sanitizeRedirectURL(1217-1237)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Deploy-Preview
🔇 Additional comments (5)
src/hooks/useLogin.tsx (1)
18-30: Open redirect vulnerability has been addressed.The guarded redirect handling now properly validates
redirect_uriviasanitizeRedirectURLand only redirects when the result is truthy. This successfully blocks external redirects as previously flagged.src/components/Setup/Views/Welcome.tsx (1)
27-33: LGTM!The guarded redirect properly validates
redirect_uriviasanitizeRedirectURLand only routes when the result is truthy, falling back to/homefor invalid or external URLs.src/components/Global/GuestLoginCta/index.tsx (1)
46-50: LGTM!The guarded redirect correctly validates the
redirect_uriviasanitizeRedirectURLand only navigates when the result is truthy. Invalid redirects keep the user on the current page, which is appropriate behavior for this context.src/components/Setup/Views/SetupPasskey.tsx (1)
51-57: LGTM!The guarded redirect with early return is correct. It validates
redirect_uriviasanitizeRedirectURL, routes when valid, and falls through to the localStorage-based redirect logic when invalid. This pattern fits well with the subsequent redirect handling below.src/utils/general.utils.ts (1)
1217-1237: LGTM! Secure redirect sanitization.The updated
sanitizeRedirectURLfunction properly guards against open redirects by:
- Returning
string | nullinstead of always returning a string- Rejecting off-origin URLs (line 1225)
- Rejecting protocol-relative URLs like
//evil.com(line 1228)- Rejecting any relative path containing a protocol (line 1230)
This implementation successfully addresses the security concerns around redirect handling across the application.
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (1)
src/hooks/useLogin.tsx (1)
37-40: Move localStorage removal after successful validation.Line 38 removes the stored redirect before validating it at line 39. If
getValidRedirectUrlreturns the fallback (meaning the stored URL was invalid), you've discarded the stored value without successfully using it.Apply this diff to only remove the item when successfully using it:
} else if (localStorageRedirect) { - localStorage.removeItem('redirect') const validRedirectUrl = getValidRedirectUrl(String(localStorageRedirect), '/home') - router.push(validRedirectUrl) + if (validRedirectUrl !== '/home') { + localStorage.removeItem('redirect') + router.push(validRedirectUrl) + } else { + // Invalid redirect, keep it and go to home + router.push('/home') + }Based on past review comments.
🧹 Nitpick comments (4)
src/utils/general.utils.ts (1)
1217-1238: Consider usingconsole.warnfor rejected URLs.Line 1224 uses
console.logto record off-origin URL rejections. For better visibility in production logs and to align with Hugo0's previous suggestion, consider usingconsole.warnorSentry.captureMessageinstead.Apply this diff:
- console.log('Rejecting off-origin URL:', redirectUrl) + console.warn('Rejecting off-origin URL:', redirectUrl)src/hooks/useLogin.tsx (2)
47-49: Add error handling to login flow.The
handleLoginClickfunction doesn't catch errors fromhandleLogin(). If passkey login fails (e.g., user cancels, no passkey found), the error will bubble up uncaught, potentially breaking the component.Apply this diff to add error handling:
const handleLoginClick = async () => { - await handleLogin() + try { + await handleLogin() + } catch (error) { + // Error already handled by useZeroDev, but we should re-throw + // so calling code can display appropriate UI feedback + throw error + } }Or alternatively, if you want to handle errors locally:
const handleLoginClick = async () => { try { await handleLogin() } catch (error) { console.error('Login error:', error) // Optionally show user feedback here } }
30-45: LimituseEffectdependencies toredirect_uri(avoid unintended re-triggers)
- Extract
redirect_urioutside the effect and list only it (rather thansearchParams) in the dependency array- Or guard the redirect logic with a ref flag so it only runs once after login
src/components/Global/EarlyUserModal/index.tsx (1)
42-47: Remove commented-out code.The commented block contains the old content structure and should be removed to reduce clutter, as the new consolidated message on lines 39-40 replaces it.
Apply this diff:
- {/* <span className="mt-2 block"> - <b>Friends you invite: </b> you earn a share of their fees. - </span> - <span className="block"> - <b> Their invites: </b> you earn a smaller share, too. - </span> */}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
src/components/Global/EarlyUserModal/index.tsx(2 hunks)src/components/Global/GuestLoginCta/index.tsx(1 hunks)src/components/Payment/PaymentForm/index.tsx(6 hunks)src/components/Setup/Views/SetupPasskey.tsx(3 hunks)src/hooks/useLogin.tsx(1 hunks)src/hooks/useZeroDev.ts(1 hunks)src/utils/general.utils.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/hooks/useZeroDev.ts
- src/components/Setup/Views/SetupPasskey.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-10-24T12:45:22.708Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#478
File: src/components/Request/Create/Views/Initial.view.tsx:169-176
Timestamp: 2024-10-24T12:45:22.708Z
Learning: When calling `handleOnNext` in `src/components/Request/Create/Views/Initial.view.tsx`, it's acceptable to duplicate parameter lists for readability instead of refactoring to avoid duplication.
Applied to files:
src/components/Payment/PaymentForm/index.tsx
🧬 Code graph analysis (2)
src/hooks/useLogin.tsx (3)
src/context/authContext.tsx (1)
useAuth(191-197)src/hooks/useZeroDev.ts (1)
useZeroDev(36-187)src/utils/general.utils.ts (2)
getFromLocalStorage(112-134)getValidRedirectUrl(1342-1358)
src/components/Payment/PaymentForm/index.tsx (2)
src/context/authContext.tsx (1)
useAuth(191-197)src/services/invites.ts (1)
invitesApi(7-83)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Deploy-Preview
🔇 Additional comments (2)
src/utils/general.utils.ts (1)
1342-1358: LGTM! Good abstraction for redirect validation.The
getValidRedirectUrlhelper consolidates decoding, sanitization, and fallback logic into a single, reusable function. The error handling for URI decoding and the fallback pattern are appropriate.src/hooks/useLogin.tsx (1)
7-21: LGTM! Clear documentation.The JSDoc provides good context for the hook's purpose and behavior. This addresses Hugo0's previous request for file-level documentation.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/Claim/Link/Initial.view.tsx (1)
161-168: Prevent stuck loading on early returnsetLoadingState('Loading') is set on Line 161, but if recipient.address === '' you return without resetting it.
Apply:
- if (recipient.address === '') return + if (recipient.address === '') { + setLoadingState('Idle') + return + }
♻️ Duplicate comments (4)
src/components/Global/GuestLoginCta/index.tsx (1)
51-51: Wrap the message in an Error object for proper Sentry stack traces.While the
Sentry.captureExceptioncall was correctly moved to theelseblock (addressing the false-positive issue from the previous review), the message is still passed as a string rather than anErrorobject. This degrades the quality of error reports in Sentry by omitting stack trace information.Apply this diff to complete the fix:
- Sentry.captureException(`Invalid redirect URL ${redirect_uri}`) + Sentry.captureException(new Error(`Invalid redirect URL: ${redirect_uri}`))src/components/Claim/Link/Initial.view.tsx (1)
169-196: Avoid duplicating invite-accept logic across flowsThis logic mirrors PaymentForm’s invite pre-accept. Extract a small shared helper/hook (e.g., useInviteAcceptance) to keep behavior consistent and reduce future fixes in multiple places.
src/components/Payment/PaymentForm/index.tsx (2)
322-322: DRY: centralize invite acceptance logicThis duplicates the pre-accept logic used in the claim flow. Extract a small helper/hook to keep behavior consistent and reduce future fixes in multiple places.
322-344: Return a boolean from handleAcceptInvite; reset state in finally; don’t block on fetchUser
- Caller can’t know if acceptance failed; it always continues.
- Clear inviteError on success; set isAcceptingInvite in finally.
- Guard missing username.
- Keep fetchUser non-blocking to avoid UI stall.
Apply:
- const handleAcceptInvite = async () => { - try { - setIsAcceptingInvite(true) - const inviteCode = `${recipient?.identifier}INVITESYOU` - const result = await invitesApi.acceptInvite(inviteCode, EInviteType.PAYMENT_LINK) - - if (!result.success) { - console.error('Failed to accept invite') - setInviteError(true) - return false - } - - // fetch user so that we have the latest state and user can access the app. - // We dont need to wait for this, can happen in background. - await fetchUser() - setIsAcceptingInvite(false) - } catch (error) { - console.error('Failed to accept invite', error) - setInviteError(true) - setIsAcceptingInvite(false) - return - } - } + const handleAcceptInvite = async (): Promise<boolean> => { + setIsAcceptingInvite(true) + try { + if (recipient?.recipientType !== 'USERNAME' || !recipient?.identifier) { + return true + } + const inviteCode = `${recipient.identifier}INVITESYOU` + const { success } = await invitesApi.acceptInvite(inviteCode, EInviteType.PAYMENT_LINK) + if (!success) { + console.error('Failed to accept invite') + setInviteError(true) + return false + } + setInviteError(false) + void fetchUser() + return true + } catch (error) { + console.error('Failed to accept invite', error) + setInviteError(true) + return false + } finally { + setIsAcceptingInvite(false) + } + }Based on learnings
🧹 Nitpick comments (2)
src/components/Global/GuestLoginCta/index.tsx (1)
46-52: ReplacesanitizeRedirectURLwithgetValidRedirectUrl
Useconst validRedirectUrl = getValidRedirectUrl(redirect_uri, '/home')androuter.push(validRedirectUrl)to centralize validation and fallback. If remaining on-page is intentional, add a comment explaining why this flow differs.src/components/Payment/PaymentForm/index.tsx (1)
439-458: Stabilize handleAcceptInvite to avoid stale closures (optional)Wrap handleAcceptInvite in useCallback with [recipient?.identifier, recipient?.recipientType, fetchUser] and keep it in the dependency list. This avoids unnecessary re-creations and subtle stale state.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/components/Claim/Link/Initial.view.tsx(3 hunks)src/components/Global/EarlyUserModal/index.tsx(1 hunks)src/components/Global/GuestLoginCta/index.tsx(1 hunks)src/components/Payment/PaymentForm/index.tsx(7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/Global/EarlyUserModal/index.tsx
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-09-29T18:34:33.596Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1251
File: src/components/Invites/JoinWaitlistPage.tsx:41-55
Timestamp: 2025-09-29T18:34:33.596Z
Learning: In the JoinWaitlistPage component, after successfully accepting an invite via invitesApi.acceptInvite(), calling fetchUser() is sufficient to update the user state and automatically display the app. No manual navigation to /home or other pages is required since the user is already on the home page and the app will be displayed once user.hasAppAccess is updated.
Applied to files:
src/components/Claim/Link/Initial.view.tsx
📚 Learning: 2024-10-24T12:45:22.708Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#478
File: src/components/Request/Create/Views/Initial.view.tsx:169-176
Timestamp: 2024-10-24T12:45:22.708Z
Learning: When calling `handleOnNext` in `src/components/Request/Create/Views/Initial.view.tsx`, it's acceptable to duplicate parameter lists for readability instead of refactoring to avoid duplication.
Applied to files:
src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2025-04-29T19:36:38.121Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#823
File: src/context/kernelClient.context.tsx:79-86
Timestamp: 2025-04-29T19:36:38.121Z
Learning: When Hugo0 asks to "resolve coderabbit comments", they want to acknowledge the comment without necessarily implementing the suggested changes, as the current implementation might be intentional for their specific use case.
Applied to files:
src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2024-11-18T21:36:11.486Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.
Applied to files:
src/components/Payment/PaymentForm/index.tsx
🧬 Code graph analysis (2)
src/components/Claim/Link/Initial.view.tsx (3)
src/context/authContext.tsx (1)
useAuth(191-197)src/services/invites.ts (1)
invitesApi(7-83)src/redux/slices/send-flow-slice.ts (1)
setErrorState(89-91)
src/components/Payment/PaymentForm/index.tsx (2)
src/context/authContext.tsx (1)
useAuth(191-197)src/services/invites.ts (1)
invitesApi(7-83)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Deploy-Preview
🔇 Additional comments (2)
src/components/Global/GuestLoginCta/index.tsx (1)
37-39: Verify the guard clause behavior when user already has a passkey.The guard clause prevents login attempts when
passkeyAddressalready exists, and the button is disabled in this state (line 69). However, the component continues to render the login CTA even when the user is already authenticated. TheuseEffecton lines 24-28 only logs this condition without taking further action.Confirm this is the intended behavior. If users with an existing passkey should not see this component at all, consider either:
- Conditionally rendering the entire component based on authentication state in the parent, or
- Adding an auto-redirect in the
useEffectto move authenticated users to an appropriate page (e.g.,/home).If showing a disabled login button to authenticated users is intentional (e.g., for consistent UI layout), document this design decision with a comment.
src/components/Payment/PaymentForm/index.tsx (1)
36-37: LGTM on imports, auth usage, and button loading
- invitesApi/EInviteType imports correct.
- useAuth now returns fetchUser; usage consistent.
- Button shows loading during invite acceptance.
Also applies to: 71-71, 696-696
…r missing inviter username
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/components/Claim/Link/Initial.view.tsx (1)
169-205: Don’t treat “missing user” as “no app access”
if (!user?.user.hasAppAccess)also matches whenuserisundefined, so guests try to accept the invite without credentials, hit the invite API with no JWT, and the flow aborts with the generic error. Align with the other call sites and gate onuser?.user?.hasAppAccess === falseso we only attempt acceptance once we actually have a user object.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/components/Claim/Link/Initial.view.tsx(3 hunks)src/components/Payment/PaymentForm/index.tsx(7 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-09-29T18:34:33.596Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1251
File: src/components/Invites/JoinWaitlistPage.tsx:41-55
Timestamp: 2025-09-29T18:34:33.596Z
Learning: In the JoinWaitlistPage component, after successfully accepting an invite via invitesApi.acceptInvite(), calling fetchUser() is sufficient to update the user state and automatically display the app. No manual navigation to /home or other pages is required since the user is already on the home page and the app will be displayed once user.hasAppAccess is updated.
Applied to files:
src/components/Claim/Link/Initial.view.tsx
📚 Learning: 2024-10-24T12:45:22.708Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#478
File: src/components/Request/Create/Views/Initial.view.tsx:169-176
Timestamp: 2024-10-24T12:45:22.708Z
Learning: When calling `handleOnNext` in `src/components/Request/Create/Views/Initial.view.tsx`, it's acceptable to duplicate parameter lists for readability instead of refactoring to avoid duplication.
Applied to files:
src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2025-04-29T19:36:38.121Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#823
File: src/context/kernelClient.context.tsx:79-86
Timestamp: 2025-04-29T19:36:38.121Z
Learning: When Hugo0 asks to "resolve coderabbit comments", they want to acknowledge the comment without necessarily implementing the suggested changes, as the current implementation might be intentional for their specific use case.
Applied to files:
src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2024-11-18T21:36:11.486Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.
Applied to files:
src/components/Payment/PaymentForm/index.tsx
🧬 Code graph analysis (2)
src/components/Claim/Link/Initial.view.tsx (2)
src/context/authContext.tsx (1)
useAuth(191-197)src/services/invites.ts (1)
invitesApi(7-83)
src/components/Payment/PaymentForm/index.tsx (2)
src/context/authContext.tsx (1)
useAuth(191-197)src/services/invites.ts (1)
invitesApi(7-83)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Deploy-Preview
…invite acceptance state in PaymentForm
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/Claim/Link/Initial.view.tsx (1)
161-169: Prevent spinner lock when recipient is emptyYou set Loading and then return early on empty recipient, leaving the view stuck. Reset loading (or guard before setting it).
Apply:
- setLoadingState('Loading') + setLoadingState('Loading') @@ - if (recipient.address === '') return + if (recipient.address === '') { + setLoadingState('Idle') + return + }
♻️ Duplicate comments (2)
src/components/Payment/PaymentForm/index.tsx (1)
349-352: Let handleAcceptInvite own inviteError reset; drop manual clearing hereRedundant clearing risks masking state and isn’t needed if success path resets inviteError.
Apply:
- // clear invite error - if (inviteError) { - setInviteError(false) - }src/components/Claim/Link/Initial.view.tsx (1)
169-196: Harden invite-accept: strict guard, Sentry on failure, and non-blocking refresh
- Use user?.user?.hasAppAccess === false (don’t treat undefined as false).
- Capture unsuccessful accept to Sentry.
- Explicitly background refresh via void fetchUser().
Apply:
- // If the user doesn't have app access, accept the invite before claiming the link - if (!user?.user.hasAppAccess) { + // If the user doesn't have app access, accept the invite before claiming the link + if (user?.user?.hasAppAccess === false) { @@ - const inviteCode = `${inviterUsername}INVITESYOU` - const result = await invitesApi.acceptInvite(inviteCode, EInviteType.PAYMENT_LINK) - if (!result.success) { - console.error('Failed to accept invite') + const inviteCode = `${inviterUsername}INVITESYOU` + const { success } = await invitesApi.acceptInvite(inviteCode, EInviteType.PAYMENT_LINK) + if (!success) { + Sentry.captureMessage('Invite acceptance unsuccessful', { level: 'warning' }) setErrorState({ showError: true, errorMessage: 'Something went wrong. Please try again or contact support.', }) setLoadingState('Idle') return } @@ - // fetch user so that we have the latest state and user can access the app. - // We dont need to wait for this, can happen in background. - fetchUser() + // Refresh user in background + void fetchUser()Based on learnings
🧹 Nitpick comments (1)
src/components/Payment/PaymentForm/index.tsx (1)
355-358: Use strict hasAppAccess check to avoid unintended invite attemptsAvoid treating undefined as false.
Apply:
- if (recipient.recipientType === 'USERNAME' && !user?.user.hasAppAccess) { + if (recipient.recipientType === 'USERNAME' && user?.user?.hasAppAccess === false) {Based on learnings
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/components/Claim/Link/Initial.view.tsx(3 hunks)src/components/Payment/PaymentForm/index.tsx(7 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2024-10-24T12:45:22.708Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#478
File: src/components/Request/Create/Views/Initial.view.tsx:169-176
Timestamp: 2024-10-24T12:45:22.708Z
Learning: When calling `handleOnNext` in `src/components/Request/Create/Views/Initial.view.tsx`, it's acceptable to duplicate parameter lists for readability instead of refactoring to avoid duplication.
Applied to files:
src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2025-04-29T19:36:38.121Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#823
File: src/context/kernelClient.context.tsx:79-86
Timestamp: 2025-04-29T19:36:38.121Z
Learning: When Hugo0 asks to "resolve coderabbit comments", they want to acknowledge the comment without necessarily implementing the suggested changes, as the current implementation might be intentional for their specific use case.
Applied to files:
src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2024-11-18T21:36:11.486Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.
Applied to files:
src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2025-09-29T18:34:33.596Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1251
File: src/components/Invites/JoinWaitlistPage.tsx:41-55
Timestamp: 2025-09-29T18:34:33.596Z
Learning: In the JoinWaitlistPage component, after successfully accepting an invite via invitesApi.acceptInvite(), calling fetchUser() is sufficient to update the user state and automatically display the app. No manual navigation to /home or other pages is required since the user is already on the home page and the app will be displayed once user.hasAppAccess is updated.
Applied to files:
src/components/Claim/Link/Initial.view.tsx
🧬 Code graph analysis (2)
src/components/Payment/PaymentForm/index.tsx (2)
src/context/authContext.tsx (1)
useAuth(191-197)src/services/invites.ts (1)
invitesApi(7-83)
src/components/Claim/Link/Initial.view.tsx (2)
src/context/authContext.tsx (1)
useAuth(191-197)src/services/invites.ts (1)
invitesApi(7-83)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Deploy-Preview
No description provided.