import React from 'react'; import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, ScrollView, RefreshControl, } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { SafeAreaView } from 'react-native-safe-area-context'; import { AppColors, BorderRadius, FontSizes, Spacing, FontWeights, Shadows, } from '@/constants/theme'; interface ScreenLoadingProps { isLoading: boolean; error?: string | null; children: React.ReactNode; onRetry?: () => void; loadingMessage?: string; fullScreen?: boolean; } /** * Wrapper component for screens with loading/error/content states * Provides consistent loading and error UI across the app */ export function ScreenLoading({ isLoading, error, children, onRetry, loadingMessage = 'Loading...', fullScreen = true, }: ScreenLoadingProps) { if (isLoading) { return ( {loadingMessage} ); } if (error) { return ( Something went wrong {error} {onRetry && ( Try Again )} ); } return <>{children}; } interface RefreshableScreenProps { isRefreshing: boolean; onRefresh: () => void; children: React.ReactNode; contentContainerStyle?: object; showsVerticalScrollIndicator?: boolean; } /** * ScrollView with built-in pull-to-refresh * Provides consistent refresh behavior across the app */ export function RefreshableScreen({ isRefreshing, onRefresh, children, contentContainerStyle, showsVerticalScrollIndicator = false, }: RefreshableScreenProps) { return ( } > {children} ); } interface EmptyStateProps { icon?: keyof typeof Ionicons.glyphMap; title: string; description?: string; actionLabel?: string; onAction?: () => void; } /** * Empty state component for when there's no content to display */ export function EmptyState({ icon = 'folder-open-outline', title, description, actionLabel, onAction, }: EmptyStateProps) { return ( {title} {description && {description}} {actionLabel && onAction && ( {actionLabel} )} ); } interface LoadingButtonStateProps { isLoading: boolean; loadingText?: string; children: React.ReactNode; } /** * Wrapper for button content that shows loading state */ export function LoadingButtonState({ isLoading, loadingText = 'Loading...', children, }: LoadingButtonStateProps) { if (isLoading) { return ( {loadingText && {loadingText}} ); } return <>{children}; } const styles = StyleSheet.create({ container: { justifyContent: 'center', alignItems: 'center', padding: Spacing.xl, }, fullScreen: { flex: 1, backgroundColor: AppColors.background, }, loadingMessage: { marginTop: Spacing.md, fontSize: FontSizes.base, color: AppColors.textSecondary, textAlign: 'center', }, errorIcon: { marginBottom: Spacing.md, }, errorTitle: { fontSize: FontSizes.lg, fontWeight: FontWeights.semibold, color: AppColors.textPrimary, marginBottom: Spacing.sm, textAlign: 'center', }, errorMessage: { fontSize: FontSizes.base, color: AppColors.textSecondary, textAlign: 'center', marginBottom: Spacing.lg, paddingHorizontal: Spacing.lg, }, retryButton: { flexDirection: 'row', alignItems: 'center', backgroundColor: AppColors.primary, paddingHorizontal: Spacing.lg, paddingVertical: Spacing.md, borderRadius: BorderRadius.lg, gap: Spacing.sm, ...Shadows.primary, }, retryText: { fontSize: FontSizes.base, fontWeight: FontWeights.semibold, color: AppColors.white, }, scrollView: { flex: 1, }, emptyContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: Spacing.xl, }, emptyIcon: { width: 80, height: 80, borderRadius: 40, backgroundColor: AppColors.surfaceSecondary, justifyContent: 'center', alignItems: 'center', marginBottom: Spacing.lg, }, emptyTitle: { fontSize: FontSizes.lg, fontWeight: FontWeights.semibold, color: AppColors.textPrimary, textAlign: 'center', marginBottom: Spacing.sm, }, emptyDescription: { fontSize: FontSizes.base, color: AppColors.textSecondary, textAlign: 'center', marginBottom: Spacing.lg, paddingHorizontal: Spacing.lg, }, emptyAction: { backgroundColor: AppColors.primary, paddingHorizontal: Spacing.lg, paddingVertical: Spacing.md, borderRadius: BorderRadius.lg, }, emptyActionText: { fontSize: FontSizes.base, fontWeight: FontWeights.semibold, color: AppColors.white, }, buttonLoading: { flexDirection: 'row', alignItems: 'center', gap: Spacing.sm, }, buttonLoadingText: { fontSize: FontSizes.base, color: AppColors.white, fontWeight: FontWeights.medium, }, });