152 lines
4.1 KiB
TypeScript
152 lines
4.1 KiB
TypeScript
// Created by: Claude
|
|
// Date: 2026-01-03
|
|
// Purpose: Indicateur de qualité de connexion WebRTC
|
|
// Refs: client/CLAUDE.md
|
|
|
|
import React, { useEffect, useState } from 'react'
|
|
import styles from './ConnectionIndicator.module.css'
|
|
|
|
export interface ConnectionIndicatorProps {
|
|
peerConnection?: RTCPeerConnection
|
|
peerId: string
|
|
username: string
|
|
}
|
|
|
|
type ConnectionQuality = 'excellent' | 'good' | 'poor' | 'disconnected'
|
|
|
|
const ConnectionIndicator: React.FC<ConnectionIndicatorProps> = ({
|
|
peerConnection,
|
|
peerId,
|
|
username,
|
|
}) => {
|
|
const [quality, setQuality] = useState<ConnectionQuality>('disconnected')
|
|
const [stats, setStats] = useState<{
|
|
rtt?: number // Round-trip time en ms
|
|
packetsLost?: number
|
|
jitter?: number // En ms
|
|
}>({})
|
|
|
|
useEffect(() => {
|
|
if (!peerConnection) {
|
|
setQuality('disconnected')
|
|
return
|
|
}
|
|
|
|
// Surveiller l'état de connexion
|
|
const handleConnectionStateChange = () => {
|
|
const state = peerConnection.connectionState
|
|
console.log(`[${username}] Connection state:`, state)
|
|
|
|
if (state === 'connected') {
|
|
setQuality('good')
|
|
} else if (state === 'connecting') {
|
|
setQuality('poor')
|
|
} else if (state === 'disconnected' || state === 'failed' || state === 'closed') {
|
|
setQuality('disconnected')
|
|
}
|
|
}
|
|
|
|
peerConnection.addEventListener('connectionstatechange', handleConnectionStateChange)
|
|
handleConnectionStateChange() // État initial
|
|
|
|
// Récupérer les stats toutes les 2 secondes
|
|
const statsInterval = setInterval(async () => {
|
|
if (peerConnection.connectionState !== 'connected') {
|
|
return
|
|
}
|
|
|
|
try {
|
|
const stats = await peerConnection.getStats()
|
|
let rtt: number | undefined
|
|
let packetsLost = 0
|
|
let jitter: number | undefined
|
|
|
|
stats.forEach((report) => {
|
|
if (report.type === 'candidate-pair' && report.state === 'succeeded') {
|
|
rtt = report.currentRoundTripTime ? report.currentRoundTripTime * 1000 : undefined
|
|
}
|
|
|
|
if (report.type === 'inbound-rtp' && report.kind === 'video') {
|
|
packetsLost = report.packetsLost || 0
|
|
jitter = report.jitter ? report.jitter * 1000 : undefined
|
|
}
|
|
})
|
|
|
|
setStats({ rtt, packetsLost, jitter })
|
|
|
|
// Déterminer la qualité selon RTT
|
|
if (rtt !== undefined) {
|
|
if (rtt < 100) {
|
|
setQuality('excellent')
|
|
} else if (rtt < 200) {
|
|
setQuality('good')
|
|
} else {
|
|
setQuality('poor')
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error getting WebRTC stats:', error)
|
|
}
|
|
}, 2000)
|
|
|
|
return () => {
|
|
peerConnection.removeEventListener('connectionstatechange', handleConnectionStateChange)
|
|
clearInterval(statsInterval)
|
|
}
|
|
}, [peerConnection, username])
|
|
|
|
const getQualityIcon = () => {
|
|
switch (quality) {
|
|
case 'excellent':
|
|
return '📶'
|
|
case 'good':
|
|
return '📡'
|
|
case 'poor':
|
|
return '⚠️'
|
|
case 'disconnected':
|
|
return '❌'
|
|
}
|
|
}
|
|
|
|
const getQualityLabel = () => {
|
|
switch (quality) {
|
|
case 'excellent':
|
|
return 'Excellente'
|
|
case 'good':
|
|
return 'Bonne'
|
|
case 'poor':
|
|
return 'Faible'
|
|
case 'disconnected':
|
|
return 'Déconnecté'
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className={`${styles.indicator} ${styles[quality]}`} title={getTooltip()}>
|
|
<span className={styles.icon}>{getQualityIcon()}</span>
|
|
<span className={styles.label}>{getQualityLabel()}</span>
|
|
</div>
|
|
)
|
|
|
|
function getTooltip(): string {
|
|
if (quality === 'disconnected') {
|
|
return 'Pas de connexion'
|
|
}
|
|
|
|
const parts: string[] = []
|
|
if (stats.rtt !== undefined) {
|
|
parts.push(`RTT: ${stats.rtt.toFixed(0)}ms`)
|
|
}
|
|
if (stats.packetsLost !== undefined && stats.packetsLost > 0) {
|
|
parts.push(`Paquets perdus: ${stats.packetsLost}`)
|
|
}
|
|
if (stats.jitter !== undefined) {
|
|
parts.push(`Jitter: ${stats.jitter.toFixed(1)}ms`)
|
|
}
|
|
|
|
return parts.length > 0 ? parts.join(' | ') : getQualityLabel()
|
|
}
|
|
}
|
|
|
|
export default ConnectionIndicator
|