🚀 Auto-deploy: melhoria no snap e medição AR em 25/05/2026 11:15:46
This commit is contained in:
+133
-11
@@ -1,4 +1,5 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { supabase } from "@/integrations/supabase/client";
|
||||
import { ArrowLeft, Glasses, Box, Ruler, FileText, Upload, Loader2, Users } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useModelStore } from "@/stores/useModelStore";
|
||||
@@ -38,11 +39,120 @@ const Viewer = () => {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [converting, setConverting] = useState(false);
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
const roomId = searchParams.get("room");
|
||||
const channelRef = useRef<any>(null);
|
||||
|
||||
// Se não houver modelo e não estivermos em Reunião, volta para a Home
|
||||
useEffect(() => {
|
||||
if (!model) {
|
||||
if (!model && !roomId) {
|
||||
navigate("/");
|
||||
}
|
||||
}, [model, navigate]);
|
||||
}, [model, roomId, navigate]);
|
||||
|
||||
const sendMeetingUpdate = useCallback((ft: typeof fineTuning, secEn: typeof sectionEnabled, secLevel: typeof sectionLevel, secInv: typeof sectionInvert) => {
|
||||
if (!roomId || !channelRef.current) return;
|
||||
|
||||
const enabledAxis = (['x', 'y', 'z'] as const).find(ax => secEn[ax]);
|
||||
const isSectionActive = !!enabledAxis;
|
||||
const sectionAxis = enabledAxis || 'y';
|
||||
const sectionLevelVal = isSectionActive ? secLevel[sectionAxis] : 0.0;
|
||||
const sectionInvertVal = isSectionActive ? secInv[sectionAxis] : false;
|
||||
|
||||
// Obtém dados da maquete ativa na store
|
||||
const activeModelStore = useModelStore.getState().model;
|
||||
|
||||
const payload = {
|
||||
rotation: [ft.rotX, ft.rotY, ft.rotZ],
|
||||
scale: ft.scale,
|
||||
sectionEnabled: isSectionActive,
|
||||
sectionAxis,
|
||||
sectionLevel: sectionLevelVal,
|
||||
sectionInvert: sectionInvertVal,
|
||||
modelUrl: activeModelStore?.url,
|
||||
modelFileName: activeModelStore?.fileName,
|
||||
modelFileSize: activeModelStore?.fileSize
|
||||
};
|
||||
|
||||
console.log("📡 [Viewer Meeting Sync] Enviando atualização de apresentação:", payload);
|
||||
channelRef.current.send({
|
||||
type: 'broadcast',
|
||||
event: 'presentation_update',
|
||||
payload
|
||||
});
|
||||
}, [roomId]);
|
||||
|
||||
// Conecta ao canal do Supabase se o Viewer foi aberto no modo Reunião
|
||||
useEffect(() => {
|
||||
if (!roomId) return;
|
||||
|
||||
const finalRoomId = roomId.trim().toUpperCase();
|
||||
console.log(`🔌 [Viewer Meeting Sync] Conectando ao canal da reunião: ${finalRoomId}`);
|
||||
|
||||
const channel = supabase.channel(`meeting_room_${finalRoomId}`, {
|
||||
config: {
|
||||
broadcast: { self: false }
|
||||
}
|
||||
});
|
||||
|
||||
channelRef.current = channel;
|
||||
|
||||
channel.subscribe((status) => {
|
||||
console.log(`🔌 [Viewer Meeting Sync] Status do canal: ${status}`);
|
||||
if (status === 'SUBSCRIBED') {
|
||||
const state = useModelStore.getState();
|
||||
sendMeetingUpdate(state.fineTuning, state.sectionEnabled, state.sectionLevel, state.sectionInvert);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
console.log(`🔌 [Viewer Meeting Sync] Desconectando do canal da reunião: ${finalRoomId}`);
|
||||
channel.unsubscribe();
|
||||
supabase.removeChannel(channel);
|
||||
};
|
||||
}, [roomId, sendMeetingUpdate]);
|
||||
|
||||
// Observa mudanças primitivas nas ferramentas 3D para propagação imediata via Broadcast
|
||||
const rotX = fineTuning.rotX;
|
||||
const rotY = fineTuning.rotY;
|
||||
const rotZ = fineTuning.rotZ;
|
||||
const scaleVal = fineTuning.scale;
|
||||
|
||||
const secEnabledX = sectionEnabled.x;
|
||||
const secEnabledY = sectionEnabled.y;
|
||||
const secEnabledZ = sectionEnabled.z;
|
||||
|
||||
const secLevelX = sectionLevel.x;
|
||||
const secLevelY = sectionLevel.y;
|
||||
const secLevelZ = sectionLevel.z;
|
||||
|
||||
const secInvertX = sectionInvert.x;
|
||||
const secInvertY = sectionInvert.y;
|
||||
const secInvertZ = sectionInvert.z;
|
||||
|
||||
const activeModelFileName = model?.fileName;
|
||||
|
||||
useEffect(() => {
|
||||
if (!roomId || !channelRef.current) return;
|
||||
sendMeetingUpdate(fineTuning, sectionEnabled, sectionLevel, sectionInvert);
|
||||
}, [
|
||||
roomId,
|
||||
rotX,
|
||||
rotY,
|
||||
rotZ,
|
||||
scaleVal,
|
||||
secEnabledX,
|
||||
secEnabledY,
|
||||
secEnabledZ,
|
||||
secLevelX,
|
||||
secLevelY,
|
||||
secLevelZ,
|
||||
secInvertX,
|
||||
secInvertY,
|
||||
secInvertZ,
|
||||
activeModelFileName,
|
||||
sendMeetingUpdate
|
||||
]);
|
||||
|
||||
const handleFileUpload = useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
@@ -102,7 +212,7 @@ const Viewer = () => {
|
||||
}
|
||||
}, [addModel, models.length, maxModels]);
|
||||
|
||||
if (!model) return null;
|
||||
// Removido o bloqueio para permitir carregamento de modelo na sala
|
||||
|
||||
const handleEnterXR = async () => {
|
||||
if (!xrSupported) {
|
||||
@@ -152,8 +262,14 @@ const Viewer = () => {
|
||||
{/* Top bar */}
|
||||
<header className="flex items-center justify-between border-b bg-card px-4 py-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<Button variant="ghost" size="icon" onClick={() => navigate("/")}>
|
||||
<ArrowLeft className="h-5 w-5" />
|
||||
<Button
|
||||
variant="ghost"
|
||||
size={roomId ? "default" : "icon"}
|
||||
className={roomId ? "gap-1.5 px-3 text-xs font-mono h-9 hover:bg-slate-800" : ""}
|
||||
onClick={() => roomId ? navigate(`/meeting/${roomId}`) : navigate("/")}
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
{roomId && "Voltar para Sala"}
|
||||
</Button>
|
||||
<div className="flex items-center gap-2">
|
||||
<Box className="h-5 w-5 text-primary" />
|
||||
@@ -222,11 +338,17 @@ const Viewer = () => {
|
||||
<h2 className="mb-2 font-mono text-xs font-semibold uppercase tracking-widest text-muted-foreground">
|
||||
Peça selecionada
|
||||
</h2>
|
||||
<div className="space-y-3">
|
||||
<InfoItem icon={FileText} label="Arquivo" value={model.fileName} />
|
||||
<InfoItem icon={Box} label="Tamanho" value={`${(model.fileSize / (1024 * 1024)).toFixed(2)} MB`} />
|
||||
<InfoItem icon={Ruler} label="Escala" value={`${scaleRatio.label} (mm)`} />
|
||||
</div>
|
||||
{model ? (
|
||||
<div className="space-y-3">
|
||||
<InfoItem icon={FileText} label="Arquivo" value={model.fileName} />
|
||||
<InfoItem icon={Box} label="Tamanho" value={`${(model.fileSize / (1024 * 1024)).toFixed(2)} MB`} />
|
||||
<InfoItem icon={Ruler} label="Escala" value={`${scaleRatio.label} (mm)`} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-lg border border-dashed border-muted p-4 text-center bg-muted/20">
|
||||
<p className="font-mono text-xs text-muted-foreground">Nenhuma peça ativa na cena</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
Reference in New Issue
Block a user