🚀 Auto-deploy: melhoria no snap e medição AR em 25/05/2026 16:24:11
This commit is contained in:
+43
-18
@@ -236,12 +236,12 @@ function MiiAvatarBackup({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<group ref={groupRef} position={positionVector} rotation={rotationEuler} scale={scale}>
|
<group ref={groupRef} position={positionVector} rotation={rotationEuler} scale={scale}>
|
||||||
<group position={[0, 0.32, 0]}>
|
<group position={[0, 0.38, 0]}>
|
||||||
<mesh position={[0, 0, -0.001]}>
|
<mesh position={[0, 0, -0.001]}>
|
||||||
<planeGeometry args={[0.22, 0.05]} />
|
<planeGeometry args={[0.44, 0.1]} />
|
||||||
<meshBasicMaterial color="#0f172a" transparent opacity={0.7} />
|
<meshBasicMaterial color="#0f172a" transparent opacity={0.7} />
|
||||||
</mesh>
|
</mesh>
|
||||||
<Text fontSize={0.024} color="#ffffff" anchorX="center" anchorY="middle" fontWeight="bold">
|
<Text fontSize={0.048} color="#ffffff" anchorX="center" anchorY="middle" fontWeight="bold">
|
||||||
{username}
|
{username}
|
||||||
</Text>
|
</Text>
|
||||||
</group>
|
</group>
|
||||||
@@ -253,9 +253,9 @@ function MiiAvatarBackup({
|
|||||||
</mesh>
|
</mesh>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Cabeça do Avatar: Toon Head do Dicebear em HTML Billboard para resiliência contra erros de CORS/Rede */}
|
{/* Cabeça do Avatar: Toon Head do Dicebear em HTML Transformado em 3D Plano (olhando para a mesa) */}
|
||||||
<group ref={headRef} position={[0, 0.16, 0]}>
|
<group ref={headRef} position={[0, 0.16, 0]}>
|
||||||
<Html center distanceFactor={1.2}>
|
<Html transform center distanceFactor={1.2}>
|
||||||
<div className="w-16 h-16 flex items-center justify-center pointer-events-none select-none">
|
<div className="w-16 h-16 flex items-center justify-center pointer-events-none select-none">
|
||||||
<img
|
<img
|
||||||
src={avatarUrl}
|
src={avatarUrl}
|
||||||
@@ -447,14 +447,14 @@ function RealMiiAvatar({
|
|||||||
}, [props.rotation]);
|
}, [props.rotation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group ref={groupRef} position={positionVector} rotation={rotationEuler}>
|
<group ref={groupRef} position={positionVector} rotation={rotationEuler} scale={scale}>
|
||||||
{/* Balão de Nome acima da cabeça */}
|
{/* Balão de Nome acima da cabeça */}
|
||||||
<group position={[0, 0.76, 0]}>
|
<group position={[0, 0.9, 0]}>
|
||||||
<mesh position={[0, 0, -0.001]}>
|
<mesh position={[0, 0, -0.001]}>
|
||||||
<planeGeometry args={[0.26, 0.055]} />
|
<planeGeometry args={[0.52, 0.11]} />
|
||||||
<meshBasicMaterial color="#0f172a" transparent opacity={0.8} />
|
<meshBasicMaterial color="#0f172a" transparent opacity={0.8} />
|
||||||
</mesh>
|
</mesh>
|
||||||
<Text fontSize={0.026} color="#ffffff" anchorX="center" anchorY="middle" fontWeight="bold">
|
<Text fontSize={0.052} color="#ffffff" anchorX="center" anchorY="middle" fontWeight="bold">
|
||||||
{username}
|
{username}
|
||||||
</Text>
|
</Text>
|
||||||
</group>
|
</group>
|
||||||
@@ -832,12 +832,38 @@ export default function MeetingRoom() {
|
|||||||
|
|
||||||
// Estados da Sala (Conexão Supabase)
|
// Estados da Sala (Conexão Supabase)
|
||||||
const [peers, setPeers] = useState<Record<string, PeerData>>({});
|
const [peers, setPeers] = useState<Record<string, PeerData>>({});
|
||||||
|
|
||||||
|
const mergedPeers = useMemo(() => {
|
||||||
|
if (!inRoom) return peers;
|
||||||
|
|
||||||
|
// Se o usuário local já estiver no peers (via sync do Supabase), usamos o peers original
|
||||||
|
if (peers[selectedChair]) {
|
||||||
|
return peers;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caso contrário, injetamos o usuário local no dicionário temporário
|
||||||
|
return {
|
||||||
|
...peers,
|
||||||
|
[selectedChair]: {
|
||||||
|
presence_ref: 'local_user',
|
||||||
|
username: name,
|
||||||
|
avatarId: selectedAvatar,
|
||||||
|
chairId: selectedChair,
|
||||||
|
role: isPresenter ? 'presenter' : 'guest',
|
||||||
|
cargo: cargo || 'Apresentador',
|
||||||
|
empresa: empresa || 'SteelXR Corp',
|
||||||
|
joinedAt: new Date().toISOString()
|
||||||
|
} as unknown as PeerData
|
||||||
|
};
|
||||||
|
}, [peers, inRoom, selectedChair, name, selectedAvatar, isPresenter, cargo, empresa]);
|
||||||
|
|
||||||
|
const activeUsersCount = useMemo(() => Object.keys(mergedPeers).length, [mergedPeers]);
|
||||||
|
|
||||||
const [chatMessages, setChatMessages] = useState<{ id: string; sender: string; text: string; time: string }[]>([]);
|
const [chatMessages, setChatMessages] = useState<{ id: string; sender: string; text: string; time: string }[]>([]);
|
||||||
const [newMessage, setNewMessage] = useState('');
|
const [newMessage, setNewMessage] = useState('');
|
||||||
const [lastChatSender, setLastChatSender] = useState<string | null>(null);
|
const [lastChatSender, setLastChatSender] = useState<string | null>(null);
|
||||||
const [isMuted, setIsMuted] = useState(false);
|
const [isMuted, setIsMuted] = useState(false);
|
||||||
const [isTalking, setIsTalking] = useState(false);
|
const [isTalking, setIsTalking] = useState(false);
|
||||||
const [activeUsersCount, setActiveUsersCount] = useState(1);
|
|
||||||
const [roomCode, setRoomCode] = useState(roomId || '');
|
const [roomCode, setRoomCode] = useState(roomId || '');
|
||||||
const [activeTab, setActiveTab] = useState<'chat' | 'participants'>('participants');
|
const [activeTab, setActiveTab] = useState<'chat' | 'participants'>('participants');
|
||||||
|
|
||||||
@@ -1050,7 +1076,6 @@ export default function MeetingRoom() {
|
|||||||
});
|
});
|
||||||
console.log("👥 [Presence Sync] Peers formatados:", formattedPeers);
|
console.log("👥 [Presence Sync] Peers formatados:", formattedPeers);
|
||||||
setPeers(formattedPeers);
|
setPeers(formattedPeers);
|
||||||
setActiveUsersCount(count);
|
|
||||||
})
|
})
|
||||||
.on('presence', { event: 'join' }, ({ key, newPresences }) => {
|
.on('presence', { event: 'join' }, ({ key, newPresences }) => {
|
||||||
if (key.startsWith('lobby_watcher_')) return;
|
if (key.startsWith('lobby_watcher_')) return;
|
||||||
@@ -1405,8 +1430,8 @@ export default function MeetingRoom() {
|
|||||||
<ThreeMeetingRoomScene currentActiveModel={activeModel} presentationState={presentationState} isFocusMode={isFocusMode} />
|
<ThreeMeetingRoomScene currentActiveModel={activeModel} presentationState={presentationState} isFocusMode={isFocusMode} />
|
||||||
|
|
||||||
{/* Renderizar avatares de TODOS na sala - Ocultados se modo foco */}
|
{/* Renderizar avatares de TODOS na sala - Ocultados se modo foco */}
|
||||||
{!isFocusMode && Object.keys(peers).map((chairKey) => {
|
{!isFocusMode && Object.keys(mergedPeers).map((chairKey) => {
|
||||||
const peer = peers[chairKey];
|
const peer = mergedPeers[chairKey];
|
||||||
const chairConf = CHAIRS.find((c) => c.id === peer.chairId);
|
const chairConf = CHAIRS.find((c) => c.id === peer.chairId);
|
||||||
if (!chairConf) return null;
|
if (!chairConf) return null;
|
||||||
|
|
||||||
@@ -1414,14 +1439,14 @@ export default function MeetingRoom() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MiiAvatar
|
<MiiAvatar
|
||||||
key={peer.presence_ref}
|
key={peer.presence_ref || 'local'}
|
||||||
avatarId={peer.avatarId}
|
avatarId={peer.avatarId}
|
||||||
username={isLocalUser ? `${peer.username} (Você)` : peer.username}
|
username={isLocalUser ? `${peer.username} (Você)` : peer.username}
|
||||||
position={chairConf.pos}
|
position={chairConf.pos}
|
||||||
rotation={chairConf.rot}
|
rotation={chairConf.rot}
|
||||||
isTalking={isLocalUser ? isTalking : (isTalking && Math.random() > 0.5)}
|
isTalking={isLocalUser ? isTalking : (isTalking && Math.random() > 0.5)}
|
||||||
isLastChatSender={peer.username === lastChatSender}
|
isLastChatSender={peer.username === lastChatSender}
|
||||||
scale={1.2}
|
scale={4.8}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -1711,13 +1736,13 @@ export default function MeetingRoom() {
|
|||||||
<span className="font-mono text-[9px] text-muted-foreground uppercase tracking-widest block mb-2">Quem está na sala</span>
|
<span className="font-mono text-[9px] text-muted-foreground uppercase tracking-widest block mb-2">Quem está na sala</span>
|
||||||
|
|
||||||
<div className="space-y-2.5">
|
<div className="space-y-2.5">
|
||||||
{Object.keys(peers).map((chairKey) => {
|
{Object.keys(mergedPeers).map((chairKey) => {
|
||||||
const peer = peers[chairKey];
|
const peer = mergedPeers[chairKey];
|
||||||
const isHost = peer.role === 'presenter';
|
const isHost = peer.role === 'presenter';
|
||||||
const isMe = peer.chairId === selectedChair;
|
const isMe = peer.chairId === selectedChair;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={peer.presence_ref} className="rounded-lg border border-slate-800 bg-slate-950/40 p-3 flex items-start gap-3 relative overflow-hidden">
|
<div key={peer.presence_ref || 'local'} className="rounded-lg border border-slate-800 bg-slate-950/40 p-3 flex items-start gap-3 relative overflow-hidden">
|
||||||
{/* Indicador da cor do avatar do Mii via classe estática Tailwind */}
|
{/* Indicador da cor do avatar do Mii via classe estática Tailwind */}
|
||||||
<div className={`h-8 w-8 rounded-full border border-slate-800 flex items-center justify-center font-bold text-xs ${getAvatarBgClass(peer.avatarId)}`}>
|
<div className={`h-8 w-8 rounded-full border border-slate-800 flex items-center justify-center font-bold text-xs ${getAvatarBgClass(peer.avatarId)}`}>
|
||||||
{peer.username.substring(0, 2).toUpperCase()}
|
{peer.username.substring(0, 2).toUpperCase()}
|
||||||
|
|||||||
Reference in New Issue
Block a user