/**
* 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, 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 { DataDeletionModal } from '@/src/shared/components/DataDeletionModal'
import { deleteSyncedData } from '@/src/shared/services/sync'
// ═══════════════════════════════════════════════════════════════════════════
// 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 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 && (
<>
Personalization
{profile.syncStatus === 'synced' ? 'Personalization Enabled' : 'Generic Programs'}
{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: 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,
},
})
}