fix: harden upload resilience and contracts
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Component, useEffect, useState } from 'react'
|
||||
import type { ComponentType, ReactNode } from 'react'
|
||||
import type { ModelStats } from './SceneViewer'
|
||||
|
||||
interface ModelViewerProps {
|
||||
@@ -10,10 +11,51 @@ interface ModelViewerProps {
|
||||
size: string
|
||||
}
|
||||
|
||||
function getPreviewErrorMessage(error: unknown) {
|
||||
return error instanceof Error ? error.message : 'Erreur preview inconnue'
|
||||
}
|
||||
|
||||
function PreviewFallback({ message }: { message?: string }) {
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center px-6 text-center">
|
||||
<div className="max-w-sm space-y-2">
|
||||
<p className="text-sm font-medium text-gray-300">Preview 3D indisponible pour ce modele.</p>
|
||||
<p className="text-xs text-gray-500">
|
||||
L'upload reste possible. {message ? `Detail technique : ${message}` : ''}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
class PreviewErrorBoundary extends Component<
|
||||
{ children: ReactNode },
|
||||
{ message: string | null }
|
||||
> {
|
||||
state = { message: null }
|
||||
|
||||
static getDerivedStateFromError(error: unknown) {
|
||||
return { message: getPreviewErrorMessage(error) }
|
||||
}
|
||||
|
||||
componentDidCatch(error: unknown) {
|
||||
console.error('[ERROR] Preview 3D indisponible', error)
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.message) {
|
||||
return <PreviewFallback message={this.state.message} />
|
||||
}
|
||||
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
export default function ModelViewer({ url, assetUrls, filename, size }: ModelViewerProps) {
|
||||
const canPreview = filename.toLowerCase().endsWith('.gltf')
|
||||
const [stats, setStats] = useState<ModelStats | null>(null)
|
||||
const [Scene, setScene] = useState<React.ComponentType<{
|
||||
const [sceneError, setSceneError] = useState<string | null>(null)
|
||||
const [Scene, setScene] = useState<ComponentType<{
|
||||
url: string
|
||||
assetUrls: Record<string, string>
|
||||
onStatsReady: (stats: ModelStats) => void
|
||||
@@ -23,13 +65,19 @@ export default function ModelViewer({ url, assetUrls, filename, size }: ModelVie
|
||||
if (!canPreview) return
|
||||
|
||||
let cancel = false
|
||||
setSceneError(null)
|
||||
setStats(null)
|
||||
|
||||
import('./SceneViewer').then((mod) => {
|
||||
if (!cancel) setScene(() => mod.default)
|
||||
})
|
||||
import('./SceneViewer')
|
||||
.then((mod) => {
|
||||
if (!cancel) setScene(() => mod.default)
|
||||
})
|
||||
.catch((error: unknown) => {
|
||||
if (!cancel) setSceneError(getPreviewErrorMessage(error))
|
||||
})
|
||||
|
||||
return () => { cancel = true }
|
||||
}, [canPreview])
|
||||
}, [canPreview, url])
|
||||
|
||||
if (!canPreview) {
|
||||
return (
|
||||
@@ -87,7 +135,13 @@ export default function ModelViewer({ url, assetUrls, filename, size }: ModelVie
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<Scene url={url} assetUrls={assetUrls} onStatsReady={setStats} />
|
||||
{sceneError ? (
|
||||
<PreviewFallback message={sceneError} />
|
||||
) : (
|
||||
<PreviewErrorBoundary key={url}>
|
||||
<Scene url={url} assetUrls={assetUrls} onStatsReady={setStats} />
|
||||
</PreviewErrorBoundary>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user