- useDetection.analyze() now awaits a requestAnimationFrame before calling runInference. Without it React commits isAnalyzing=true and immediately hits the synchronous TFLite runSync that blocks the JS thread for 500-1500ms — the analyzing skeleton overlay appears AFTER the inference, defeating its purpose. - Same hook enforces a minimum 600ms total before resolving so a cached/fast inference doesn't show a skeleton flicker that reads as a glitch. - ScannerScreen.handleCapture is split: capture stays inline, processImage(uri) is now its own async function. Cleaner control flow when a take succeeds but analysis is delegated. - The previously dead "image gallery" icon next to the shutter is now a real TouchableOpacity that fires a "coming soon" toast (we'll wire it to expo-image-picker once we add the lib + native rebuild). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
46 lines
1.5 KiB
TypeScript
46 lines
1.5 KiB
TypeScript
import { useState, useCallback } from 'react';
|
|
import { runInference } from '@/services/tflite/model';
|
|
import type { Detection } from '@/types/detection';
|
|
|
|
export function useDetection() {
|
|
const [isAnalyzing, setIsAnalyzing] = useState(false);
|
|
const [lastDetection, setLastDetection] = useState<Detection | null>(null);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const analyze = useCallback(async (imageUri?: string): Promise<Detection | null> => {
|
|
setIsAnalyzing(true);
|
|
setError(null);
|
|
|
|
// Yield au scheduler pour que React commit le render `isAnalyzing=true`
|
|
// (le skeleton overlay) AVANT que runSync() ne bloque le JS thread ~500-1500ms.
|
|
await new Promise<void>((resolve) =>
|
|
requestAnimationFrame(() => resolve()),
|
|
);
|
|
|
|
const startedAt = Date.now();
|
|
try {
|
|
const detection = await runInference(imageUri);
|
|
// UX : maintenir le skeleton visible au moins 600ms pour éviter un flash
|
|
// perçu comme un bug ("rien ne se passe") quand l'inférence est très rapide.
|
|
const elapsed = Date.now() - startedAt;
|
|
if (elapsed < 600) {
|
|
await new Promise((r) => setTimeout(r, 600 - elapsed));
|
|
}
|
|
setLastDetection(detection);
|
|
return detection;
|
|
} catch (err) {
|
|
setError("Erreur lors de l'analyse. Veuillez reessayer.");
|
|
return null;
|
|
} finally {
|
|
setIsAnalyzing(false);
|
|
}
|
|
}, []);
|
|
|
|
const reset = useCallback(() => {
|
|
setLastDetection(null);
|
|
setError(null);
|
|
}, []);
|
|
|
|
return { analyze, isAnalyzing, lastDetection, error, reset };
|
|
}
|