// components/templates/ModalTemplateDetails.tsx import React, { useState, useEffect, createContext, useContext } from 'react'; import { View, ScrollView, Modal, TouchableOpacity, ActivityIndicator } from 'react-native'; import { Text } from '@/components/ui/text'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Separator } from '@/components/ui/separator'; import { Card, CardContent } from '@/components/ui/card'; import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs'; import { NavigationContainer } from '@react-navigation/native'; import { useTheme } from '@react-navigation/native'; import { useColorScheme } from '@/lib/useColorScheme'; import { WorkoutTemplate } from '@/types/templates'; import { useWorkoutStore } from '@/stores/workoutStore'; import { formatTime } from '@/utils/formatTime'; import { Calendar } from 'lucide-react-native'; import { X, ChevronLeft, Star, Copy, Heart, Dumbbell, Target, Hash, Clock, MessageSquare, Zap, Repeat, Bookmark } from 'lucide-react-native'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { cn } from '@/lib/utils'; import type { CustomTheme } from '@/lib/theme'; // Create the tab navigator const Tab = createMaterialTopTabNavigator(); // Create a context to share the template with the tabs interface TemplateContextType { template: WorkoutTemplate | null; onStartWorkout?: () => void; } const TemplateContext = createContext({ template: null }); // Custom hook to access the template function useTemplate() { const context = useContext(TemplateContext); if (!context.template) { throw new Error('useTemplate must be used within a TemplateContextProvider'); } return { template: context.template, onStartWorkout: context.onStartWorkout }; } interface ModalTemplateDetailsProps { templateId: string; open: boolean; onClose: () => void; onFavoriteChange?: (templateId: string, isFavorite: boolean) => void; } // Overview Tab Component function OverviewTab() { const { template } = useTemplate(); const { title, type, category, description, exercises = [], tags = [], metadata, availability } = template; // Calculate source type from availability const sourceType = availability.source.includes('nostr') ? 'nostr' : availability.source.includes('powr') ? 'powr' : 'local'; const isEditable = sourceType === 'local'; return ( {/* Basic Info Section */} {sourceType === 'local' ? 'My Template' : sourceType === 'powr' ? 'POWR Template' : 'Nostr Template'} {type} {/* Category Section */} Category {category} {/* Description Section */} {description && ( Description {description} )} {/* Exercises Section */} Exercises {exercises.map((exerciseConfig, index) => ( {exerciseConfig.exercise.title} {exerciseConfig.targetSets} sets × {exerciseConfig.targetReps} reps {exerciseConfig.notes && ( {exerciseConfig.notes} )} ))} {/* Tags Section */} {tags.length > 0 && ( Tags {tags.map(tag => ( {tag} ))} )} {/* Workout Parameters Section */} Workout Parameters {template.rounds && ( Rounds: {template.rounds} )} {template.duration && ( Duration: {formatTime(template.duration * 1000)} )} {template.interval && ( Interval: {formatTime(template.interval * 1000)} )} {template.restBetweenRounds && ( Rest Between Rounds: {formatTime(template.restBetweenRounds * 1000)} )} {metadata?.averageDuration && ( Average Completion Time: {Math.round(metadata.averageDuration / 60)} minutes )} {/* Usage Stats Section */} {metadata && ( Usage {metadata.useCount && ( Used {metadata.useCount} times )} {metadata.lastUsed && ( Last used: {new Date(metadata.lastUsed).toLocaleDateString()} )} )} {/* Action Buttons */} {isEditable ? ( ) : ( )} ); } // History Tab Component function HistoryTab() { const { template } = useTemplate(); const [isLoading, setIsLoading] = useState(false); // Format date helper const formatDate = (date: Date) => { return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }); }; // Mock workout history - this would come from your database in a real app const mockWorkoutHistory = [ { id: 'hist1', date: new Date(2024, 1, 25), duration: 62, // minutes completed: true, notes: "Increased weight on squats by 10lbs", exercises: [ { name: "Barbell Squat", sets: 3, reps: 8, weight: 215 }, { name: "Bench Press", sets: 3, reps: 8, weight: 175 }, { name: "Bent Over Row", sets: 3, reps: 8, weight: 155 } ] }, { id: 'hist2', date: new Date(2024, 1, 18), duration: 58, // minutes completed: true, exercises: [ { name: "Barbell Squat", sets: 3, reps: 8, weight: 205 }, { name: "Bench Press", sets: 3, reps: 8, weight: 175 }, { name: "Bent Over Row", sets: 3, reps: 8, weight: 155 } ] }, { id: 'hist3', date: new Date(2024, 1, 11), duration: 65, // minutes completed: false, notes: "Stopped early due to time constraints", exercises: [ { name: "Barbell Squat", sets: 3, reps: 8, weight: 205 }, { name: "Bench Press", sets: 3, reps: 8, weight: 170 }, { name: "Bent Over Row", sets: 2, reps: 8, weight: 150 } ] } ]; // Simulate loading history data useEffect(() => { const loadHistory = async () => { setIsLoading(true); // Simulate loading delay await new Promise(resolve => setTimeout(resolve, 500)); setIsLoading(false); }; loadHistory(); }, [template.id]); return ( {/* Performance Summary */} Performance Summary Total Workouts {mockWorkoutHistory.length} Avg Duration {Math.round(mockWorkoutHistory.reduce((acc, w) => acc + w.duration, 0) / mockWorkoutHistory.length)} min Completion {Math.round(mockWorkoutHistory.filter(w => w.completed).length / mockWorkoutHistory.length * 100)}% {/* History List */} Workout History {isLoading ? ( Loading history... ) : mockWorkoutHistory.length > 0 ? ( {mockWorkoutHistory.map((workout) => ( {formatDate(workout.date)} {workout.completed ? "Completed" : "Incomplete"} Duration {workout.duration} min Sets {workout.exercises.reduce((acc, ex) => acc + ex.sets, 0)} Volume {workout.exercises.reduce((acc, ex) => acc + (ex.sets * ex.reps * ex.weight), 0)} lbs {workout.notes && ( {workout.notes} )} ))} ) : ( No workout history available yet Complete a workout using this template to see your history )} ); } // Social Tab Component function SocialTab() { const { template } = useTemplate(); const [isLoading, setIsLoading] = useState(false); // Mock social feed data const mockSocialFeed = [ { id: 'social1', userName: 'FitnessFanatic', userAvatar: 'https://randomuser.me/api/portraits/men/32.jpg', pubkey: 'npub1q8s7vw...', timestamp: new Date(Date.now() - 3600000 * 2), // 2 hours ago content: 'Just crushed this Full Body workout! New PR on bench press 🎉', metrics: { duration: 58, // in minutes volume: 4500, // total weight exercises: 5 }, likes: 12, comments: 3, zaps: 5, reposts: 2, bookmarked: false }, { id: 'social2', userName: 'StrengthJourney', userAvatar: 'https://randomuser.me/api/portraits/women/44.jpg', pubkey: 'npub1z92r3...', timestamp: new Date(Date.now() - 3600000 * 24), // 1 day ago content: 'Modified this workout with extra leg exercises. Feeling the burn!', metrics: { duration: 65, volume: 5200, exercises: "5+2" }, likes: 8, comments: 1, zaps: 3, reposts: 0, bookmarked: false }, { id: 'social3', userName: 'GymCoach', userAvatar: 'https://randomuser.me/api/portraits/men/62.jpg', pubkey: 'npub1xne8q...', timestamp: new Date(Date.now() - 3600000 * 48), // 2 days ago content: 'Great template for beginners! I recommend starting with lighter weights.', metrics: { duration: 45, volume: 3600, exercises: 5 }, likes: 24, comments: 7, zaps: 15, reposts: 6, bookmarked: true } ]; // Social Feed Item Component function SocialFeedItem({ item }: { item: typeof mockSocialFeed[0] }) { const [liked, setLiked] = useState(false); const [zapCount, setZapCount] = useState(item.zaps); const [bookmarked, setBookmarked] = useState(item.bookmarked); const [reposted, setReposted] = useState(false); const [commentCount, setCommentCount] = useState(item.comments); const formatDate = (date: Date) => { const now = new Date(); const diffInHours = Math.floor((now.getTime() - date.getTime()) / (1000 * 60 * 60)); if (diffInHours < 1) { return 'now'; } else if (diffInHours < 24) { return `${diffInHours}h`; } else { const diffInDays = Math.floor(diffInHours / 24); return `${diffInDays}d`; } }; return ( {/* User info and timestamp */} {item.userName.substring(0, 2)} {item.userName} {formatDate(item.timestamp)} @{item.pubkey.substring(0, 10)}... {/* Post content */} {item.content} {/* Workout metrics */} Duration {item.metrics.duration} min Volume {item.metrics.volume} lbs Exercises {item.metrics.exercises} {/* Twitter-like action buttons */} {/* Comment button */} setCommentCount(prev => prev + 1)} > {commentCount > 0 && ( {commentCount} )} {/* Repost button */} setReposted(!reposted)} > {(reposted || item.reposts > 0) && ( {reposted ? item.reposts + 1 : item.reposts} )} {/* Like button */} setLiked(!liked)} > {(liked || item.likes > 0) && ( {liked ? item.likes + 1 : item.likes} )} {/* Zap button */} setZapCount(prev => prev + 1)} > {zapCount > 0 && ( {zapCount} )} {/* Bookmark button */} setBookmarked(!bookmarked)} > ); } return ( Recent Activity Nostr {isLoading ? ( Loading activity... ) : mockSocialFeed.length > 0 ? ( {mockSocialFeed.map((item) => ( ))} ) : ( No social activity found This workout hasn't been shared on Nostr yet )} ); } export function ModalTemplateDetails({ templateId, open, onClose, onFavoriteChange }: ModalTemplateDetailsProps) { const [isLoading, setIsLoading] = useState(true); const [workoutTemplate, setWorkoutTemplate] = useState(null); const [isFavorite, setIsFavorite] = useState(false); const [toggleCounter, setToggleCounter] = useState(0); const theme = useTheme() as CustomTheme; const { isDarkColorScheme } = useColorScheme(); // Use the workoutStore const { startWorkoutFromTemplate, checkFavoriteStatus, addFavorite, removeFavorite } = useWorkoutStore(); // Load template data when the modal opens or templateId changes useEffect(() => { async function loadTemplate() { // Don't load data if the modal is closed if (!open) return; try { setIsLoading(true); if (!templateId) { setIsLoading(false); return; } // Always fetch the current favorite status from the store const currentIsFavorite = checkFavoriteStatus(templateId); setIsFavorite(currentIsFavorite); console.log(`Initial load: Template ${templateId} isFavorite: ${currentIsFavorite}`); // TODO: Implement fetching from database if needed // For now, create a mock template if we couldn't find it const mockTemplate: WorkoutTemplate = { id: templateId, title: "Sample Workout", type: "strength", category: "Full Body", exercises: [{ exercise: { id: "ex1", title: "Barbell Squat", type: "strength", category: "Legs", tags: ["compound", "legs"], availability: { source: ["local"] }, created_at: Date.now() }, targetSets: 3, targetReps: 8 }], isPublic: false, tags: ["strength", "beginner"], version: 1, created_at: Date.now(), availability: { source: ["local"] } }; setWorkoutTemplate(mockTemplate); setIsLoading(false); } catch (error) { console.error("Error loading template:", error); setIsLoading(false); } } loadTemplate(); }, [templateId, open, checkFavoriteStatus]); const handleStartWorkout = async () => { if (!workoutTemplate) return; try { // Use the workoutStore action to start a workout from template await startWorkoutFromTemplate(workoutTemplate.id); // Close the modal onClose(); // Navigate to the active workout screen // We'll leave navigation to the parent component } catch (error) { console.error("Error starting workout:", error); } }; // Inside your handleToggleFavorite function, update it to: const handleToggleFavorite = async () => { if (!workoutTemplate) return; try { // Get the current state directly from the store const currentIsFavorite = checkFavoriteStatus(workoutTemplate.id); console.log(`Current store state: Template ${workoutTemplate.id} isFavorite: ${currentIsFavorite}`); // The new state will be the opposite const newFavoriteStatus = !currentIsFavorite; // Update the store first if (currentIsFavorite) { await removeFavorite(workoutTemplate.id); console.log(`Removed template with ID "${workoutTemplate.id}" from favorites (store action)`); } else { await addFavorite(workoutTemplate); console.log(`Added template "${workoutTemplate.title}" to favorites (store action)`); } // Verify the change took effect in the store const updatedIsFavorite = checkFavoriteStatus(workoutTemplate.id); console.log(`After store update: Template ${workoutTemplate.id} isFavorite: ${updatedIsFavorite}`); // Now update our local state setIsFavorite(updatedIsFavorite); // Force re-render if needed setToggleCounter(prev => prev + 1); // Notify parent component if (onFavoriteChange) { onFavoriteChange(workoutTemplate.id, updatedIsFavorite); } console.log(`Toggled favorite UI: ${workoutTemplate.id} is now ${updatedIsFavorite ? 'favorited' : 'unfavorited'}`); } catch (error) { console.error("Error toggling favorite:", error); } }; console.log(`Template ${workoutTemplate?.id} isFavorite: ${isFavorite}`); // Don't render anything if the modal is closed if (!open) return null; return ( {/* Loading State */} {isLoading || !workoutTemplate ? ( Loading template... ) : ( <> {/* Header */} {workoutTemplate.title} {/* Tab Navigator */} {() => } {() => } {() => } {/* Footer with Start button */} )} )};