diff --git a/ui/src/app/api/caption/get/route.ts b/ui/src/app/api/caption/get/route.ts index 4f8d2818..968624ec 100644 --- a/ui/src/app/api/caption/get/route.ts +++ b/ui/src/app/api/caption/get/route.ts @@ -5,8 +5,18 @@ import path from 'path'; import { getDatasetsRoot } from '@/server/settings'; export async function POST(request: NextRequest) { - - const body = await request.json(); + let body; + try { + body = await request.json(); + } catch { + // Client aborted the request before body was fully sent + return new NextResponse(null, { status: 499 }); + } + + if (request.signal.aborted) { + return new NextResponse(null, { status: 499 }); + } + const { imgPath } = body; console.log('Received POST request for caption:', imgPath); try { diff --git a/ui/src/components/DatasetImageCard.tsx b/ui/src/components/DatasetImageCard.tsx index 5d16f601..7f90c302 100644 --- a/ui/src/components/DatasetImageCard.tsx +++ b/ui/src/components/DatasetImageCard.tsx @@ -28,18 +28,19 @@ const DatasetImageCard: React.FC = ({ const [isCaptionLoaded, setIsCaptionLoaded] = useState(false); const [caption, setCaption] = useState(''); const [savedCaption, setSavedCaption] = useState(''); - const isGettingCaption = useRef(false); + const abortControllerRef = useRef(null); const fetchCaption = async () => { - if (isGettingCaption.current || isCaptionLoaded) return; - isGettingCaption.current = true; + if (isCaptionLoaded) return; + abortControllerRef.current?.abort(); + const controller = new AbortController(); + abortControllerRef.current = controller; apiClient - .post(`/api/caption/get`, { imgPath: imageUrl }) + .post(`/api/caption/get`, { imgPath: imageUrl }, { signal: controller.signal }) .then(res => res.data) .then(data => { console.log('Caption fetched:', data); if (data) { - // fix issue where caption could be non string data = `${data}`; } setCaption(data || ''); @@ -47,10 +48,13 @@ const DatasetImageCard: React.FC = ({ setIsCaptionLoaded(true); }) .catch(error => { + if (controller.signal.aborted) return; console.error('Error fetching caption:', error); }) .finally(() => { - isGettingCaption.current = false; + if (abortControllerRef.current === controller) { + abortControllerRef.current = null; + } }); }; @@ -88,6 +92,8 @@ const DatasetImageCard: React.FC = ({ } } else { setInViewport(false); + // Cancel any in-flight caption fetch when scrolling away + abortControllerRef.current?.abort(); } }, { threshold: 0.1 }, @@ -199,11 +205,6 @@ const DatasetImageCard: React.FC = ({ - {inViewport && isVisible && !isItAudio && ( -
- {imageUrl} -
- )}