67 lines
1.9 KiB
TypeScript
67 lines
1.9 KiB
TypeScript
import { useState, useCallback } from 'react'
|
|
import { ThemeProvider } from './contexts/ThemeContext'
|
|
import FloatingHeader from './components/FloatingHeader'
|
|
import IpInput from './components/IpInput'
|
|
import LatencyMap from './components/LatencyMap'
|
|
import ResultsPanel from './components/ResultsPanel'
|
|
import { testAllNodes } from './api/latency'
|
|
import { LatencyResult } from '@shared/types'
|
|
import './styles/index.css'
|
|
|
|
function AppContent() {
|
|
const [results, setResults] = useState<Map<string, LatencyResult>>(new Map())
|
|
const [testing, setTesting] = useState(false)
|
|
const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null)
|
|
|
|
const handleTest = useCallback(async (target: string) => {
|
|
setTesting(true)
|
|
setResults(new Map())
|
|
setSelectedNodeId(null)
|
|
|
|
await testAllNodes(target, (result) => {
|
|
setResults((prev) => new Map(prev).set(result.nodeId, result))
|
|
})
|
|
|
|
setTesting(false)
|
|
}, [])
|
|
|
|
const handleNodeSelect = useCallback((nodeId: string | null) => {
|
|
setSelectedNodeId(nodeId)
|
|
}, [])
|
|
|
|
return (
|
|
<div className="app">
|
|
<FloatingHeader />
|
|
|
|
<main className="app-main" style={{ paddingTop: '5rem' }}>
|
|
<p className="app-description">
|
|
Test network latency from global locations to any IP address or domain
|
|
</p>
|
|
<IpInput onTest={handleTest} testing={testing} />
|
|
<LatencyMap
|
|
results={results}
|
|
selectedNodeId={selectedNodeId}
|
|
onNodeSelect={handleNodeSelect}
|
|
/>
|
|
<ResultsPanel
|
|
results={results}
|
|
selectedNodeId={selectedNodeId}
|
|
onNodeSelect={handleNodeSelect}
|
|
/>
|
|
</main>
|
|
|
|
<footer className="app-footer">
|
|
<p>© 2024 Latency Test. Powered by GlobalPing.</p>
|
|
</footer>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function App() {
|
|
return (
|
|
<ThemeProvider>
|
|
<AppContent />
|
|
</ThemeProvider>
|
|
)
|
|
}
|