134 lines
4.0 KiB
TypeScript
134 lines
4.0 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useRef, useState } from 'react';
|
|
import { useParams } from 'react-router';
|
|
import { supabase } from '~/lib/supabase';
|
|
|
|
// --- A-FRAME/AR.JS CUSTOM ELEMENTS TYPING FIX ---
|
|
declare module 'react' {
|
|
namespace JSX {
|
|
interface IntrinsicElements {
|
|
'a-scene': any;
|
|
'a-assets': any;
|
|
'a-asset-item': any;
|
|
'a-marker': any;
|
|
'a-entity': any;
|
|
'a-text': any;
|
|
'model-viewer': React.DetailedHTMLProps<
|
|
React.HTMLAttributes<HTMLElement>,
|
|
HTMLElement
|
|
> & {
|
|
src?: string;
|
|
'ios-src'?: string;
|
|
ar?: boolean;
|
|
'ar-modes'?: string;
|
|
'ar-scale'?: string;
|
|
'ar-placement'?: string;
|
|
'camera-controls'?: boolean;
|
|
'touch-action'?: string;
|
|
alt?: string;
|
|
'shadow-intensity'?: string | number;
|
|
'shadow-softness'?: string | number;
|
|
exposure?: string | number;
|
|
'interaction-prompt'?: string;
|
|
'min-camera-orbit'?: string;
|
|
'max-camera-orbit'?: string;
|
|
'camera-orbit'?: string;
|
|
'field-of-view'?: string;
|
|
scale?: string;
|
|
'auto-rotate'?: boolean;
|
|
'rotation-per-second'?: string;
|
|
onLoad?: (e: any) => void;
|
|
'onAr-status'?: (e: any) => void;
|
|
'onModel-visibility'?: (e: any) => void;
|
|
'onCamera-change'?: (e: any) => void;
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
export default function ARViewer() {
|
|
const { id } = useParams();
|
|
const modelViewerRef = useRef<any>(null);
|
|
|
|
const [asset, setAsset] = useState<any>(null);
|
|
const [scriptLoaded, setScriptLoaded] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (!id) return;
|
|
|
|
const loadAsset = async () => {
|
|
const { data, error } = await supabase
|
|
.from('ar_assets')
|
|
.select('*')
|
|
.eq('id', id)
|
|
.single();
|
|
|
|
console.log('Supabase response:', { data, error });
|
|
|
|
if (!error && data) {
|
|
setAsset(data);
|
|
}
|
|
};
|
|
|
|
loadAsset();
|
|
}, [id]);
|
|
|
|
useEffect(() => {
|
|
if (document.getElementById('model-viewer-script')) {
|
|
setScriptLoaded(true);
|
|
return;
|
|
}
|
|
|
|
const script = document.createElement('script');
|
|
script.id = 'model-viewer-script';
|
|
script.type = 'module';
|
|
script.src =
|
|
'https://ajax.googleapis.com/ajax/libs/model-viewer/3.4.0/model-viewer.min.js';
|
|
|
|
script.onload = () => {
|
|
console.log('model-viewer loaded');
|
|
setScriptLoaded(true);
|
|
};
|
|
|
|
document.head.appendChild(script);
|
|
}, []);
|
|
|
|
if (!scriptLoaded) {
|
|
return (
|
|
<pre style={{ padding: 20 }}>
|
|
Loading AR Viewer…
|
|
{'\n\n'}
|
|
asset: {JSON.stringify(asset, null, 2)}
|
|
{'\n'}
|
|
scriptLoaded: {String(scriptLoaded)}
|
|
{'\n'}
|
|
id: {String(id)}
|
|
</pre>
|
|
);
|
|
}
|
|
return (
|
|
<model-viewer
|
|
ref={modelViewerRef}
|
|
src={asset?.glb_url}
|
|
ios-src={asset?.usdz_url}
|
|
ar
|
|
ar-modes='webxr scene-viewer quick-look'
|
|
camera-controls
|
|
auto-rotate
|
|
style={{ width: '100vw', height: '80vh' }}
|
|
onError={(e) => {
|
|
console.error('Model viewer error:', e);
|
|
console.error('Failed to load:', asset?.glb_url);
|
|
}}
|
|
onLoad={() => console.log('Model loaded successfully')}
|
|
/>
|
|
// <div className='bg-white text-2xl text-red-400'>
|
|
// Id : {asset?.id}
|
|
// AR Viewer for asset: {asset?.name}
|
|
// glb URL: {asset?.glb_url}
|
|
// usdz URL: {asset?.usdz_url}
|
|
// </div>
|
|
);
|
|
}
|