mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-24 01:51:13 -05:00
120 lines
3.5 KiB
TypeScript
120 lines
3.5 KiB
TypeScript
/**
|
|
* Entity Photos Hook
|
|
*
|
|
* Fetches photos for a specific entity with intelligent caching and sort support.
|
|
*
|
|
* Features:
|
|
* - Caches photos for 5 minutes (staleTime)
|
|
* - Background refetch every 15 minutes (gcTime)
|
|
* - Supports 'newest' and 'oldest' sorting without refetching
|
|
* - Performance monitoring in dev mode
|
|
*
|
|
* @param entityType - Type of entity ('park', 'ride', 'company', etc.)
|
|
* @param entityId - UUID of the entity
|
|
* @param sortBy - Sort order: 'newest' (default) or 'oldest'
|
|
*
|
|
* @returns TanStack Query result with photo array
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* const { data: photos, isLoading, refetch } = useEntityPhotos('park', parkId, 'newest');
|
|
*
|
|
* // After uploading new photos:
|
|
* await uploadPhotos();
|
|
* refetch(); // Refresh this component
|
|
* invalidateEntityPhotos('park', parkId); // Refresh all components
|
|
* ```
|
|
*/
|
|
|
|
import { useQuery, UseQueryResult, useQueryClient } from '@tanstack/react-query';
|
|
import { useEffect } from 'react';
|
|
import { supabase } from '@/integrations/supabase/client';
|
|
import { queryKeys } from '@/lib/queryKeys';
|
|
|
|
interface EntityPhoto {
|
|
id: string;
|
|
url: string;
|
|
caption?: string;
|
|
title?: string;
|
|
user_id: string;
|
|
created_at: string;
|
|
}
|
|
|
|
export function useEntityPhotos(
|
|
entityType: string,
|
|
entityId: string,
|
|
sortBy: 'newest' | 'oldest' = 'newest',
|
|
enableRealtime = false // New parameter for opt-in real-time updates
|
|
): UseQueryResult<EntityPhoto[]> {
|
|
const queryClient = useQueryClient();
|
|
|
|
const query = useQuery({
|
|
queryKey: queryKeys.photos.entity(entityType, entityId, sortBy),
|
|
queryFn: async () => {
|
|
const startTime = performance.now();
|
|
|
|
const { data, error } = await supabase
|
|
.from('photos')
|
|
.select('id, cloudflare_image_url, title, caption, submitted_by, created_at, order_index')
|
|
.eq('entity_type', entityType)
|
|
.eq('entity_id', entityId)
|
|
.order('created_at', { ascending: sortBy === 'oldest' });
|
|
|
|
if (error) throw error;
|
|
|
|
const result = data?.map((photo) => ({
|
|
id: photo.id,
|
|
url: photo.cloudflare_image_url,
|
|
caption: photo.caption || undefined,
|
|
title: photo.title || undefined,
|
|
user_id: photo.submitted_by,
|
|
created_at: photo.created_at,
|
|
})) || [];
|
|
|
|
// Performance monitoring (dev only)
|
|
if (import.meta.env.DEV) {
|
|
const duration = performance.now() - startTime;
|
|
if (duration > 1000) {
|
|
console.warn(`⚠️ Slow query: useEntityPhotos took ${duration.toFixed(0)}ms`, { entityType, entityId, photoCount: result.length });
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
enabled: !!entityType && !!entityId,
|
|
staleTime: 5 * 60 * 1000,
|
|
gcTime: 15 * 60 * 1000,
|
|
refetchOnWindowFocus: false,
|
|
});
|
|
|
|
// Real-time subscription for photo uploads (opt-in)
|
|
useEffect(() => {
|
|
if (!enableRealtime || !entityType || !entityId) return;
|
|
|
|
const channel = supabase
|
|
.channel(`photos-${entityType}-${entityId}`)
|
|
.on(
|
|
'postgres_changes',
|
|
{
|
|
event: 'INSERT',
|
|
schema: 'public',
|
|
table: 'photos',
|
|
filter: `entity_type=eq.${entityType},entity_id=eq.${entityId}`,
|
|
},
|
|
(payload) => {
|
|
console.log('📸 New photo uploaded:', payload.new);
|
|
queryClient.invalidateQueries({
|
|
queryKey: queryKeys.photos.entity(entityType, entityId)
|
|
});
|
|
}
|
|
)
|
|
.subscribe();
|
|
|
|
return () => {
|
|
supabase.removeChannel(channel);
|
|
};
|
|
}, [enableRealtime, entityType, entityId, queryClient, sortBy]);
|
|
|
|
return query;
|
|
}
|