Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThis PR refactors Crisp chat integration from an inline widget to a proxy-based same-origin iframe approach. It adds a Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai review in depth. Does this fix crisp metadata and session persistence? Is there risk or performance concerns? Does it align with crisp docs https://docs.crisp.chat/guides/chatbox-sdks/web-sdk/dollar-crisp/ |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 1756 🌐 Web query: 💡 Result: Yes — the Crisp Web SDK exposes a session:loaded event via the $crisp interface.
Example and related methods are documented in the Crisp Web SDK docs. [1][2] Sources: 🌐 Web query: 💡 Result: Short best-practices for persisting user metadata & sessions with the Crisp Chat SDK:
References: 🏁 Script executed: Length of output: 407 🏁 Script executed: Length of output: 10120 @Hugo0, I've completed an in-depth review of the Crisp implementation. Here's my analysis: ✅ What's FixedMetadata Attachment: YES - properly fixed
Code Quality: Excellent improvements
|
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (4)
src/components/Profile/components/ProfileHeader.tsx (1)
48-59: Add a tooltip to clarify what's being copied.The CopyToClipboard button copies the
username(e.g., "johndoe123"), which may differ from the displayname(e.g., "John Doe"). Without a tooltip or visual cue, users might be confused about what they're copying.Apply this diff to add a tooltip:
- <div className="flex items-center gap-1.5"> + <div className="flex items-center gap-1.5"> <VerifiedUserLabel name={name} username={username} isVerified={isVerified} className="text-2xl font-bold" iconSize={20} haveSentMoneyToUser={haveSentMoneyToUser} isAuthenticatedUserVerified={isAuthenticatedUserVerified && isSelfProfile} // can be true only for self profile /> - <CopyToClipboard textToCopy={username} fill="black" iconSize="4" /> + <CopyToClipboard + textToCopy={username} + fill="black" + iconSize="4" + tooltip="Copy username" + /> </div>Note: Verify that the
CopyToClipboardcomponent accepts atooltipprop. If not, wrap it with aTooltipcomponent (already used elsewhere in the codebase).src/components/Home/HomeCarouselCTA/index.tsx (1)
8-38: Clean component structure with minor type assertion issue.The component logic is well-organized with proper early returns and conditional onClose handling. However, the type assertion on line 21 (
icon={cta.icon as IconName}) is unnecessary sincecta.iconis already typed asIconNamein theCarouselCTAtype definition (see src/hooks/useHomeCarouselCTAs.tsx lines 10-21).<CarouselCTA key={cta.id} title={cta.title} description={cta.description} - icon={cta.icon as IconName} + icon={cta.icon} onClose={() => {src/utils/qr-payment.utils.ts (1)
9-13: Handle invalid numeric strings more robustly.The function doesn't validate that
usdAmountis a valid numeric string. IfparseFloat(usdAmount)returnsNaN, the function will returnNaN(sinceMath.round(NaN * 100) = NaN). Consider adding validation:export function calculateSavingsInCents(usdAmount: string | null | undefined): number { if (!usdAmount) return 0 - const savingsAmount = parseFloat(usdAmount) * 0.05 + const amount = parseFloat(usdAmount) + if (isNaN(amount)) return 0 + const savingsAmount = amount * 0.05 return Math.round(savingsAmount * 100) }src/components/Home/HomeCarouselCTA/CarouselCTA.tsx (1)
107-142: Both modal CTAs perform the same action.Both the "Got it!" and "Reinstall later" buttons simply close the modal without any differentiation. Consider whether "Reinstall later" should perform additional actions (e.g., snoozing the prompt for a period, tracking analytics, etc.).
If different behaviors are intended, clarify the distinction. Otherwise, consider simplifying to a single CTA if both actions are equivalent.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (22)
src/app/(mobile-ui)/home/page.tsx(4 hunks)src/app/(mobile-ui)/qr-pay/page.tsx(9 hunks)src/app/(mobile-ui)/support/page.tsx(1 hunks)src/app/(mobile-ui)/withdraw/manteca/page.tsx(5 hunks)src/components/Common/ActionList.tsx(2 hunks)src/components/CrispChat.tsx(1 hunks)src/components/Global/Slider/index.tsx(1 hunks)src/components/Global/SupportDrawer/index.tsx(1 hunks)src/components/Home/HomeBanners/BannerCard.tsx(0 hunks)src/components/Home/HomeBanners/index.tsx(0 hunks)src/components/Home/HomeCarouselCTA/CarouselCTA.tsx(1 hunks)src/components/Home/HomeCarouselCTA/index.tsx(1 hunks)src/components/Profile/components/ProfileHeader.tsx(2 hunks)src/constants/carousel.consts.ts(1 hunks)src/constants/index.ts(1 hunks)src/hooks/useCrispIframeInitialization.ts(1 hunks)src/hooks/useCrispInitialization.ts(1 hunks)src/hooks/useCrispUserData.ts(1 hunks)src/hooks/useHomeCarouselCTAs.tsx(3 hunks)src/utils/crisp.ts(1 hunks)src/utils/general.utils.ts(1 hunks)src/utils/qr-payment.utils.ts(1 hunks)
💤 Files with no reviewable changes (2)
- src/components/Home/HomeBanners/BannerCard.tsx
- src/components/Home/HomeBanners/index.tsx
🧰 Additional context used
🧠 Learnings (28)
📓 Common learnings
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 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.
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 942
File: src/components/AddMoney/consts/index.ts:2151-2162
Timestamp: 2025-06-30T10:44:08.048Z
Learning: Hugo0 often agrees with refactoring suggestions but defers implementation due to time constraints, preferring to track improvements as follow-up issues when they're part of larger architectural changes.
📚 Learning: 2025-10-08T17:13:13.155Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1299
File: src/app/(mobile-ui)/points/page.tsx:41-51
Timestamp: 2025-10-08T17:13:13.155Z
Learning: In `src/app/(mobile-ui)/points/page.tsx`, the icon name "invite-heart" is intentionally used (not "inviter-heart") when displaying who invited the current user, as this is a deliberate design choice despite semantic differences with UserHeader usage.
Applied to files:
src/components/Profile/components/ProfileHeader.tsxsrc/app/(mobile-ui)/withdraw/manteca/page.tsxsrc/app/(mobile-ui)/home/page.tsx
📚 Learning: 2025-08-19T09:08:16.945Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1107
File: src/components/LandingPage/hero.tsx:160-160
Timestamp: 2025-08-19T09:08:16.945Z
Learning: In the Hero component at src/components/LandingPage/hero.tsx, the team prefers to keep the main heading as h2 (not h1) and does not want a heading prop parameter - the heading content should remain hardcoded in the component.
Applied to files:
src/components/Home/HomeCarouselCTA/index.tsx
📚 Learning: 2025-08-26T17:38:37.055Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1132
File: src/components/Common/ActionList.tsx:153-156
Timestamp: 2025-08-26T17:38:37.055Z
Learning: In ActionList.tsx, when there are circular dependency concerns with ACTION_METHODS being imported by other components, the preferred solution is to move ACTION_METHODS to a separate constants file (like src/constants/actionlist.consts.ts) rather than using prop drilling. This centralizes constants management and creates a cleaner dependency graph.
Applied to files:
src/components/Common/ActionList.tsx
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:25:45.170Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(...)` return strings, ensuring that `calculatedFee` consistently returns a string without the need for additional type conversion.
Applied to files:
src/utils/qr-payment.utils.tssrc/utils/general.utils.tssrc/app/(mobile-ui)/qr-pay/page.tsxsrc/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:28:25.280Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(estimatedGasCost, 3)` return strings, ensuring consistent return types for `calculatedFee`.
Applied to files:
src/utils/qr-payment.utils.tssrc/utils/general.utils.tssrc/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2025-10-29T11:27:59.248Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1368
File: src/components/Common/ActionList.tsx:109-111
Timestamp: 2025-10-29T11:27:59.248Z
Learning: In `src/components/Common/ActionList.tsx`, the `balance` from `useWallet()` hook is always in USDC (as a formatted string), making it directly comparable to USD amounts without conversion. The comparison `Number(balance) >= amountInUsd` is intentional and correct.
Applied to files:
src/utils/qr-payment.utils.tssrc/app/(mobile-ui)/qr-pay/page.tsxsrc/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2025-05-13T10:05:24.057Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 845
File: src/components/Request/link/views/Create.request.link.view.tsx:81-81
Timestamp: 2025-05-13T10:05:24.057Z
Learning: In the peanut-ui project, pages that handle request flows (like Create.request.link.view.tsx) are only accessible to logged-in users who will always have a username, making null checks for user?.user.username unnecessary in these contexts.
Applied to files:
src/components/CrispChat.tsx
📚 Learning: 2025-10-02T15:23:01.513Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1266
File: src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx:46-57
Timestamp: 2025-10-02T15:23:01.513Z
Learning: In the withdraw flow at src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx, the points calculation query intentionally uses crypto.randomUUID() in the queryKey dependency array to bypass React Query caching, ensuring fresh points estimates on every render. This is the intended behavior.
Applied to files:
src/app/(mobile-ui)/qr-pay/page.tsxsrc/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2025-09-18T09:30:42.901Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1230
File: src/app/(mobile-ui)/withdraw/page.tsx:92-97
Timestamp: 2025-09-18T09:30:42.901Z
Learning: In src/app/(mobile-ui)/withdraw/page.tsx, the useEffect that calls setShowAllWithdrawMethods(true) when amountFromContext exists is intentionally designed to run only on component mount (empty dependency array), not when amountFromContext changes. This is the correct behavior for the withdraw flow where showing all methods should only happen on initial load when an amount is already present.
Applied to files:
src/app/(mobile-ui)/qr-pay/page.tsxsrc/app/(mobile-ui)/withdraw/manteca/page.tsxsrc/app/(mobile-ui)/home/page.tsx
📚 Learning: 2025-09-08T03:13:09.111Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 1190
File: src/app/(mobile-ui)/qr-pay/page.tsx:156-176
Timestamp: 2025-09-08T03:13:09.111Z
Learning: In the peanut-ui mobile app, the `/qr-pay` route is only accessed through the DirectSendQR component which always includes the qrCode parameter in the URL when redirecting users to the QR pay page after scanning MERCADO_PAGO or PIX QR codes.
Applied to files:
src/app/(mobile-ui)/qr-pay/page.tsxsrc/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2025-09-08T03:13:09.111Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 1190
File: src/app/(mobile-ui)/qr-pay/page.tsx:156-176
Timestamp: 2025-09-08T03:13:09.111Z
Learning: In the peanut-ui mobile app, the `/qr-pay` route is only accessed through the DirectSendQR component which always includes the qrCode parameter in the URL when redirecting users to the QR pay page.
Applied to files:
src/app/(mobile-ui)/qr-pay/page.tsx
📚 Learning: 2024-10-22T18:10:56.955Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 469
File: src/app/request/pay/page.tsx:25-25
Timestamp: 2024-10-22T18:10:56.955Z
Learning: In the `src/app/request/pay/page.tsx` file, the `PreviewType` enum values are strings, so when adding `previewType` to `URLSearchParams`, there's no need to convert them to strings.
Applied to files:
src/app/(mobile-ui)/qr-pay/page.tsx
📚 Learning: 2025-09-05T07:31:11.396Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1185
File: src/components/Claim/useClaimLink.tsx:14-0
Timestamp: 2025-09-05T07:31:11.396Z
Learning: In the peanut-ui codebase, `window.history.replaceState` is preferred over `router.replace` when immediate/synchronous URL parameter updates are required, as `router.replace` is asynchronous and doesn't guarantee instant URL changes that subsequent code can rely on. This pattern is used consistently across usePaymentInitiator.ts, Confirm.payment.view.tsx, and useClaimLink.tsx.
Applied to files:
src/app/(mobile-ui)/qr-pay/page.tsx
📚 Learning: 2025-08-22T07:28:32.281Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1104
File: src/components/Payment/PaymentForm/index.tsx:522-545
Timestamp: 2025-08-22T07:28:32.281Z
Learning: In `src/components/Payment/PaymentForm/index.tsx`, the `handleCompleteDaimoPayment` function is only for updating payment status in the backend after a successful Daimo payment. Payment success/failure is handled by Daimo itself, so try/catch error handling and error display are not needed for backend sync failures - users shouldn't see errors if payment succeeded but database update failed.
Applied to files:
src/app/(mobile-ui)/qr-pay/page.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-08T20:13:42.967Z
Learning: When the token price cannot be fetched in `src/components/Request/Pay/Pay.tsx` within the `PayRequestLink` component, set `tokenPriceData.price` to 0 to ensure the UI remains functional. Since Squid uses their own price engine for x-chain fulfillment transactions, this approach will not affect the transaction computation.
Applied to files:
src/app/(mobile-ui)/qr-pay/page.tsxsrc/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2024-12-02T17:19:18.532Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 551
File: src/components/Request/Create/Views/Initial.view.tsx:151-156
Timestamp: 2024-12-02T17:19:18.532Z
Learning: In the `InitialView` component at `src/components/Request/Create/Views/Initial.view.tsx`, when setting the default chain and token in the `useEffect` triggered by `isPeanutWallet`, it's acceptable to omit the setters from the dependency array and not include additional error handling for invalid defaults.
Applied to files:
src/app/(mobile-ui)/qr-pay/page.tsx
📚 Learning: 2025-08-26T15:25:53.328Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1132
File: src/app/[...recipient]/client.tsx:394-397
Timestamp: 2025-08-26T15:25:53.328Z
Learning: In `src/components/Common/ActionListDaimoPayButton.tsx`, the `handleCompleteDaimoPayment` function should not display error messages to users when DB update fails because the Daimo payment itself has succeeded - showing errors would be confusing since the payment was successful.
Applied to files:
src/app/(mobile-ui)/qr-pay/page.tsx
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsxsrc/app/(mobile-ui)/home/page.tsx
📚 Learning: 2025-09-11T17:46:12.507Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 1200
File: src/app/(mobile-ui)/recover-funds/page.tsx:9-9
Timestamp: 2025-09-11T17:46:12.507Z
Learning: Functions in Next.js that are not marked with "use server" and contain secrets are unsafe to import in client components, as they get bundled into the client JavaScript and can leak environment variables to the browser.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2025-09-11T17:46:12.507Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 1200
File: src/app/(mobile-ui)/recover-funds/page.tsx:9-9
Timestamp: 2025-09-11T17:46:12.507Z
Learning: In Next.js applications, functions marked with "use server" are server actions that are safe to import in client components. Next.js generates proxy stubs for these functions, ensuring the actual implementation and any secrets (like process.env variables) remain on the server and are not bundled into the client JavaScript.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2024-10-29T16:06:38.812Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Create/useCreateLink.tsx:647-657
Timestamp: 2024-10-29T16:06:38.812Z
Learning: In the React code for `useCreateLink` in `src/components/Create/useCreateLink.tsx`, the `switchNetwork` function used within `useCallback` hooks is stable and does not need to be included in the dependency arrays.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2025-07-05T16:58:25.340Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 0
File: :0-0
Timestamp: 2025-07-05T16:58:25.340Z
Learning: Hugo0 successfully refactored sessionStorage usage to React Context in the onramp flow, demonstrating preference for centralized state management over browser storage for component-shared state in React applications.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2025-05-15T14:47:26.891Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 857
File: src/hooks/useWebSocket.ts:77-82
Timestamp: 2025-05-15T14:47:26.891Z
Learning: The useWebSocket hook in src/hooks/useWebSocket.ts is designed to provide raw history entries, while the components using it (such as HomeHistory.tsx) are responsible for implementing deduplication logic based on UUID to prevent duplicate entries when combining WebSocket data with other data sources.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 413
File: src/context/tokenSelector.context.tsx:118-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `TokenContextProvider` component within `src/context/tokenSelector.context.tsx`, in the TypeScript React application, when data changes and before calling `fetchAndSetTokenPrice`, it is necessary to reset `selectedTokenData`, `selectedTokenPrice`, `selectedTokenDecimals`, and `inputDenomination` to discard stale data.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2024-10-22T18:11:36.864Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 469
File: src/app/request/pay/page.tsx:32-49
Timestamp: 2024-10-22T18:11:36.864Z
Learning: In `src/app/request/pay/page.tsx`, the `id` parameter is accessed via `searchParams.id` in the `generateMetadata` function.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2025-10-24T13:44:39.473Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1332
File: src/components/Global/TokenAmountInput/index.tsx:141-150
Timestamp: 2025-10-24T13:44:39.473Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), the slider feature (controlled by `showSlider` prop) is only shown for USD input mode. When the slider is used with `maxAmount`, the `selectedAmount` is computed in USD and `isInputUsd` is always `true`, so the conversion in `onChange` handles it correctly.
Applied to files:
src/components/Global/Slider/index.tsx
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 564
File: src/components/Request/Pay/Views/Initial.view.tsx:430-430
Timestamp: 2024-12-11T10:13:22.806Z
Learning: In the React TypeScript file `src/components/Request/Pay/Views/Initial.view.tsx`, when reviewing the `InitialView` component, do not flag potential issues with using non-null assertion `!` on the `slippagePercentage` variable, as handling undefined values in this context is considered out of scope.
Applied to files:
src/components/Global/Slider/index.tsx
🧬 Code graph analysis (13)
src/components/Profile/components/ProfileHeader.tsx (1)
src/components/UserHeader/index.tsx (1)
VerifiedUserLabel(44-122)
src/components/Home/HomeCarouselCTA/index.tsx (2)
src/hooks/useHomeCarouselCTAs.tsx (2)
useHomeCarouselCTAs(24-96)CarouselCTA(11-22)src/components/Global/Icons/Icon.tsx (1)
IconName(69-134)
src/app/(mobile-ui)/support/page.tsx (2)
src/hooks/useCrispUserData.ts (1)
useCrispUserData(13-33)src/hooks/useCrispIframeInitialization.ts (1)
useCrispIframeInitialization(13-57)
src/hooks/useCrispIframeInitialization.ts (2)
src/hooks/useCrispUserData.ts (1)
CrispUserData(4-11)src/utils/crisp.ts (1)
setCrispUserData(9-49)
src/hooks/useCrispUserData.ts (1)
src/context/authContext.tsx (1)
useAuth(185-191)
src/hooks/useCrispInitialization.ts (2)
src/hooks/useCrispUserData.ts (1)
CrispUserData(4-11)src/utils/crisp.ts (1)
setCrispUserData(9-49)
src/components/Home/HomeCarouselCTA/CarouselCTA.tsx (3)
src/components/Global/Icons/Icon.tsx (2)
IconName(69-134)Icon(209-218)src/hooks/useHomeCarouselCTAs.tsx (1)
CarouselCTA(11-22)src/constants/carousel.consts.ts (2)
CAROUSEL_CLOSE_BUTTON_POSITION(2-2)CAROUSEL_CLOSE_ICON_SIZE(3-3)
src/utils/qr-payment.utils.ts (1)
src/utils/general.utils.ts (1)
formatNumberForDisplay(232-278)
src/components/Global/SupportDrawer/index.tsx (2)
src/hooks/useCrispUserData.ts (1)
useCrispUserData(13-33)src/hooks/useCrispIframeInitialization.ts (1)
useCrispIframeInitialization(13-57)
src/components/CrispChat.tsx (1)
src/utils/crisp.ts (1)
setCrispUserData(9-49)
src/app/(mobile-ui)/qr-pay/page.tsx (4)
src/services/points.ts (1)
pointsApi(6-131)src/hooks/usePointsConfetti.ts (1)
usePointsConfetti(9-28)src/constants/query.consts.ts (1)
TRANSACTIONS(2-2)src/utils/qr-payment.utils.ts (3)
calculateSavingsInCents(9-13)isArgentinaMantecaQrPayment(21-27)getSavingsMessage(35-49)
src/app/(mobile-ui)/withdraw/manteca/page.tsx (3)
src/services/points.ts (1)
pointsApi(6-131)src/hooks/usePointsConfetti.ts (1)
usePointsConfetti(9-28)src/constants/query.consts.ts (1)
TRANSACTIONS(2-2)
src/hooks/useHomeCarouselCTAs.tsx (4)
src/components/Global/Icons/Icon.tsx (1)
IconName(69-134)src/context/authContext.tsx (1)
useAuth(185-191)src/hooks/useNotifications.ts (1)
useNotifications(22-424)src/hooks/useKycStatus.tsx (1)
useKycStatus(12-36)
⏰ 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 (23)
src/components/Profile/components/ProfileHeader.tsx (1)
14-14: LGTM!The import is correctly placed and necessary for the new copy-to-clipboard functionality.
src/components/Common/ActionList.tsx (1)
36-36: LGTM: Import added correctly.The import statement follows the existing pattern and is properly structured.
src/hooks/useCrispUserData.ts (1)
8-32: LGTM! Crisp user data enrichment looks solid.The additions of
fullNameandavatarfields, along with deriving the actual email from user data (with a sensible fallback), align well with the PR objectives to fix Crisp metadata attachment.src/app/(mobile-ui)/support/page.tsx (1)
3-16: LGTM! Clean integration of the new Crisp initialization pattern.The hook-based approach properly replaces the old polling logic and correctly gates initialization based on
userData.userId.src/app/(mobile-ui)/withdraw/manteca/page.tsx (2)
298-312: LGTM! Points calculation and confetti integration are well-structured.The early prefetch strategy (lines 298-309) and unconditional hook call pattern (line 312) follow React best practices. The query is properly gated and cached.
318-318: Good catch adding queryClient to the dependency array.This ensures the effect re-runs if the queryClient instance changes, preventing stale closure issues.
src/app/(mobile-ui)/qr-pay/page.tsx (3)
321-333: Points prefetch strategy looks good.Query is properly gated with
enabledconditions and uses appropriate cache settings.
972-976: Argentina savings calculation looks appropriate.The savings calculation and conditional display based on QR type and payment processor align with the feature requirements.
757-758: Confetti single-fire behavior verified—implementation is sound.The
hasShownConfettiRefguard ensures confetti fires only once per component instance. AlthoughpointsDivRefappears in three conditional renders (lines 1017, 1084, 1223), only one branch renders at any given time, and the ref's state persists across re-renders, preventing re-fire regardless of which DOM node becomes active.src/components/Global/Slider/index.tsx (1)
7-7: Good precision improvement for 3-way splits.Using
100/3instead of33provides the exact value (33.333...%) for equal splitting among three people.src/constants/index.ts (1)
3-3: LGTM! Clean re-export of new carousel constants.src/constants/carousel.consts.ts (1)
1-3: LGTM! Simple styling constants for carousel CTAs.src/app/(mobile-ui)/home/page.tsx (2)
202-209: Good race condition fix for the add money modal.The guard
!showAddMoneyPromptModalon line 202 prevents setting state when already visible, and separating the sessionStorage write into its own effect (lines 223-227) avoids timing issues.
222-227: Clean separation of sessionStorage persistence.Moving the sessionStorage write to a separate effect that depends on
showAddMoneyPromptModalensures it only fires when the modal actually becomes visible, avoiding the race condition mentioned in the comment.src/utils/crisp.ts (2)
12-17: LGTM! Email handling improves session persistence.The email field is now properly set, which is critical for maintaining Crisp sessions across devices and browsers. The conditional check ensures we only set the email when it's available.
19-23: Good fallback logic for nickname.The preference order (fullName → username → empty string) is sensible and handles all cases gracefully.
src/components/CrispChat.tsx (2)
27-27: Good change: userId is more reliable than username.Switching the precondition from
usernametouserIdis a better choice since userId is a more stable identifier for gating Crisp initialization.
35-47: Well-structured initialization flow.The three-pronged approach (immediate, event listener, fallback) ensures Crisp data is set reliably regardless of timing. The 1-second fallback is a reasonable compromise between responsiveness and avoiding unnecessary retries.
src/utils/qr-payment.utils.ts (1)
35-49: Good message formatting with proper threshold handling.The function appropriately switches between cents and dollars based on the $1 threshold, and correctly handles singular/plural forms. The use of
formatNumberForDisplayensures consistent formatting.src/hooks/useCrispInitialization.ts (1)
13-45: Well-implemented hook with proper cleanup.This hook correctly handles the Crisp initialization flow with immediate execution, event listener, and fallback timer. The cleanup function properly clears the timer and removes the event listener, preventing memory leaks.
src/components/Home/HomeCarouselCTA/CarouselCTA.tsx (2)
54-66: Good accessibility implementation.The
getAriaLabelfunction provides context-aware labels for the close button, improving screen reader support. The fallback to a generic label ensures all cases are covered.
92-96: Clear rendering logic with logo precedence.The comment on line 92 clearly explains the precedence (logo over icon), and the conditional rendering matches this intent. The ternary on line 88 ensures the background is transparent when a logo is used.
src/utils/general.utils.ts (1)
217-278: I'll verify the browser compatibility concern by checking the browserslist configuration and the support forroundingMode: 'trunc'.Now let me check the repository for browserslist configuration and any polyfill usage:
Browser compatibility for
roundingModeis not a concern.Next.js defaults to targeting Chrome 111, Edge 111, Firefox 111, and Safari 16.4, and the
roundingModeoption shipped in Chrome 106, Firefox 93, and Safari 15.4. The project's minimum browser targets exceed the version requirements forroundingModesupport, so the code is fully compatible without requiring polyfills or fallback handling.
| import { useNotifications } from '@/hooks/useNotifications' | ||
| import useKycStatus from '@/hooks/useKycStatus' | ||
| import HomeBanners from '@/components/Home/HomeBanners' | ||
| import HomeCarouselCTA from '@/components/Home/HomeCarouselCTA' |
There was a problem hiding this comment.
Component swap looks straightforward.
The replacement of HomeBanners with HomeCarouselCTA appears unrelated to the Crisp fix mentioned in the PR objectives. Consider confirming this is an intended change in this PR.
Also applies to: 270-270
🤖 Prompt for AI Agents
In src/app/(mobile-ui)/home/page.tsx around lines 36 and 270, the import and
component swap from HomeBanners to HomeCarouselCTA appears unrelated to the
Crisp fix; confirm this change is intentional for this PR and either revert the
swap if it was accidental or add a concise inline comment/PR description noting
why HomeCarouselCTA replaces HomeBanners (include any behavior/UI differences).
Also run the app locally to validate the mobile home renders as expected after
the swap and update tests/snapshots if necessary.
| ) | ||
| })} | ||
| </div> | ||
| {flow === 'claim' && !isLoggedIn && <SupportCTA />} |
There was a problem hiding this comment.
🧩 Analysis chain
Verify the relationship to Crisp fixes and conditional logic rationale.
The implementation looks correct, but please clarify:
-
Is
SupportCTArelated to the Crisp chat improvements mentioned in the PR objectives? This would help understand how this change fits into the overall Crisp metadata/session persistence fixes. -
Why is the support CTA only shown for the claim flow (
flow === 'claim') and not for the request flow? Non-logged-in users fulfilling requests might also need support access.
Run the following script to examine the SupportCTA component implementation and verify its relationship to Crisp:
🏁 Script executed:
#!/bin/bash
# Description: Check SupportCTA implementation to verify Crisp integration
# Find and examine the SupportCTA component
fd -t f "SupportCTA" --exec cat {}
# Check for Crisp-related code in SupportCTA
rg -n "crisp|Crisp|\$crisp" --type=tsx --type=ts -g "*SupportCTA*"Length of output: 27
🏁 Script executed:
#!/bin/bash
# Description: Find and examine SupportCTA component with corrected rg syntax
# Find SupportCTA files
fd -e tsx -e ts | grep -i supportcta
# Find SupportCTA component definition
rg -n "SupportCTA" --max-count=20
# Check for Crisp integration in SupportCTA files
rg -n "crisp|Crisp" -A 3 -B 3 | head -50Length of output: 3122
🏁 Script executed:
#!/bin/bash
# If files found, examine the SupportCTA component directly
fd -e tsx -e ts -path "*SupportCTA*" -exec cat {} \;Length of output: 168
🏁 Script executed:
#!/bin/bash
# Examine SupportCTA component implementation
cat src/components/Global/SupportCTA/index.tsx
# Check context of both uses - line 318 in ActionList
echo "=== ActionList context around line 318 ==="
sed -n '310,325p' src/components/Common/ActionList.tsx
# Check context of second use in client.tsx
echo "=== client.tsx context around line 579 ==="
sed -n '570,585p' src/app/[...recipient]/client.tsxLength of output: 1813
This change appears unrelated to the PR's stated Crisp metadata/session fixes objectives.
SupportCTA is a simple component that links to /support and does not integrate with Crisp. The conditional logic at line 318 (flow === 'claim' && !isLoggedIn) is intentional for its context (showing support only in the claim payment flow for guests), but this change should be addressed separately from Crisp fixes.
Clarify whether this belongs in this PR or should be moved to a separate PR focused on improving guest user experience.
🤖 Prompt for AI Agents
In src/components/Common/ActionList.tsx around line 318, the added conditional
rendering "{flow === 'claim' && !isLoggedIn && <SupportCTA />}" is unrelated to
the Crisp metadata/session fixes; revert this change from this branch (remove
the line) and either open a separate PR for the guest support CTA improvement
or, if you intend it here, update this PR title/description and tests to
explicitly include guest UX changes and Crisp-irrelevant logic. Ensure the
revert removes only that conditional and that any associated imports/unused
variables are cleaned up, or move the change into a new branch/PR with a clear
description linking back to this discussion.
| const handleLoad = () => { | ||
| // Try immediately | ||
| setData() | ||
|
|
||
| // Listen for Crisp loaded event in iframe | ||
| try { | ||
| const iframeWindow = iframe.contentWindow as any | ||
| if (iframeWindow?.$crisp) { | ||
| iframeWindow.$crisp.push(['on', 'session:loaded', setData]) | ||
| } | ||
| } catch (e) { | ||
| // Ignore CORS errors | ||
| } | ||
|
|
||
| // Fallback: try once after a delay (simplified from multiple timeouts) | ||
| setTimeout(setData, 1000) | ||
| } | ||
|
|
||
| iframe.addEventListener('load', handleLoad) | ||
| return () => iframe.removeEventListener('load', handleLoad) | ||
| }, [iframeRef, userData, prefilledMessage, enabled]) |
There was a problem hiding this comment.
Incomplete cleanup in useEffect.
The cleanup function only removes the iframe load listener, but doesn't clean up the fallback timer or the session:loaded event listener. This could lead to memory leaks and stale closures executing after the component unmounts.
export function useCrispIframeInitialization(
iframeRef: React.RefObject<HTMLIFrameElement>,
userData: CrispUserData,
prefilledMessage?: string,
enabled: boolean = true
) {
+ const fallbackTimerRef = useRef<NodeJS.Timeout>()
+
useEffect(() => {
if (!enabled || !iframeRef.current || !userData.userId) return
const iframe = iframeRef.current
const setData = () => {
try {
const iframeWindow = iframe.contentWindow as any
if (!iframeWindow?.$crisp) return
setCrispUserData(iframeWindow.$crisp, userData, prefilledMessage)
} catch (e) {
// Silently fail if CORS blocks access - no harm done
console.debug('Could not set Crisp data in iframe (expected if CORS-blocked):', e)
}
}
const handleLoad = () => {
// Try immediately
setData()
// Listen for Crisp loaded event in iframe
try {
const iframeWindow = iframe.contentWindow as any
if (iframeWindow?.$crisp) {
iframeWindow.$crisp.push(['on', 'session:loaded', setData])
}
} catch (e) {
// Ignore CORS errors
}
// Fallback: try once after a delay (simplified from multiple timeouts)
- setTimeout(setData, 1000)
+ fallbackTimerRef.current = setTimeout(setData, 1000)
}
iframe.addEventListener('load', handleLoad)
- return () => iframe.removeEventListener('load', handleLoad)
+ return () => {
+ iframe.removeEventListener('load', handleLoad)
+ if (fallbackTimerRef.current) {
+ clearTimeout(fallbackTimerRef.current)
+ }
+ try {
+ const iframeWindow = iframe.contentWindow as any
+ if (iframeWindow?.$crisp) {
+ iframeWindow.$crisp.push(['off', 'session:loaded', setData])
+ }
+ } catch (e) {
+ // Ignore CORS errors
+ }
+ }
}, [iframeRef, userData, prefilledMessage, enabled])
}Committable suggestion skipped: line range outside the PR's diff.
|
@coderabbitai the base has changed. And we've made code changes. Review in depth again |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 730 🏁 Script executed: Length of output: 11715 🏁 Script executed: Length of output: 73 🏁 Script executed: Length of output: 262 🏁 Script executed: Length of output: 140 🏁 Script executed: Length of output: 2303 🏁 Script executed: Length of output: 6988 🏁 Script executed: Length of output: 128 @Hugo0, excellent work! I've completed an in-depth re-review. You've addressed all critical issues from my previous review. Here's the comprehensive analysis: 🎉 Major Improvements Since Last Review✅ 1. Cross-Device Session Persistence (FIXED)In // Set session identifier (tokenId) for cross-device/cross-session persistence
if (userId) {
crispInstance.push(['set', 'session:identifier', [userId]])
}
✅ 2. Session Reset on Logout (FIXED)In // Reset Crisp session to prevent session merging with next user
if (typeof window !== 'undefined' && window.$crisp) {
resetCrispSession(window.$crisp)
}
✅ 3. Enhanced Metadata & Code QualityImprovements in
📊 Complete Implementation AnalysisMetadata Attachment: ✅ FULLY WORKING
Session Persistence: ✅ FULLY WORKING
Code Quality: ✅ EXCELLENT
Alignment with Crisp Docs: ✅ EXCELLENT
|
|
@coderabbitai new approach. Review. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 1792 🏁 Script executed: Length of output: 10723 🏁 Script executed: Length of output: 495 🏁 Script executed: Length of output: 4076 🏁 Script executed: Length of output: 699 🏁 Script executed: Length of output: 2267 🏁 Script executed: Length of output: 1242 @Hugo0, I've reviewed your new approach thoroughly. This is a clever architectural improvement with one critical regression. Here's my in-depth analysis: 🎨 New Architecture OverviewYou've implemented a dual-strategy approach:
✅ What's Excellent About This Approach1. URL Parameter Strategy - CORS-Proof ⭐⭐⭐// useCrispEmbedUrl.ts - Brilliant!
params.append('user_email', userData.email)
params.append('user_nickname', userData.fullName || userData.username)
params.append('user_avatar', userData.avatar)
2. Hybrid Approach - Best of Both Worlds// Best-effort metadata enhancement
useCrispIframeSessionData(iframeRef, userData, prefilledMessage)
3. Code Quality - Much Cleaner
❌ Critical Regression - Missing
|
| Scenario | With URL Params Only | With tokenId Added |
|---|---|---|
| Same device, same browser | ✅ Works (cookies) | ✅ Works |
| Same device, cookies cleared | ❌ New session | ✅ Persists |
| Different device | ❌ New session | ✅ Persists |
| Different browser | ❌ New session | ✅ Persists |
| Incognito/private mode | ❌ New session | ✅ Persists |
Your PR objectives state: "Sessions persist across devices/browsers using the email identifier"
Current status:
🔧 How to Fix - Add tokenId Back
For Global Widget (CrispChat.tsx → useCrispInitialization):
Update src/utils/crisp.ts:
export function setCrispUserData(crispInstance: any, userData: CrispUserData, prefilledMessage?: string): void {
if (!crispInstance) return
const { username, userId, email, fullName, avatar, grafanaLink } = userData
// Set session identifier (tokenId) for cross-device/cross-session persistence
// This MUST be set BEFORE user:email for proper session binding
if (userId) {
crispInstance.push(['set', 'session:identifier', [userId]])
}
// Set user email - this is critical for session persistence across devices/browsers
if (email) {
crispInstance.push(['set', 'user:email', [email]])
}
// ... rest of your code
}For Iframe Embeds (SupportDrawer + Support page):
Update src/hooks/useCrispIframeSessionData.ts:
export function useCrispIframeSessionData(
iframeRef: React.RefObject<HTMLIFrameElement>,
userData: CrispUserData,
prefilledMessage?: string
) {
useEffect(() => {
if (!iframeRef.current || !userData.userId) return
const iframe = iframeRef.current
const setSessionData = () => {
try {
const iframeWindow = iframe.contentWindow as any
if (iframeWindow?.$crisp) {
// Set session identifier FIRST (critical for persistence)
if (userData.userId) {
iframeWindow.$crisp.push(['set', 'session:identifier', [userData.userId]])
}
// Set session:data (metadata)
iframeWindow.$crisp.push([
'set',
'session:data',
[
[
['username', userData.username || ''],
['user_id', userData.userId || ''],
['full_name', userData.fullName || ''],
['grafana_dashboard', userData.grafanaLink || ''],
],
],
])
if (prefilledMessage) {
iframeWindow.$crisp.push(['set', 'message:text', [prefilledMessage]])
}
console.log('[Crisp] ✅ Successfully set session metadata + tokenId via JavaScript')
}
} catch (e: any) {
// CORS error expected - URL params already handle user identification
if (e.message?.includes('cross-origin') || e.name === 'SecurityError') {
console.debug(
'[Crisp] Session metadata cannot be set (CORS) - user identification via URL params is sufficient'
)
}
}
}
// ... rest of your code
}, [iframeRef, userData, prefilledMessage])
}📊 Complete Feature Matrix
| Feature | Status | Notes |
|---|---|---|
| Metadata Attachment | ✅ Excellent | Email, fullName, avatar, username, userId, Grafana |
| URL Parameter Strategy | ✅ Excellent | CORS-proof user identification |
| Hybrid JS Enhancement | ✅ Excellent | Best-effort metadata, graceful degradation |
| session:loaded Event | ✅ Present | Used in global widget |
| session:reset on Logout | ✅ Present | In authContext |
| Code Quality | ✅ Excellent | Clean, DRY, well-documented |
| CORS Reliability | ✅ Excellent | URL params are CORS-proof |
| Cross-Device Persistence | ❌ MISSING | No tokenId/session:identifier |
🎯 Final Verdict
Does this fix Crisp metadata and session persistence?
- Metadata: ✅ YES - Fully fixed and more reliable than before
- Session Persistence:
⚠️ PARTIALLY - Cookie-based only, needs tokenId for cross-device
Are there risks or performance concerns?
- ✅ No risks - This approach is actually more robust
- ✅ Performance improved - Removed 50-retry polling
⚠️ Feature gap - Missing cross-device persistence from previous version
Does it align with Crisp documentation?
- ✅ Excellent alignment with URL parameter approach
- ✅ Proper use of
session:loadedevent - ✅ Correct session:reset on logout
⚠️ Missing Crisp's recommendedsession:identifierfor tokenId
💡 Recommendations
High Priority:
- Add
session:identifierback to both implementations (see code above)- This was in your previous version and I recommended keeping it
- Required for true cross-device/cross-browser persistence
- Takes 2 minutes to add
Medium Priority:
2. Consider fallback if CORS blocks tokenId in iframe:
- URL params handle user ID even if tokenId JavaScript fails
- Not critical but nice to have
Optional:
3. Add HMAC identity verification if email spoofing is a concern (mentioned in previous review)
🌟 Summary
Your URL parameter approach is brilliant and more reliable than the previous JavaScript-only strategy. However, you accidentally removed the tokenId feature during the refactor, which breaks cross-device session persistence.
Action Required: Add the 3 lines of session:identifier code (shown above) to both setCrispUserData and useCrispIframeSessionData, and you'll have the best of both worlds:
- ✅ CORS-proof user identification (URL params)
- ✅ Cross-device session persistence (tokenId)
- ✅ Enhanced metadata (JavaScript when CORS allows)
Would you like me to create a complete code snippet showing both files with tokenId added back?
🧠 Learnings used
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 594
File: src/components/Request/Pay/Views/Initial.view.tsx:188-213
Timestamp: 2024-12-31T15:08:15.640Z
Learning: Always resolve coderabbit comments when reviewing code, since they can contain useful suggestions.
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 852
File: src/hooks/useTransactionHistory.ts:177-239
Timestamp: 2025-05-14T21:16:06.369Z
Learning: When reviewing code, pay attention to and resolve CodeRabbit comments as they can contain useful refactoring suggestions and insights.
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 942
File: src/components/AddMoney/consts/index.ts:2151-2162
Timestamp: 2025-06-30T10:44:08.048Z
Learning: Hugo0 often agrees with refactoring suggestions but defers implementation due to time constraints, preferring to track improvements as follow-up issues when they're part of larger architectural changes.
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 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.
|
@coderabbitai the approach completely changed. Previously it didnt really work; no data would arrive in crisp. evaluate the new approach. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 6833 🌐 Web query: 💡 Result: Short answer
Notes / recommended usage
Sources
🏁 Script executed: Length of output: 4996 🏁 Script executed: Length of output: 2790 🏁 Script executed: Length of output: 2818 🏁 Script executed: Length of output: 6109 @Hugo0, excellent work! This new proxy-based architecture is a major breakthrough that elegantly solves the core problem. Here's my comprehensive evaluation: 🎉 What Changed & Why It Works Now❌ Previous Approach (Broken - No Data Arrived)// Tried to access iframe.$crisp from parent window
iframe.contentWindow.$crisp // ❌ CORS blocked this
✅ New Approach (Working - Data Arrives!)// Same-origin proxy page at /crisp-proxy
Parent → iframe(/crisp-proxy?user_email=...) → Proxy page JS → Crisp SDK
🏗️ Architecture AnalysisBrilliant Design Decisions1. Same-Origin Proxy Pattern ⭐⭐⭐ // SupportDrawer → loads /crisp-proxy (same origin as main app)
<iframe src={crispProxyUrl} /> // ✅ No CORS restrictions!
2. URL Parameter Data Flow ⭐⭐⭐ // useCrispProxyUrl.ts - All data via URL
params.append('user_email', userData.email)
params.append('user_nickname', userData.fullName || userData.username)
params.append('session_data', JSON.stringify({...}))
3. postMessage Communication ⭐⭐ // Proxy notifies parent when ready
window.parent.postMessage({ type: 'CRISP_READY' }, origin)
// Parent can reset proxy sessions
handleMessage({ type: 'CRISP_RESET_SESSION' })
4. Hybrid Reset Strategy ⭐⭐ // postMessage for mounted iframes
iframeWindow.postMessage({ type: 'CRISP_RESET_SESSION' }, ...)
// sessionStorage flag for non-mounted iframes
sessionStorage.setItem('crisp_needs_reset', 'true')
5. Crisp Fullscreen Config ⭐ CRISP_RUNTIME_CONFIG = {
lock_maximized: true,
lock_full_view: true,
cross_origin_cookies: true // Critical for iframe persistence!
}
✅ What's Working Perfectly
🤔 Session Persistence - Email vs tokenIdYour PR objectives state: "Sessions persist across devices/browsers using the email identifier" Let me clarify the two levels of persistence: 1. User Identity Persistence (✅ You Have This)// Email identifies the user
if (email) {
window.$crisp.push(['set', 'user:email', [email]])
}
This works through:
2. Conversation Continuity (
|
There was a problem hiding this comment.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
src/app/(mobile-ui)/support/page.tsx(1 hunks)src/app/crisp-proxy/page.tsx(1 hunks)src/components/CrispChat.tsx(2 hunks)src/components/Global/SupportDrawer/index.tsx(1 hunks)src/context/authContext.tsx(2 hunks)src/hooks/useCrispEmbedUrl.ts(1 hunks)src/hooks/useCrispInitialization.ts(1 hunks)src/hooks/useCrispProxyUrl.ts(1 hunks)src/hooks/useCrispUserData.ts(1 hunks)src/utils/crisp.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- src/components/CrispChat.tsx
- src/app/(mobile-ui)/support/page.tsx
- src/hooks/useCrispUserData.ts
- src/hooks/useCrispInitialization.ts
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 594
File: src/components/Request/Pay/Views/Initial.view.tsx:188-213
Timestamp: 2024-12-31T15:08:15.640Z
Learning: Always resolve coderabbit comments when reviewing code, since they can contain useful suggestions.
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 852
File: src/hooks/useTransactionHistory.ts:177-239
Timestamp: 2025-05-14T21:16:06.369Z
Learning: When reviewing code, pay attention to and resolve CodeRabbit comments as they can contain useful refactoring suggestions and insights.
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 942
File: src/components/AddMoney/consts/index.ts:2151-2162
Timestamp: 2025-06-30T10:44:08.048Z
Learning: Hugo0 often agrees with refactoring suggestions but defers implementation due to time constraints, preferring to track improvements as follow-up issues when they're part of larger architectural changes.
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 413
File: src/context/tokenSelector.context.tsx:118-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `TokenContextProvider` component within `src/context/tokenSelector.context.tsx`, in the TypeScript React application, when data changes and before calling `fetchAndSetTokenPrice`, it is necessary to reset `selectedTokenData`, `selectedTokenPrice`, `selectedTokenDecimals`, and `inputDenomination` to discard stale data.
Applied to files:
src/context/authContext.tsx
📚 Learning: 2025-01-16T13:14:40.363Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 631
File: src/components/Create/Create.tsx:108-112
Timestamp: 2025-01-16T13:14:40.363Z
Learning: In the Peanut UI codebase, the `resetTokenContextProvider` function from `tokenSelectorContext` is a stable function reference that doesn't change, so it doesn't need to be included in useEffect dependencies.
Applied to files:
src/context/authContext.tsx
📚 Learning: 2024-10-29T16:06:38.812Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Create/useCreateLink.tsx:647-657
Timestamp: 2024-10-29T16:06:38.812Z
Learning: In the React code for `useCreateLink` in `src/components/Create/useCreateLink.tsx`, the `switchNetwork` function used within `useCallback` hooks is stable and does not need to be included in the dependency arrays.
Applied to files:
src/context/authContext.tsx
📚 Learning: 2025-07-05T16:58:25.340Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 0
File: :0-0
Timestamp: 2025-07-05T16:58:25.340Z
Learning: Hugo0 successfully refactored sessionStorage usage to React Context in the onramp flow, demonstrating preference for centralized state management over browser storage for component-shared state in React applications.
Applied to files:
src/context/authContext.tsx
📚 Learning: 2025-09-11T17:46:12.507Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 1200
File: src/app/(mobile-ui)/recover-funds/page.tsx:9-9
Timestamp: 2025-09-11T17:46:12.507Z
Learning: In Next.js applications, functions marked with "use server" are server actions that are safe to import in client components. Next.js generates proxy stubs for these functions, ensuring the actual implementation and any secrets (like process.env variables) remain on the server and are not bundled into the client JavaScript.
Applied to files:
src/context/authContext.tsx
📚 Learning: 2025-09-11T17:46:12.507Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 1200
File: src/app/(mobile-ui)/recover-funds/page.tsx:9-9
Timestamp: 2025-09-11T17:46:12.507Z
Learning: Functions in Next.js that are not marked with "use server" and contain secrets are unsafe to import in client components, as they get bundled into the client JavaScript and can leak environment variables to the browser.
Applied to files:
src/context/authContext.tsx
📚 Learning: 2025-08-07T12:53:50.946Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1072
File: src/app/(setup)/setup/page.tsx:173-175
Timestamp: 2025-08-07T12:53:50.946Z
Learning: In the peanut-ui setup flow at `src/app/(setup)/setup/page.tsx`, when handling unsupported scenarios, both device not supported and browser not supported cases should show the same "Unsupported browser" message using the `UnsupportedBrowserModal` component, rather than having distinct messaging for each scenario.
Applied to files:
src/components/Global/SupportDrawer/index.tsx
🧬 Code graph analysis (5)
src/hooks/useCrispEmbedUrl.ts (1)
src/hooks/useCrispUserData.ts (1)
CrispUserData(9-20)
src/context/authContext.tsx (1)
src/utils/crisp.ts (1)
resetCrispProxySessions(76-107)
src/utils/crisp.ts (1)
src/hooks/useCrispUserData.ts (1)
CrispUserData(9-20)
src/hooks/useCrispProxyUrl.ts (1)
src/hooks/useCrispUserData.ts (1)
CrispUserData(9-20)
src/components/Global/SupportDrawer/index.tsx (4)
src/context/SupportModalContext.tsx (1)
useSupportModalContext(39-45)src/hooks/useCrispUserData.ts (1)
useCrispUserData(26-55)src/hooks/useCrispProxyUrl.ts (1)
useCrispProxyUrl(15-69)src/components/Global/PeanutLoading/index.tsx (1)
PeanutLoading(4-19)
🔇 Additional comments (7)
src/context/authContext.tsx (1)
152-156: LGTM! Crisp session reset properly integrated into logout flow.The placement is correct—resetting Crisp sessions after clearing browser state but before fetching the new user prevents session data from bleeding across user accounts. The window guard and delegated error handling in
resetCrispProxySessions()ensure robustness.src/components/Global/SupportDrawer/index.tsx (1)
3-49: Excellent refactor to proxy-based architecture.The shift from direct Crisp iframe to same-origin proxy elegantly solves the CORS and initialization timing issues. Key strengths:
- Proper origin validation on postMessage (line 20)
- Loading state correctly resets when drawer closes (lines 32-36)
- Memoized proxy URL avoids unnecessary recalculations
- Clean separation of concerns with dedicated hooks
src/hooks/useCrispProxyUrl.ts (1)
1-69: LGTM! Well-structured proxy URL builder.The hook correctly:
- Encodes user identity and session metadata as URL parameters
- Uses JSON serialization for complex session data (lines 39-48)
- Handles missing/optional fields gracefully with fallbacks
- Memoizes with comprehensive dependencies to avoid unnecessary recalculations
src/utils/crisp.ts (1)
55-107: Well-implemented session reset utilities.Both
resetCrispSessionandresetCrispProxySessionsare robust:
- Proper guards for window/instance availability
- Error handling with debug logging
- Clever use of sessionStorage flag (line 103) to reset proxy pages that mount after logout
- PostMessage to active iframes ensures immediate reset
src/app/crisp-proxy/page.tsx (3)
22-30: Good: cross_origin_cookies enables session persistence in iframes.Setting
cross_origin_cookies: true(line 27) is essential for maintaining Crisp sessions across iframe contexts. This ensures session data persists even when the proxy iframe is embedded.
108-119: Robust initialization with polling fallback and timeout.The polling approach (lines 111-116) with a 5-second safety timeout (line 118) ensures initialization completes even if Crisp loads slowly, while preventing resource leaks from infinite intervals.
154-160: Suspense boundary correctly wraps useSearchParams.The Suspense wrapper is required because
useSearchParamscan suspend during SSR or initial hydration in Next.js 15. The fallback maintains the container dimensions during loading.
2 similar comments
There was a problem hiding this comment.
@Hugo0 overall lgtm, left a couple of suggestions, and nits, also hardcoded ids/urls bad, approving for fast tracking
src/hooks/useCrispUserData.ts
Outdated
| import { useMemo } from 'react' | ||
|
|
||
| const GRAFANA_DASHBOARD_BASE_URL = | ||
| 'https://teampeanut.grafana.net/d/ad31f645-81ca-4779-bfb2-bff8e03d9057/explore-peanut-wallet-user' |
| ? `${GRAFANA_DASHBOARD_BASE_URL}?orgId=1&var-GRAFANA_VAR_Username=${encodeURIComponent(username)}&from=now-30d&to=now&timezone=browser` | ||
| : undefined | ||
|
|
||
| const walletAddress = |
There was a problem hiding this comment.
suggestion: use address key from useWallet hook
src/context/authContext.tsx
Outdated
| clearRedirectUrl, | ||
| updateUserPreferences, | ||
| } from '@/utils' | ||
| import { resetCrispSession, resetCrispProxySessions } from '@/utils/crisp' |
src/hooks/useCrispInitialization.ts
Outdated
| /** | ||
| * Initializes Crisp user data on the main window $crisp instance | ||
| * | ||
| * Used for the main Crisp widget (not iframe). Sets user identification and metadata |
There was a problem hiding this comment.
qn: afaik the widget is only active on landing page right? we have iframe drawer/support-page on other flows?
if yes, then why are we handling case for widget? a lil more detailed comment can help
src/hooks/useCrispEmbedUrl.ts
Outdated
| import { useMemo } from 'react' | ||
| import { type CrispUserData } from '@/hooks/useCrispUserData' | ||
|
|
||
| const CRISP_WEBSITE_ID = '916078be-a6af-4696-82cb-bc08d43d9125' |
There was a problem hiding this comment.
boooooo, hard coded
| const CRISP_WEBSITE_ID = '916078be-a6af-4696-82cb-bc08d43d9125' | ||
| const CRISP_EMBED_BASE_URL = `https://go.crisp.chat/chat/embed/?website_id=${CRISP_WEBSITE_ID}` | ||
|
|
||
| /** |
There was a problem hiding this comment.
praise: love the hook
- Extract hardcoded URLs and IDs to constants files (DRY) - Remove unused useCrispInitialization hook and CrispChat default component - Clean up unused imports (resetCrispSession) - Add clarifying comments for wallet address source - Move Grafana/Arbiscan URLs to constants/support.ts - Move CRISP_WEBSITE_ID to constants/crisp.ts (was duplicated 3x)
|
@coderabbitai review new changes. Did anything break? |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 927 🏁 Script executed: Length of output: 20257 🏁 Script executed: Length of output: 1338 🏁 Script executed: Length of output: 1412 @Hugo0, I've reviewed the latest changes. Nothing broke – the refactor is actually a clean simplification. However, there are two items to address: 📊 What Changed (Latest Commit)✅ Architectural SimplificationYou moved from a dual approach (global widget + iframe proxy) to a single approach (iframe proxy only): Before:
After:
Result: This is a good simplification that removes unnecessary complexity while keeping everything working. ✅ What Still Works Perfectly
🧹 Two Cleanup Items1. Dead Code:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/hooks/useCrispEmbedUrl.ts (1)
22-24: Consider skipping user_nickname parameter when both values are falsy.The fallback chain
userData.fullName || userData.username || ''will appenduser_nickname=(empty string) to the URL when both values are undefined. For consistency with the email and avatar handling, consider only appending the parameter when a meaningful value exists.-if (userData.fullName || userData.username) { - params.append('user_nickname', userData.fullName || userData.username || '') -} +const nickname = userData.fullName || userData.username +if (nickname) { + params.append('user_nickname', nickname) +}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
src/app/crisp-proxy/page.tsx(1 hunks)src/components/CrispChat.tsx(1 hunks)src/components/index.ts(1 hunks)src/constants/crisp.ts(1 hunks)src/constants/support.ts(1 hunks)src/context/authContext.tsx(2 hunks)src/hooks/useCrispEmbedUrl.ts(1 hunks)src/hooks/useCrispUserData.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/app/crisp-proxy/page.tsx
- src/hooks/useCrispUserData.ts
🧰 Additional context used
🧠 Learnings (12)
📓 Common learnings
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 594
File: src/components/Request/Pay/Views/Initial.view.tsx:188-213
Timestamp: 2024-12-31T15:08:15.640Z
Learning: Always resolve coderabbit comments when reviewing code, since they can contain useful suggestions.
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 942
File: src/components/AddMoney/consts/index.ts:2151-2162
Timestamp: 2025-06-30T10:44:08.048Z
Learning: Hugo0 often agrees with refactoring suggestions but defers implementation due to time constraints, preferring to track improvements as follow-up issues when they're part of larger architectural changes.
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 413
File: src/context/tokenSelector.context.tsx:118-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `TokenContextProvider` component within `src/context/tokenSelector.context.tsx`, in the TypeScript React application, when data changes and before calling `fetchAndSetTokenPrice`, it is necessary to reset `selectedTokenData`, `selectedTokenPrice`, `selectedTokenDecimals`, and `inputDenomination` to discard stale data.
Applied to files:
src/context/authContext.tsx
📚 Learning: 2025-01-16T13:14:40.363Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 631
File: src/components/Create/Create.tsx:108-112
Timestamp: 2025-01-16T13:14:40.363Z
Learning: In the Peanut UI codebase, the `resetTokenContextProvider` function from `tokenSelectorContext` is a stable function reference that doesn't change, so it doesn't need to be included in useEffect dependencies.
Applied to files:
src/context/authContext.tsx
📚 Learning: 2025-07-05T16:58:25.340Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 0
File: :0-0
Timestamp: 2025-07-05T16:58:25.340Z
Learning: Hugo0 successfully refactored sessionStorage usage to React Context in the onramp flow, demonstrating preference for centralized state management over browser storage for component-shared state in React applications.
Applied to files:
src/context/authContext.tsxsrc/hooks/useCrispEmbedUrl.ts
📚 Learning: 2024-10-29T16:06:38.812Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Create/useCreateLink.tsx:647-657
Timestamp: 2024-10-29T16:06:38.812Z
Learning: In the React code for `useCreateLink` in `src/components/Create/useCreateLink.tsx`, the `switchNetwork` function used within `useCallback` hooks is stable and does not need to be included in the dependency arrays.
Applied to files:
src/context/authContext.tsxsrc/hooks/useCrispEmbedUrl.ts
📚 Learning: 2025-09-11T17:46:12.507Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 1200
File: src/app/(mobile-ui)/recover-funds/page.tsx:9-9
Timestamp: 2025-09-11T17:46:12.507Z
Learning: In Next.js applications, functions marked with "use server" are server actions that are safe to import in client components. Next.js generates proxy stubs for these functions, ensuring the actual implementation and any secrets (like process.env variables) remain on the server and are not bundled into the client JavaScript.
Applied to files:
src/context/authContext.tsx
📚 Learning: 2025-09-11T17:46:12.507Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 1200
File: src/app/(mobile-ui)/recover-funds/page.tsx:9-9
Timestamp: 2025-09-11T17:46:12.507Z
Learning: Functions in Next.js that are not marked with "use server" and contain secrets are unsafe to import in client components, as they get bundled into the client JavaScript and can leak environment variables to the browser.
Applied to files:
src/context/authContext.tsx
📚 Learning: 2025-04-29T19:36:38.121Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 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/hooks/useCrispEmbedUrl.ts
📚 Learning: 2025-06-30T10:44:08.048Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 942
File: src/components/AddMoney/consts/index.ts:2151-2162
Timestamp: 2025-06-30T10:44:08.048Z
Learning: Hugo0 often agrees with refactoring suggestions but defers implementation due to time constraints, preferring to track improvements as follow-up issues when they're part of larger architectural changes.
Applied to files:
src/hooks/useCrispEmbedUrl.ts
📚 Learning: 2024-10-18T08:54:22.142Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 458
File: src/components/Offramp/Confirm.view.tsx:96-96
Timestamp: 2024-10-18T08:54:22.142Z
Learning: In the `src/components/Offramp/Confirm.view.tsx` file, it's acceptable to include crass or informal language in code comments.
Applied to files:
src/hooks/useCrispEmbedUrl.ts
📚 Learning: 2025-05-14T21:16:06.369Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 852
File: src/hooks/useTransactionHistory.ts:177-239
Timestamp: 2025-05-14T21:16:06.369Z
Learning: When reviewing code, pay attention to and resolve CodeRabbit comments as they can contain useful refactoring suggestions and insights.
Applied to files:
src/hooks/useCrispEmbedUrl.ts
📚 Learning: 2024-12-31T15:08:15.640Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 594
File: src/components/Request/Pay/Views/Initial.view.tsx:188-213
Timestamp: 2024-12-31T15:08:15.640Z
Learning: Always resolve coderabbit comments when reviewing code, since they can contain useful suggestions.
Applied to files:
src/hooks/useCrispEmbedUrl.ts
🧬 Code graph analysis (2)
src/context/authContext.tsx (1)
src/utils/crisp.ts (1)
resetCrispProxySessions(76-107)
src/hooks/useCrispEmbedUrl.ts (2)
src/constants/crisp.ts (1)
CRISP_WEBSITE_ID(6-6)src/hooks/useCrispUserData.ts (1)
CrispUserData(6-17)
🔇 Additional comments (5)
src/components/index.ts (1)
3-3: LGTM! Clarifying comment helps document the refactor.The inline comment effectively communicates that the CrispChat module now only exports CrispButton following the architectural change to a modal-based support interface.
src/components/CrispChat.tsx (1)
5-24: LGTM! Clean refactor to modal-based approach.The component is now properly simplified to a trigger button, delegating the Crisp integration complexity to the SupportDrawer/modal architecture. The documentation clearly explains the rationale for the iframe proxy approach.
src/constants/support.ts (1)
1-10: LGTM! Well-documented configuration constants.The base URLs are appropriately extracted as constants with clear documentation. Hardcoding these external service URLs is the correct approach for configuration values.
src/constants/crisp.ts (1)
1-6: LGTM! Appropriate configuration constant.The hardcoded Crisp website ID is correct here—it's a public identifier used for client-side integration, not a secret. Good documentation and clear naming.
src/context/authContext.tsx (1)
152-156: LGTM! Critical security fix to prevent session leakage.The Crisp session reset during logout is essential to prevent conversation history from leaking between users. The placement is correct—after clearing session storage but before fetching the next user—and the window guard ensures SSR safety.
| if (userData.email) { | ||
| params.append('user_email', userData.email) | ||
| } | ||
| if (userData.fullName || userData.username) { | ||
| params.append('user_nickname', userData.fullName || userData.username || '') | ||
| } | ||
| if (userData.avatar) { | ||
| params.append('user_avatar', userData.avatar) | ||
| } |
There was a problem hiding this comment.
Missing session:identifier parameter prevents cross-device persistence.
According to the PR objectives and Crisp best practices, session:identifier (tokenId) should be set as a URL parameter to ensure conversation continuity across devices and browser sessions. The current implementation only sets user_email, user_nickname, and user_avatar, which provides basic identification but not guaranteed session persistence.
Per the PR objectives: "High priority: ensure session:identifier (tokenId) is set (before user email) in both widget and iframe/proxy JS."
Consider adding the session identifier parameter:
const params = new URLSearchParams()
+if (userData.userId) {
+ params.append('token_id', userData.userId)
+}
if (userData.email) {
params.append('user_email', userData.email)
}Note: Crisp's embed URL parameter for session identifier is token_id. It should be set before user_email to establish session context first.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/hooks/useCrispEmbedUrl.ts around lines 19 to 27, the code appends
user_email, user_nickname and user_avatar but omits the session identifier
required for cross-device persistence; add the Crisp session identifier URL
parameter 'token_id' (from userData.tokenId or tokenId) and append it before
appending user_email so the session context is established first; only append
token_id when present and preserve existing parameter order and null/undefined
checks.
Fix Crisp Chat Metadata and Session Persistence
Problems:
Changes:
username@peanut.to) for proper user identificationfullName,avatar, and updated Grafana dashboard linksession:loadedevent instead of polling (removed 50 retry attempts)useCrispIframeInitialization)Result: