glam/frontend/src/hooks/useWikidataImage.ts
2025-12-11 22:32:09 +01:00

142 lines
3.8 KiB
TypeScript

/**
* React Hook for fetching Wikidata images on-demand
*
* This hook fetches the main image (P18 property) from Wikidata for an institution
* when we have a wikidata_id but no wikidata_image_url.
*
* Usage:
* ```tsx
* const wikidataImageUrl = useWikidataImage(institution.wikidata_id, institution.wikidata_image_url);
* // wikidataImageUrl will be:
* // - institution.wikidata_image_url if it exists
* // - fetched URL from Wikidata if wikidata_id exists
* // - undefined otherwise
* ```
*/
import { useState, useEffect, useRef } from 'react';
import { fetchWikidataImageUrl } from '../utils/wikidata';
/**
* Hook to get Wikidata image URL for an institution
*
* @param wikidataId - Wikidata entity ID (e.g., "Q190804")
* @param existingUrl - Already-known image URL (if available)
* @param enabled - Whether to fetch (default true)
* @returns The image URL (existing or fetched) or undefined
*/
export function useWikidataImage(
wikidataId: string | undefined,
existingUrl: string | undefined,
enabled: boolean = true
): string | undefined {
const [imageUrl, setImageUrl] = useState<string | undefined>(existingUrl);
const [_isLoading, setIsLoading] = useState(false);
const prevWikidataIdRef = useRef<string | undefined>(undefined);
useEffect(() => {
// If we already have a URL, use it
if (existingUrl) {
setImageUrl(existingUrl);
return;
}
// If disabled or no wikidata_id, clear and return
if (!enabled || !wikidataId) {
setImageUrl(undefined);
return;
}
// Skip if we already fetched for this ID
if (prevWikidataIdRef.current === wikidataId) {
return;
}
prevWikidataIdRef.current = wikidataId;
// Fetch from Wikidata
let cancelled = false;
setIsLoading(true);
fetchWikidataImageUrl(wikidataId)
.then((url) => {
if (!cancelled) {
setImageUrl(url || undefined);
setIsLoading(false);
}
})
.catch((error) => {
if (!cancelled) {
console.error('[useWikidataImage] Error fetching:', error);
setIsLoading(false);
}
});
return () => {
cancelled = true;
};
}, [wikidataId, existingUrl, enabled]);
return imageUrl;
}
/**
* Hook that returns both the image URL and loading state
*/
export function useWikidataImageWithStatus(
wikidataId: string | undefined,
existingUrl: string | undefined,
enabled: boolean = true
): { imageUrl: string | undefined; isLoading: boolean } {
const [imageUrl, setImageUrl] = useState<string | undefined>(existingUrl);
const [isLoading, setIsLoading] = useState(false);
const prevWikidataIdRef = useRef<string | undefined>(undefined);
useEffect(() => {
// If we already have a URL, use it
if (existingUrl) {
setImageUrl(existingUrl);
setIsLoading(false);
return;
}
// If disabled or no wikidata_id, clear and return
if (!enabled || !wikidataId) {
setImageUrl(undefined);
setIsLoading(false);
return;
}
// Skip if we already fetched for this ID (but allow refetch on ID change)
if (prevWikidataIdRef.current === wikidataId && imageUrl !== undefined) {
return;
}
prevWikidataIdRef.current = wikidataId;
// Fetch from Wikidata
let cancelled = false;
setIsLoading(true);
fetchWikidataImageUrl(wikidataId)
.then((url) => {
if (!cancelled) {
setImageUrl(url || undefined);
setIsLoading(false);
}
})
.catch((error) => {
if (!cancelled) {
console.error('[useWikidataImage] Error fetching:', error);
setImageUrl(undefined);
setIsLoading(false);
}
});
return () => {
cancelled = true;
};
}, [wikidataId, existingUrl, enabled, imageUrl]);
return { imageUrl, isLoading };
}
export default useWikidataImage;