
* 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.
9.1 KiB
History List View
Last Updated: 2025-04-02
Status: Implemented
Related To: History Overview, Calendar View
Introduction
The History List View is the primary interface for browsing workout history in the POWR app. It presents a chronological feed of completed workouts grouped by month, with detailed cards showing workout information and exercise summaries.
Features
Feature | Status | Notes |
---|---|---|
Chronological Feed | ✅ Implemented | Displays workouts in reverse chronological order |
Monthly Grouping | ✅ Implemented | Organizes workouts by month for better context |
Workout Source Filtering | ✅ Implemented | Toggle between all or local-only workouts |
Nostr Authentication UI | ✅ Implemented | Prompt for unauthenticated users |
Workout Cards | ✅ Implemented | Detailed workout summary cards |
Pull-to-refresh | ✅ Implemented | Updates list with latest workout data |
Visual Styling | ✅ Implemented | Consistent visual design with the app |
Offline Support | ✅ Implemented | Works with locally cached workout data |
Mock Data | ✅ Implemented | Example data shown when no workouts exist |
Implementation Details
The History List View is implemented as a screen component in app/(tabs)/history/workoutHistory.tsx
. It integrates with the unified workout history service through the useWorkoutHistory
hook.
Data Structure
Workouts are organized into a hierarchical structure:
- Monthly Groups: Workouts are first grouped by month (e.g., "MARCH 2025")
- Workout Cards: Each workout is displayed as a card within its month group
- Exercise Summaries: Each card displays a summary of exercises performed
This organization provides users with temporal context while browsing their history.
Source Filtering
For authenticated users, the History List View provides a toggle to filter workouts by source:
// Source filtering UI
<View className="flex-row border border-border rounded-full overflow-hidden">
<Pressable
onPress={() => setIncludeNostr(true)}
style={{
backgroundColor: includeNostr ? primaryBgColor : 'transparent',
paddingHorizontal: 12,
paddingVertical: 6,
}}
>
<Text style={{
color: includeNostr ? primaryTextColor : mutedTextColor,
fontSize: 14,
fontWeight: includeNostr ? '600' : '400',
}}>
All Workouts
</Text>
</Pressable>
<Pressable
onPress={() => setIncludeNostr(false)}
style={{
backgroundColor: !includeNostr ? primaryBgColor : 'transparent',
paddingHorizontal: 12,
paddingVertical: 6,
}}
>
<Text style={{
color: !includeNostr ? primaryTextColor : mutedTextColor,
fontSize: 14,
fontWeight: !includeNostr ? '600' : '400',
}}>
Local Only
</Text>
</Pressable>
</View>
This filtering is implemented at the service level through the useWorkoutHistory
hook:
// Create memoized filters to prevent recreation on every render
const filters = React.useMemo(() => {
if (includeNostr) {
return undefined;
} else {
// Explicitly type the array to match WorkoutFilters interface
return { source: ['local' as const] };
}
}, [includeNostr]);
// Use the unified workout history hook with filters
const {
workouts: allWorkouts,
loading,
refreshing: hookRefreshing,
refresh,
error
} = useWorkoutHistory({
includeNostr,
filters,
realtime: true
});
Month Grouping
Workouts are grouped by month using a helper function:
// Group workouts by month
const groupWorkoutsByMonth = (workouts: Workout[]) => {
const grouped: Record<string, Workout[]> = {};
workouts.forEach(workout => {
const monthKey = format(workout.startTime, 'MMMM yyyy');
if (!grouped[monthKey]) {
grouped[monthKey] = [];
}
grouped[monthKey].push(workout);
});
return Object.entries(grouped);
};
The results are then rendered as section headers with workout cards:
{groupedWorkouts.map(([month, monthWorkouts]) => (
<View key={month} className="mb-6">
<Text className="text-foreground text-xl font-semibold mb-4">
{month.toUpperCase()}
</Text>
{monthWorkouts.map((workout) => (
<WorkoutCard
key={workout.id}
workout={workout}
showDate={true}
showExercises={true}
/>
))}
</View>
))}
Nostr Authentication Prompt
For unauthenticated users, the History List View displays a prompt to login with Nostr:
{!isAuthenticated && (
<View className="mx-4 mt-4 p-4 bg-primary/5 rounded-lg border border-primary/20">
<Text className="text-foreground font-medium mb-2">
Connect with Nostr
</Text>
<Text className="text-muted-foreground mb-4">
Login with Nostr to see your workouts from other devices and back up your workout history.
</Text>
<Button
variant="purple"
onPress={() => setIsLoginSheetOpen(true)}
className="w-full"
>
<Text className="text-white">Login with Nostr</Text>
</Button>
</View>
)}
Mock Data Integration
When no workouts are available (typically for new users), the History List View provides example data to demonstrate the interface:
// Check if we need to use mock data (empty workouts)
if (allWorkouts.length === 0 && !error) {
console.log('No workouts found, using mock data');
setWorkouts(mockWorkouts);
setUseMockData(true);
} else {
setWorkouts(allWorkouts);
setUseMockData(false);
}
// And show a message when using mock data
{useMockData && (
<View className="bg-primary/5 rounded-lg p-4 mb-4 border border-border">
<Text className="text-muted-foreground text-sm">
Showing example data. Your completed workouts will appear here.
</Text>
</View>
)}
Technical Considerations
Performance Optimization
The History List View includes several optimizations:
- Memoization of filter objects to prevent unnecessary re-renders
- Careful state updates to avoid cascading re-renders
- Efficient grouping algorithm for workout organization
- Virtualized lists for large workout histories
Error Handling
Robust error handling ensures a smooth user experience:
- Empty state handling for no workouts
- Loading indicators during data fetching
- Error recovery with fallback to local data
- Clear feedback when using mock data
Interaction with Calendar View
The History List View works in conjunction with the Calendar View through the Material Top Tab Navigator:
const Tab = createMaterialTopTabNavigator();
export default function HistoryLayout() {
return (
<TabScreen>
<Header useLogo={true} showNotifications={true} />
<Tab.Navigator
initialRouteName="workouts"
screenOptions={{...}}
>
<Tab.Screen
name="workouts"
component={HistoryScreen}
options={{ title: 'History' }}
/>
<Tab.Screen
name="calendar"
component={CalendarScreen}
options={{ title: 'Calendar' }}
/>
</Tab.Navigator>
</TabScreen>
);
}
This integration provides users with multiple ways to view their workout history based on their preference.
Usage Example
// History List core implementation
const HistoryScreen = () => {
const [workouts, setWorkouts] = useState<Workout[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [useMockData, setUseMockData] = useState(false);
const [includeNostr, setIncludeNostr] = useState(true);
// Use the unified workout history hook
const {
workouts: allWorkouts,
loading,
refresh
} = useWorkoutHistory({
includeNostr,
filters: includeNostr ? undefined : { source: ['local'] },
realtime: true
});
// Group workouts by month
const groupedWorkouts = groupWorkoutsByMonth(workouts);
return (
<ScrollView
className="flex-1"
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={refresh} />
}
>
{/* Authentication UI */}
{/* Source filtering */}
{/* Grouped workout list */}
</ScrollView>
);
};
Future Enhancements
Planned improvements for the History List View:
- Advanced Search: Full-text search across workout titles, exercises, and notes
- Additional Filtering: More granular filtering by exercise type, duration, and intensity
- Batch Operations: Multi-select and bulk actions for workouts
- Expanded Exercise Details: More detailed exercise information in workout cards
- Social Sharing: Direct sharing of workouts to social media or messaging apps
- Stats Summary: Monthly workout statistics summary at the top of each month group
Related Documentation
- History Overview - Overview of the History tab features and architecture
- Calendar View - Details on the calendar visualization
- Workout Cards - Information on workout card components
- Migration Guide - Guide for migrating to the unified history service