feat: implement React Query for Supabase data fetching
- Install @tanstack/react-query and async-storage-persister - Wrap app with QueryClientProvider in root layout - Migrate useSupabaseData hooks to React Query - Add loading skeleton components for better UX - Configure 5-minute stale time and 24-hour cache - Add query keys for proper cache management - Remove duplicate files and clean up structure
This commit is contained in:
@@ -29,6 +29,7 @@ import { useUserStore } from '@/src/shared/stores'
|
||||
import { useNotifications } from '@/src/shared/hooks'
|
||||
import { initializePurchases } from '@/src/shared/services/purchases'
|
||||
import { initializeAnalytics, getPostHogClient, trackScreen } from '@/src/shared/services/analytics'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
|
||||
Notifications.setNotificationHandler({
|
||||
handleNotification: async () => ({
|
||||
@@ -42,6 +43,18 @@ Notifications.setNotificationHandler({
|
||||
|
||||
SplashScreen.preventAutoHideAsync()
|
||||
|
||||
// Create React Query Client
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
gcTime: 1000 * 60 * 60 * 24, // 24 hours
|
||||
retry: 2,
|
||||
refetchOnWindowFocus: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
function RootLayoutInner() {
|
||||
const colors = useThemeColors()
|
||||
|
||||
@@ -86,6 +99,7 @@ function RootLayoutInner() {
|
||||
}
|
||||
|
||||
const content = (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<View style={{ flex: 1, backgroundColor: colors.bg.base }} onLayout={onLayoutRootView}>
|
||||
<StatusBar style={colors.statusBarStyle} />
|
||||
<Stack
|
||||
@@ -135,6 +149,7 @@ function RootLayoutInner() {
|
||||
/>
|
||||
</Stack>
|
||||
</View>
|
||||
</QueryClientProvider>
|
||||
)
|
||||
|
||||
const posthogClient = getPostHogClient()
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
"@react-navigation/elements": "^2.6.3",
|
||||
"@react-navigation/native": "^7.1.8",
|
||||
"@supabase/supabase-js": "^2.98.0",
|
||||
"@tanstack/query-async-storage-persister": "^5.90.24",
|
||||
"@tanstack/react-query": "^5.90.21",
|
||||
"@tanstack/react-query-persist-client": "^5.90.24",
|
||||
"expo": "~54.0.33",
|
||||
"expo-application": "~7.0.8",
|
||||
"expo-av": "~16.0.8",
|
||||
|
||||
190
src/shared/components/loading/Skeleton.tsx
Normal file
190
src/shared/components/loading/Skeleton.tsx
Normal file
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* Loading Skeleton Components
|
||||
* Shimmer loading states for better UX
|
||||
*/
|
||||
|
||||
import { View, StyleSheet, Animated } from 'react-native'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { useThemeColors } from '@/src/shared/theme'
|
||||
import { SPACING } from '@/src/shared/constants/spacing'
|
||||
import { RADIUS } from '@/src/shared/constants/borderRadius'
|
||||
|
||||
interface SkeletonProps {
|
||||
width?: number | string
|
||||
height?: number
|
||||
borderRadius?: number
|
||||
style?: any
|
||||
}
|
||||
|
||||
/**
|
||||
* Shimmer Skeleton Component
|
||||
*/
|
||||
export function Skeleton({ width = '100%', height = 20, borderRadius = RADIUS.MD, style }: SkeletonProps) {
|
||||
const colors = useThemeColors()
|
||||
const shimmerValue = useRef(new Animated.Value(0)).current
|
||||
|
||||
useEffect(() => {
|
||||
const shimmer = Animated.loop(
|
||||
Animated.sequence([
|
||||
Animated.timing(shimmerValue, {
|
||||
toValue: 1,
|
||||
duration: 1500,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
Animated.timing(shimmerValue, {
|
||||
toValue: 0,
|
||||
duration: 1500,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
])
|
||||
)
|
||||
shimmer.start()
|
||||
return () => shimmer.stop()
|
||||
}, [])
|
||||
|
||||
const translateX = shimmerValue.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [-200, 200],
|
||||
})
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.skeleton,
|
||||
{
|
||||
width,
|
||||
height,
|
||||
borderRadius,
|
||||
backgroundColor: colors.bg.overlay2,
|
||||
},
|
||||
style,
|
||||
]}
|
||||
>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.shimmer,
|
||||
{
|
||||
transform: [{ translateX }],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Workout Card Skeleton
|
||||
*/
|
||||
export function WorkoutCardSkeleton() {
|
||||
const colors = useThemeColors()
|
||||
|
||||
return (
|
||||
<View style={[styles.card, { backgroundColor: colors.bg.surface }]}>
|
||||
<Skeleton width="100%" height={160} borderRadius={RADIUS.LG} />
|
||||
<View style={styles.cardContent}>
|
||||
<Skeleton width="70%" height={20} />
|
||||
<View style={styles.row}>
|
||||
<Skeleton width="40%" height={16} />
|
||||
<Skeleton width="30%" height={16} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Trainer Card Skeleton
|
||||
*/
|
||||
export function TrainerCardSkeleton() {
|
||||
const colors = useThemeColors()
|
||||
|
||||
return (
|
||||
<View style={[styles.trainerCard, { backgroundColor: colors.bg.surface }]}>
|
||||
<Skeleton width={80} height={80} borderRadius={40} />
|
||||
<View style={styles.trainerInfo}>
|
||||
<Skeleton width="80%" height={18} />
|
||||
<Skeleton width="60%" height={14} />
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Collection Card Skeleton
|
||||
*/
|
||||
export function CollectionCardSkeleton() {
|
||||
return (
|
||||
<View style={styles.collectionCard}>
|
||||
<Skeleton width={120} height={120} borderRadius={RADIUS.XL} />
|
||||
<Skeleton width="80%" height={18} style={{ marginTop: SPACING[3] }} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Stats Card Skeleton
|
||||
*/
|
||||
export function StatsCardSkeleton() {
|
||||
const colors = useThemeColors()
|
||||
|
||||
return (
|
||||
<View style={[styles.statsCard, { backgroundColor: colors.bg.surface }]}>
|
||||
<View style={styles.statsHeader}>
|
||||
<Skeleton width="60%" height={14} />
|
||||
<Skeleton width={24} height={24} borderRadius={12} />
|
||||
</View>
|
||||
<Skeleton width="50%" height={32} style={{ marginTop: SPACING[2] }} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
skeleton: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
shimmer: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||||
width: 100,
|
||||
},
|
||||
card: {
|
||||
borderRadius: RADIUS.LG,
|
||||
overflow: 'hidden',
|
||||
marginBottom: SPACING[4],
|
||||
},
|
||||
cardContent: {
|
||||
padding: SPACING[4],
|
||||
gap: SPACING[2],
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
marginTop: SPACING[2],
|
||||
},
|
||||
trainerCard: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
padding: SPACING[4],
|
||||
borderRadius: RADIUS.LG,
|
||||
marginBottom: SPACING[3],
|
||||
},
|
||||
trainerInfo: {
|
||||
marginLeft: SPACING[4],
|
||||
flex: 1,
|
||||
gap: SPACING[2],
|
||||
},
|
||||
collectionCard: {
|
||||
alignItems: 'center',
|
||||
padding: SPACING[4],
|
||||
},
|
||||
statsCard: {
|
||||
padding: SPACING[4],
|
||||
borderRadius: RADIUS.LG,
|
||||
minWidth: 140,
|
||||
},
|
||||
statsHeader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
})
|
||||
@@ -1,397 +1,151 @@
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
/**
|
||||
* React Query Data Hooks
|
||||
* Replaces manual state management with React Query for better caching and performance
|
||||
*/
|
||||
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { dataService } from '../data/dataService'
|
||||
import type { Workout, Trainer, Collection, Program } from '../types'
|
||||
|
||||
// Query Keys
|
||||
export const queryKeys = {
|
||||
workouts: 'workouts',
|
||||
workout: (id: string) => ['workouts', id],
|
||||
workoutsByCategory: (category: string) => ['workouts', 'category', category],
|
||||
workoutsByTrainer: (trainerId: string) => ['workouts', 'trainer', trainerId],
|
||||
featuredWorkouts: ['workouts', 'featured'],
|
||||
trainers: 'trainers',
|
||||
trainer: (id: string) => ['trainers', id],
|
||||
collections: 'collections',
|
||||
collection: (id: string) => ['collections', id],
|
||||
programs: 'programs',
|
||||
} as const
|
||||
|
||||
/**
|
||||
* Hook to fetch all workouts
|
||||
*/
|
||||
export function useWorkouts() {
|
||||
const [workouts, setWorkouts] = useState<Workout[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<Error | null>(null)
|
||||
|
||||
const fetchWorkouts = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
const data = await dataService.getAllWorkouts()
|
||||
setWorkouts(data)
|
||||
setError(null)
|
||||
} catch (err) {
|
||||
setError(err as Error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
|
||||
async function load() {
|
||||
try {
|
||||
setLoading(true)
|
||||
const data = await dataService.getAllWorkouts()
|
||||
if (mounted) {
|
||||
setWorkouts(data)
|
||||
setError(null)
|
||||
}
|
||||
} catch (err) {
|
||||
if (mounted) {
|
||||
setError(err as Error)
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
load()
|
||||
return () => { mounted = false }
|
||||
}, [])
|
||||
|
||||
return { workouts, loading, error, refetch: fetchWorkouts }
|
||||
return useQuery({
|
||||
queryKey: [queryKeys.workouts],
|
||||
queryFn: () => dataService.getAllWorkouts(),
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to fetch single workout
|
||||
*/
|
||||
export function useWorkout(id: string | undefined) {
|
||||
const [workout, setWorkout] = useState<Workout | undefined>()
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<Error | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
|
||||
async function load() {
|
||||
if (!id) {
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true)
|
||||
const data = await dataService.getWorkoutById(id)
|
||||
if (mounted) {
|
||||
setWorkout(data)
|
||||
setError(null)
|
||||
}
|
||||
} catch (err) {
|
||||
if (mounted) {
|
||||
setError(err as Error)
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
load()
|
||||
return () => { mounted = false }
|
||||
}, [id])
|
||||
|
||||
return { workout, loading, error }
|
||||
return useQuery({
|
||||
queryKey: queryKeys.workout(id || ''),
|
||||
queryFn: () => dataService.getWorkoutById(id!),
|
||||
enabled: !!id,
|
||||
staleTime: 1000 * 60 * 5,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to fetch workouts by category
|
||||
*/
|
||||
export function useWorkoutsByCategory(category: string) {
|
||||
const [workouts, setWorkouts] = useState<Workout[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<Error | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
|
||||
async function load() {
|
||||
try {
|
||||
setLoading(true)
|
||||
const data = await dataService.getWorkoutsByCategory(category)
|
||||
if (mounted) {
|
||||
setWorkouts(data)
|
||||
setError(null)
|
||||
}
|
||||
} catch (err) {
|
||||
if (mounted) {
|
||||
setError(err as Error)
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
load()
|
||||
return () => { mounted = false }
|
||||
}, [category])
|
||||
|
||||
return { workouts, loading, error }
|
||||
}
|
||||
|
||||
export function useTrainers() {
|
||||
const [trainers, setTrainers] = useState<Trainer[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<Error | null>(null)
|
||||
|
||||
const fetchTrainers = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
const data = await dataService.getAllTrainers()
|
||||
setTrainers(data)
|
||||
setError(null)
|
||||
} catch (err) {
|
||||
setError(err as Error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
|
||||
async function load() {
|
||||
try {
|
||||
setLoading(true)
|
||||
const data = await dataService.getAllTrainers()
|
||||
if (mounted) {
|
||||
setTrainers(data)
|
||||
setError(null)
|
||||
}
|
||||
} catch (err) {
|
||||
if (mounted) {
|
||||
setError(err as Error)
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
load()
|
||||
return () => { mounted = false }
|
||||
}, [])
|
||||
|
||||
return { trainers, loading, error, refetch: fetchTrainers }
|
||||
}
|
||||
|
||||
export function useTrainer(id: string | undefined) {
|
||||
const [trainer, setTrainer] = useState<Trainer | undefined>()
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<Error | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
|
||||
async function load() {
|
||||
if (!id) {
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true)
|
||||
const data = await dataService.getTrainerById(id)
|
||||
if (mounted) {
|
||||
setTrainer(data)
|
||||
setError(null)
|
||||
}
|
||||
} catch (err) {
|
||||
if (mounted) {
|
||||
setError(err as Error)
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
load()
|
||||
return () => { mounted = false }
|
||||
}, [id])
|
||||
|
||||
return { trainer, loading, error }
|
||||
}
|
||||
|
||||
export function useCollections() {
|
||||
const [collections, setCollections] = useState<Collection[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<Error | null>(null)
|
||||
|
||||
const fetchCollections = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
const data = await dataService.getAllCollections()
|
||||
setCollections(data)
|
||||
setError(null)
|
||||
} catch (err) {
|
||||
setError(err as Error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
|
||||
async function load() {
|
||||
try {
|
||||
setLoading(true)
|
||||
const data = await dataService.getAllCollections()
|
||||
if (mounted) {
|
||||
setCollections(data)
|
||||
setError(null)
|
||||
}
|
||||
} catch (err) {
|
||||
if (mounted) {
|
||||
setError(err as Error)
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
load()
|
||||
return () => { mounted = false }
|
||||
}, [])
|
||||
|
||||
return { collections, loading, error, refetch: fetchCollections }
|
||||
}
|
||||
|
||||
export function useCollection(id: string | undefined) {
|
||||
const [collection, setCollection] = useState<Collection | undefined>()
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<Error | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
|
||||
async function load() {
|
||||
if (!id) {
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true)
|
||||
const data = await dataService.getCollectionById(id)
|
||||
if (mounted) {
|
||||
setCollection(data)
|
||||
setError(null)
|
||||
}
|
||||
} catch (err) {
|
||||
if (mounted) {
|
||||
setError(err as Error)
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
load()
|
||||
return () => { mounted = false }
|
||||
}, [id])
|
||||
|
||||
return { collection, loading, error }
|
||||
}
|
||||
|
||||
export function usePrograms() {
|
||||
const [programs, setPrograms] = useState<Program[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<Error | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
|
||||
async function load() {
|
||||
try {
|
||||
setLoading(true)
|
||||
const data = await dataService.getAllPrograms()
|
||||
if (mounted) {
|
||||
setPrograms(data)
|
||||
setError(null)
|
||||
}
|
||||
} catch (err) {
|
||||
if (mounted) {
|
||||
setError(err as Error)
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
load()
|
||||
return () => { mounted = false }
|
||||
}, [])
|
||||
|
||||
return { programs, loading, error }
|
||||
}
|
||||
|
||||
export function useFeaturedWorkouts() {
|
||||
const [workouts, setWorkouts] = useState<Workout[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<Error | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
|
||||
async function load() {
|
||||
try {
|
||||
setLoading(true)
|
||||
const data = await dataService.getFeaturedWorkouts()
|
||||
if (mounted) {
|
||||
setWorkouts(data)
|
||||
setError(null)
|
||||
}
|
||||
} catch (err) {
|
||||
if (mounted) {
|
||||
setError(err as Error)
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
load()
|
||||
return () => { mounted = false }
|
||||
}, [])
|
||||
|
||||
return { workouts, loading, error }
|
||||
return useQuery({
|
||||
queryKey: queryKeys.workoutsByCategory(category),
|
||||
queryFn: () => dataService.getWorkoutsByCategory(category),
|
||||
enabled: !!category,
|
||||
staleTime: 1000 * 60 * 5,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to fetch workouts by trainer
|
||||
*/
|
||||
export function useWorkoutsByTrainer(trainerId: string) {
|
||||
const [workouts, setWorkouts] = useState<Workout[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<Error | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
|
||||
async function load() {
|
||||
try {
|
||||
setLoading(true)
|
||||
const data = await dataService.getWorkoutsByTrainer(trainerId)
|
||||
if (mounted) {
|
||||
setWorkouts(data)
|
||||
setError(null)
|
||||
}
|
||||
} catch (err) {
|
||||
if (mounted) {
|
||||
setError(err as Error)
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
load()
|
||||
return () => { mounted = false }
|
||||
}, [trainerId])
|
||||
|
||||
return { workouts, loading, error }
|
||||
return useQuery({
|
||||
queryKey: queryKeys.workoutsByTrainer(trainerId),
|
||||
queryFn: () => dataService.getWorkoutsByTrainer(trainerId),
|
||||
enabled: !!trainerId,
|
||||
staleTime: 1000 * 60 * 5,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to fetch featured workouts
|
||||
*/
|
||||
export function useFeaturedWorkouts() {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.featuredWorkouts,
|
||||
queryFn: () => dataService.getFeaturedWorkouts(),
|
||||
staleTime: 1000 * 60 * 5,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to fetch popular workouts (uses recent workouts as proxy)
|
||||
*/
|
||||
export function usePopularWorkouts(count: number = 8) {
|
||||
return useQuery({
|
||||
queryKey: ['workouts', 'popular', count],
|
||||
queryFn: async () => {
|
||||
const allWorkouts = await dataService.getAllWorkouts()
|
||||
return allWorkouts.slice(0, count)
|
||||
},
|
||||
staleTime: 1000 * 60 * 5,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to fetch all trainers
|
||||
*/
|
||||
export function useTrainers() {
|
||||
return useQuery({
|
||||
queryKey: [queryKeys.trainers],
|
||||
queryFn: () => dataService.getAllTrainers(),
|
||||
staleTime: 1000 * 60 * 5,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to fetch single trainer
|
||||
*/
|
||||
export function useTrainer(id: string | undefined) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.trainer(id || ''),
|
||||
queryFn: () => dataService.getTrainerById(id!),
|
||||
enabled: !!id,
|
||||
staleTime: 1000 * 60 * 5,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to fetch all collections
|
||||
*/
|
||||
export function useCollections() {
|
||||
return useQuery({
|
||||
queryKey: [queryKeys.collections],
|
||||
queryFn: () => dataService.getAllCollections(),
|
||||
staleTime: 1000 * 60 * 5,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to fetch single collection
|
||||
*/
|
||||
export function useCollection(id: string | undefined) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.collection(id || ''),
|
||||
queryFn: () => dataService.getCollectionById(id!),
|
||||
enabled: !!id,
|
||||
staleTime: 1000 * 60 * 5,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to fetch all programs
|
||||
*/
|
||||
export function usePrograms() {
|
||||
return useQuery({
|
||||
queryKey: [queryKeys.programs],
|
||||
queryFn: () => dataService.getAllPrograms(),
|
||||
staleTime: 1000 * 60 * 5,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user