// app/(packs)/import.tsx import React, { useState, useEffect } from 'react'; import { View, ScrollView, StyleSheet, ActivityIndicator, TouchableOpacity } from 'react-native'; import { router, Stack, useLocalSearchParams } from 'expo-router'; import { useNDK } from '@/lib/hooks/useNDK'; import { Text } from '@/components/ui/text'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'; import { Checkbox } from '@/components/ui/checkbox'; import { nip19 } from 'nostr-tools'; import { findTagValue } from '@/utils/nostr-utils'; import { usePOWRPackService } from '@/components/DatabaseProvider'; import { POWRPackImport, POWRPackSelection } from '@/types/powr-pack'; import { InfoIcon, X, CheckCircle2 } from 'lucide-react-native'; import { useIconColor } from '@/lib/theme/iconUtils'; import { useColorScheme } from '@/lib/theme/useColorScheme'; import { COLORS } from '@/lib/theme/colors'; export default function ImportPOWRPackScreen() { const { ndk } = useNDK(); const powrPackService = usePOWRPackService(); const params = useLocalSearchParams<{ naddr?: string }>(); const [naddrInput, setNaddrInput] = useState(params.naddr || ''); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [packData, setPackData] = useState(null); const [selectedTemplates, setSelectedTemplates] = useState([]); const [selectedExercises, setSelectedExercises] = useState([]); const [dependencies, setDependencies] = useState>({}); const [isImporting, setIsImporting] = useState(false); const [importSuccess, setImportSuccess] = useState(false); const { getIconProps } = useIconColor(); const { isDarkColorScheme } = useColorScheme(); // Auto-fetch pack when naddr is provided via params useEffect(() => { if (params.naddr && !isLoading && !packData) { setIsLoading(true); handleFetchPack() .catch(err => { console.error("Auto-fetch error:", err); setIsLoading(false); }); } }, [params.naddr]); // Handle close button press const handleClose = () => { router.back(); }; // Handle fetch button click const handleFetchPack = async () => { if (!naddrInput.trim()) { setError('Please enter a valid naddr'); return; } if (!ndk) { setError('NDK is not initialized'); return; } setIsLoading(true); setError(null); setPackData(null); setSelectedTemplates([]); setSelectedExercises([]); setDependencies({}); try { // Validate naddr format const isValid = naddrInput.startsWith('naddr1'); if (!isValid) { throw new Error('Invalid naddr format. Should start with "naddr1"'); } // Fetch pack data const packImport = await powrPackService.fetchPackFromNaddr(naddrInput, ndk); // Debug logging console.log("Fetched pack event:", packImport.packEvent.id); console.log("Templates count:", packImport.templates.length); console.log("Exercises count:", packImport.exercises.length); setPackData(packImport); // Analyze dependencies const deps = powrPackService.analyzeDependencies(packImport.templates, packImport.exercises); setDependencies(deps); // Pre-select all items setSelectedTemplates(packImport.templates.map(t => t.id)); setSelectedExercises(packImport.exercises.map(e => e.id)); } catch (err) { console.error('Error fetching pack:', err); setError(err instanceof Error ? err.message : 'Failed to fetch pack'); } finally { setIsLoading(false); } }; // Handle template selection change const handleTemplateChange = (templateId: string, isSelected: boolean) => { setSelectedTemplates(prev => { const updated = isSelected ? [...prev, templateId] : prev.filter(id => id !== templateId); // Update required exercises updateRequiredExercises(updated); return updated; }); }; // Handle exercise selection change const handleExerciseChange = (exerciseId: string, isSelected: boolean) => { // Don't allow deselecting if it's required by a selected template if (!isSelected && isRequiredByTemplate(exerciseId)) { return; } setSelectedExercises(prev => isSelected ? [...prev, exerciseId] : prev.filter(id => id !== exerciseId) ); }; // Check if an exercise is required by any selected template const isRequiredByTemplate = (exerciseId: string): boolean => { return selectedTemplates.some(templateId => dependencies[templateId]?.includes(exerciseId) ); }; // Update exercise selection based on template dependencies const updateRequiredExercises = (selectedTemplateIds: string[]) => { // Start with currently manually selected exercises const manuallySelected = selectedExercises.filter(id => !Object.values(dependencies).flat().includes(id) ); // Add all exercises required by selected templates const requiredExercises = selectedTemplateIds.flatMap(templateId => dependencies[templateId] || [] ); // Combine manual selections with required ones, removing duplicates const allExercises = [...new Set([...manuallySelected, ...requiredExercises])]; setSelectedExercises(allExercises); }; // Handle import button click const handleImport = async () => { if (!packData) return; setIsImporting(true); setError(null); try { const packId = generatePackId(); const selection: POWRPackSelection = { packId, selectedTemplates, selectedExercises, templateDependencies: dependencies }; await powrPackService.importPack(packData, selection); setImportSuccess(true); // Navigate back after a short delay setTimeout(() => { router.back(); }, 2000); } catch (err) { console.error('Error importing pack:', err); setError(err instanceof Error ? err.message : 'Failed to import pack'); } finally { setIsImporting(false); } }; // Generate a unique pack ID const generatePackId = (): string => { return 'pack_' + Date.now().toString(36) + Math.random().toString(36).substr(2); }; // Get pack title from event const getPackTitle = (): string => { if (!packData?.packEvent) return 'Unknown Pack'; return findTagValue(packData.packEvent.tags, 'name') || 'Unnamed Pack'; }; // Get pack description from event const getPackDescription = (): string => { if (!packData?.packEvent) return ''; return findTagValue(packData.packEvent.tags, 'about') || packData.packEvent.content || ''; }; return ( ( ), }} /> {/* Input section */} Enter POWR Pack Address Paste a POWR Pack naddr to import {/* Helper text explaining naddr format */} Paste a POWR Pack address (naddr1...) to import templates and exercises shared by the community. {packData ? ( // Success indicator when pack is loaded POWR Pack loaded successfully! ) : ( // Fetch button when no pack is loaded )} {/* Error message */} {error && ( {error} )} {/* Success message */} {importSuccess && ( Pack successfully imported! )} {/* Pack content */} {packData && ( {getPackTitle()} {getPackDescription() ? ( {getPackDescription()} ) : null} Select items to import: {/* Templates section */} {packData.templates && packData.templates.length > 0 ? ( Workout Templates {packData.templates.length} templates available {packData.templates.map(template => { const title = findTagValue(template.tags, 'title') || 'Unnamed Template'; return ( handleTemplateChange(template.id, checked === true) } id={`template-${template.id}`} className="border-input" /> handleTemplateChange(template.id, !selectedTemplates.includes(template.id)) } > {title} ); })} ) : ( No templates available in this pack )} {/* Exercises section */} {packData.exercises && packData.exercises.length > 0 ? ( Exercises {packData.exercises.length} exercises available {packData.exercises.map(exercise => { const title = findTagValue(exercise.tags, 'title') || 'Unnamed Exercise'; const isRequired = isRequiredByTemplate(exercise.id); return ( handleExerciseChange(exercise.id, checked === true) } disabled={isRequired} id={`exercise-${exercise.id}`} className="border-input" /> { if (!isRequired) { handleExerciseChange(exercise.id, !selectedExercises.includes(exercise.id)) } }} > {title} {isRequired && ( Required )} ); })} ) : ( No exercises available in this pack )} {/* Import button */} )} ); } const styles = StyleSheet.create({ container: { flex: 1, padding: 16, }, scrollContent: { paddingBottom: 80, }, input: { marginBottom: 8, }, packContent: { marginTop: 8, }, itemRow: { flexDirection: 'row', alignItems: 'center', paddingVertical: 10, borderBottomWidth: 1, borderBottomColor: '#f0f0f0', }, requiredBadge: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 8, paddingVertical: 2, borderRadius: 12, marginLeft: 8, }, closeButton: { padding: 8, } });