/** * TabataFit Browse Screen * React Native UI — wired to shared data */ import { View, StyleSheet, ScrollView, Pressable, Dimensions, Text as RNText } from 'react-native' import { useRouter } from 'expo-router' import { useSafeAreaInsets } from 'react-native-safe-area-context' import { LinearGradient } from 'expo-linear-gradient' import { BlurView } from 'expo-blur' import Ionicons from '@expo/vector-icons/Ionicons' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useHaptics } from '@/src/shared/hooks' import { COLLECTIONS, PROGRAMS, getFeaturedCollection, COLLECTION_COLORS, WORKOUTS, } from '@/src/shared/data' import { useTranslatedCollections, useTranslatedPrograms, useTranslatedWorkouts } from '@/src/shared/data/useTranslatedData' import { StyledText } from '@/src/shared/components/StyledText' import { useThemeColors, BRAND } from '@/src/shared/theme' import type { ThemeColors } from '@/src/shared/theme/types' import { SPACING, LAYOUT } from '@/src/shared/constants/spacing' import { RADIUS } from '@/src/shared/constants/borderRadius' const { width: SCREEN_WIDTH } = Dimensions.get('window') const FONTS = { LARGE_TITLE: 34, TITLE: 28, TITLE_2: 22, HEADLINE: 17, SUBHEADLINE: 15, CAPTION_1: 12, CAPTION_2: 11, } function TextButton({ children, onPress }: { children: string; onPress?: () => void }) { return ( {children} ) } // New Releases: last 4 workouts const NEW_RELEASES = WORKOUTS.slice(-4) // ═══════════════════════════════════════════════════════════════════════════ // MAIN SCREEN // ═══════════════════════════════════════════════════════════════════════════ export default function BrowseScreen() { const { t } = useTranslation() const insets = useSafeAreaInsets() const router = useRouter() const haptics = useHaptics() const colors = useThemeColors() const styles = useMemo(() => createStyles(colors), [colors]) const featuredCollection = getFeaturedCollection() const translatedCollections = useTranslatedCollections(COLLECTIONS) const translatedPrograms = useTranslatedPrograms(PROGRAMS) const translatedNewReleases = useTranslatedWorkouts(NEW_RELEASES) const handleWorkoutPress = (id: string) => { haptics.buttonTap() router.push(`/workout/${id}`) } const handleCollectionPress = (id: string) => { haptics.buttonTap() router.push(`/collection/${id}`) } return ( {/* Header */} {t('screens:browse.title')} {/* Featured Collection */} {featuredCollection && ( handleCollectionPress(featuredCollection.id)}> {t('screens:browse.featured')} {t(`content:collections.${featuredCollection.id}.title`, { defaultValue: featuredCollection.title })} {t(`content:collections.${featuredCollection.id}.description`, { defaultValue: featuredCollection.description })} {t('plurals.workout', { count: featuredCollection.workoutIds.length })} )} {/* Collections Grid */} {t('screens:browse.collections')} {translatedCollections.map((collection) => { const color = COLLECTION_COLORS[collection.id] ?? BRAND.PRIMARY return ( handleCollectionPress(collection.id)} > {collection.icon} {collection.title} {t('plurals.workout', { count: collection.workoutIds.length })} ) })} {/* Programs */} {t('screens:browse.programs')} {t('seeAll')} {translatedPrograms.map((program) => ( {t(`levels.${program.level.toLowerCase()}`)} {program.title} {t('screens:browse.weeksCount', { count: program.weeks })} {t('screens:browse.timesPerWeek', { count: program.workoutsPerWeek })} ))} {/* New Releases */} {t('screens:browse.newReleases')} {translatedNewReleases.map((workout) => ( handleWorkoutPress(workout.id)} > {workout.title} {t('durationLevel', { duration: workout.duration, level: t(`levels.${workout.level.toLowerCase()}`) })} ))} ) } // ═══════════════════════════════════════════════════════════════════════════ // STYLES // ═══════════════════════════════════════════════════════════════════════════ const COLLECTION_CARD_WIDTH = (SCREEN_WIDTH - LAYOUT.SCREEN_PADDING * 2 - SPACING[3]) / 2 function createStyles(colors: ThemeColors) { return StyleSheet.create({ container: { flex: 1, backgroundColor: colors.bg.base, }, scrollView: { flex: 1, }, scrollContent: { paddingHorizontal: LAYOUT.SCREEN_PADDING, }, // Featured Collection featuredCard: { height: 200, borderRadius: RADIUS.GLASS_CARD, overflow: 'hidden', marginBottom: SPACING[8], marginTop: SPACING[4], ...colors.shadow.lg, }, featuredBadge: { flexDirection: 'row', alignItems: 'center', backgroundColor: 'rgba(0, 0, 0, 0.3)', paddingHorizontal: SPACING[2], paddingVertical: SPACING[1], borderRadius: RADIUS.SM, alignSelf: 'flex-start', margin: SPACING[4], gap: SPACING[1], }, featuredBadgeText: { fontSize: 11, fontWeight: 'bold', color: '#FFFFFF', }, featuredInfo: { position: 'absolute', bottom: SPACING[5], left: SPACING[5], right: SPACING[5], }, featuredStats: { flexDirection: 'row', gap: SPACING[4], marginTop: SPACING[3], }, featuredStat: { flexDirection: 'row', alignItems: 'center', gap: SPACING[1], }, // Section section: { marginBottom: SPACING[6], }, sectionHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: SPACING[4], }, // Collections Grid collectionsGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: SPACING[3], marginTop: SPACING[3], }, collectionCard: { width: COLLECTION_CARD_WIDTH, paddingVertical: SPACING[4], paddingHorizontal: SPACING[3], borderRadius: RADIUS.LG, overflow: 'hidden', borderWidth: 1, borderColor: colors.border.glass, gap: SPACING[1], }, collectionIconBg: { width: 44, height: 44, borderRadius: 12, alignItems: 'center', justifyContent: 'center', marginBottom: SPACING[2], }, collectionEmoji: { fontSize: 22, }, // Programs programsScroll: { gap: SPACING[3], }, programCard: { width: 200, height: 140, borderRadius: RADIUS.LG, overflow: 'hidden', padding: SPACING[4], borderWidth: 1, borderColor: colors.border.glass, }, programHeader: { flexDirection: 'row', justifyContent: 'flex-end', marginBottom: SPACING[2], }, programLevelBadge: { backgroundColor: 'rgba(255, 107, 53, 0.2)', paddingHorizontal: SPACING[2], paddingVertical: SPACING[1], borderRadius: RADIUS.SM, }, programMeta: { flexDirection: 'row', gap: SPACING[3], marginTop: SPACING[3], }, programMetaItem: { flexDirection: 'row', alignItems: 'center', gap: SPACING[1], }, // New Releases releaseRow: { flexDirection: 'row', alignItems: 'center', paddingVertical: SPACING[3], paddingHorizontal: SPACING[4], backgroundColor: colors.bg.surface, borderRadius: RADIUS.LG, marginBottom: SPACING[2], gap: SPACING[3], }, releaseAvatar: { width: 44, height: 44, borderRadius: 22, alignItems: 'center', justifyContent: 'center', }, releaseInitial: { fontSize: 18, fontWeight: '700', color: '#FFFFFF', }, releaseInfo: { flex: 1, gap: 2, }, }) }