Add loading animation for statistics display in App component
This commit is contained in:
10
src/App.css
10
src/App.css
@@ -524,3 +524,13 @@
|
|||||||
.chat-feed::-webkit-scrollbar-thumb:hover {
|
.chat-feed::-webkit-scrollbar-thumb:hover {
|
||||||
background: var(--accent-hover);
|
background: var(--accent-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Loading Animation */
|
||||||
|
@keyframes loading-shimmer {
|
||||||
|
0% {
|
||||||
|
background-position: -200% 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 200% 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
69
src/App.tsx
69
src/App.tsx
@@ -33,6 +33,7 @@ function App() {
|
|||||||
totalPlayTime: 0,
|
totalPlayTime: 0,
|
||||||
totalKills: 0
|
totalKills: 0
|
||||||
})
|
})
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
const [recentChats, setRecentChats] = useState<ChatMessage[]>([])
|
const [recentChats, setRecentChats] = useState<ChatMessage[]>([])
|
||||||
const [topPlayers, setTopPlayers] = useState<TopPlayer[]>([])
|
const [topPlayers, setTopPlayers] = useState<TopPlayer[]>([])
|
||||||
|
|
||||||
@@ -77,8 +78,12 @@ function App() {
|
|||||||
totalPlayTime: playTime.totalPlayTime || 0,
|
totalPlayTime: playTime.totalPlayTime || 0,
|
||||||
totalKills: kills.totalKillCount || 0
|
totalKills: kills.totalKillCount || 0
|
||||||
})
|
})
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error fetching stats:', error)
|
||||||
|
setLoading(false)
|
||||||
})
|
})
|
||||||
.catch(error => console.error('Error fetching stats:', error))
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// Fetch recent chat messages (limit to top 5)
|
// Fetch recent chat messages (limit to top 5)
|
||||||
@@ -188,22 +193,74 @@ function App() {
|
|||||||
<div className="stats-container">
|
<div className="stats-container">
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-icon">👥</div>
|
<div className="stat-icon">👥</div>
|
||||||
<div className="stat-number">{stats.totalPlayers.toLocaleString()}</div>
|
<div className="stat-number">
|
||||||
|
{loading ? (
|
||||||
|
<div style={{
|
||||||
|
width: '60px',
|
||||||
|
height: '40px',
|
||||||
|
background: 'linear-gradient(90deg, var(--bg-secondary) 25%, var(--border-color) 50%, var(--bg-secondary) 75%)',
|
||||||
|
backgroundSize: '200% 100%',
|
||||||
|
animation: 'loading-shimmer 1.5s infinite',
|
||||||
|
borderRadius: '4px'
|
||||||
|
}} />
|
||||||
|
) : (
|
||||||
|
stats.totalPlayers.toLocaleString()
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="stat-label">{t('stats.totalPlayers')}</div>
|
<div className="stat-label">{t('stats.totalPlayers')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-icon"><EFBFBD></div>
|
<div className="stat-icon">🔗</div>
|
||||||
<div className="stat-number">{stats.totalConnects.toLocaleString()}</div>
|
<div className="stat-number">
|
||||||
|
{loading ? (
|
||||||
|
<div style={{
|
||||||
|
width: '60px',
|
||||||
|
height: '40px',
|
||||||
|
background: 'linear-gradient(90deg, var(--bg-secondary) 25%, var(--border-color) 50%, var(--bg-secondary) 75%)',
|
||||||
|
backgroundSize: '200% 100%',
|
||||||
|
animation: 'loading-shimmer 1.5s infinite',
|
||||||
|
borderRadius: '4px'
|
||||||
|
}} />
|
||||||
|
) : (
|
||||||
|
stats.totalConnects.toLocaleString()
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="stat-label">{t('stats.totalConnects')}</div>
|
<div className="stat-label">{t('stats.totalConnects')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-icon">⏱️</div>
|
<div className="stat-icon">⏱️</div>
|
||||||
<div className="stat-number">{formatPlayTime(stats.totalPlayTime)}</div>
|
<div className="stat-number">
|
||||||
|
{loading ? (
|
||||||
|
<div style={{
|
||||||
|
width: '80px',
|
||||||
|
height: '40px',
|
||||||
|
background: 'linear-gradient(90deg, var(--bg-secondary) 25%, var(--border-color) 50%, var(--bg-secondary) 75%)',
|
||||||
|
backgroundSize: '200% 100%',
|
||||||
|
animation: 'loading-shimmer 1.5s infinite',
|
||||||
|
borderRadius: '4px'
|
||||||
|
}} />
|
||||||
|
) : (
|
||||||
|
formatPlayTime(stats.totalPlayTime)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="stat-label">{t('stats.totalPlayTime')}</div>
|
<div className="stat-label">{t('stats.totalPlayTime')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-icon">🎯</div>
|
<div className="stat-icon">🎯</div>
|
||||||
<div className="stat-number">{stats.totalKills.toLocaleString()}</div>
|
<div className="stat-number">
|
||||||
|
{loading ? (
|
||||||
|
<div style={{
|
||||||
|
width: '60px',
|
||||||
|
height: '40px',
|
||||||
|
background: 'linear-gradient(90deg, var(--bg-secondary) 25%, var(--border-color) 50%, var(--bg-secondary) 75%)',
|
||||||
|
backgroundSize: '200% 100%',
|
||||||
|
animation: 'loading-shimmer 1.5s infinite',
|
||||||
|
borderRadius: '4px'
|
||||||
|
}} />
|
||||||
|
) : (
|
||||||
|
stats.totalKills.toLocaleString()
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="stat-label">{t('stats.totalKills')}</div>
|
<div className="stat-label">{t('stats.totalKills')}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user