refactor: remplacement système de canaux statiques par canaux virtuels depuis routing
This commit is contained in:
@@ -84,6 +84,20 @@
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
}
|
||||
|
||||
/* Canal virtuel */
|
||||
.user-item.virtual-channel {
|
||||
border-left: 3px solid var(--color-accent);
|
||||
}
|
||||
|
||||
.user-item.virtual-channel.muted {
|
||||
opacity: 0.5;
|
||||
border-left-color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.user-avatar.channel {
|
||||
background: var(--color-accent);
|
||||
}
|
||||
|
||||
/* Avatar */
|
||||
.user-avatar {
|
||||
width: 40px;
|
||||
@@ -190,3 +204,44 @@
|
||||
max-height: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Bouton mute/unmute */
|
||||
.mute-button {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: none;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
flex-shrink: 0;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.mute-button:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.mute-button:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.mute-button svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.user-item.muted .mute-button {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
color: var(--color-error);
|
||||
}
|
||||
|
||||
.channel-label {
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-accent);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@@ -1,27 +1,69 @@
|
||||
import './UserList.css';
|
||||
|
||||
/**
|
||||
* Liste des participants connectés
|
||||
* Liste des participants connectés (utilisateurs + canaux virtuels)
|
||||
*/
|
||||
export default function UserList({ participants }) {
|
||||
export default function UserList({ participants, onToggleMute }) {
|
||||
if (participants.length === 0) {
|
||||
return (
|
||||
<div className="user-list empty">
|
||||
<p className="empty-message">Aucun autre participant</p>
|
||||
<p className="empty-message">Aucun participant ou canal</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Séparer canaux virtuels et utilisateurs
|
||||
const virtualChannels = participants.filter(p => p.isVirtual);
|
||||
const users = participants.filter(p => !p.isVirtual);
|
||||
|
||||
return (
|
||||
<div className="user-list">
|
||||
<div className="user-list-header">
|
||||
<span className="user-count">
|
||||
{participants.length} participant{participants.length > 1 ? 's' : ''}
|
||||
{virtualChannels.length > 0 && `${virtualChannels.length} canal${virtualChannels.length > 1 ? 'aux' : ''}`}
|
||||
{virtualChannels.length > 0 && users.length > 0 && ' • '}
|
||||
{users.length > 0 && `${users.length} utilisateur${users.length > 1 ? 's' : ''}`}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="user-list-items">
|
||||
{participants.map((participant) => (
|
||||
{/* Canaux virtuels en premier */}
|
||||
{virtualChannels.map((participant) => (
|
||||
<div
|
||||
key={participant.identity}
|
||||
className={`user-item virtual-channel ${participant.isSpeaking ? 'speaking' : ''} ${participant.isMuted ? 'muted' : ''}`}
|
||||
>
|
||||
<div className="user-avatar channel">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" width="24" height="24">
|
||||
<path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div className="user-info">
|
||||
<span className="user-name">{participant.name}</span>
|
||||
<span className="user-status channel-label">Canal audio</span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
className="mute-button"
|
||||
onClick={() => onToggleMute(participant.identity, participant.isVirtual)}
|
||||
title={participant.isMuted ? 'Activer' : 'Désactiver'}
|
||||
>
|
||||
{participant.isMuted ? (
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Utilisateurs WebRTC */}
|
||||
{users.map((participant) => (
|
||||
<div
|
||||
key={participant.identity}
|
||||
className={`user-item ${participant.isSpeaking ? 'speaking' : ''}`}
|
||||
|
||||
Reference in New Issue
Block a user