POWR/docs/features/profile/tabs/activity_tab.md
DocNR 969163313a fix(auth): Improve authentication state handling and avatar display
* Add style prop to UserAvatar component for better customization
* Refactor UserAvatar to use getAvatarSeed utility for consistent avatar generation
* Fix React hook ordering issues in profile/overview.tsx to prevent crashes during auth state changes
* Add proper state initialization and cleanup during authentication transitions
* Ensure consistent fallback avatar display for unauthenticated users

These changes improve stability during login/logout operations and provide better visual continuity with Robohash avatars when profile images aren't available.
2025-04-02 21:11:25 -04:00

8.0 KiB
Raw Permalink Blame History

Activity Tab

Last Updated: 2025-04-02
Status: Implemented
Related To: Profile Tab Overview, Progress Tab

Introduction

The Activity tab provides users with a summary of their workout activities, including statistics, recent workouts, and personal records. It serves as a quick overview dashboard for tracking workout progress and achievements.

Features

Feature Status Notes
Workout Stats Cards Implemented Shows completed workouts, templates, programs, and PRs
Personal Records Implemented Lists recent personal records with dates
Recent Workouts Implemented Shows most recent completed workouts
Quick Action Buttons Implemented Navigation to start workout and view progress
Authentication Detection Implemented Login prompt for unauthenticated users
Integration with Analytics Implemented Uses AnalyticsService for workout statistics

Implementation Details

The Activity tab is implemented in app/(tabs)/profile/activity.tsx. It integrates with the AnalyticsService for personal records and the WorkoutService for workout history.

Stats Cards Implementation

The Activity tab displays four key statistics in card format:

{/* 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 Implementation

The Activity tab displays the user's personal records:

{/* 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 Implementation

The tab also shows the user's most recent completed workouts:

{/* 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>

Analytics Integration

The Activity tab integrates with the AnalyticsService to display personal records:

// 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]);

Technical Considerations

Authentication Handling

Like other profile tabs, the Activity tab handles authentication with a consistent pattern:

// Show different UI when not authenticated
if (!isAuthenticated) {
  return <NostrProfileLogin message="Login with your Nostr private key to view your activity and stats." />;
}

Data Loading

The component manages loading states and provides appropriate UI feedback:

if (loading || workoutsLoading || templatesLoading) {
  return (
    <View className="flex-1 items-center justify-center">
      <ActivityIndicator />
    </View>
  );
}

Hook Dependencies

The component carefully manages effect hook dependencies to prevent unnecessary re-renders:

useEffect(() => {
  async function loadRecords() {
    // ...implementation
  }
  
  loadRecords();
}, [isAuthenticated, analytics]); // Only rerun when these dependencies change

User Experience Flow

  1. Authentication Check:

    • If user is not authenticated, display NostrProfileLogin component
    • If authenticated, proceed to load activity data
  2. Data Loading:

    • Fetch completed workouts count from WorkoutService
    • Load templates count from TemplateService
    • Fetch personal records from AnalyticsService
    • Show loading indicator during data fetch
  3. Data Display:

    • Show stats cards with summary numbers
    • Display recent personal records with details
    • Show recent completed workouts with WorkoutCard component
    • Provide quick action buttons for common tasks
  4. Navigation Options:

    • "View All" links to relevant detailed screens (Progress, History)
    • Quick action buttons for starting a workout or viewing progress

Future Enhancements

  1. Programs Integration: Complete implementation of programs count when feature is available
  2. Additional Stats: Add more workout statistics like total volume, duration
  3. Enhanced Personal Records: Add visual indicators for progress trends
  4. Streak Tracking: Add workout streak and consistency metrics
  5. Workout Insights: Add AI-generated insights based on workout patterns