Add loading skeletons for server data display in Servers component

This commit is contained in:
2025-10-05 08:14:39 +08:00
parent 193a4ff1a0
commit 1ce25cf0f2

View File

@@ -32,6 +32,37 @@ interface ServerData extends ServerInfo {
error?: string
}
// Loading Skeleton Component
const LoadingSkeleton = ({ width = '60px', height = '40px', className = '', style = {} }: { width?: string, height?: string, className?: string, style?: React.CSSProperties }) => (
<div
className={`loading-skeleton ${className}`}
style={{
width,
height,
background: 'linear-gradient(135deg, var(--bg-secondary) 0%, var(--border-color) 50%, var(--bg-secondary) 100%)',
backgroundSize: '200% 200%',
animation: 'loading-shimmer 2.5s infinite ease-in-out, loading-pulse 1.5s infinite ease-in-out',
borderRadius: '8px',
position: 'relative',
overflow: 'hidden',
boxShadow: '0 2px 8px rgba(0,0,0,0.08)',
...style
}}
>
<div
style={{
position: 'absolute',
top: 0,
left: '-100%',
width: '100%',
height: '100%',
background: 'linear-gradient(90deg, transparent, rgba(255,255,255,0.12), transparent)',
animation: 'loading-wave 2.5s infinite ease-in-out'
}}
/>
</div>
)
function Servers() {
const { t } = useTranslation()
@@ -49,6 +80,7 @@ function Servers() {
]
const [servers, setServers] = useState<ServerData[]>([])
const [loading, setLoading] = useState(true)
const [selectedCategory, setSelectedCategory] = useState<string>('All')
// Fetch A2S data for all servers
@@ -84,6 +116,7 @@ function Servers() {
const results = await Promise.all(serverPromises)
setServers(results)
setLoading(false)
}
fetchServerData()
@@ -174,7 +207,69 @@ function Servers() {
gridTemplateColumns: 'repeat(auto-fit, minmax(350px, 1fr))',
gap: '2rem'
}}>
{filteredServers.map((server) => (
{loading ? (
// Show loading skeletons
serverList.map((_, index) => (
<div
key={`loading-${index}`}
className="server-card"
style={{
background: 'var(--bg-secondary)',
border: '1px solid var(--border-color)',
borderRadius: '12px',
padding: '1.5rem',
position: 'relative',
overflow: 'hidden'
}}
>
{/* Status Indicator Skeleton */}
<div style={{
position: 'absolute',
top: '1rem',
right: '1rem',
width: '12px',
height: '12px',
borderRadius: '50%',
backgroundColor: 'var(--border-color)'
}} />
{/* Server Header Skeleton */}
<div style={{ marginBottom: '1rem' }}>
<LoadingSkeleton width="180px" height="24px" style={{ marginBottom: '0.5rem' }} />
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
<LoadingSkeleton width="60px" height="16px" />
<LoadingSkeleton width="40px" height="16px" />
</div>
</div>
{/* Server Stats Skeleton */}
<div style={{
display: 'grid',
gridTemplateColumns: '1fr 1fr',
gap: '1rem',
marginBottom: '1rem'
}}>
<div>
<LoadingSkeleton width="50px" height="14px" style={{ marginBottom: '0.25rem' }} />
<LoadingSkeleton width="40px" height="20px" />
</div>
<div>
<LoadingSkeleton width="50px" height="14px" style={{ marginBottom: '0.25rem' }} />
<LoadingSkeleton width="40px" height="20px" />
</div>
</div>
{/* Server Details Skeleton */}
<div style={{ marginBottom: '1rem' }}>
<LoadingSkeleton width="120px" height="16px" style={{ marginBottom: '0.5rem' }} />
</div>
{/* Join Button Skeleton */}
<LoadingSkeleton width="100%" height="40px" />
</div>
))
) : (
filteredServers.map((server) => (
<div
key={`${server.ip}:${server.port}`}
className="server-card"
@@ -328,7 +423,8 @@ function Servers() {
{server.status === 'online' ? t('servers.joinServer') : t('servers.offline')}
</button>
</div>
))}
))
)}
</div>
</section>