Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/components/Global/Banner/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client'

import { useEffect } from 'react'
import { usePathname } from 'next/navigation'
import { MaintenanceBanner } from './MaintenanceBanner'
import { MarqueeWrapper } from '../MarqueeWrapper'
Expand All @@ -8,6 +9,7 @@ import { HandThumbsUp } from '@/assets'
import Image from 'next/image'
import { useModalsContext } from '@/context/ModalsContext'
import { GIT_COMMIT_HASH, IS_PRODUCTION } from '@/constants/general.consts'
import { getRunMode, isRealMoneyMode, logRunMode } from '@/utils/mode'

export function Banner() {
const pathname = usePathname()
Expand All @@ -29,17 +31,43 @@ export function Banner() {
function FeedbackBanner() {
const { setIsSupportModalOpen } = useModalsContext()

// Log run-mode once on mount (dev only). Big yellow banner in the
// browser console so you can never confuse sandbox for staging at a
// glance. Real-money modes get a red banner instead.
useEffect(() => {
if (IS_PRODUCTION) return
logRunMode()
}, [])

const handleClick = () => {
setIsSupportModalOpen(true)
}

const mode = !IS_PRODUCTION ? getRunMode() : null
const realMoney = !IS_PRODUCTION && isRealMoneyMode()

return (
<button onClick={handleClick} className="w-full cursor-pointer">
<MarqueeWrapper backgroundColor="bg-primary-1" direction="left">
<span className="z-10 mx-4 flex items-center gap-2 text-sm font-semibold">
Peanut is in beta! Thank you for being an early user, share your feedback here
<Image src={HandThumbsUp} alt="Thumbs up" className="h-4 w-4" />
{!IS_PRODUCTION && <span className="ml-2 text-sm font-semibold">version: {GIT_COMMIT_HASH}</span>}
{mode && (
// High-contrast yellow-on-black pill. Visually impossible
// to miss; the goal is "you can never accidentally think
// you're in sandbox when you're hitting prod." Real-money
// modes get a flashing red emoji prefix.
<span
className={
'ml-2 rounded-sm border border-black px-2 py-0.5 text-xs font-extrabold ' +
(realMoney ? 'bg-red-500 text-white' : 'bg-yellow-300 text-black')
}
>
{realMoney ? '⚠ REAL MONEY · ' : '⚙ '}
{mode.preset.toUpperCase()}
</span>
)}
</span>
</MarqueeWrapper>
</button>
Expand Down
13 changes: 12 additions & 1 deletion src/components/TransactionDetails/TransactionDetailsReceipt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { formatAmount, formatDate, isStableCoin, formatCurrency } from '@/utils/
import { formatPoints } from '@/utils/format.utils'
import { getAvatarUrl } from '@/utils/history.utils'
import { formatIban, printableAddress, shortenAddress, shortenStringLong, slugify } from '@/utils/general.utils'
import { maskAccountIdentifier } from '@/utils/account-mask.utils'
import { cancelOnramp } from '@/app/actions/onramp'
import { captureException } from '@sentry/nextjs'
import { useQueryClient } from '@tanstack/react-query'
Expand Down Expand Up @@ -529,9 +530,15 @@ export const TransactionDetailsReceipt = ({
<span>
{isGuestBankClaim
? transaction.bankAccountDetails.identifier
: formatIban(transaction.bankAccountDetails.identifier)}
: maskAccountIdentifier(
transaction.bankAccountDetails.identifier,
transaction.bankAccountDetails.type
)}
</span>
{!isGuestBankClaim && (
// Copy yields the FULL identifier — masking is for
// visual privacy on shared screens / receipts; the user
// owns the account and may need to paste it elsewhere.
<CopyToClipboard
textToCopy={formatIban(transaction.bankAccountDetails.identifier)}
iconSize="4"
Expand Down Expand Up @@ -769,6 +776,10 @@ export const TransactionDetailsReceipt = ({
await navigator.share({ text })
} else {
await navigator.clipboard.writeText(text)
// Desktop-fallback path: navigator.share is mobile-only.
// Without a toast the click is silent — users assume the
// button is broken (Hugo's screenshot b on 2026-04-29).
toast.info('Invite link copied!')
}
} catch {
// user cancelled share sheet — ignore
Expand Down
Loading
Loading