POWR/lib/hooks/useWorkoutHistory.ts

166 lines
5.1 KiB
TypeScript
Raw Permalink Normal View History

// 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<Workout[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(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<Workout[]> => {
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<Workout | null> => {
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<string> => {
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<string> => {
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
};
}