🚀 Auto-deploy: melhoria no snap e medição AR em 25/05/2026 00:47:27
This commit is contained in:
+75
-23
@@ -1,5 +1,36 @@
|
||||
import { useEffect, useRef, useState, useMemo, Suspense } from 'react';
|
||||
import React, { useEffect, useRef, useState, useMemo, Suspense } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
|
||||
interface ErrorBoundaryProps {
|
||||
children: React.ReactNode;
|
||||
fallback: React.ReactNode;
|
||||
}
|
||||
|
||||
interface ErrorBoundaryState {
|
||||
hasError: boolean;
|
||||
}
|
||||
|
||||
class ModelErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
||||
constructor(props: ErrorBoundaryProps) {
|
||||
super(props);
|
||||
this.state = { hasError: false };
|
||||
}
|
||||
|
||||
static getDerivedStateFromError() {
|
||||
return { hasError: true };
|
||||
}
|
||||
|
||||
componentDidCatch(error: any, errorInfo: any) {
|
||||
console.error("🚨 [ModelErrorBoundary] Falha de renderização/rede no modelo:", error, errorInfo);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return this.props.fallback;
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
import { Canvas, useFrame } from '@react-three/fiber';
|
||||
import { OrbitControls, Text, useGLTF } from '@react-three/drei';
|
||||
import * as THREE from 'three';
|
||||
@@ -526,28 +557,42 @@ function ThreeMeetingRoomScene({ currentActiveModel, presentationState }: { curr
|
||||
|
||||
{/* Modelo 3D da Maquete Holográfica */}
|
||||
<group position={[0, 0.45, 0]}>
|
||||
<Suspense fallback={
|
||||
<mesh>
|
||||
<sphereGeometry args={[0.1, 16, 16]} />
|
||||
<meshBasicMaterial color="#00f5ff" wireframe />
|
||||
</mesh>
|
||||
<ModelErrorBoundary fallback={
|
||||
// Se falhar ao carregar o modelo (ex: blob local do host inacessível por convidados), renderiza o holograma geométrico
|
||||
<group rotation={[0, Date.now() * 0.00012, 0]}>
|
||||
<mesh position={[0, 0, 0]}>
|
||||
<octahedronGeometry args={[0.22]} />
|
||||
<meshStandardMaterial color="#f43f5e" transparent opacity={0.7} wireframe emissive="#f43f5e" emissiveIntensity={0.5} />
|
||||
</mesh>
|
||||
<mesh position={[0, -0.15, 0]}>
|
||||
<boxGeometry args={[0.3, 0.02, 0.3]} />
|
||||
<meshStandardMaterial color="#ef4444" transparent opacity={0.4} />
|
||||
</mesh>
|
||||
</group>
|
||||
}>
|
||||
{currentActiveModel ? (
|
||||
<HologramModel model={currentActiveModel} presentationState={presentationState} />
|
||||
) : (
|
||||
// Holograma Padrão Geométrico se não houver maquete carregada
|
||||
<group rotation={[0, Date.now() * 0.00012, 0]}>
|
||||
<mesh position={[0, 0, 0]}>
|
||||
<octahedronGeometry args={[0.22]} />
|
||||
<meshStandardMaterial color="#0ea5e9" transparent opacity={0.7} wireframe emissive="#0ea5e9" emissiveIntensity={0.5} />
|
||||
</mesh>
|
||||
<mesh position={[0, 0, 0]} rotation={[0, Math.PI / 4, 0]}>
|
||||
<boxGeometry args={[0.25, 0.04, 0.25]} />
|
||||
<meshStandardMaterial color="#3b82f6" transparent opacity={0.4} emissive="#3b82f6" emissiveIntensity={0.2} />
|
||||
</mesh>
|
||||
</group>
|
||||
)}
|
||||
</Suspense>
|
||||
<Suspense fallback={
|
||||
<mesh>
|
||||
<sphereGeometry args={[0.1, 16, 16]} />
|
||||
<meshBasicMaterial color="#00f5ff" wireframe />
|
||||
</mesh>
|
||||
}>
|
||||
{currentActiveModel ? (
|
||||
<HologramModel model={currentActiveModel} presentationState={presentationState} />
|
||||
) : (
|
||||
// Holograma Padrão Geométrico se não houver maquete carregada
|
||||
<group rotation={[0, Date.now() * 0.00012, 0]}>
|
||||
<mesh position={[0, 0, 0]}>
|
||||
<octahedronGeometry args={[0.22]} />
|
||||
<meshStandardMaterial color="#0ea5e9" transparent opacity={0.7} wireframe emissive="#0ea5e9" emissiveIntensity={0.5} />
|
||||
</mesh>
|
||||
<mesh position={[0, 0, 0]} rotation={[0, Math.PI / 4, 0]}>
|
||||
<boxGeometry args={[0.25, 0.04, 0.25]} />
|
||||
<meshStandardMaterial color="#3b82f6" transparent opacity={0.4} emissive="#3b82f6" emissiveIntensity={0.2} />
|
||||
</mesh>
|
||||
</group>
|
||||
)}
|
||||
</Suspense>
|
||||
</ModelErrorBoundary>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
@@ -721,7 +766,7 @@ export default function MeetingRoom() {
|
||||
};
|
||||
|
||||
// Inicializa o Supabase Presence & Broadcast para conexões oficiais
|
||||
const connectToRoom = (targetRoomId: string) => {
|
||||
const connectToRoom = async (targetRoomId: string) => {
|
||||
if (!name.trim()) {
|
||||
toast.error('Digite seu nome primeiro');
|
||||
return;
|
||||
@@ -742,6 +787,13 @@ export default function MeetingRoom() {
|
||||
|
||||
const finalRoomId = targetRoomId.trim().toUpperCase() || Math.random().toString(36).substring(3, 8).toUpperCase();
|
||||
setRoomCode(finalRoomId);
|
||||
|
||||
// Evita o erro "cannot add presence callbacks ... after subscribe()" removendo canais antigos com o mesmo tópico
|
||||
const oldChannels = supabase.getChannels().filter(c => c.topic === `realtime:meeting_room_${finalRoomId}`);
|
||||
for (const old of oldChannels) {
|
||||
console.log(`🔌 [MeetingRoom] Removendo canal antigo ativo para evitar conflitos: ${old.topic}`);
|
||||
await supabase.removeChannel(old);
|
||||
}
|
||||
|
||||
console.log(`🔌 [MeetingRoom] Conectando à sala: ${finalRoomId} como ${isPresenter ? 'Apresentador' : 'Convidado'}`);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user