diff --git a/src/pages/Viewer.tsx b/src/pages/Viewer.tsx index bb549dd..06ee5ca 100644 --- a/src/pages/Viewer.tsx +++ b/src/pages/Viewer.tsx @@ -223,7 +223,11 @@ const Viewer = () => { toast.error("WebXR não é suportado neste navegador. Use o navegador do Meta Quest 3."); return; } - navigate("/xr"); + // Entra diretamente no AR para não perder o clique do usuário! + import('@/stores/useXRStore').then(({ xrStore }) => { + xrStore.enterAR().catch(e => console.error('[Viewer] XR enterAR error:', e)); + navigate("/xr"); + }); }; const handleEnterMeeting = () => { diff --git a/src/pages/XRSession.tsx b/src/pages/XRSession.tsx index 7abac38..5c02aac 100644 --- a/src/pages/XRSession.tsx +++ b/src/pages/XRSession.tsx @@ -2,7 +2,8 @@ import { useEffect, useRef, useMemo, useCallback, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { Canvas, useFrame, useThree } from '@react-three/fiber'; import { useGLTF, Grid, Html, Line, OrbitControls, Text } from '@react-three/drei'; -import { XR, createXRStore, useXR, XROrigin, useXRHitTest } from '@react-three/xr'; +import { XR, useXR, XROrigin, useXRHitTest } from '@react-three/xr'; +import { xrStore as store } from '@/stores/useXRStore'; import * as THREE from 'three'; import { useModelStore } from '@/stores/useModelStore'; import { toast } from 'sonner'; @@ -45,39 +46,7 @@ if (navigator.xr) { }); } -// Lê flags de feature WebXR do localStorage (default: todas ON). -// O usuário liga/desliga pelo HUD AR (aba WebXR) para diagnosticar qual -// feature ativa o grid de segurança do Quest. Mudanças exigem sair e re-entrar do AR. -function loadXRFeatures() { - const defaults = { - handTracking: true, planeDetection: true, hitTest: true, domOverlay: true, - anchors: true, meshDetection: true, depthSensing: true, layers: true, - bodyTracking: true, lightEstimation: true, - }; - try { - const raw = localStorage.getItem('xrFeatures'); - if (raw) return { ...defaults, ...JSON.parse(raw) }; - } catch (err) { - console.warn('[XR] Falha ao ler xrFeatures do localStorage:', err); - } - return defaults; -} -const _xrf = loadXRFeatures(); -console.log('[XR] Features:', _xrf); - -const store = createXRStore({ - hand: { left: true, right: true }, - controller: { left: true, right: true }, - handTracking: _xrf.handTracking, - planeDetection: _xrf.planeDetection, - hitTest: _xrf.hitTest, - domOverlay: _xrf.domOverlay, - anchors: _xrf.anchors, - meshDetection: _xrf.meshDetection, - depthSensing: _xrf.depthSensing, - layers: _xrf.layers, - bodyTracking: _xrf.bodyTracking, -}); +// `store` is now imported from `@/stores/useXRStore` // ─── XRModel ─────────────────────────────────────────── function XRModel({ sceneModel }: { sceneModel: import('@/stores/useModelStore').SceneModel }) { @@ -1113,6 +1082,20 @@ const XRSession = () => { if (!model) return null; + // Auto-return to viewer if user exits AR session + useEffect(() => { + // If we are mounted, and the session is completely absent/ended (inXR is false), + // we wait a brief moment to avoid returning during initial transition, then return. + if (!inXR) { + const timer = setTimeout(() => { + if (!store.getState().session) { + navigate('/viewer'); + } + }, 500); + return () => clearTimeout(timer); + } + }, [inXR, navigate]); + return (