From b949a7c2276cb1d8fa62a14260fcbddff435ab8f Mon Sep 17 00:00:00 2001 From: admtracksteel Date: Mon, 25 May 2026 11:35:38 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20Auto-deploy:=20melhoria=20no=20s?= =?UTF-8?q?nap=20e=20medi=C3=A7=C3=A3o=20AR=20em=2025/05/2026=2011:35:38?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/MeetingRoom.tsx | 103 ++++++++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 15 deletions(-) diff --git a/src/pages/MeetingRoom.tsx b/src/pages/MeetingRoom.tsx index 319a596..25cad6b 100644 --- a/src/pages/MeetingRoom.tsx +++ b/src/pages/MeetingRoom.tsx @@ -78,14 +78,14 @@ interface PeerData { // --- Configurações da Mesa de Reunião --- const CHAIRS = [ - { id: '1', name: 'Assento 1', pos: [-1.2, 0.95, -1.45], lookAt: [0, 0.95, 0] }, - { id: '2', name: 'Assento 2', pos: [-0.4, 0.95, -1.45], lookAt: [0, 0.95, 0] }, - { id: '3', name: 'Assento 3', pos: [0.4, 0.95, -1.45], lookAt: [0, 0.95, 0] }, - { id: '4', name: 'Assento 4', pos: [1.2, 0.95, -1.45], lookAt: [0, 0.95, 0] }, - { id: '5', name: 'Assento 5', pos: [1.2, 0.95, 1.45], lookAt: [0, 0.95, 0] }, - { id: '6', name: 'Assento 6', pos: [0.4, 0.95, 1.45], lookAt: [0, 0.95, 0] }, - { id: '7', name: 'Assento 7', pos: [-0.4, 0.95, 1.45], lookAt: [0, 0.95, 0] }, - { id: '8', name: 'Assento 8', pos: [-1.2, 0.95, 1.45], lookAt: [0, 0.95, 0] }, + { id: '1', name: 'Assento 1', pos: [-1.2, 0.95, -1.45], rot: [0, 0, 0], lookAt: [0, 0.95, 0] }, + { id: '2', name: 'Assento 2', pos: [-0.4, 0.95, -1.45], rot: [0, 0, 0], lookAt: [0, 0.95, 0] }, + { id: '3', name: 'Assento 3', pos: [0.4, 0.95, -1.45], rot: [0, 0, 0], lookAt: [0, 0.95, 0] }, + { id: '4', name: 'Assento 4', pos: [1.2, 0.95, -1.45], rot: [0, 0, 0], lookAt: [0, 0.95, 0] }, + { id: '5', name: 'Assento 5', pos: [1.2, 0.95, 1.45], rot: [0, Math.PI, 0], lookAt: [0, 0.95, 0] }, + { id: '6', name: 'Assento 6', pos: [0.4, 0.95, 1.45], rot: [0, Math.PI, 0], lookAt: [0, 0.95, 0] }, + { id: '7', name: 'Assento 7', pos: [-0.4, 0.95, 1.45], rot: [0, Math.PI, 0], lookAt: [0, 0.95, 0] }, + { id: '8', name: 'Assento 8', pos: [-1.2, 0.95, 1.45], rot: [0, Math.PI, 0], lookAt: [0, 0.95, 0] }, ]; const AVATAR_PRESETS: AvatarPreset[] = [ @@ -123,8 +123,33 @@ const getAvatarBgClass = (avatarId: number): string => { // --- Subcomponentes 3D --- // Avatar estilo Mii (Nintendo Wii) - Versão Melhorada (Mais Feliz e Expressivo) -function MiiAvatar({ avatarId, username, isTalking, scale = 1, ...props }: { avatarId: number; username: string; isTalking?: boolean; scale?: number } & Record) { +function MiiAvatar({ + avatarId, + username, + isTalking, + scale = 1, + isLastChatSender = false, + ...props +}: { + avatarId: number; + username: string; + isTalking?: boolean; + scale?: number; + isLastChatSender?: boolean; +} & Record) { const groupRef = useRef(null); + const ringRef = useRef(null); + const lastSenderRef = useRef(isLastChatSender); + const pulseEndTimeRef = useRef(0); + + // Monitora quando este avatar se torna o último a escrever no chat + useEffect(() => { + if (isLastChatSender && !lastSenderRef.current) { + // Pulso rápido de 1.5 segundos (pisca 3 vezes rápido) + pulseEndTimeRef.current = Date.now() + 1500; + } + lastSenderRef.current = isLastChatSender; + }, [isLastChatSender]); const mii = useMemo(() => { const skin = SKIN_COLORS[(avatarId - 1) % SKIN_COLORS.length]; @@ -143,9 +168,38 @@ function MiiAvatar({ avatarId, username, isTalking, scale = 1, ...props }: { ava }, [avatarId]); useFrame((state) => { + // Animação de flutuação original if (groupRef.current && props.position && Array.isArray(props.position) && props.position.length >= 2) { groupRef.current.position.y = (props.position[1] as number) + Math.sin(state.clock.getElapsedTime() * 2 + avatarId) * 0.012; } + + // Animação avançada do anel de fala / chat + if (ringRef.current) { + const now = Date.now(); + const material = ringRef.current.material as THREE.MeshBasicMaterial; + + if (isTalking) { + // Modo Microfone: Azul + material.color.set('#3b82f6'); + material.opacity = 0.6 + Math.sin(state.clock.getElapsedTime() * 10) * 0.3; + ringRef.current.scale.set(1, 1, 1); + } else if (isLastChatSender) { + // Modo Chat: Amarelo + material.color.set('#eab308'); + + const isPulsing = now < pulseEndTimeRef.current; + if (isPulsing) { + // Pulsação rápida inicial + material.opacity = 0.5 + Math.sin(state.clock.getElapsedTime() * 25) * 0.4; + const s = 1.0 + Math.sin(state.clock.getElapsedTime() * 25) * 0.15; + ringRef.current.scale.set(s, s, 1); + } else { + // Amarelo constante pós-pulsação + material.opacity = 0.75; + ringRef.current.scale.set(1, 1, 1); + } + } + } }); const positionVector = useMemo(() => { @@ -175,11 +229,15 @@ function MiiAvatar({ avatarId, username, isTalking, scale = 1, ...props }: { ava - {/* Indicador de Fala (anel pulsante) */} - {isTalking && ( - - - + {/* Indicador de Fala & Chat (anel abaixo da cabeça e 50% menor) */} + {(isTalking || isLastChatSender) && ( + + + )} @@ -735,6 +793,7 @@ export default function MeetingRoom() { const [peers, setPeers] = useState>({}); const [chatMessages, setChatMessages] = useState<{ id: string; sender: string; text: string; time: string }[]>([]); const [newMessage, setNewMessage] = useState(''); + const [lastChatSender, setLastChatSender] = useState(null); const [isMuted, setIsMuted] = useState(false); const [isTalking, setIsTalking] = useState(false); const [activeUsersCount, setActiveUsersCount] = useState(1); @@ -972,6 +1031,7 @@ export default function MeetingRoom() { time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), }, ]); + setLastChatSender(payload.payload.sender); }); // Escuta atualizações de Apresentação da Maquete via Broadcast (apenas para convidados) @@ -1045,14 +1105,25 @@ export default function MeetingRoom() { // Enviar Mensagem de Chat const handleSendMessage = () => { if (!newMessage.trim() || !channelRef.current) return; + const senderName = name.substring(0, 10); channelRef.current.send({ type: 'broadcast', event: 'chat_msg', payload: { - sender: name.substring(0, 10), + sender: senderName, text: newMessage.trim(), }, }); + setChatMessages((prev) => [ + ...prev, + { + id: Math.random().toString(), + sender: senderName, + text: newMessage.trim(), + time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), + }, + ]); + setLastChatSender(senderName); setNewMessage(''); }; @@ -1295,7 +1366,9 @@ export default function MeetingRoom() { avatarId={peer.avatarId} username={isLocalUser ? `${peer.username} (Você)` : peer.username} position={chairConf.pos} + rotation={chairConf.rot} isTalking={isLocalUser ? isTalking : (isTalking && Math.random() > 0.5)} + isLastChatSender={peer.username === lastChatSender} scale={1.2} /> );