/**
* TabataFit Paywall Screen
* Premium subscription purchase flow
*/
import React, { useMemo } 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 { useThemeColors, BRAND, GRADIENTS } from '@/src/shared/theme'
import type { ThemeColors } from '@/src/shared/theme/types'
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
// ═══════════════════════════════════════════════════════════════════════════
interface PlanCardStyles {
planCard: object
planCardPressed: object
savingsBadge: object
savingsText: object
planInfo: object
planTitle: object
planPeriod: object
planPrice: object
checkmark: object
}
function PlanCard({
title,
price,
period,
savings,
isSelected,
onPress,
colors,
styles,
}: {
title: string
price: string
period: string
savings?: string
isSelected: boolean
onPress: () => void
colors: ThemeColors
styles: PlanCardStyles
}) {
const haptics = useHaptics()
const handlePress = () => {
haptics.selection()
onPress()
}
return (
[
styles.planCard,
isSelected && { borderColor: BRAND.PRIMARY },
pressed && styles.planCardPressed,
{
backgroundColor: colors.bg.surface,
borderColor: isSelected ? BRAND.PRIMARY : colors.border.glass,
},
]}
>
{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 colors = useThemeColors()
const styles = useMemo(() => createStyles(colors), [colors])
// Extract plan card styles for the child component
const planCardStyles = useMemo(
() => ({
planCard: styles.planCard,
planCardPressed: styles.planCardPressed,
savingsBadge: styles.savingsBadge,
savingsText: styles.savingsText,
planInfo: styles.planInfo,
planTitle: styles.planTitle,
planPeriod: styles.planPeriod,
planPrice: styles.planPrice,
checkmark: styles.checkmark,
}),
[styles],
)
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 (
{/* Close Button */}
{/* Header */}
TabataFit+
{t('paywall.subtitle')}
{/* Features Grid */}
{PREMIUM_FEATURES.map((feature) => (
{t(`paywall.features.${feature.key}`)}
))}
{/* Plan Selection */}
setSelectedPlan('annual')}
colors={colors}
styles={planCardStyles}
/>
setSelectedPlan('monthly')}
colors={colors}
styles={planCardStyles}
/>
{/* 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
// ═══════════════════════════════════════════════════════════════════════════
function createStyles(colors: ThemeColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.bg.base,
},
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: colors.text.primary,
textAlign: 'center',
},
subtitle: {
fontSize: 16,
color: colors.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,
alignItems: 'center',
justifyContent: 'center',
marginBottom: SPACING[2],
},
featureText: {
fontSize: 13,
textAlign: 'center',
},
plansContainer: {
marginTop: SPACING[6],
gap: SPACING[3],
},
planCard: {
flexDirection: 'row',
alignItems: 'center',
borderRadius: RADIUS.LG,
padding: SPACING[4],
borderWidth: 2,
},
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: colors.text.primary,
},
planInfo: {
flex: 1,
},
planTitle: {
fontSize: 16,
fontWeight: '600',
},
planPeriod: {
fontSize: 13,
marginTop: 2,
},
planPrice: {
fontSize: 20,
fontWeight: '700',
},
checkmark: {
marginLeft: SPACING[2],
},
priceNote: {
fontSize: 13,
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',
},
footer: {
marginTop: SPACING[5],
alignItems: 'center',
gap: SPACING[4],
},
restoreText: {
fontSize: 14,
},
termsText: {
fontSize: 11,
textAlign: 'center',
lineHeight: 18,
paddingHorizontal: SPACING[4],
},
})
}