/** * TabataFit Profile Screen — Premium React Native * Apple Fitness+ inspired design, pure React Native components */ import { useRouter } from 'expo-router' import { View, ScrollView, StyleSheet, TouchableOpacity, Switch, Text as RNText, TextStyle, } from 'react-native' import { useSafeAreaInsets } from 'react-native-safe-area-context' import * as Linking from 'expo-linking' import Constants from 'expo-constants' import { useTranslation } from 'react-i18next' import { useMemo } from 'react' import { useUserStore } from '@/src/shared/stores' import { requestNotificationPermissions, usePurchases } from '@/src/shared/hooks' import { useThemeColors, BRAND } from '@/src/shared/theme' import type { ThemeColors } from '@/src/shared/theme/types' // ═══════════════════════════════════════════════════════════════════════════ // STYLED TEXT COMPONENT // ═══════════════════════════════════════════════════════════════════════════ interface TextProps { children: React.ReactNode style?: TextStyle size?: number weight?: 'normal' | 'bold' | '600' | '700' | '800' | '900' color?: string center?: boolean } function Text({ children, style, size, weight, color, center }: TextProps) { const colors = useThemeColors() return ( {children} ) } // ═══════════════════════════════════════════════════════════════════════════ // COMPONENT: PROFILE SCREEN // ═══════════════════════════════════════════════════════════════════════════ export default function ProfileScreen() { const { t } = useTranslation('screens') const router = useRouter() const insets = useSafeAreaInsets() const colors = useThemeColors() const styles = useMemo(() => createStyles(colors), [colors]) const profile = useUserStore((s) => s.profile) const settings = useUserStore((s) => s.settings) const updateSettings = useUserStore((s) => s.updateSettings) const updateProfile = useUserStore((s) => s.updateProfile) const { restorePurchases, isPremium } = usePurchases() const planLabel = isPremium ? 'TabataFit+' : t('profile.freePlan') const avatarInitial = profile.name?.[0]?.toUpperCase() || 'U' // Mock stats (replace with real data from activityStore when available) const stats = { workouts: 47, streak: 12, calories: 12500, } const handleSignOut = () => { updateProfile({ name: '', email: '', subscription: 'free', onboardingCompleted: false, }) router.replace('/onboarding') } const handleRestore = async () => { await restorePurchases() } const handleReminderToggle = async (enabled: boolean) => { if (enabled) { const granted = await requestNotificationPermissions() if (!granted) return } updateSettings({ reminders: enabled }) } const handleRateApp = () => { Linking.openURL('https://apps.apple.com/app/tabatafit/id1234567890') } const handleContactUs = () => { Linking.openURL('mailto:contact@tabatafit.app') } const handlePrivacyPolicy = () => { router.push('/privacy') } const handleFAQ = () => { Linking.openURL('https://tabatafit.app/faq') } // App version const appVersion = Constants.expoConfig?.version ?? '1.0.0' return ( {/* ════════════════════════════════════════════════════════════════════ PROFILE HEADER CARD ═══════════════════════════════════════════════════════════════════ */} {/* Avatar with gradient background */} {avatarInitial} {/* Name & Plan */} {profile.name || t('profile.guest')} {planLabel} {isPremium && ( )} {/* Stats Row */} 🔥 {stats.workouts} {t('profile.statsWorkouts')} 📅 {stats.streak} {t('profile.statsStreak')} ⚡️ {Math.round(stats.calories / 1000)}k {t('profile.statsCalories')} {/* ════════════════════════════════════════════════════════════════════ UPGRADE CTA (FREE USERS ONLY) ═══════════════════════════════════════════════════════════════════ */} {!isPremium && ( router.push('/paywall')} > ✨ {t('profile.upgradeTitle')} {t('profile.upgradeDescription')} {t('profile.learnMore')} → )} {/* ════════════════════════════════════════════════════════════════════ WORKOUT SETTINGS ═══════════════════════════════════════════════════════════════════ */} {t('profile.sectionWorkout')} {t('profile.hapticFeedback')} updateSettings({ haptics: v })} trackColor={{ false: colors.bg.overlay1, true: BRAND.PRIMARY }} thumbColor="#FFFFFF" /> {t('profile.soundEffects')} updateSettings({ soundEffects: v })} trackColor={{ false: colors.bg.overlay1, true: BRAND.PRIMARY }} thumbColor="#FFFFFF" /> {t('profile.voiceCoaching')} updateSettings({ voiceCoaching: v })} trackColor={{ false: colors.bg.overlay1, true: BRAND.PRIMARY }} thumbColor="#FFFFFF" /> {/* ════════════════════════════════════════════════════════════════════ NOTIFICATIONS ═══════════════════════════════════════════════════════════════════ */} {t('profile.sectionNotifications')} {t('profile.dailyReminders')} {settings.reminders && ( {t('profile.reminderTime')} {settings.reminderTime} )} {/* ════════════════════════════════════════════════════════════════════ ABOUT ═══════════════════════════════════════════════════════════════════ */} {t('profile.sectionAbout')} {t('profile.version')} {appVersion} {t('profile.rateApp')} {t('profile.contactUs')} {t('profile.faq')} {t('profile.privacyPolicy')} {/* ════════════════════════════════════════════════════════════════════ ACCOUNT (PREMIUM USERS ONLY) ═══════════════════════════════════════════════════════════════════ */} {isPremium && ( <> {t('profile.sectionAccount')} {t('profile.restorePurchases')} )} {/* ════════════════════════════════════════════════════════════════════ SIGN OUT ═══════════════════════════════════════════════════════════════════ */} {t('profile.signOut')} ) } // ═══════════════════════════════════════════════════════════════════════════ // STYLES // ═══════════════════════════════════════════════════════════════════════════ function createStyles(colors: ThemeColors) { return StyleSheet.create({ container: { flex: 1, backgroundColor: colors.bg.base, }, scrollView: { flex: 1, }, scrollContent: { flexGrow: 1, }, section: { marginHorizontal: 16, marginTop: 20, backgroundColor: colors.bg.surface, borderRadius: 10, overflow: 'hidden', }, sectionHeader: { fontSize: 13, fontWeight: '600', color: colors.text.tertiary, textTransform: 'uppercase', marginLeft: 32, marginTop: 20, marginBottom: 8, }, headerContainer: { alignItems: 'center', paddingVertical: 24, paddingHorizontal: 16, }, avatarContainer: { width: 90, height: 90, borderRadius: 45, backgroundColor: BRAND.PRIMARY, justifyContent: 'center', alignItems: 'center', shadowColor: BRAND.PRIMARY, shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.5, shadowRadius: 20, elevation: 10, }, nameContainer: { marginTop: 16, alignItems: 'center', }, planContainer: { flexDirection: 'row', alignItems: 'center', marginTop: 4, gap: 4, }, statsContainer: { flexDirection: 'row', justifyContent: 'center', marginTop: 16, gap: 32, }, statItem: { alignItems: 'center', }, premiumContainer: { paddingVertical: 16, paddingHorizontal: 16, }, premiumContent: { gap: 4, }, row: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: 12, paddingHorizontal: 16, borderBottomWidth: 0.5, borderBottomColor: colors.border.glassLight, }, rowLast: { borderBottomWidth: 0, }, rowLabel: { fontSize: 17, color: colors.text.primary, }, rowValue: { fontSize: 17, color: colors.text.tertiary, }, rowTime: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: 12, paddingHorizontal: 16, borderTopWidth: 0.5, borderTopColor: colors.border.glassLight, }, button: { paddingVertical: 14, alignItems: 'center', }, destructive: { fontSize: 17, color: BRAND.DANGER, }, signOutSection: { marginTop: 20, }, }) }