// app/(social)/workout/[id].tsx import React, { useEffect, useState } from 'react'; import { View, ScrollView, ActivityIndicator, TouchableOpacity } from 'react-native'; import { Text } from '@/components/ui/text'; import { Card, CardContent, CardHeader } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar'; import { User, Calendar, Clock, ChevronLeft, Dumbbell, Heart, MessageCircle, CheckCircle } from 'lucide-react-native'; import { useLocalSearchParams, useRouter, Stack } from 'expo-router'; import { useNDK } from '@/lib/hooks/useNDK'; import { useProfile } from '@/lib/hooks/useProfile'; import { parseWorkoutRecord, POWR_EVENT_KINDS } from '@/types/nostr-workout'; import { SocialFeedService } from '@/lib/social/socialFeedService'; import { format } from 'date-fns'; import { Input } from '@/components/ui/input'; export default function WorkoutDetailScreen() { const { id } = useLocalSearchParams<{ id: string }>(); const router = useRouter(); const { ndk } = useNDK(); const [event, setEvent] = useState(null); const [workout, setWorkout] = useState(null); const [comments, setComments] = useState([]); const [loading, setLoading] = useState(true); const [commentCount, setCommentCount] = useState(0); const [likeCount, setLikeCount] = useState(0); const [liked, setLiked] = useState(false); const [commentText, setCommentText] = useState(''); const [isPostingComment, setIsPostingComment] = useState(false); // Profile for the workout author const { profile } = useProfile(workout?.author); // Fetch the workout event useEffect(() => { if (!ndk || !id) return; const fetchEvent = async () => { try { setLoading(true); // Fetch the workout event const filter = { ids: [id], kinds: [POWR_EVENT_KINDS.WORKOUT_RECORD] }; const events = await ndk.fetchEvents(filter); if (events.size > 0) { const workoutEvent = Array.from(events)[0]; setEvent(workoutEvent); // Parse the workout data const parsedWorkout = parseWorkoutRecord(workoutEvent); setWorkout(parsedWorkout); // Fetch comments const socialService = new SocialFeedService(ndk); const fetchedComments = await socialService.getComments(id); setComments(fetchedComments); setCommentCount(fetchedComments.length); // Fetch likes const likesFilter = { kinds: [POWR_EVENT_KINDS.REACTION], '#e': [id] }; const likes = await ndk.fetchEvents(likesFilter); setLikeCount(likes.size); } setLoading(false); } catch (error) { console.error('Error fetching workout:', error); setLoading(false); } }; fetchEvent(); }, [ndk, id]); // Handle like button press const handleLike = async () => { if (!ndk || !event) return; try { const socialService = new SocialFeedService(ndk); await socialService.reactToEvent(event); setLiked(true); setLikeCount(prev => prev + 1); } catch (error) { console.error('Error liking workout:', error); } }; // Handle comment submission const handleSubmitComment = async () => { if (!ndk || !event || !commentText.trim() || isPostingComment) return; setIsPostingComment(true); try { const socialService = new SocialFeedService(ndk); const comment = await socialService.postComment(event, commentText.trim()); // Add the new comment to the list setComments(prev => [...prev, comment]); setCommentCount(prev => prev + 1); // Clear the input setCommentText(''); } catch (error) { console.error('Error posting comment:', error); } finally { setIsPostingComment(false); } }; // Format date string const formatDate = (timestamp?: number) => { if (!timestamp) return 'Unknown date'; try { return format(new Date(timestamp), 'PPP'); } catch (error) { return 'Invalid date'; } }; // Format time string const formatTime = (timestamp?: number) => { if (!timestamp) return ''; try { return format(new Date(timestamp), 'p'); } catch (error) { return ''; } }; // Format duration const formatDuration = (startTime?: number, endTime?: number) => { if (!startTime || !endTime) return 'Unknown duration'; const durationMs = endTime - startTime; const minutes = Math.floor(durationMs / 60000); if (minutes < 60) { return `${minutes} minutes`; } const hours = Math.floor(minutes / 60); const mins = minutes % 60; if (mins === 0) { return `${hours} ${hours === 1 ? 'hour' : 'hours'}`; } return `${hours} ${hours === 1 ? 'hour' : 'hours'} ${mins} ${mins === 1 ? 'minute' : 'minutes'}`; }; // Format comment time const formatCommentTime = (timestamp: number) => { try { const date = new Date(timestamp * 1000); return format(date, 'MMM d, yyyy • h:mm a'); } catch (error) { return 'Unknown time'; } }; // Handle back button press const handleBack = () => { router.back(); }; // Set up header with proper back button for iOS return ( <> ( ), }} /> {loading ? ( Loading workout... ) : !workout ? ( Workout not found ) : ( {/* Workout header */} {workout.title} {/* Author info */} {profile?.image ? ( ) : ( )} {profile?.name || 'Nostr User'} {profile?.nip05 && ( )} {/* Time and date */} {formatDate(workout.startTime)} {formatTime(workout.startTime)} {workout.endTime && ( {formatDuration(workout.startTime, workout.endTime)} )} {/* Workout type */} {workout.type} {/* Workout notes */} {workout.notes && ( Notes {workout.notes} )} {/* Exercise list */} Exercises {workout.exercises.length === 0 ? ( No exercises recorded ) : ( workout.exercises.map((exercise: any, index: number) => ( {exercise.name} {exercise.weight && ( {exercise.weight}kg )} {exercise.reps && ( {exercise.reps} reps )} {exercise.rpe && ( RPE {exercise.rpe} )} {exercise.setType && ( {exercise.setType} )} )) )} {/* Interactions */} Interactions {/* Comments section */} Comments {/* Comment input */} {/* Comments list */} {comments.length === 0 ? ( No comments yet. Be the first to comment! ) : ( comments.map((comment, index) => ( {/* Comment author */} {comment.pubkey.slice(0, 8)}... {formatCommentTime(comment.created_at)} {/* Comment content */} {comment.content} )) )} {/* Back button at bottom for additional usability */} )} ); }