Skip to content
Merged
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
14 changes: 14 additions & 0 deletions jest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ global.TextDecoder = TextDecoder

require('dotenv').config({ path: '.env.test' })

// Setup minimal environment variables for tests
process.env.NEXT_PUBLIC_ALCHEMY_API_KEY = process.env.NEXT_PUBLIC_ALCHEMY_API_KEY || 'test-key'
process.env.NEXT_PUBLIC_INFURA_API_KEY = process.env.NEXT_PUBLIC_INFURA_API_KEY || 'test-key'
process.env.VAPID_PUBLIC_KEY = process.env.VAPID_PUBLIC_KEY || 'test-vapid-public'
process.env.VAPID_PRIVATE_KEY = process.env.VAPID_PRIVATE_KEY || 'test-vapid-private'
process.env.VAPID_SUBJECT = process.env.VAPID_SUBJECT || 'mailto:test@example.com'

// Add any global test setup here
global.console = {
...console,
Expand All @@ -21,3 +28,10 @@ global.console = {
jest.mock('next/cache', () => ({
unstable_cache: (fn: Function) => fn,
}))

jest.mock('@/app/actions/tokens', () => ({
fetchTokenPriceApi: jest.fn(() => Promise.resolve({ price: 100, symbol: 'TEST' })),
getTokenBalances: jest.fn(() => Promise.resolve([])),
getTokenPrice: jest.fn(() => Promise.resolve(100)),
fetchERC20Data: jest.fn(() => Promise.resolve({ name: 'Test Token', symbol: 'TEST', decimals: 18 })),
}))
29 changes: 28 additions & 1 deletion next.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const os = require('os')
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
// Only enable in production builds when explicitly requested
enabled: process.env.ANALYZE === 'true' && process.env.NODE_ENV !== 'development',
Comment on lines +3 to +4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor: Comment doesn't match condition.

The comment states "Only enable in production builds" but the condition NODE_ENV !== 'development' would also enable the analyzer in test or staging environments (not just production). If the intent is production-only, use NODE_ENV === 'production'. Otherwise, update the comment to reflect the actual behavior (e.g., "Only enable in non-development builds").

Apply this diff if production-only is the intent:

-    // Only enable in production builds when explicitly requested
-    enabled: process.env.ANALYZE === 'true' && process.env.NODE_ENV !== 'development',
+    // Only enable in production builds when explicitly requested
+    enabled: process.env.ANALYZE === 'true' && process.env.NODE_ENV === 'production',

Or update the comment if the current behavior is intentional:

-    // Only enable in production builds when explicitly requested
+    // Only enable in non-development builds when explicitly requested
     enabled: process.env.ANALYZE === 'true' && process.env.NODE_ENV !== 'development',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Only enable in production builds when explicitly requested
enabled: process.env.ANALYZE === 'true' && process.env.NODE_ENV !== 'development',
// Only enable in production builds when explicitly requested
enabled: process.env.ANALYZE === 'true' && process.env.NODE_ENV === 'production',
Suggested change
// Only enable in production builds when explicitly requested
enabled: process.env.ANALYZE === 'true' && process.env.NODE_ENV !== 'development',
// Only enable in non-development builds when explicitly requested
enabled: process.env.ANALYZE === 'true' && process.env.NODE_ENV !== 'development',
🤖 Prompt for AI Agents
In next.config.js around lines 3 to 4 the comment "Only enable in production
builds" is misleading because the condition uses NODE_ENV !== 'development'
which also enables the analyzer in test/staging; either change the condition to
NODE_ENV === 'production' to enforce production-only, or update the comment to
"Only enable in non-development builds" to reflect current behavior—pick one and
make the corresponding change so comment and condition match.

})

const redirectsConfig = require('./redirects.json')
Expand Down Expand Up @@ -36,6 +37,32 @@ let nextConfig = {
},
],
},

// Turbopack configuration for faster dev builds
turbopack: {
resolveAlias: {
// Optimize common aliases
'@': './src',
},
},

// External packages that shouldn't be bundled (server-side only)
serverExternalPackages: [],

// Disable source maps in production (already handled by Sentry)
productionBrowserSourceMaps: false,

// Transpile packages for better compatibility
transpilePackages: ['@squirrel-labs/peanut-sdk'],

// Experimental features for optimization
experimental: {
// Optimize package imports for tree-shaking
optimizePackageImports: ['@chakra-ui/react', 'framer-motion', '@headlessui/react'],
// Speed up webpack builds (fallback mode when not using --turbo)
webpackBuildWorker: true,
},

webpack: (config, { isServer, dev }) => {
if (!dev || !process.env.NEXT_TURBO) {
if (isServer) {
Expand Down
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,18 @@
]
},
"transformIgnorePatterns": [
"node_modules/(?!(@wagmi|wagmi|viem|@viem|@squirrel-labs)/)"
"node_modules/(?!(@wagmi|wagmi|viem|@viem|@squirrel-labs|@reown|@walletconnect|@justaname\\.id|@zerodev|permissionless)/)"
],
"moduleNameMapper": {
"\\.(svg|png|jpg|jpeg|gif)$": "jest-transform-stub",
"^@/(.*)$": "<rootDir>/src/$1",
"^wagmi/chains$": "<rootDir>/src/utils/__mocks__/wagmi.ts",
"^@squirrel-labs/peanut-sdk$": "<rootDir>/src/utils/__mocks__/peanut-sdk.ts",
"^next/cache$": "<rootDir>/src/utils/__mocks__/next-cache.ts"
"^@reown/appkit/react$": "<rootDir>/src/utils/__mocks__/reown-appkit.ts",
"^@justaname\\.id/react$": "<rootDir>/src/utils/__mocks__/justaname.ts",
"^web-push$": "<rootDir>/src/utils/__mocks__/web-push.ts",
"^next/cache$": "<rootDir>/src/utils/__mocks__/next-cache.ts",
"^@zerodev/sdk(.*)$": "<rootDir>/src/utils/__mocks__/zerodev-sdk.ts"
},
"setupFilesAfterEnv": [
"<rootDir>/jest.setup.ts"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'
import MantecaAddMoney from '@/components/AddMoney/components/MantecaAddMoney'
import { CountryData, countryData } from '@/components/AddMoney/consts'
import { type CountryData, countryData } from '@/components/AddMoney/consts'
import { MantecaSupportedExchanges } from '@/components/AddMoney/consts'
import { useParams } from 'next/navigation'

Expand Down
2 changes: 1 addition & 1 deletion src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { useOnrampFlow } from '@/context/OnrampFlowContext'
import { useWallet } from '@/hooks/wallet/useWallet'
import { formatAmount } from '@/utils'
import { countryData } from '@/components/AddMoney/consts'
import { BridgeKycStatus } from '@/utils/bridge-accounts.utils'
import { type BridgeKycStatus } from '@/utils/bridge-accounts.utils'
import { useWebSocket } from '@/hooks/useWebSocket'
import { useAuth } from '@/context/authContext'
import { useCreateOnramp } from '@/hooks/useCreateOnramp'
Expand Down
6 changes: 3 additions & 3 deletions src/app/(mobile-ui)/add-money/crypto/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import { CryptoSourceListCard } from '@/components/AddMoney/components/CryptoSou
import {
CRYPTO_EXCHANGES,
CRYPTO_WALLETS,
CryptoSource,
CryptoToken,
type CryptoSource,
type CryptoToken,
DEPOSIT_CRYPTO_TOKENS,
} from '@/components/AddMoney/consts'
import { CryptoDepositQR } from '@/components/AddMoney/views/CryptoDepositQR.view'
import NetworkSelectionView, { SelectedNetwork } from '@/components/AddMoney/views/NetworkSelection.view'
import NetworkSelectionView, { type SelectedNetwork } from '@/components/AddMoney/views/NetworkSelection.view'
import TokenSelectionView from '@/components/AddMoney/views/TokenSelection.view'
import NavHeader from '@/components/Global/NavHeader'
import PeanutLoading from '@/components/Global/PeanutLoading'
Expand Down
2 changes: 1 addition & 1 deletion src/app/(mobile-ui)/claim/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { getLinkDetails } from '@/app/actions/claimLinks'
import { Claim } from '@/components'
import { BASE_URL } from '@/constants'
import { formatAmount, resolveAddressToUsername } from '@/utils'
import { Metadata } from 'next'
import { type Metadata } from 'next'
import getOrigin from '@/lib/hosting/get-origin'

export const dynamic = 'force-dynamic'
Expand Down
61 changes: 52 additions & 9 deletions src/app/(mobile-ui)/dev/shake-test/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ export default function DevShakeTestPage() {
const [holdTimer, setHoldTimer] = useState<NodeJS.Timeout | null>(null)
const [progressInterval, setProgressInterval] = useState<NodeJS.Timeout | null>(null)
const [showSuccess, setShowSuccess] = useState(false)
const [holdStartTime, setHoldStartTime] = useState<number | null>(null)

const startHold = useCallback(() => {
setHoldProgress(0)
setIsShaking(true)
setShowSuccess(false)

const startTime = Date.now()
setHoldStartTime(startTime)
let lastIntensity: 'weak' | 'medium' | 'strong' | 'intense' = 'weak'

// Update progress and shake intensity
Expand Down Expand Up @@ -94,25 +96,65 @@ export default function DevShakeTestPage() {
}, [])

const cancelHold = useCallback(() => {
const PREVIEW_DURATION_MS = 500

// Calculate how long the user held
const elapsed = holdStartTime ? Date.now() - holdStartTime : 0

// Clear the completion timer (we'll never complete on release)
if (holdTimer) clearTimeout(holdTimer)
setHoldTimer(null)

// If it was a quick tap, let the preview animation continue for 500ms before resetting
if (elapsed > 0 && elapsed < PREVIEW_DURATION_MS) {
const remainingPreviewTime = PREVIEW_DURATION_MS - elapsed

// Let animations continue for the preview duration
const resetTimer = setTimeout(() => {
// Clean up after preview
if (progressInterval) clearInterval(progressInterval)
setProgressInterval(null)
setHoldProgress(0)
setIsShaking(false)
setShakeIntensity('none')
setHoldStartTime(null)

if ('vibrate' in navigator) {
navigator.vibrate(0)
}
}, remainingPreviewTime)

setHoldTimer(resetTimer)
} else {
// Released after preview duration - reset immediately
if (progressInterval) clearInterval(progressInterval)
setProgressInterval(null)
setHoldProgress(0)
setIsShaking(false)
setShakeIntensity('none')
setHoldStartTime(null)

if ('vibrate' in navigator) {
navigator.vibrate(0)
}
}
}, [holdTimer, progressInterval, holdStartTime])

const reset = useCallback(() => {
if (holdTimer) clearTimeout(holdTimer)
if (progressInterval) clearInterval(progressInterval)
setHoldTimer(null)
setProgressInterval(null)
setHoldProgress(0)
setIsShaking(false)
setShakeIntensity('none')

// Stop any ongoing vibration when user releases early
setHoldStartTime(null)
setShowSuccess(false)
if ('vibrate' in navigator) {
navigator.vibrate(0)
}
}, [holdTimer, progressInterval])

const reset = useCallback(() => {
cancelHold()
setShowSuccess(false)
}, [cancelHold])

return (
<div className={`flex min-h-[inherit] flex-col gap-8 ${getShakeClass(isShaking, shakeIntensity)}`}>
<NavHeader title="🧪 Dev Shake Test" />
Expand Down Expand Up @@ -191,7 +233,7 @@ export default function DevShakeTestPage() {
</Button>

<div className="text-center text-xs text-gray-500">
Press and hold the button to test the progressive shake
Hold the button for the full duration (quick taps show 500ms preview)
</div>
</div>
) : (
Expand All @@ -216,7 +258,8 @@ export default function DevShakeTestPage() {
<li>✓ Button fills with black as you hold</li>
<li>✓ Shake starts weak and gets progressively stronger</li>
<li>✓ Haptic feedback intensifies with shake (PWA only)</li>
<li>✓ Shake stops when you release early</li>
<li>✓ Quick tap shows preview but resets (must hold full duration)</li>
<li>✓ Release early cancels the action</li>
<li>✓ After full hold: shake stops, confetti appears, final haptic</li>
<li>✓ Works on mobile touch and desktop mouse</li>
</ul>
Expand Down
2 changes: 1 addition & 1 deletion src/app/(mobile-ui)/history/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import { CardPosition } from '@/components/Global/Card'
import { type CardPosition } from '@/components/Global/Card'
import EmptyState from '@/components/Global/EmptyStates/EmptyState'
import NoDataEmptyState from '@/components/Global/EmptyStates/NoDataEmptyState'
import NavHeader from '@/components/Global/NavHeader'
Expand Down
2 changes: 1 addition & 1 deletion src/app/(mobile-ui)/home/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import { Button, ButtonSize, ButtonVariant } from '@/components/0_Bruddle'
import { Button, type ButtonSize, type ButtonVariant } from '@/components/0_Bruddle'
import PageContainer from '@/components/0_Bruddle/PageContainer'
import { Icon } from '@/components/Global/Icons/Icon'
import IOSInstallPWAModal from '@/components/Global/IOSInstallPWAModal'
Expand Down
2 changes: 1 addition & 1 deletion src/app/(mobile-ui)/notifications/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'

import PageContainer from '@/components/0_Bruddle/PageContainer'
import Card, { CardPosition } from '@/components/Global/Card'
import Card, { type CardPosition } from '@/components/Global/Card'
import NavHeader from '@/components/Global/NavHeader'
import PeanutLoading from '@/components/Global/PeanutLoading'
import { notificationsApi, type InAppItem } from '@/services/notifications'
Expand Down
2 changes: 1 addition & 1 deletion src/app/(mobile-ui)/points/invites/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { STAR_STRAIGHT_ICON } from '@/assets'
import Image from 'next/image'
import EmptyState from '@/components/Global/EmptyStates/EmptyState'
import { getInitialsFromName } from '@/utils'
import { PointsInvite } from '@/services/services.types'
import { type PointsInvite } from '@/services/services.types'

const InvitesPage = () => {
const router = useRouter()
Expand Down
Loading
Loading