Skip to content
Open
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
99 changes: 58 additions & 41 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState, type ComponentType, type CSSProperties } from 'react'
import React, { useEffect, useState, Suspense, type ComponentType, type CSSProperties } from 'react'
import { Routes, Route, useNavigate, useLocation } from 'react-router-dom'
import { I18nProvider } from './components/I18nProvider'
import './i18n/index.js'
Expand All @@ -7,41 +7,7 @@ import './styles/responsive.css'
import Sidebar from './components/layout/Sidebar'
import MobileHeader from './components/layout/MobileHeader'
import ConnectPanel from './components/dashboard/ConnectPanel'
import Overview from './components/dashboard/Overview'
import Account from './components/dashboard/Account'
import Transactions from './components/dashboard/Transactions'
import Contracts from './components/dashboard/Contracts'
import NetworkStats from './components/dashboard/NetworkStats'
import Faucet from './components/dashboard/Faucet'
import Builder from './components/dashboard/Builder'
import Compare from './components/dashboard/AccountComparison'
import WalletConnect from './components/dashboard/WalletConnect'
import TransactionSigner from './components/dashboard/TransactionSigner'
import PriceTicker from './components/dashboard/PriceTicker'
import PortfolioValue from './components/dashboard/PortfolioValue'
import NetworkMetricsChart from './components/charts/NetworkMetricsChart'
import AccountActivityChart from './components/charts/AccountActivityChart'
import BalanceHistoryChart from './components/charts/BalanceHistoryChart'
import AdvancedChartSuite from './components/charts/AdvancedChartSuite'
import TransactionBuilder from './components/dashboard/TransactionBuilder'
import ContractInteraction from './components/dashboard/ContractInteraction'
import ContractABI from './components/dashboard/ContractABI'
import AdvancedTransactionSimulation from './components/dashboard/AdvancedTransactionSimulation'
import TransactionSimulator from './components/dashboard/TransactionSimulator'
import DEXExplorer from './components/dashboard/DEXExplorer'
import ExplorerEmbed from './components/dashboard/ExplorerEmbed'
import RealTimeLedger from './components/dashboard/RealTimeLedger'
import Analytics from './components/dashboard/Analytics'
import SystemHealth from './components/dashboard/SystemHealth'
import Settings from './components/dashboard/Settings'
import { AssetDiscovery } from './components/assets'
import { MultisigManager } from './components/multisig'
import AuditLog from './components/dashboard/AuditLog'
import { AnchorIntegration } from './components/anchors'
import AdvancedSearch from './components/dashboard/AdvancedSearch'
import CacheStats from './components/dashboard/CacheStats'
import LiveActivityFeed from './components/dashboard/LiveActivityFeed'
import ClaimableBalances from './components/dashboard/ClaimableBalances'
import ChunkLoadingFallback from './components/ChunkLoadingFallback'
import RealTimeNotificationCenter from './components/notifications/RealTimeNotificationCenter'
import { useRealTimeNotifications } from './hooks/useRealTimeNotifications'
import { pruneCaches } from './lib/cacheManager'
Expand All @@ -61,6 +27,43 @@ import GlobalSearch from './components/search/GlobalSearch'
import UserPreferences from './components/preferences/UserPreferences'
import MobileNavigation from './components/layout/MobileNavigation'
import KeyboardNavigation from './components/accessibility/KeyboardNavigation'
import PriceTicker from './components/dashboard/PriceTicker'

// Route-based lazy-loaded components
const Overview = React.lazy(() => import('./components/dashboard/Overview'))
const Account = React.lazy(() => import('./components/dashboard/Account'))
const Transactions = React.lazy(() => import('./components/dashboard/Transactions'))
const Contracts = React.lazy(() => import('./components/dashboard/Contracts'))
const NetworkStats = React.lazy(() => import('./components/dashboard/NetworkStats'))
const Faucet = React.lazy(() => import('./components/dashboard/Faucet'))
const Builder = React.lazy(() => import('./components/dashboard/Builder'))
const Compare = React.lazy(() => import('./components/dashboard/AccountComparison'))
const WalletConnect = React.lazy(() => import('./components/dashboard/WalletConnect'))
const TransactionSigner = React.lazy(() => import('./components/dashboard/TransactionSigner'))
const PortfolioValue = React.lazy(() => import('./components/dashboard/PortfolioValue'))
const NetworkMetricsChart = React.lazy(() => import('./components/charts/NetworkMetricsChart'))
const AccountActivityChart = React.lazy(() => import('./components/charts/AccountActivityChart'))
const BalanceHistoryChart = React.lazy(() => import('./components/charts/BalanceHistoryChart'))
const AdvancedChartSuite = React.lazy(() => import('./components/charts/AdvancedChartSuite'))
const TransactionBuilder = React.lazy(() => import('./components/dashboard/TransactionBuilder'))
const ContractInteraction = React.lazy(() => import('./components/dashboard/ContractInteraction'))
const ContractABI = React.lazy(() => import('./components/dashboard/ContractABI'))
const AdvancedTransactionSimulation = React.lazy(() => import('./components/dashboard/AdvancedTransactionSimulation'))
const TransactionSimulator = React.lazy(() => import('./components/dashboard/TransactionSimulator'))
const DEXExplorer = React.lazy(() => import('./components/dashboard/DEXExplorer'))
const ExplorerEmbed = React.lazy(() => import('./components/dashboard/ExplorerEmbed'))
const RealTimeLedger = React.lazy(() => import('./components/dashboard/RealTimeLedger'))
const Analytics = React.lazy(() => import('./components/dashboard/Analytics'))
const SystemHealth = React.lazy(() => import('./components/dashboard/SystemHealth'))
const Settings = React.lazy(() => import('./components/dashboard/Settings'))
const AssetDiscovery = React.lazy(() => import('./components/assets').then(m => ({ default: m.AssetDiscovery })))
const MultisigManager = React.lazy(() => import('./components/multisig').then(m => ({ default: m.MultisigManager })))
const AuditLog = React.lazy(() => import('./components/dashboard/AuditLog'))
const AnchorIntegration = React.lazy(() => import('./components/anchors').then(m => ({ default: m.AnchorIntegration })))
const AdvancedSearch = React.lazy(() => import('./components/dashboard/AdvancedSearch'))
const CacheStats = React.lazy(() => import('./components/dashboard/CacheStats'))
const LiveActivityFeed = React.lazy(() => import('./components/dashboard/LiveActivityFeed'))
const ClaimableBalances = React.lazy(() => import('./components/dashboard/ClaimableBalances'))

interface SearchResult {
type?: string
Expand All @@ -82,10 +85,18 @@ const ChartsTab: ComponentType = () => {
>
{t('charts.title')}
</div>
<NetworkMetricsChart />
<AccountActivityChart />
<BalanceHistoryChart />
<AdvancedChartSuite />
<Suspense fallback={<ChunkLoadingFallback />}>
<NetworkMetricsChart />
</Suspense>
<Suspense fallback={<ChunkLoadingFallback />}>
<AccountActivityChart />
</Suspense>
<Suspense fallback={<ChunkLoadingFallback />}>
<BalanceHistoryChart />
</Suspense>
<Suspense fallback={<ChunkLoadingFallback />}>
<AdvancedChartSuite />
</Suspense>
</div>
)
}
Expand Down Expand Up @@ -348,7 +359,13 @@ function DashboardLayout() {
<PriceTicker />
</div>
<ErrorBoundary onRetry={handleRetry} maxRetries={2}>
{!connectedAddress ? <ConnectPanel /> : <ActiveComponent />}
{!connectedAddress ? (
<ConnectPanel />
) : (
<Suspense fallback={<ChunkLoadingFallback />}>
<ActiveComponent />
</Suspense>
)}
</ErrorBoundary>
</main>
<TourLauncher />
Expand Down
46 changes: 46 additions & 0 deletions src/components/ChunkLoadingFallback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react'

export const ChunkLoadingFallback: React.FC = () => {
return (
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
minHeight: '400px',
width: '100%',
}}
>
<div style={{ textAlign: 'center' }}>
<div
style={{
width: '48px',
height: '48px',
border: '3px solid var(--border)',
borderTop: '3px solid var(--cyan, #06b6d4)',
borderRadius: '50%',
animation: 'spin 1s linear infinite',
margin: '0 auto 16px',
}}
/>
<div
style={{
fontFamily: 'var(--font-display)',
fontSize: '16px',
color: 'var(--text-secondary)',
fontWeight: 500,
}}
>
Loading module...
</div>
<style>{`
@keyframes spin {
to { transform: rotate(360deg); }
}
`}</style>
</div>
</div>
)
}

export default ChunkLoadingFallback
25 changes: 24 additions & 1 deletion src/components/ErrorBoundary.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,24 @@ class ErrorBoundary extends React.Component {

componentDidCatch(error, errorInfo) {
// Enhanced error handling with categorization
const isChunkError =
error?.message?.includes('chunk') ||
error?.message?.includes('Failed to fetch') ||
error?.message?.includes('loading chunk');

const errorDetails = handleGlobalError(error, 'ErrorBoundary', {
componentStack: errorInfo.componentStack,
errorBoundary: this.constructor.name,
props: this.props,
retryCount: this.state.retryCount
retryCount: this.state.retryCount,
isChunkError
});

logger.error('Caught error in ErrorBoundary', {
errorBoundary: this.constructor.name,
retryCount: this.state.retryCount,
isChunkError,
message: error?.message,
}, error);

this.setState({ errorDetails });
Expand All @@ -51,10 +59,25 @@ class ErrorBoundary extends React.Component {
retryWithBackoff = async () => {
const { onRetry } = this.props;
const { retryCount } = this.state;
const isChunkError =
this.state.error?.message?.includes('chunk') ||
this.state.error?.message?.includes('Failed to fetch') ||
this.state.error?.message?.includes('loading chunk');

this.setState({ isRetrying: true });

try {
if (isChunkError) {
// For chunk loading errors, clear service worker cache and reload
if ('caches' in window) {
const cacheNames = await caches.keys();
await Promise.all(cacheNames.map(name => caches.delete(name)));
}
// Do a hard refresh to clear browser cache
window.location.reload();
return;
}

if (onRetry) {
await retryWithBackoff(onRetry, 3, 'ErrorBoundary');
}
Expand Down