diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index d8ce2b7..ec58eb2 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -2,11 +2,11 @@ import { useRouter } from 'expo-router'; import { useEffect } from 'react'; import { Alert, ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native'; -import { CourseCardSkeleton, Skeleton, AppText as Text } from '@/src/components'; -import { sampleCourse } from '@/src/data/sampleCourse'; -import { useDynamicFontSize, useAnalytics } from '@/src/hooks'; -import { useAppStore } from '@/src/store'; -import { AnalyticsEvent, ScreenName } from '@/src/utils/trackingEvents'; +import { CourseCardSkeleton, Skeleton, AppText as Text } from '@/components'; +import { sampleCourse } from '@/data/sampleCourse'; +import { useDynamicFontSize, useAnalytics } from '@/hooks'; +import { useAppStore } from '@/store'; +import { AnalyticsEvent, ScreenName } from '@/utils/trackingEvents'; export default function HomeScreen() { const router = useRouter(); @@ -71,6 +71,7 @@ export default function HomeScreen() { className="flex-1 bg-gray-50 dark:bg-slate-800" contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false} + removeClippedSubviews={true} > {/* Header */} diff --git a/src/components/grid/AdvancedDataGrid.tsx b/src/components/grid/AdvancedDataGrid.tsx index 49fa2e5..370a9e5 100644 --- a/src/components/grid/AdvancedDataGrid.tsx +++ b/src/components/grid/AdvancedDataGrid.tsx @@ -142,7 +142,7 @@ export const AdvancedDataGrid = ({ /> {/* ── Scrollable grid area ─────────────────────────────────────────── */} - + {/* Column headers */} ({ showsHorizontalScrollIndicator={false} contentContainerStyle={styles.row} keyboardShouldPersistTaps="handled" + removeClippedSubviews={true} > {filterableColumns.map(col => { const currentValue = getFilterValue(col.key); diff --git a/src/components/mobile/AchievementBadges.tsx b/src/components/mobile/AchievementBadges.tsx index 462ee03..96437aa 100644 --- a/src/components/mobile/AchievementBadges.tsx +++ b/src/components/mobile/AchievementBadges.tsx @@ -199,6 +199,7 @@ export const AchievementBadges: React.FC = ({ showsHorizontalScrollIndicator={false} contentContainerStyle={styles.scrollContent} accessibilityLabel="Horizontal achievements list" + removeClippedSubviews={true} > {achievements.map(renderBadge)} diff --git a/src/components/mobile/BookmarkList.tsx b/src/components/mobile/BookmarkList.tsx index 431fd23..bd205c6 100644 --- a/src/components/mobile/BookmarkList.tsx +++ b/src/components/mobile/BookmarkList.tsx @@ -26,7 +26,7 @@ export const BookmarkList = () => { } return ( - + {bookmarks.map(item => ( = ({ data={connections} renderItem={renderConnectionItem} keyExtractor={item => item.id} + removeClippedSubviews={true} style={styles.list} /> ) : ( diff --git a/src/components/mobile/DownloadQueue.tsx b/src/components/mobile/DownloadQueue.tsx index 1834f73..ae6ae41 100644 --- a/src/components/mobile/DownloadQueue.tsx +++ b/src/components/mobile/DownloadQueue.tsx @@ -55,6 +55,7 @@ export function DownloadQueue() { data={tasks} keyExtractor={item => item.id} renderItem={renderItem} + removeClippedSubviews={true} contentContainerStyle={{ paddingVertical: 8 }} ListHeaderComponent={() => ( diff --git a/src/components/mobile/FilterSheet.tsx b/src/components/mobile/FilterSheet.tsx index e80f565..a1ee9c2 100644 --- a/src/components/mobile/FilterSheet.tsx +++ b/src/components/mobile/FilterSheet.tsx @@ -154,6 +154,7 @@ export function FilterSheet({ contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false} keyboardShouldPersistTaps="handled" + removeClippedSubviews={true} > {filters.map(field => ( {renderLessonContent(lesson)} diff --git a/src/components/mobile/MobileCourseViewer.tsx b/src/components/mobile/MobileCourseViewer.tsx index 5fd27c6..30d6a43 100644 --- a/src/components/mobile/MobileCourseViewer.tsx +++ b/src/components/mobile/MobileCourseViewer.tsx @@ -284,7 +284,7 @@ export default function MobileCourseViewer({ return ( - + {/* Lesson Content */} {lesson.content} diff --git a/src/components/mobile/MobileProfile.tsx b/src/components/mobile/MobileProfile.tsx index 70d75f9..aac9801 100644 --- a/src/components/mobile/MobileProfile.tsx +++ b/src/components/mobile/MobileProfile.tsx @@ -265,7 +265,7 @@ export const MobileProfile: React.FC = ({ return ( - + @@ -431,7 +431,7 @@ export const MobileProfile: React.FC = ({ return ( - + {/* ── Profile Header ─────────────────────────────────────────────── */} = ({ 🔥 {profile.stats.streak} Day Streak - Keep it up! You're on fire. + Keep it up! You're on fire. diff --git a/src/components/mobile/MobileQuizManager/MobileQuestionCard.tsx b/src/components/mobile/MobileQuizManager/MobileQuestionCard.tsx index 2c7b78f..7424d99 100644 --- a/src/components/mobile/MobileQuizManager/MobileQuestionCard.tsx +++ b/src/components/mobile/MobileQuizManager/MobileQuestionCard.tsx @@ -68,6 +68,7 @@ export default function MobileQuestionCard({ style={styles.container} contentContainerStyle={styles.contentContainer} showsVerticalScrollIndicator={false} + removeClippedSubviews={true} > {/* Question Header */} diff --git a/src/components/mobile/MobileQuizManager/QuizCarousel.tsx b/src/components/mobile/MobileQuizManager/QuizCarousel.tsx index 192367c..7f1b271 100644 --- a/src/components/mobile/MobileQuizManager/QuizCarousel.tsx +++ b/src/components/mobile/MobileQuizManager/QuizCarousel.tsx @@ -98,6 +98,7 @@ export default function QuizCarousel({ ref={scrollViewRef} horizontal pagingEnabled + removeClippedSubviews={true} showsHorizontalScrollIndicator={false} onScroll={handleScroll} onScrollBeginDrag={handleScrollBeginDrag} diff --git a/src/components/mobile/MobileQuizManager/QuizResults.tsx b/src/components/mobile/MobileQuizManager/QuizResults.tsx index f645c02..fa33be4 100644 --- a/src/components/mobile/MobileQuizManager/QuizResults.tsx +++ b/src/components/mobile/MobileQuizManager/QuizResults.tsx @@ -24,6 +24,7 @@ export default function QuizResults({ quiz, score, passed, onBack, onRetake }: Q style={styles.container} contentContainerStyle={styles.contentContainer} showsVerticalScrollIndicator={false} + removeClippedSubviews={true} > {/* Result Icon */} diff --git a/src/components/mobile/MobileQuizManager/index.tsx b/src/components/mobile/MobileQuizManager/index.tsx index 9100bed..3ad2ca7 100644 --- a/src/components/mobile/MobileQuizManager/index.tsx +++ b/src/components/mobile/MobileQuizManager/index.tsx @@ -134,6 +134,7 @@ export default function MobileQuizManager({ style={styles.scrollView} contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false} + removeClippedSubviews={true} > {/* Header */} diff --git a/src/components/mobile/MobileSearch.tsx b/src/components/mobile/MobileSearch.tsx index b6a8608..77721e8 100644 --- a/src/components/mobile/MobileSearch.tsx +++ b/src/components/mobile/MobileSearch.tsx @@ -287,6 +287,7 @@ export const MobileSearch = ({ renderItem={({ item }) => ( onResultPress?.(item)} /> )} + removeClippedSubviews={true} contentContainerStyle={styles.resultsList} ListEmptyComponent={ Try a different query or adjust filters. diff --git a/src/components/mobile/MobileSettings.tsx b/src/components/mobile/MobileSettings.tsx index 9f1eaf9..8497597 100644 --- a/src/components/mobile/MobileSettings.tsx +++ b/src/components/mobile/MobileSettings.tsx @@ -1,420 +1,420 @@ -import React, { useState } from 'react'; -import { - Alert, - ActivityIndicator, - LayoutAnimation, - Platform, - ScrollView, - TouchableOpacity, - UIManager, - View, -} from 'react-native'; - -import { - BarChart2, - Bell, - ChevronDown, - ChevronUp, - Download, - Eye, - Globe, - HardDrive, - Lock, - LogOut, - MapPin, - Play, - Settings2, - Shield, - Sun, - Trash2, - Type, - User, - Vibrate, - Wifi, - RefreshCw, - Fingerprint as FingerprintPattern, -} from 'lucide-react-native'; - -import { useAppStore } from '../../store'; -import { useNotificationStore } from '../../store/notificationStore'; -import { useSettingsStore } from '../../store/settingsStore'; -import { useBiometricAuth } from '../../hooks/useBiometricAuth'; -import { useDynamicFontSize } from '../../hooks'; - -import { NativeToggle } from './NativeToggle'; -import { PickerOption, SettingsPicker } from './SettingsPicker'; -import { SettingsSection } from './SettingsSection'; -import { AppText } from '../common/AppText'; - -// Enable LayoutAnimation on Android -if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) { - UIManager.setLayoutAnimationEnabledExperimental(true); -} - -// ───────────────────────────────────────────────────────────── -// Shared Row -// ───────────────────────────────────────────────────────────── - -interface SettingRowProps { - icon: React.ReactNode; - iconBg?: string; - label: string; - description?: string; - right?: React.ReactNode; - onPress?: () => void; - destructive?: boolean; -} - -function SettingRow({ - icon, - iconBg = 'bg-gray-100 dark:bg-gray-700', - label, - description, - right, - onPress, - destructive = false, -}: SettingRowProps) { - const Row = onPress ? TouchableOpacity : View; - const { scale } = useDynamicFontSize(); - - return ( - - - {icon} - - - - - {label} - - - {description && ( - - {description} - - )} - - - {right ?? (onPress ? : null)} - - ); -} - -// ───────────────────────────────────────────────────────────── -// Options -// ───────────────────────────────────────────────────────────── - -const VISIBILITY_OPTIONS: PickerOption[] = [ - { label: 'Public', value: 'public' }, - { label: 'Friends Only', value: 'friends_only' }, - { label: 'Private', value: 'private' }, -]; - -const THEME_OPTIONS: PickerOption[] = [ - { label: 'Light', value: 'light' }, - { label: 'Dark', value: 'dark' }, -]; - -const QUALITY_OPTIONS: PickerOption[] = [ - { label: 'Low', value: 'low' }, - { label: 'Medium', value: 'medium' }, - { label: 'High', value: 'high' }, -]; - -const STORAGE_OPTIONS: PickerOption[] = [ - { label: '1 GB', value: '1GB' }, - { label: '2 GB', value: '2GB' }, - { label: '5 GB', value: '5GB' }, - { label: 'Unlimited', value: 'unlimited' }, -]; - -const LANGUAGE_OPTIONS: PickerOption[] = [ - { label: 'English', value: 'english' }, - { label: 'Spanish', value: 'spanish' }, - { label: 'French', value: 'french' }, -]; - -const FONT_SIZE_OPTIONS: PickerOption[] = [ - { label: 'Small', value: 'small' }, - { label: 'Medium', value: 'medium' }, - { label: 'Large', value: 'large' }, -]; - -// ───────────────────────────────────────────────────────────── -// AdvancedToggle – pill button for expanding advanced settings -// ───────────────────────────────────────────────────────────── - -interface AdvancedToggleProps { - expanded: boolean; - onToggle: () => void; -} - -function AdvancedToggle({ expanded, onToggle }: AdvancedToggleProps) { - return ( - - - - - {expanded ? 'Hide Advanced Settings' : 'Advanced Settings'} - - - {expanded ? ( - - ) : ( - - )} - - ); -} - -// ───────────────────────────────────────────────────────────── -// Component -// ───────────────────────────────────────────────────────────── - -export function MobileSettings({ - onSignOut, - onChangePassword, - onLinkedAccounts, -}: any) { - const { theme, setTheme } = useAppStore(); - const { preferences, setPreference } = useNotificationStore(); - - // Progressive disclosure: advanced settings collapsed by default - const [showAdvancedSettings, setShowAdvancedSettings] = useState(false); - - const { - profileVisibility, - setProfileVisibility, - twoFactorEnabled, - setTwoFactorEnabled, - dataSharing, - setDataSharing, - analyticsEnabled, - setAnalyticsEnabled, - locationServices, - setLocationServices, - downloadOverWifiOnly, - setDownloadOverWifiOnly, - autoDownload, - setAutoDownload, - downloadQuality, - setDownloadQuality, - storageLimit, - setStorageLimit, - language, - setLanguage, - fontSize, - setFontSize, - autoplay, - setAutoplay, - hapticFeedback, - setHapticFeedback, - } = useSettingsStore(); - - const { - isAvailable: biometricAvailable, - isEnabled: biometricEnabled, - biometricType, - enable: enableBiometric, - disable: disableBiometric, - isLoading: biometricLoading, - } = useBiometricAuth(); - - const { scale } = useDynamicFontSize(); - - const handleBiometricToggle = async (value: boolean) => { - if (value) { - const ok = await enableBiometric(); - if (!ok) { - Alert.alert('Biometric Login', 'Enable failed. Check device settings.'); - } - } else { - await disableBiometric(); - } - }; - - const handleSignOut = () => { - Alert.alert('Sign Out', 'Are you sure?', [ - { text: 'Cancel', style: 'cancel' }, - { text: 'Sign Out', style: 'destructive', onPress: onSignOut }, - ]); - }; - - const handleManualSync = async () => { - Alert.alert('Sync', 'Sync data with server?', [ - { text: 'Cancel', style: 'cancel' }, - { - text: 'Sync', - onPress: async () => { - try { - Alert.alert('Syncing...'); - // await syncService.manualSync(); - Alert.alert('Success'); - } catch { - Alert.alert('Failed to sync'); - } - }, - }, - ]); - }; - - const handleClearDownloads = () => { - Alert.alert('Clear Downloads', 'Remove all downloads?', [ - { text: 'Cancel', style: 'cancel' }, - { text: 'Clear', style: 'destructive' }, - ]); - }; - - const handleToggleAdvanced = () => { - LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); - setShowAdvancedSettings(prev => !prev); - }; - - return ( - - - {/* ── ESSENTIAL: ACCOUNT ─────────────────────────────── */} - - } - label="Profile Visibility" - right={ - - } - /> - - } - label="Two-Factor Auth" - right={} - /> - - {biometricAvailable && ( - - ) : ( - - ) - } - label="Biometric Login" - description={biometricEnabled ? 'Enabled' : 'Disabled'} - right={ - - } - /> - )} - - } label="Change Password" onPress={onChangePassword} /> - - - {/* ── ESSENTIAL: APP ─────────────────────────────────── */} - - } - label="Theme" - right={ - - } - /> - - - {/* ── PROGRESSIVE DISCLOSURE: ADVANCED SETTINGS ──────── */} - - - {showAdvancedSettings && ( - <> - {/* PRIVACY */} - - } - label="Analytics" - right={} - /> - - - {/* DOWNLOADS */} - - } - label="WiFi Only" - right={ - - } - /> - - } - label="Quality" - right={ - - } - /> - - } - label="Clear Downloads" - onPress={handleClearDownloads} - destructive - /> - - - {/* SYNC */} - - } - label="Manual Sync" - onPress={handleManualSync} - /> - - - )} - - {/* ── ESSENTIAL: ACCOUNT ACTIONS ─────────────────────── */} - - } - label="Sign Out" - onPress={handleSignOut} - destructive - /> - - - ); -} - -export default MobileSettings; +import React, { useState } from 'react'; +import { + Alert, + ActivityIndicator, + LayoutAnimation, + Platform, + ScrollView, + TouchableOpacity, + UIManager, + View, +} from 'react-native'; + +import { + BarChart2, + Bell, + ChevronDown, + ChevronUp, + Download, + Eye, + Globe, + HardDrive, + Lock, + LogOut, + MapPin, + Play, + Settings2, + Shield, + Sun, + Trash2, + Type, + User, + Vibrate, + Wifi, + RefreshCw, + Fingerprint as FingerprintPattern, +} from 'lucide-react-native'; + +import { useAppStore } from '../../store'; +import { useNotificationStore } from '../../store/notificationStore'; +import { useSettingsStore } from '../../store/settingsStore'; +import { useBiometricAuth } from '../../hooks/useBiometricAuth'; +import { useDynamicFontSize } from '../../hooks'; + +import { NativeToggle } from './NativeToggle'; +import { PickerOption, SettingsPicker } from './SettingsPicker'; +import { SettingsSection } from './SettingsSection'; +import { AppText } from '../common/AppText'; + +// Enable LayoutAnimation on Android +if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) { + UIManager.setLayoutAnimationEnabledExperimental(true); +} + +// ───────────────────────────────────────────────────────────── +// Shared Row +// ───────────────────────────────────────────────────────────── + +interface SettingRowProps { + icon: React.ReactNode; + iconBg?: string; + label: string; + description?: string; + right?: React.ReactNode; + onPress?: () => void; + destructive?: boolean; +} + +function SettingRow({ + icon, + iconBg = 'bg-gray-100 dark:bg-gray-700', + label, + description, + right, + onPress, + destructive = false, +}: SettingRowProps) { + const Row = onPress ? TouchableOpacity : View; + const { scale } = useDynamicFontSize(); + + return ( + + + {icon} + + + + + {label} + + + {description && ( + + {description} + + )} + + + {right ?? (onPress ? : null)} + + ); +} + +// ───────────────────────────────────────────────────────────── +// Options +// ───────────────────────────────────────────────────────────── + +const VISIBILITY_OPTIONS: PickerOption[] = [ + { label: 'Public', value: 'public' }, + { label: 'Friends Only', value: 'friends_only' }, + { label: 'Private', value: 'private' }, +]; + +const THEME_OPTIONS: PickerOption[] = [ + { label: 'Light', value: 'light' }, + { label: 'Dark', value: 'dark' }, +]; + +const QUALITY_OPTIONS: PickerOption[] = [ + { label: 'Low', value: 'low' }, + { label: 'Medium', value: 'medium' }, + { label: 'High', value: 'high' }, +]; + +const STORAGE_OPTIONS: PickerOption[] = [ + { label: '1 GB', value: '1GB' }, + { label: '2 GB', value: '2GB' }, + { label: '5 GB', value: '5GB' }, + { label: 'Unlimited', value: 'unlimited' }, +]; + +const LANGUAGE_OPTIONS: PickerOption[] = [ + { label: 'English', value: 'english' }, + { label: 'Spanish', value: 'spanish' }, + { label: 'French', value: 'french' }, +]; + +const FONT_SIZE_OPTIONS: PickerOption[] = [ + { label: 'Small', value: 'small' }, + { label: 'Medium', value: 'medium' }, + { label: 'Large', value: 'large' }, +]; + +// ───────────────────────────────────────────────────────────── +// AdvancedToggle – pill button for expanding advanced settings +// ───────────────────────────────────────────────────────────── + +interface AdvancedToggleProps { + expanded: boolean; + onToggle: () => void; +} + +function AdvancedToggle({ expanded, onToggle }: AdvancedToggleProps) { + return ( + + + + + {expanded ? 'Hide Advanced Settings' : 'Advanced Settings'} + + + {expanded ? ( + + ) : ( + + )} + + ); +} + +// ───────────────────────────────────────────────────────────── +// Component +// ───────────────────────────────────────────────────────────── + +export function MobileSettings({ + onSignOut, + onChangePassword, + onLinkedAccounts, +}: any) { + const { theme, setTheme } = useAppStore(); + const { preferences, setPreference } = useNotificationStore(); + + // Progressive disclosure: advanced settings collapsed by default + const [showAdvancedSettings, setShowAdvancedSettings] = useState(false); + + const { + profileVisibility, + setProfileVisibility, + twoFactorEnabled, + setTwoFactorEnabled, + dataSharing, + setDataSharing, + analyticsEnabled, + setAnalyticsEnabled, + locationServices, + setLocationServices, + downloadOverWifiOnly, + setDownloadOverWifiOnly, + autoDownload, + setAutoDownload, + downloadQuality, + setDownloadQuality, + storageLimit, + setStorageLimit, + language, + setLanguage, + fontSize, + setFontSize, + autoplay, + setAutoplay, + hapticFeedback, + setHapticFeedback, + } = useSettingsStore(); + + const { + isAvailable: biometricAvailable, + isEnabled: biometricEnabled, + biometricType, + enable: enableBiometric, + disable: disableBiometric, + isLoading: biometricLoading, + } = useBiometricAuth(); + + const { scale } = useDynamicFontSize(); + + const handleBiometricToggle = async (value: boolean) => { + if (value) { + const ok = await enableBiometric(); + if (!ok) { + Alert.alert('Biometric Login', 'Enable failed. Check device settings.'); + } + } else { + await disableBiometric(); + } + }; + + const handleSignOut = () => { + Alert.alert('Sign Out', 'Are you sure?', [ + { text: 'Cancel', style: 'cancel' }, + { text: 'Sign Out', style: 'destructive', onPress: onSignOut }, + ]); + }; + + const handleManualSync = async () => { + Alert.alert('Sync', 'Sync data with server?', [ + { text: 'Cancel', style: 'cancel' }, + { + text: 'Sync', + onPress: async () => { + try { + Alert.alert('Syncing...'); + // await syncService.manualSync(); + Alert.alert('Success'); + } catch { + Alert.alert('Failed to sync'); + } + }, + }, + ]); + }; + + const handleClearDownloads = () => { + Alert.alert('Clear Downloads', 'Remove all downloads?', [ + { text: 'Cancel', style: 'cancel' }, + { text: 'Clear', style: 'destructive' }, + ]); + }; + + const handleToggleAdvanced = () => { + LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); + setShowAdvancedSettings(prev => !prev); + }; + + return ( + + + {/* ── ESSENTIAL: ACCOUNT ─────────────────────────────── */} + + } + label="Profile Visibility" + right={ + + } + /> + + } + label="Two-Factor Auth" + right={} + /> + + {biometricAvailable && ( + + ) : ( + + ) + } + label="Biometric Login" + description={biometricEnabled ? 'Enabled' : 'Disabled'} + right={ + + } + /> + )} + + } label="Change Password" onPress={onChangePassword} /> + + + {/* ── ESSENTIAL: APP ─────────────────────────────────── */} + + } + label="Theme" + right={ + + } + /> + + + {/* ── PROGRESSIVE DISCLOSURE: ADVANCED SETTINGS ──────── */} + + + {showAdvancedSettings && ( + <> + {/* PRIVACY */} + + } + label="Analytics" + right={} + /> + + + {/* DOWNLOADS */} + + } + label="WiFi Only" + right={ + + } + /> + + } + label="Quality" + right={ + + } + /> + + } + label="Clear Downloads" + onPress={handleClearDownloads} + destructive + /> + + + {/* SYNC */} + + } + label="Manual Sync" + onPress={handleManualSync} + /> + + + )} + + {/* ── ESSENTIAL: ACCOUNT ACTIONS ─────────────────────── */} + + } + label="Sign Out" + onPress={handleSignOut} + destructive + /> + + + ); +} + +export default MobileSettings; diff --git a/src/components/mobile/MobileSyllabus.tsx b/src/components/mobile/MobileSyllabus.tsx index 146b3c6..eaf9e6f 100644 --- a/src/components/mobile/MobileSyllabus.tsx +++ b/src/components/mobile/MobileSyllabus.tsx @@ -84,6 +84,7 @@ export default function MobileSyllabus({ style={styles.container} contentContainerStyle={styles.contentContainer} showsVerticalScrollIndicator={false} + removeClippedSubviews={true} > {/* Header */} diff --git a/src/components/mobile/NotificationSettings.tsx b/src/components/mobile/NotificationSettings.tsx index 0b16df3..07b39d8 100644 --- a/src/components/mobile/NotificationSettings.tsx +++ b/src/components/mobile/NotificationSettings.tsx @@ -95,8 +95,7 @@ export function NotificationSettings() { }; return ( - - {/* Permission Status Banner */} + {permissionStatus !== 'granted' && ( diff --git a/src/components/mobile/SubscriptionManager.tsx b/src/components/mobile/SubscriptionManager.tsx index f7d39b2..3e58448 100644 --- a/src/components/mobile/SubscriptionManager.tsx +++ b/src/components/mobile/SubscriptionManager.tsx @@ -326,7 +326,7 @@ export const SubscriptionManager: React.FC = ({ )} - + {/* Current plan */} {renderCurrentPlan()} diff --git a/src/pages/mobile/MobileLogin.tsx b/src/pages/mobile/MobileLogin.tsx index bbe8bcb..9823eda 100644 --- a/src/pages/mobile/MobileLogin.tsx +++ b/src/pages/mobile/MobileLogin.tsx @@ -187,6 +187,7 @@ export const MobileLogin: React.FC = ({ contentContainerStyle={styles.scroll} keyboardShouldPersistTaps="handled" showsVerticalScrollIndicator={false} + removeClippedSubviews={true} > {/* ── Header ── */} @@ -377,7 +378,7 @@ export const MobileLogin: React.FC = ({ {onRegister && ( - Don't have an account? + Don't have an account? diff --git a/src/pages/mobile/MobileRegister.tsx b/src/pages/mobile/MobileRegister.tsx index 21018ec..1ea15dc 100644 --- a/src/pages/mobile/MobileRegister.tsx +++ b/src/pages/mobile/MobileRegister.tsx @@ -109,6 +109,7 @@ export const MobileRegister: React.FC = ({ contentContainerStyle={styles.scroll} keyboardShouldPersistTaps="handled" showsVerticalScrollIndicator={false} + removeClippedSubviews={true} > = ({ horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.tabsRow} + removeClippedSubviews={true} > {tabs.map((tab) => ( = ({