// app/(tabs)/library/exercises.tsx import React, { useRef, useState, useCallback } from 'react'; import { View, SectionList, TouchableOpacity, ViewToken } from 'react-native'; import { Text } from '@/components/ui/text'; import { Button } from '@/components/ui/button'; import { ExerciseCard } from '@/components/exercises/ExerciseCard'; import { FloatingActionButton } from '@/components/shared/FloatingActionButton'; import { NewExerciseSheet } from '@/components/library/NewExerciseSheet'; import { Dumbbell } from 'lucide-react-native'; import { BaseExercise, Exercise } from '@/types/exercise'; import { useExercises } from '@/lib/hooks/useExercises'; export default function ExercisesScreen() { const sectionListRef = useRef(null); const [showNewExercise, setShowNewExercise] = useState(false); const [currentSection, setCurrentSection] = useState(''); const { exercises, loading, error, stats, createExercise, deleteExercise, refreshExercises } = useExercises(); // Organize exercises into sections const sections = React.useMemo(() => { const exercisesByLetter = exercises.reduce((acc, exercise) => { const firstLetter = exercise.title[0].toUpperCase(); if (!acc[firstLetter]) { acc[firstLetter] = []; } acc[firstLetter].push(exercise); return acc; }, {} as Record); return Object.entries(exercisesByLetter) .map(([letter, exercises]) => ({ title: letter, data: exercises.sort((a, b) => a.title.localeCompare(b.title)) })) .sort((a, b) => a.title.localeCompare(b.title)); }, [exercises]); const handleViewableItemsChanged = useCallback(({ viewableItems }: { viewableItems: ViewToken[]; }) => { const firstSection = viewableItems.find(item => item.section)?.section?.title; if (firstSection) { setCurrentSection(firstSection); } }, []); const scrollToSection = useCallback((letter: string) => { const sectionIndex = sections.findIndex(section => section.title === letter); if (sectionIndex !== -1 && sectionListRef.current) { // Try to scroll to section sectionListRef.current.scrollToLocation({ animated: true, sectionIndex, itemIndex: 0, viewPosition: 0, // 0 means top of the view }); // Log for debugging if (__DEV__) { console.log('Scrolling to section:', { letter, sectionIndex, totalSections: sections.length }); } } }, [sections]); // Add getItemLayout to optimize scrolling const getItemLayout = useCallback((data: any, index: number) => ({ length: 100, // Approximate height of each item offset: 100 * index, index, }), []); const handleAddExercise = async (exerciseData: Omit) => { try { const newExercise: Omit = { ...exerciseData, source: 'local', created_at: Date.now(), availability: { source: ['local'] }, format_json: exerciseData.format ? JSON.stringify(exerciseData.format) : undefined, format_units_json: exerciseData.format_units ? JSON.stringify(exerciseData.format_units) : undefined }; await createExercise(newExercise); setShowNewExercise(false); } catch (error) { console.error('Error adding exercise:', error); } }; const handleDelete = async (id: string) => { try { await deleteExercise(id); } catch (error) { console.error('Error deleting exercise:', error); } }; const handleExercisePress = (exerciseId: string) => { console.log('Selected exercise:', exerciseId); }; const alphabet = '#ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); const availableLetters = new Set(sections.map(section => section.title)); if (loading) { return ( Loading exercises... ); } if (error) { return ( {error.message} ); } return ( {/* Stats Bar */} Total Exercises {stats.totalCount} Push {stats.byCategory['Push'] || 0} Pull {stats.byCategory['Pull'] || 0} Legs {stats.byCategory['Legs'] || 0} Core {stats.byCategory['Core'] || 0} {/* Exercise List with Alphabet Scroll */} {/* Main List */} item.id} getItemLayout={getItemLayout} renderSectionHeader={({ section }) => ( {section.title} )} renderItem={({ item }) => ( handleExercisePress(item.id)} onDelete={() => handleDelete(item.id)} /> )} stickySectionHeadersEnabled initialNumToRender={10} maxToRenderPerBatch={10} windowSize={5} onViewableItemsChanged={handleViewableItemsChanged} viewabilityConfig={{ itemVisiblePercentThreshold: 50 }} /> {/* Alphabet List */} true} onResponderMove={(evt) => { const touch = evt.nativeEvent; const element = evt.target; // Get the layout of the alphabet bar if (element) { const elementPosition = (element as any).measure((x: number, y: number, width: number, height: number, pageX: number, pageY: number) => { // Calculate which letter we're touching based on position const totalHeight = height; const letterHeight = totalHeight / alphabet.length; const touchY = touch.pageY - pageY; const index = Math.min( Math.max(Math.floor(touchY / letterHeight), 0), alphabet.length - 1 ); const letter = alphabet[index]; if (availableLetters.has(letter)) { scrollToSection(letter); } }); } }} > {alphabet.map((letter) => ( {letter} ))} setShowNewExercise(true)} /> setShowNewExercise(false)} onSubmit={handleAddExercise} /> ); }