🚀 Auto-deploy: melhoria no snap e medição AR em 25/05/2026 11:15:46
This commit is contained in:
+73
-11
@@ -43,7 +43,7 @@ import { toast } from 'sonner';
|
||||
import {
|
||||
Users, MessageSquare, Mic, MicOff, Home, Send,
|
||||
ArrowRight, ChevronLeft, ChevronRight, Share2, LogOut,
|
||||
RotateCw, Award, ShieldAlert, Maximize2, Minimize2
|
||||
RotateCw, Award, ShieldAlert, Maximize2, Minimize2, Box
|
||||
} from 'lucide-react';
|
||||
|
||||
// Interfaces tipadas para evitar any
|
||||
@@ -58,9 +58,12 @@ interface PresentationState {
|
||||
rotation: number[];
|
||||
scale: number;
|
||||
sectionEnabled: boolean;
|
||||
sectionAxis: string;
|
||||
sectionAxis: 'x' | 'y' | 'z';
|
||||
sectionLevel: number;
|
||||
sectionInvert: boolean;
|
||||
modelUrl?: string;
|
||||
modelFileName?: string;
|
||||
modelFileSize?: number;
|
||||
}
|
||||
|
||||
interface PeerData {
|
||||
@@ -716,12 +719,16 @@ export default function MeetingRoom() {
|
||||
const [inRoom, setInRoom] = useState(false);
|
||||
const [isConnecting, setIsConnecting] = useState(false);
|
||||
const [isFocusMode, setIsFocusMode] = useState(false);
|
||||
const [name, setName] = useState('');
|
||||
const [cargo, setCargo] = useState('');
|
||||
const [empresa, setEmpresa] = useState('');
|
||||
const [isPresenter, setIsPresenter] = useState(!roomId); // Se não há roomId na URL, ele é o host
|
||||
const [selectedAvatar, setSelectedAvatar] = useState(1);
|
||||
const [selectedChair, setSelectedChair] = useState('1');
|
||||
const [name, setName] = useState(() => localStorage.getItem('tsxr_meeting_user_name') || '');
|
||||
const [cargo, setCargo] = useState(() => localStorage.getItem('tsxr_meeting_user_cargo') || '');
|
||||
const [empresa, setEmpresa] = useState(() => localStorage.getItem('tsxr_meeting_user_empresa') || '');
|
||||
const [isPresenter, setIsPresenter] = useState(() => {
|
||||
const saved = localStorage.getItem('tsxr_meeting_user_is_presenter');
|
||||
if (saved !== null) return saved === 'true';
|
||||
return !roomId;
|
||||
});
|
||||
const [selectedAvatar, setSelectedAvatar] = useState(() => Number(localStorage.getItem('tsxr_meeting_user_avatar')) || 1);
|
||||
const [selectedChair, setSelectedChair] = useState(() => localStorage.getItem('tsxr_meeting_user_chair') || '1');
|
||||
const [inputRoomId, setInputRoomId] = useState(roomId || '');
|
||||
|
||||
// Estados da Sala (Conexão Supabase)
|
||||
@@ -843,7 +850,17 @@ export default function MeetingRoom() {
|
||||
const handlePresentationChange = (updatedFields: Partial<PresentationState>) => {
|
||||
if (!isPresenter || !channelRef.current) return;
|
||||
|
||||
const newState = { ...presentationState, ...updatedFields } as PresentationState;
|
||||
const store = useModelStore.getState();
|
||||
const currentModel = store.models.find(m => m.id === store.activeModelId) || null;
|
||||
|
||||
const newState = {
|
||||
...presentationState,
|
||||
...updatedFields,
|
||||
modelUrl: currentModel?.url,
|
||||
modelFileName: currentModel?.fileName,
|
||||
modelFileSize: currentModel?.fileSize
|
||||
} as PresentationState;
|
||||
|
||||
setPresentationState(newState);
|
||||
|
||||
channelRef.current.send({
|
||||
@@ -859,6 +876,14 @@ export default function MeetingRoom() {
|
||||
toast.error('Digite seu nome primeiro');
|
||||
return;
|
||||
}
|
||||
|
||||
// Salva no localStorage para auto-preenchimento conveniente
|
||||
localStorage.setItem('tsxr_meeting_user_name', name);
|
||||
localStorage.setItem('tsxr_meeting_user_cargo', cargo);
|
||||
localStorage.setItem('tsxr_meeting_user_empresa', empresa);
|
||||
localStorage.setItem('tsxr_meeting_user_avatar', String(selectedAvatar));
|
||||
localStorage.setItem('tsxr_meeting_user_chair', selectedChair);
|
||||
localStorage.setItem('tsxr_meeting_user_is_presenter', String(isPresenter));
|
||||
|
||||
// Validar se o avatar já está em uso por outra pessoa
|
||||
const avatarInUse = Object.values(peers).find((p) => p.avatarId === selectedAvatar);
|
||||
@@ -952,7 +977,27 @@ export default function MeetingRoom() {
|
||||
// Escuta atualizações de Apresentação da Maquete via Broadcast (apenas para convidados)
|
||||
channel.on('broadcast', { event: 'presentation_update' }, (payload) => {
|
||||
if (!isPresenter) {
|
||||
setPresentationState(payload.payload as PresentationState);
|
||||
const data = payload.payload as PresentationState;
|
||||
setPresentationState(data);
|
||||
|
||||
// Se o apresentador propagar um modelo remoto que o convidado não tem ativo, carrega
|
||||
if (data.modelUrl && data.modelFileName) {
|
||||
const store = useModelStore.getState();
|
||||
const hasModel = store.models.some(m => m.fileName === data.modelFileName);
|
||||
if (!hasModel) {
|
||||
console.log(`📥 [MeetingRoom] Carregando modelo remoto do apresentador: ${data.modelFileName}`);
|
||||
useModelStore.getState().addModel({
|
||||
fileName: data.modelFileName,
|
||||
fileSize: data.modelFileSize || 0,
|
||||
url: data.modelUrl
|
||||
});
|
||||
} else {
|
||||
const existing = store.models.find(m => m.fileName === data.modelFileName);
|
||||
if (existing && store.activeModelId !== existing.id) {
|
||||
useModelStore.getState().setActiveModel(existing.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -972,10 +1017,17 @@ export default function MeetingRoom() {
|
||||
|
||||
// Se for o apresentador, propaga o estado inicial de apresentação (vinda do Viewer) para todos
|
||||
if (isPresenter) {
|
||||
const store = useModelStore.getState();
|
||||
const currentModel = store.models.find(m => m.id === store.activeModelId) || null;
|
||||
channel.send({
|
||||
type: 'broadcast',
|
||||
event: 'presentation_update',
|
||||
payload: presentationState
|
||||
payload: {
|
||||
...presentationState,
|
||||
modelUrl: currentModel?.url,
|
||||
modelFileName: currentModel?.fileName,
|
||||
modelFileSize: currentModel?.fileSize
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1389,6 +1441,16 @@ export default function MeetingRoom() {
|
||||
>
|
||||
<RotateCw className="h-3 w-3" /> Resetar Apresentação
|
||||
</Button>
|
||||
|
||||
{/* Botão de Apresentação no Viewer */}
|
||||
<Button
|
||||
variant="default"
|
||||
size="sm"
|
||||
className="w-full h-9 font-mono text-[10px] gap-1.5 bg-sky-500 hover:bg-sky-600 text-slate-950 font-bold glow-primary"
|
||||
onClick={() => navigate(`/viewer?room=${roomCode}`)}
|
||||
>
|
||||
<Box className="h-3.5 w-3.5" /> Manipular Peça no Viewer
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user