// Mobile Speed Test Component const { useState, useEffect, useRef } = React; const MobileSpeedTest = () => { const [testState, setTestState] = useState('idle'); const [downloadSpeed, setDownloadSpeed] = useState(0); const [uploadSpeed, setUploadSpeed] = useState(0); const [ping, setPing] = useState(0); const [isp, setIsp] = useState(''); const [location, setLocation] = useState(''); const [downloadProgress, setDownloadProgress] = useState([]); const [showDetails, setShowDetails] = useState(false); const needleRef = useRef(null); const canvasRef = useRef(null); // Constants for test - will be replaced on server const TEST_SERVER = 'https://www.beanormal.com/speed-test'; const PING_TEST_COUNT = 5; const DOWNLOAD_SIZE = 96746791; // 96.7 MB const UPLOAD_SIZE = 5 * 1024 * 1024; // 5 MB // Initialize needle position and canvas on component mount useEffect(() => { if (needleRef.current) { needleRef.current.style.transform = 'rotate(-90deg) translateX(-50%)'; } // Initialize canvas initCanvas(); // Force initial needle position to -90 degrees (sola tam yatık) if (needleRef.current) { needleRef.current.style.transform = 'rotate(-90deg) translateX(-50%)'; } // Double-check with a small delay to ensure rendering is complete setTimeout(() => { if (needleRef.current) { needleRef.current.style.transform = 'rotate(-90deg) translateX(-50%)'; } }, 100); }, []); // Update needle position when download speed changes useEffect(() => { animateNeedle(downloadSpeed); }, [downloadSpeed]); // Draw the speed graph when download progress updates useEffect(() => { if (downloadProgress.length > 0) { drawSpeedGraph(); } }, [downloadProgress]); // Initialize canvas const initCanvas = () => { if (!canvasRef.current) return; const canvas = canvasRef.current; const ctx = canvas.getContext('2d'); canvas.width = canvas.offsetWidth * window.devicePixelRatio; canvas.height = canvas.offsetHeight * window.devicePixelRatio; ctx.scale(window.devicePixelRatio, window.devicePixelRatio); ctx.clearRect(0, 0, canvas.width, canvas.height); }; // Draw speed graph with real data const drawSpeedGraph = () => { if (!canvasRef.current || !downloadProgress.length) return; const canvas = canvasRef.current; const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); const width = canvas.offsetWidth; const height = canvas.offsetHeight; const maxSpeed = Math.max(...downloadProgress.map(p => p.speed)) * 1.2; // Draw the graph path ctx.beginPath(); ctx.moveTo(0, height); downloadProgress.forEach((point, index) => { const x = (index / (downloadProgress.length - 1)) * width; const y = height - (point.speed / maxSpeed) * height; ctx.lineTo(x, y); }); // Complete the path ctx.lineTo(width, height); ctx.lineTo(0, height); // Fill with gradient const gradient = ctx.createLinearGradient(0, 0, 0, height); gradient.addColorStop(0, 'rgba(138, 143, 255, 0.7)'); gradient.addColorStop(1, 'rgba(138, 143, 255, 0)'); ctx.fillStyle = gradient; ctx.fill(); // Draw the line ctx.beginPath(); ctx.moveTo(0, height); downloadProgress.forEach((point, index) => { const x = (index / (downloadProgress.length - 1)) * width; const y = height - (point.speed / maxSpeed) * height; ctx.lineTo(x, y); }); ctx.strokeStyle = '#22d3ee'; ctx.lineWidth = 2; ctx.stroke(); }; // Animate needle movement const animateNeedle = (speed) => { if (!needleRef.current) return; // If speed is 0, always set to -90 degrees explicitly (sola tam yatık) if (speed === 0) { needleRef.current.style.transform = 'rotate(-90deg) translateX(-50%)'; return; } // Calculate angle based on speed tiers (-90 to 90 degrees range, -90 = sola tam yatık, 90 = sağa tam yatık) let degrees; if (speed <= 10) { // 0-10 Mbps -> -90 to -80 degrees degrees = -90 + (speed / 10) * 10; } else if (speed <= 20) { // 10-20 Mbps -> -80 to -70 degrees degrees = -80 + ((speed - 10) / 10) * 10; } else if (speed <= 30) { // 20-30 Mbps -> -70 to -60 degrees degrees = -70 + ((speed - 20) / 10) * 10; } else if (speed <= 40) { // 30-40 Mbps -> -60 to -55 degrees degrees = -60 + ((speed - 30) / 10) * 5; } else if (speed <= 50) { // 40-50 Mbps -> -55 to -15 degrees degrees = -55 + ((speed - 40) / 10) * 40; } else if (speed <= 75) { // 50-75 Mbps -> -15 to 0 degrees degrees = -15 + ((speed - 50) / 25) * 15; } else if (speed <= 100) { // 75-100 Mbps -> 0 to 20 degrees degrees = 0 + ((speed - 75) / 25) * 20; } else if (speed <= 150) { // 100-150 Mbps -> 20 to 60 degrees degrees = 20 + ((speed - 100) / 50) * 40; } else if (speed <= 250) { // 150-250 Mbps -> 60 to 90 degrees degrees = 60 + ((speed - 150) / 100) * 30; } else { // 250+ Mbps -> capped at 90 degrees (sağa tam yatık) degrees = 90; } // Apply smooth animation const currentTransform = needleRef.current.style.transform; const currentRotation = currentTransform ? parseFloat(currentTransform.match(/rotate\(([^)]+)deg\)/)?.[1] || '-90') : -90; const duration = 300; const startTime = performance.now(); const animate = (time) => { const elapsed = time - startTime; const progress = Math.min(elapsed / duration, 1); // Ease out cubic for natural movement const easing = 1 - Math.pow(1 - progress, 3); const currentDegrees = currentRotation + (degrees - currentRotation) * easing; needleRef.current.style.transform = `rotate(${currentDegrees}deg) translateX(-50%)`; if (progress < 1) { requestAnimationFrame(animate); } }; requestAnimationFrame(animate); }; // Test ping by sending multiple small requests const testPing = async () => { const pingUrl = `${TEST_SERVER}/ping-test.php`; const pings = []; for (let i = 0; i < PING_TEST_COUNT; i++) { const start = performance.now(); try { await fetch(pingUrl, { method: 'GET', cache: 'no-store', headers: { 'Cache-Control': 'no-cache' } }); const end = performance.now(); pings.push(end - start); } catch (err) { console.error("Ping test failed", err); } } // Calculate average ping const avgPing = pings.length ? Math.round(pings.reduce((a, b) => a + b, 0) / pings.length) : 0; setPing(avgPing); return avgPing; }; // Test download speed const testDownload = async () => { const downloadUrl = `${TEST_SERVER}/download-test.php?size=${DOWNLOAD_SIZE}&cacheBust=${Date.now()}`; return new Promise((resolve) => { const xhr = new XMLHttpRequest(); let startTime; let prevLoaded = 0; let prevTime = 0; const progressUpdates = []; xhr.open('GET', downloadUrl, true); xhr.responseType = 'arraybuffer'; xhr.onloadstart = () => { startTime = performance.now(); prevTime = startTime; }; xhr.onprogress = (event) => { if (event.lengthComputable) { const currentTime = performance.now(); const loadDiff = event.loaded - prevLoaded; const timeDiff = currentTime - prevTime; if (timeDiff > 0 && loadDiff > 0) { // Calculate current speed in Mbps const currentSpeed = (loadDiff * 8) / (timeDiff / 1000) / 1000000; progressUpdates.push({ time: currentTime - startTime, loaded: event.loaded, total: event.total, speed: currentSpeed }); // Son 5 değerin ortalamasını almak için (yumuşak artış) const last5 = progressUpdates.slice(-5); const avgSpeed = last5.reduce((sum, item) => sum + item.speed, 0) / last5.length; setDownloadProgress([...progressUpdates]); setDownloadSpeed(avgSpeed); // Ortalama hızı göster (daha yumuşak artış) prevLoaded = event.loaded; prevTime = currentTime; } } }; xhr.onload = () => { if (xhr.status === 200) { const endTime = performance.now(); const duration = (endTime - startTime) / 1000; // seconds const size = xhr.response.byteLength * 8; // bits // Calculate final speed in Mbps const finalSpeed = size / duration / 1000000; resolve(finalSpeed); } else { console.error("Download test failed with status", xhr.status); resolve(0); } }; xhr.onerror = () => { console.error("XHR download error"); resolve(0); }; xhr.send(); }); }; // Test upload speed const testUpload = async () => { const uploadUrl = `${TEST_SERVER}/upload-test.php?cacheBust=${Date.now()}`; // Create data to upload (random binary data) const data = new ArrayBuffer(UPLOAD_SIZE); const view = new Uint8Array(data); for (let i = 0; i < UPLOAD_SIZE; i++) { view[i] = Math.floor(Math.random() * 256); } return new Promise((resolve) => { const xhr = new XMLHttpRequest(); const startTime = performance.now(); xhr.open('POST', uploadUrl, true); xhr.setRequestHeader('Content-Type', 'application/octet-stream'); xhr.onload = () => { if (xhr.status === 200) { const endTime = performance.now(); const duration = (endTime - startTime) / 1000; // seconds const size = UPLOAD_SIZE * 8; // bits // Calculate upload speed in Mbps const speed = size / duration / 1000000; resolve(speed); } else { console.error("Upload test failed with status", xhr.status); resolve(0); } }; xhr.onerror = () => { console.error("XHR upload error"); resolve(0); }; xhr.send(data); }); }; // Start the complete speed test const startTest = async () => { if (testState === 'running') return; setTestState('running'); setDownloadSpeed(0); setUploadSpeed(0); setPing(0); setDownloadProgress([]); // Reset needle to -90 degrees (sola tam yatık) when starting test if (needleRef.current) { needleRef.current.style.transform = 'rotate(-90deg) translateX(-50%)'; } // Get network info fetch('https://ipapi.co/json/') .then(res => res.json()) .then(data => { setIsp(data.org?.split(' ')[0] || 'Unknown ISP'); setLocation(`${data.city || ''}, ${data.region_code || ''}`); }) .catch(err => { console.error("Failed to get network info", err); setIsp('Unknown ISP'); setLocation('Unknown'); }); try { // 1. Test ping await testPing(); // 2. Test download speed const dlSpeed = await testDownload(); setDownloadSpeed(dlSpeed); // 3. Test upload speed const ulSpeed = await testUpload(); setUploadSpeed(ulSpeed); setTestState('completed'); } catch (err) { console.error("Speed test failed", err); setTestState('error'); } }; return (

Internet Speed Test

{/* Top metrics row */}
PING
{ping}
ms
DOWNLOAD
{downloadSpeed.toFixed(2)}
Mbps
UPLOAD
{uploadSpeed.toFixed(2)}
Mbps
{/* Speed chart */}
{/* Speedometer using the web version's gauge images */}
{/* Speedometer gauge background */} Speedometer {/* Needle */}
Needle
{/* Speed value display */}
{downloadSpeed.toFixed(2)}
Mbps
{/* Start button */}
{/* ISP Info */}
SERVICE PROVIDER
{isp || 'Detecting...'}
LOCATION
{location || 'Detecting...'}
{/* Details section toggle */}
{/* Details section */} {showDetails && (

Test Details

Test Method: XMLHttpRequest with real file transfer

Test Server: {TEST_SERVER.replace(/^https?:\/\//, '')}

Download Test Size: {DOWNLOAD_SIZE / (1024 * 1024)} MB

Upload Test Size: {UPLOAD_SIZE / (1024 * 1024)} MB

)}
); }; // Make it globally available window.MobileSpeedTest = MobileSpeedTest;