/** * TabataFit Profile Screen — Premium React Native * Apple Fitness+ inspired design, pure React Native components */ import { useRouter } from 'expo-router' import { View, ScrollView, StyleSheet, Pressable, Switch, } 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, useState } from 'react' import { useUserStore, useActivityStore } 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' import { StyledText } from '@/src/shared/components/StyledText' import { SPACING, LAYOUT } from '@/src/shared/constants/spacing' import { RADIUS } from '@/src/shared/constants/borderRadius' import { DataDeletionModal } from '@/src/shared/components/DataDeletionModal' import { deleteSyncedData } from '@/src/shared/services/sync' // ═══════════════════════════════════════════════════════════════════════════ // 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 setSyncStatus = useUserStore((s) => s.setSyncStatus) const { restorePurchases, isPremium } = usePurchases() const [showDeleteModal, setShowDeleteModal] = useState(false) const planLabel = isPremium ? 'TabataFit+' : t('profile.freePlan') const avatarInitial = profile.name?.[0]?.toUpperCase() || 'U' // Real stats from activity store const history = useActivityStore((s) => s.history) const streak = useActivityStore((s) => s.streak) const stats = useMemo(() => ({ workouts: history.length, streak: streak.current, calories: history.reduce((sum, r) => sum + (r.calories ?? 0), 0), }), [history, streak]) const handleSignOut = () => { updateProfile({ name: '', email: '', subscription: 'free', onboardingCompleted: false, }) router.replace('/onboarding') } const handleRestore = async () => { await restorePurchases() } const handleDeleteData = async () => { const result = await deleteSyncedData() if (result.success) { setSyncStatus('unsynced', null) setShowDeleteModal(false) } } 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} )} {/* ════════════════════════════════════════════════════════════════════ PERSONALIZATION (PREMIUM ONLY) ═══════════════════════════════════════════════════════════════════ */} {isPremium && ( <> {t('profile.sectionPersonalization')} {profile.syncStatus === 'synced' ? t('profile.personalizationEnabled') : t('profile.personalizationDisabled')} {profile.syncStatus === 'synced' ? '✓' : '○'} )} {/* ════════════════════════════════════════════════════════════════════ 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')} {/* Data Deletion Modal */} setShowDeleteModal(false)} /> ) } // ═══════════════════════════════════════════════════════════════════════════ // STYLES // ═══════════════════════════════════════════════════════════════════════════ function createStyles(colors: ThemeColors) { return StyleSheet.create({ container: { flex: 1, backgroundColor: colors.bg.base, }, scrollView: { flex: 1, }, scrollContent: { flexGrow: 1, }, section: { marginHorizontal: SPACING[4], marginTop: SPACING[5], backgroundColor: colors.bg.surface, borderRadius: RADIUS.MD, overflow: 'hidden', }, sectionHeader: { fontSize: 13, fontWeight: '600', color: colors.text.tertiary, textTransform: 'uppercase', marginLeft: SPACING[8], marginTop: SPACING[5], marginBottom: SPACING[2], }, headerContainer: { alignItems: 'center', paddingVertical: SPACING[6], paddingHorizontal: SPACING[4], }, avatarContainer: { width: 90, height: 90, borderRadius: 45, backgroundColor: BRAND.PRIMARY, justifyContent: 'center', alignItems: 'center', boxShadow: `0 4px 20px ${BRAND.PRIMARY}80`, }, nameContainer: { marginTop: SPACING[4], alignItems: 'center', }, planContainer: { flexDirection: 'row', alignItems: 'center', marginTop: SPACING[1], gap: SPACING[1], }, statsContainer: { flexDirection: 'row', justifyContent: 'center', marginTop: SPACING[4], gap: SPACING[8], }, statItem: { alignItems: 'center', }, premiumContainer: { paddingVertical: SPACING[4], paddingHorizontal: SPACING[4], }, premiumContent: { gap: SPACING[1], }, row: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: SPACING[3], paddingHorizontal: SPACING[4], 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: SPACING[3], paddingHorizontal: SPACING[4], borderTopWidth: 0.5, borderTopColor: colors.border.glassLight, }, button: { paddingVertical: SPACING[3] + 2, alignItems: 'center', }, destructive: { fontSize: 17, color: BRAND.DANGER, }, signOutSection: { marginTop: SPACING[5], }, }) }