mirror of
https://github.com/DocNR/POWR.git
synced 2025-06-06 18:31:03 +00:00
218 lines
7.7 KiB
TypeScript
218 lines
7.7 KiB
TypeScript
![]() |
// app/(tabs)/profile/activity.tsx
|
|||
|
import React, { useState, useEffect } from 'react';
|
|||
|
import { View, ScrollView, Pressable } from 'react-native';
|
|||
|
import { Text } from '@/components/ui/text';
|
|||
|
import { Button } from '@/components/ui/button';
|
|||
|
import { Card, CardContent } from '@/components/ui/card';
|
|||
|
import { useNDKCurrentUser } from '@/lib/hooks/useNDK';
|
|||
|
import { ActivityIndicator } from 'react-native';
|
|||
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|||
|
import NostrLoginSheet from '@/components/sheets/NostrLoginSheet';
|
|||
|
import { useRouter } from 'expo-router';
|
|||
|
import { useAnalytics } from '@/lib/hooks/useAnalytics';
|
|||
|
import { PersonalRecord } from '@/lib/services/AnalyticsService';
|
|||
|
import { formatDistanceToNow } from 'date-fns';
|
|||
|
import { useWorkouts } from '@/lib/hooks/useWorkouts';
|
|||
|
import { useTemplates } from '@/lib/hooks/useTemplates';
|
|||
|
import WorkoutCard from '@/components/workout/WorkoutCard';
|
|||
|
import { Dumbbell, BarChart2, Award, Calendar } from 'lucide-react-native';
|
|||
|
|
|||
|
export default function ActivityScreen() {
|
|||
|
const insets = useSafeAreaInsets();
|
|||
|
const router = useRouter();
|
|||
|
const { isAuthenticated } = useNDKCurrentUser();
|
|||
|
const analytics = useAnalytics();
|
|||
|
const { workouts, loading: workoutsLoading } = useWorkouts();
|
|||
|
const { templates, loading: templatesLoading } = useTemplates();
|
|||
|
const [isLoginSheetOpen, setIsLoginSheetOpen] = useState(false);
|
|||
|
const [records, setRecords] = useState<PersonalRecord[]>([]);
|
|||
|
const [loading, setLoading] = useState(true);
|
|||
|
|
|||
|
// Stats
|
|||
|
const completedWorkouts = workouts?.filter(w => w.isCompleted)?.length || 0;
|
|||
|
const totalTemplates = templates?.length || 0;
|
|||
|
const totalPrograms = 0; // Placeholder for programs count
|
|||
|
|
|||
|
// Load personal records
|
|||
|
useEffect(() => {
|
|||
|
async function loadRecords() {
|
|||
|
if (!isAuthenticated) return;
|
|||
|
|
|||
|
try {
|
|||
|
setLoading(true);
|
|||
|
const personalRecords = await analytics.getPersonalRecords(undefined, 3);
|
|||
|
setRecords(personalRecords);
|
|||
|
} catch (error) {
|
|||
|
console.error('Error loading personal records:', error);
|
|||
|
} finally {
|
|||
|
setLoading(false);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
loadRecords();
|
|||
|
}, [isAuthenticated, analytics]);
|
|||
|
|
|||
|
// Show different UI when not authenticated
|
|||
|
if (!isAuthenticated) {
|
|||
|
return (
|
|||
|
<View className="flex-1 items-center justify-center p-6">
|
|||
|
<Text className="text-center text-muted-foreground mb-8">
|
|||
|
Login with your Nostr private key to view your activity and stats.
|
|||
|
</Text>
|
|||
|
<Button
|
|||
|
onPress={() => setIsLoginSheetOpen(true)}
|
|||
|
className="px-6"
|
|||
|
>
|
|||
|
<Text className="text-white">Login with Nostr</Text>
|
|||
|
</Button>
|
|||
|
|
|||
|
{/* NostrLoginSheet */}
|
|||
|
<NostrLoginSheet
|
|||
|
open={isLoginSheetOpen}
|
|||
|
onClose={() => setIsLoginSheetOpen(false)}
|
|||
|
/>
|
|||
|
</View>
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
if (loading || workoutsLoading || templatesLoading) {
|
|||
|
return (
|
|||
|
<View className="flex-1 items-center justify-center">
|
|||
|
<ActivityIndicator />
|
|||
|
</View>
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
return (
|
|||
|
<ScrollView
|
|||
|
className="flex-1"
|
|||
|
contentContainerStyle={{
|
|||
|
paddingBottom: insets.bottom + 20
|
|||
|
}}
|
|||
|
>
|
|||
|
{/* Stats Cards - Row 1 */}
|
|||
|
<View className="flex-row px-4 pt-4">
|
|||
|
<View className="flex-1 pr-2">
|
|||
|
<Card>
|
|||
|
<CardContent className="p-4 items-center justify-center">
|
|||
|
<Dumbbell size={24} className="text-primary mb-2" />
|
|||
|
<Text className="text-2xl font-bold">{completedWorkouts}</Text>
|
|||
|
<Text className="text-muted-foreground">Workouts</Text>
|
|||
|
</CardContent>
|
|||
|
</Card>
|
|||
|
</View>
|
|||
|
|
|||
|
<View className="flex-1 pl-2">
|
|||
|
<Card>
|
|||
|
<CardContent className="p-4 items-center justify-center">
|
|||
|
<Calendar size={24} className="text-primary mb-2" />
|
|||
|
<Text className="text-2xl font-bold">{totalTemplates}</Text>
|
|||
|
<Text className="text-muted-foreground">Templates</Text>
|
|||
|
</CardContent>
|
|||
|
</Card>
|
|||
|
</View>
|
|||
|
</View>
|
|||
|
|
|||
|
{/* Stats Cards - Row 2 */}
|
|||
|
<View className="flex-row px-4 pt-4 mb-4">
|
|||
|
<View className="flex-1 pr-2">
|
|||
|
<Card>
|
|||
|
<CardContent className="p-4 items-center justify-center">
|
|||
|
<BarChart2 size={24} className="text-primary mb-2" />
|
|||
|
<Text className="text-2xl font-bold">{totalPrograms}</Text>
|
|||
|
<Text className="text-muted-foreground">Programs</Text>
|
|||
|
</CardContent>
|
|||
|
</Card>
|
|||
|
</View>
|
|||
|
|
|||
|
<View className="flex-1 pl-2">
|
|||
|
<Card>
|
|||
|
<CardContent className="p-4 items-center justify-center">
|
|||
|
<Award size={24} className="text-primary mb-2" />
|
|||
|
<Text className="text-2xl font-bold">{records.length}</Text>
|
|||
|
<Text className="text-muted-foreground">PRs</Text>
|
|||
|
</CardContent>
|
|||
|
</Card>
|
|||
|
</View>
|
|||
|
</View>
|
|||
|
|
|||
|
{/* Personal Records */}
|
|||
|
<Card className="mx-4 mb-4">
|
|||
|
<CardContent className="p-4">
|
|||
|
<View className="flex-row justify-between items-center mb-4">
|
|||
|
<Text className="text-lg font-semibold">Personal Records</Text>
|
|||
|
<Pressable onPress={() => router.push('/profile/progress')}>
|
|||
|
<Text className="text-primary">View All</Text>
|
|||
|
</Pressable>
|
|||
|
</View>
|
|||
|
|
|||
|
{records.length === 0 ? (
|
|||
|
<Text className="text-muted-foreground text-center py-4">
|
|||
|
No personal records yet. Complete more workouts to see your progress.
|
|||
|
</Text>
|
|||
|
) : (
|
|||
|
records.map((record) => (
|
|||
|
<View key={record.id} className="py-2 border-b border-border">
|
|||
|
<Text className="font-medium">{record.exerciseName}</Text>
|
|||
|
<Text>{record.value} {record.unit} × {record.reps} reps</Text>
|
|||
|
<Text className="text-muted-foreground text-sm">
|
|||
|
{formatDistanceToNow(new Date(record.date), { addSuffix: true })}
|
|||
|
</Text>
|
|||
|
</View>
|
|||
|
))
|
|||
|
)}
|
|||
|
</CardContent>
|
|||
|
</Card>
|
|||
|
|
|||
|
{/* Recent Workouts */}
|
|||
|
<Card className="mx-4 mb-4">
|
|||
|
<CardContent className="p-4">
|
|||
|
<View className="flex-row justify-between items-center mb-4">
|
|||
|
<Text className="text-lg font-semibold">Recent Workouts</Text>
|
|||
|
<Pressable onPress={() => router.push('/history/workoutHistory')}>
|
|||
|
<Text className="text-primary">View All</Text>
|
|||
|
</Pressable>
|
|||
|
</View>
|
|||
|
|
|||
|
{workouts && workouts.length > 0 ? (
|
|||
|
workouts
|
|||
|
.filter(workout => workout.isCompleted)
|
|||
|
.slice(0, 2)
|
|||
|
.map(workout => (
|
|||
|
<View key={workout.id} className="mb-3">
|
|||
|
<WorkoutCard
|
|||
|
workout={workout}
|
|||
|
showDate={true}
|
|||
|
showExercises={false}
|
|||
|
/>
|
|||
|
</View>
|
|||
|
))
|
|||
|
) : (
|
|||
|
<Text className="text-muted-foreground text-center py-4">
|
|||
|
No recent workouts. Complete a workout to see it here.
|
|||
|
</Text>
|
|||
|
)}
|
|||
|
</CardContent>
|
|||
|
</Card>
|
|||
|
|
|||
|
{/* Quick Actions */}
|
|||
|
<View className="p-4 gap-2">
|
|||
|
<Button
|
|||
|
variant="purple"
|
|||
|
className="mb-2"
|
|||
|
onPress={() => router.push('/')}
|
|||
|
>
|
|||
|
<Text className="text-white">Start a Workout</Text>
|
|||
|
</Button>
|
|||
|
<Button
|
|||
|
variant="outline"
|
|||
|
className="mb-2"
|
|||
|
onPress={() => router.push('/profile/progress')}
|
|||
|
>
|
|||
|
<Text>View Progress</Text>
|
|||
|
</Button>
|
|||
|
</View>
|
|||
|
</ScrollView>
|
|||
|
);
|
|||
|
}
|