first
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
// 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
|
||||
Reference in New Issue
Block a user