/** * TabataFit Paywall Screen * Premium subscription purchase flow */ import React from 'react' import { View, StyleSheet, ScrollView, Pressable, Text, } from 'react-native' import { useRouter } from 'expo-router' import { useSafeAreaInsets } from 'react-native-safe-area-context' import { LinearGradient } from 'expo-linear-gradient' import Ionicons from '@expo/vector-icons/Ionicons' import { useTranslation } from 'react-i18next' import { useHaptics, usePurchases } from '@/src/shared/hooks' import { BRAND, darkColors } from '@/src/shared/theme' import { SPACING } from '@/src/shared/constants/spacing' import { RADIUS } from '@/src/shared/constants/borderRadius' // ═══════════════════════════════════════════════════════════════════════════ // FEATURES LIST // ═══════════════════════════════════════════════════════════════════════════ const PREMIUM_FEATURES = [ { icon: 'musical-notes', key: 'music' }, { icon: 'infinity', key: 'workouts' }, { icon: 'stats-chart', key: 'stats' }, { icon: 'flame', key: 'calories' }, { icon: 'notifications', key: 'reminders' }, { icon: 'close-circle', key: 'ads' }, ] // ═══════════════════════════════════════════════════════════════════════════ // COMPONENTS // ═══════════════════════════════════════════════════════════════════════════ function PlanCard({ title, price, period, savings, isSelected, onPress, }: { title: string price: string period: string savings?: string isSelected: boolean onPress: () => void }) { const haptics = useHaptics() const handlePress = () => { haptics.selection() onPress() } return ( [ styles.planCard, isSelected && styles.planCardSelected, pressed && styles.planCardPressed, ]} > {savings && ( {savings} )} {title} {period} {price} {isSelected && ( )} ) } // ═══════════════════════════════════════════════════════════════════════════ // MAIN SCREEN // ═══════════════════════════════════════════════════════════════════════════ export default function PaywallScreen() { const { t } = useTranslation('screens') const router = useRouter() const insets = useSafeAreaInsets() const haptics = useHaptics() const { monthlyPackage, annualPackage, purchasePackage, restorePurchases, isLoading, } = usePurchases() const [selectedPlan, setSelectedPlan] = React.useState<'monthly' | 'annual'>('annual') // Get prices from RevenueCat packages const monthlyPrice = monthlyPackage?.product.priceString ?? '$4.99' const annualPrice = annualPackage?.product.priceString ?? '$29.99' const annualMonthlyEquivalent = annualPackage ? (annualPackage.product.price / 12).toFixed(2) : '2.49' const handlePurchase = async () => { haptics.buttonTap() const pkg = selectedPlan === 'monthly' ? monthlyPackage : annualPackage if (!pkg) { console.log('[Paywall] No package available for purchase') return } const result = await purchasePackage(pkg) if (result.success) { haptics.workoutComplete() router.back() } else if (!result.cancelled) { console.log('[Paywall] Purchase error:', result.error) } } const handleRestore = async () => { haptics.selection() const restored = await restorePurchases() if (restored) { haptics.workoutComplete() router.back() } } const handleClose = () => { haptics.selection() router.back() } return ( {/* Background Gradient */} {/* Close Button */} {/* Header */} TabataFit+ {t('paywall.subtitle')} {/* Features Grid */} {PREMIUM_FEATURES.map((feature) => ( {t(`paywall.features.${feature.key}`)} ))} {/* Plan Selection */} setSelectedPlan('annual')} /> setSelectedPlan('monthly')} /> {/* Price Note */} {selectedPlan === 'annual' && ( {t('paywall.equivalent', { price: annualMonthlyEquivalent })} )} {/* CTA Button */} {isLoading ? t('paywall.processing') : t('paywall.subscribe')} {/* Restore & Terms */} {t('paywall.restore')} {t('paywall.terms')} ) } // ═══════════════════════════════════════════════════════════════════════════ // STYLES // ═══════════════════════════════════════════════════════════════════════════ const styles = StyleSheet.create({ container: { flex: 1, }, gradient: { ...StyleSheet.absoluteFillObject, }, closeButton: { position: 'absolute', top: SPACING[4], right: SPACING[4], width: 44, height: 44, alignItems: 'center', justifyContent: 'center', zIndex: 10, }, scrollView: { flex: 1, }, scrollContent: { paddingHorizontal: SPACING[5], paddingTop: SPACING[8], }, header: { alignItems: 'center', }, title: { fontSize: 32, fontWeight: '700', color: '#FFF', textAlign: 'center', }, subtitle: { fontSize: 16, color: darkColors.text.secondary, textAlign: 'center', marginTop: SPACING[2], }, featuresGrid: { flexDirection: 'row', flexWrap: 'wrap', marginTop: SPACING[6], marginHorizontal: -SPACING[2], }, featureItem: { width: '33%', alignItems: 'center', paddingVertical: SPACING[3], }, featureIcon: { width: 48, height: 48, borderRadius: 24, backgroundColor: 'rgba(255, 107, 53, 0.15)', alignItems: 'center', justifyContent: 'center', marginBottom: SPACING[2], }, featureText: { fontSize: 13, color: darkColors.text.secondary, textAlign: 'center', }, plansContainer: { marginTop: SPACING[6], gap: SPACING[3], }, planCard: { flexDirection: 'row', alignItems: 'center', backgroundColor: 'rgba(255, 255, 255, 0.08)', borderRadius: RADIUS.LG, padding: SPACING[4], borderWidth: 2, borderColor: 'transparent', }, planCardSelected: { borderColor: BRAND.PRIMARY, backgroundColor: 'rgba(255, 107, 53, 0.1)', }, planCardPressed: { opacity: 0.8, }, savingsBadge: { position: 'absolute', top: -8, right: SPACING[3], backgroundColor: BRAND.PRIMARY, paddingHorizontal: SPACING[2], paddingVertical: 2, borderRadius: RADIUS.SM, }, savingsText: { fontSize: 10, fontWeight: '700', color: '#FFF', }, planInfo: { flex: 1, }, planTitle: { fontSize: 16, fontWeight: '600', color: darkColors.text.primary, }, planPeriod: { fontSize: 13, color: darkColors.text.tertiary, marginTop: 2, }, planPrice: { fontSize: 20, fontWeight: '700', color: BRAND.PRIMARY, }, checkmark: { marginLeft: SPACING[2], }, priceNote: { fontSize: 13, color: darkColors.text.tertiary, textAlign: 'center', marginTop: SPACING[3], }, ctaButton: { borderRadius: RADIUS.LG, overflow: 'hidden', marginTop: SPACING[6], }, ctaButtonDisabled: { opacity: 0.6, }, ctaGradient: { paddingVertical: SPACING[4], alignItems: 'center', }, ctaText: { fontSize: 17, fontWeight: '600', color: '#FFF', }, footer: { marginTop: SPACING[5], alignItems: 'center', gap: SPACING[4], }, restoreText: { fontSize: 14, color: darkColors.text.tertiary, }, termsText: { fontSize: 11, color: darkColors.text.tertiary, textAlign: 'center', lineHeight: 18, paddingHorizontal: SPACING[4], }, })