From 9e619cbcc1fcd4b969883cda77b52d59040c19b2 Mon Sep 17 00:00:00 2001 From: Zishan Mohd Date: Fri, 7 Nov 2025 21:18:51 +0530 Subject: [PATCH 1/2] Disable Add money modal --- src/app/(mobile-ui)/home/page.tsx | 69 ++----------------------------- 1 file changed, 3 insertions(+), 66 deletions(-) diff --git a/src/app/(mobile-ui)/home/page.tsx b/src/app/(mobile-ui)/home/page.tsx index 5cca971d8..bfa89bffc 100644 --- a/src/app/(mobile-ui)/home/page.tsx +++ b/src/app/(mobile-ui)/home/page.tsx @@ -61,7 +61,6 @@ export default function Home() { const username = user?.user.username const [showIOSPWAInstallModal, setShowIOSPWAInstallModal] = useState(false) - const [showAddMoneyPromptModal, setShowAddMoneyPromptModal] = useState(false) const [showBalanceWarningModal, setShowBalanceWarningModal] = useState(false) // const [showReferralCampaignModal, setShowReferralCampaignModal] = useState(false) const [isPostSignupActionModalVisible, setIsPostSignupActionModalVisible] = useState(false) @@ -155,75 +154,12 @@ export default function Home() { balanceInUsd > BALANCE_WARNING_THRESHOLD && !hasSeenBalanceWarning && !showIOSPWAInstallModal && - !showAddMoneyPromptModal && !isPostSignupActionModalVisible ) { setShowBalanceWarningModal(true) } } - }, [ - balance, - isFetchingBalance, - showIOSPWAInstallModal, - showAddMoneyPromptModal, - isPostSignupActionModalVisible, - user, - ]) - - // effect for showing add money prompt modal - useEffect(() => { - if (typeof window === 'undefined' || isFetchingBalance || !user || !address) return - - // Don't show modal if balance is still loading (undefined) - if (balance === undefined) return - - const hasSeenAddMoneyPromptThisSession = sessionStorage.getItem('hasSeenAddMoneyPromptThisSession') - const showNoMoreJailModal = sessionStorage.getItem('showNoMoreJailModal') - - // determine if we should show the add money modal based on all conditions - // show if: - // 1. balance is zero. - // 2. user hasn't seen this prompt in the current session. - // 3. setup notifications modal is not visible (priority: setup modal > add money prompt) - // 4. the iOS PWA install modal is not currently active. - // 5. the balance warning modal is not currently active. - // 6. no other post-signup modal is active - const shouldShow = - balance === 0n && - !hasSeenAddMoneyPromptThisSession && - !showPermissionModal && - !showIOSPWAInstallModal && - !showBalanceWarningModal && - !isPostSignupActionModalVisible && - showNoMoreJailModal !== 'true' && - !user?.showEarlyUserModal // Give Early User and No more jail modal precedence, showing two modals together isn't ideal and it messes up their functionality - - if (shouldShow && !showAddMoneyPromptModal) { - // Only set state, don't set sessionStorage here to avoid race conditions - setShowAddMoneyPromptModal(true) - } else if (showAddMoneyPromptModal && showPermissionModal) { - // priority enforcement: hide add money modal if notification modal appears - // this handles race conditions where both modals try to show simultaneously - setShowAddMoneyPromptModal(false) - } - }, [ - balance, - isFetchingBalance, - showPermissionModal, - showIOSPWAInstallModal, - showBalanceWarningModal, - isPostSignupActionModalVisible, - showAddMoneyPromptModal, - user, - address, - ]) - - // Set sessionStorage flag when modal becomes visible to prevent showing again - useEffect(() => { - if (showAddMoneyPromptModal) { - sessionStorage.setItem('hasSeenAddMoneyPromptThisSession', 'true') - } - }, [showAddMoneyPromptModal]) + }, [balance, isFetchingBalance, showIOSPWAInstallModal, isPostSignupActionModalVisible, user]) if (isLoading) { return @@ -283,7 +219,8 @@ export default function Home() { setShowIOSPWAInstallModal(false)} /> {/* Add Money Prompt Modal */} - setShowAddMoneyPromptModal(false)} /> + {/* TODO @dev Disabling this, re-enable after properly fixing */} + {/* setShowAddMoneyPromptModal(false)} /> */} From 2258296de1b9b74145728ea2d5a1c41fdfa2d195 Mon Sep 17 00:00:00 2001 From: Zishan Mohd Date: Fri, 7 Nov 2025 21:19:31 +0530 Subject: [PATCH 2/2] Add QR payment CTA --- src/components/Global/DirectSendQR/index.tsx | 3 +- src/components/Global/Icons/qr-code.tsx | 2 +- .../Home/HomeCarouselCTA/CarouselCTA.tsx | 4 +- src/components/Home/HomeCarouselCTA/index.tsx | 1 + src/context/QrCodeContext.tsx | 23 ++++++++++ src/context/contextProvider.tsx | 5 ++- src/hooks/useHomeCarouselCTAs.tsx | 45 +++++++++++-------- 7 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 src/context/QrCodeContext.tsx diff --git a/src/components/Global/DirectSendQR/index.tsx b/src/components/Global/DirectSendQR/index.tsx index 2cffa0c34..7e2ea18ff 100644 --- a/src/components/Global/DirectSendQR/index.tsx +++ b/src/components/Global/DirectSendQR/index.tsx @@ -18,6 +18,7 @@ import { twMerge } from 'tailwind-merge' import ActionModal from '../ActionModal' import { Icon, type IconName } from '../Icons/Icon' import { EQrType, NAME_BY_QR_TYPE, parseEip681, recognizeQr } from './utils' +import { useQrCodeContext } from '@/context/QrCodeContext' const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL! @@ -180,7 +181,6 @@ export default function DirectSendQr({ icon?: IconName disabled?: boolean }) { - const [isQRScannerOpen, setIsQRScannerOpen] = useState(false) const [isModalOpen, setIsModalOpen] = useState(false) const [showPermissionModal, setShowPermissionModal] = useState(false) const [qrType, setQrType] = useState(undefined) @@ -196,6 +196,7 @@ export default function DirectSendQr({ if (!user?.user.username) return '' return `${BASE_URL}/pay/${user.user.username}` }, [user?.user.username]) + const { isQRScannerOpen, setIsQRScannerOpen } = useQrCodeContext() const startScanner = () => { setIsQRScannerOpen(true) diff --git a/src/components/Global/Icons/qr-code.tsx b/src/components/Global/Icons/qr-code.tsx index 82b98eceb..8fb1c5553 100644 --- a/src/components/Global/Icons/qr-code.tsx +++ b/src/components/Global/Icons/qr-code.tsx @@ -1,7 +1,7 @@ import { type FC, type SVGProps } from 'react' export const QrCodeIcon: FC> = (props) => ( - + { const [showPermissionDeniedModal, setShowPermissionDeniedModal] = useState(false) @@ -95,7 +97,7 @@ const CarouselCTA = ({ )} > {/* Show icon only if logo isn't provided. Logo takes precedence over icon. */} - {!logo && } + {!logo && } {logo && ( )} diff --git a/src/components/Home/HomeCarouselCTA/index.tsx b/src/components/Home/HomeCarouselCTA/index.tsx index 779b4ccff..2a4e67803 100644 --- a/src/components/Home/HomeCarouselCTA/index.tsx +++ b/src/components/Home/HomeCarouselCTA/index.tsx @@ -32,6 +32,7 @@ const HomeCarouselCTA = () => { iconContainerClassName={cta.iconContainerClassName} isPermissionDenied={cta.isPermissionDenied} secondaryIcon={cta.secondaryIcon} + iconSize={16} /> ))} diff --git a/src/context/QrCodeContext.tsx b/src/context/QrCodeContext.tsx new file mode 100644 index 000000000..53c740f71 --- /dev/null +++ b/src/context/QrCodeContext.tsx @@ -0,0 +1,23 @@ +'use client' + +import { createContext, useContext, useState, type ReactNode } from 'react' + +interface QrCodeContextType { + isQRScannerOpen: boolean + setIsQRScannerOpen: (isOpen: boolean) => void +} + +const QrCodeContext = createContext(undefined) + +export function QrCodeProvider({ children }: { children: ReactNode }) { + const [isQRScannerOpen, setIsQRScannerOpen] = useState(false) + return {children} +} + +export function useQrCodeContext() { + const context = useContext(QrCodeContext) + if (context === undefined) { + throw new Error('useQrCodeContext must be used within a QrCodeProvider') + } + return context +} diff --git a/src/context/contextProvider.tsx b/src/context/contextProvider.tsx index 01dcb29c7..d285c51e7 100644 --- a/src/context/contextProvider.tsx +++ b/src/context/contextProvider.tsx @@ -9,6 +9,7 @@ import { WithdrawFlowContextProvider } from './WithdrawFlowContext' import { ClaimBankFlowContextProvider } from './ClaimBankFlowContext' import { RequestFulfilmentFlowContextProvider } from './RequestFulfillmentFlowContext' import { SupportModalProvider } from './SupportModalContext' +import { QrCodeProvider } from './QrCodeContext' export const ContextProvider = ({ children }: { children: React.ReactNode }) => { return ( @@ -22,7 +23,9 @@ export const ContextProvider = ({ children }: { children: React.ReactNode }) => - {children} + + {children} + diff --git a/src/hooks/useHomeCarouselCTAs.tsx b/src/hooks/useHomeCarouselCTAs.tsx index 6103e5653..bdea5d4e7 100644 --- a/src/hooks/useHomeCarouselCTAs.tsx +++ b/src/hooks/useHomeCarouselCTAs.tsx @@ -7,7 +7,7 @@ import { useNotifications } from './useNotifications' import { useRouter } from 'next/navigation' import useKycStatus from './useKycStatus' import type { StaticImageData } from 'next/image' -import { PIX } from '@/assets' +import { useQrCodeContext } from '@/context/QrCodeContext' export type CarouselCTA = { id: string @@ -21,6 +21,7 @@ export type CarouselCTA = { isPermissionDenied?: boolean iconContainerClassName?: string secondaryIcon?: StaticImageData | string + iconSize?: number } export const useHomeCarouselCTAs = () => { @@ -29,27 +30,35 @@ export const useHomeCarouselCTAs = () => { const { showReminderBanner, requestPermission, snoozeReminderBanner, afterPermissionAttempt, isPermissionDenied } = useNotifications() const router = useRouter() - const { isUserKycApproved, isUserBridgeKycUnderReview } = useKycStatus() + const { isUserKycApproved, isUserBridgeKycUnderReview, isUserMantecaKycApproved } = useKycStatus() + + const { setIsQRScannerOpen } = useQrCodeContext() const generateCarouselCTAs = useCallback(() => { const _carouselCTAs: CarouselCTA[] = [] - _carouselCTAs.push({ - id: 'merchant-map-pix', - title: 'Up to 10% cashback for Tier 2 users with PIX Payments', - description: 'Click to explore participating merchants. Pay with PIX QR, save instantly, earn points.', - iconContainerClassName: 'bg-secondary-1', - icon: 'shield', - onClick: () => { - window.open( - 'https://peanutprotocol.notion.site/Peanut-Foodie-Guide-29a83811757980e79896f2a610d6591a', - '_blank', - 'noopener,noreferrer' - ) - }, - logo: PIX, - secondaryIcon: 'https://flagcdn.com/w320/br.png', - }) + // Show QR code payment prompt if user's Bridge or Manteca KYC is approved. + if (isUserKycApproved || isUserMantecaKycApproved) { + _carouselCTAs.push({ + id: 'qr-payment', + title: ( +

+ Pay with QR code payments +

+ ), + description: ( +

+ Get the best exchange rate, pay like a local and earn points. +

+ ), + iconContainerClassName: 'bg-secondary-1', + icon: 'qr-code', + onClick: () => { + setIsQRScannerOpen(true) + }, + iconSize: 16, + }) + } // add notification prompt as first item if it should be shown if (showReminderBanner) {