// lib/hooks/useWorkoutHistory.ts import { useState, useEffect, useCallback, useMemo } from 'react'; import { useSQLiteContext } from 'expo-sqlite'; import { useNDKCurrentUser } from '@/lib/hooks/useNDK'; import { UnifiedWorkoutHistoryService, WorkoutFilters } from '@/lib/db/services/UnifiedWorkoutHistoryService'; import { Workout } from '@/types/workout'; interface UseWorkoutHistoryOptions { includeNostr?: boolean; filters?: WorkoutFilters; realtime?: boolean; } /** * Hook for fetching and managing workout history from both local database and Nostr * This hook uses the UnifiedWorkoutHistoryService to provide a consistent interface * for working with workouts from multiple sources. */ export function useWorkoutHistory(options: UseWorkoutHistoryOptions = {}) { const { includeNostr = true, filters, realtime = false } = options; const db = useSQLiteContext(); const { currentUser, isAuthenticated } = useNDKCurrentUser(); const [workouts, setWorkouts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [refreshing, setRefreshing] = useState(false); // Initialize service const workoutHistoryService = useMemo(() => new UnifiedWorkoutHistoryService(db), [db]); // Load workouts function const loadWorkouts = useCallback(async () => { try { setLoading(true); setError(null); let result: Workout[]; if (filters) { // Use filters if provided result = await workoutHistoryService.filterWorkouts(filters); } else { // Otherwise get all workouts result = await workoutHistoryService.getAllWorkouts({ includeNostr, isAuthenticated }); } setWorkouts(result); return result; } catch (err) { console.error('Error loading workouts:', err); setError(err instanceof Error ? err : new Error(String(err))); return []; } finally { setLoading(false); setRefreshing(false); } }, [workoutHistoryService, filters, includeNostr, isAuthenticated]); // Initial load useEffect(() => { loadWorkouts(); }, [loadWorkouts]); // Set up real-time subscription if enabled useEffect(() => { if (!realtime || !isAuthenticated || !currentUser?.pubkey) { return; } // Only create the subscription when we need it (not dependent on includeNostr to prevent re-subs) const subId = workoutHistoryService.subscribeToNostrWorkouts( currentUser.pubkey, (newWorkout) => { // Only update state if we're including Nostr workouts if (includeNostr) { setWorkouts(prev => { // Check if workout already exists const exists = prev.some(w => w.id === newWorkout.id); if (exists) { // Update existing workout return prev.map(w => w.id === newWorkout.id ? newWorkout : w); } else { // Add new workout return [newWorkout, ...prev]; } }); } } ); // Clean up subscription return () => { workoutHistoryService.unsubscribeFromNostrWorkouts(subId); }; // Remove includeNostr from dependencies to prevent re-subs when it changes // We handle the includeNostr state inside the callback instead }, [workoutHistoryService, currentUser?.pubkey, isAuthenticated, realtime]); // Refresh function for pull-to-refresh const refresh = useCallback(() => { setRefreshing(true); return loadWorkouts(); }, [loadWorkouts]); // Get workouts for a specific date const getWorkoutsByDate = useCallback(async (date: Date): Promise => { try { return await workoutHistoryService.getWorkoutsByDate(date); } catch (err) { console.error('Error getting workouts by date:', err); return []; } }, [workoutHistoryService]); // Get workout details const getWorkoutDetails = useCallback(async (workoutId: string): Promise => { try { return await workoutHistoryService.getWorkoutDetails(workoutId); } catch (err) { console.error('Error getting workout details:', err); return null; } }, [workoutHistoryService]); // Publish workout to Nostr const publishWorkoutToNostr = useCallback(async (workoutId: string): Promise => { try { return await workoutHistoryService.publishWorkoutToNostr(workoutId); } catch (err) { console.error('Error publishing workout to Nostr:', err); throw err; } }, [workoutHistoryService]); // Import Nostr workout to local const importNostrWorkoutToLocal = useCallback(async (eventId: string): Promise => { try { return await workoutHistoryService.importNostrWorkoutToLocal(eventId); } catch (err) { console.error('Error importing Nostr workout:', err); throw err; } }, [workoutHistoryService]); return { workouts, loading, error, refreshing, refresh, getWorkoutsByDate, getWorkoutDetails, publishWorkoutToNostr, importNostrWorkoutToLocal, service: workoutHistoryService }; }