import { useCallback, useMemo, useState } from 'react'; import { ScrollView, StyleSheet, Platform, View, Text, useWindowDimensions } from 'react-native'; import { useFocusEffect } from 'expo-router'; import Animated, { FadeInDown } from 'react-native-reanimated'; import { Ionicons } from '@expo/vector-icons'; import { BlurView } from 'expo-blur'; import { useAuthStore, usePacksStore, useStreakStore, useSubscriptionStore } from '../../src/store'; import { GradientBackground } from '../../src/components/ui'; import { Paywall } from '../../src/components/paywall'; import { CompactHeader, ContentRow, DaresTile, LiveDrawTile } from '../../src/components/discovery'; import { StreakDisplay } from '../../src/components/StreakDisplay'; import { colors, spacing, typography, radius } from '../../src/theme'; import type { QuestionPack, Category } from '../../src/types'; const MAX_CONTENT_WIDTH = 600; // Group packs by category function groupPacksByCategory( packs: QuestionPack[], categories: Category[] ): { category: Category; packs: QuestionPack[] }[] { const categoryMap = new Map(); const uncategorized: QuestionPack[] = []; // Initialize map with empty arrays for each category categories.forEach((cat) => { categoryMap.set(cat.id, []); }); // Group packs packs.forEach((pack) => { if (pack.category_id && categoryMap.has(pack.category_id)) { categoryMap.get(pack.category_id)!.push(pack); } else { uncategorized.push(pack); } }); // Build result array maintaining category sort order const result: { category: Category; packs: QuestionPack[] }[] = []; categories.forEach((cat) => { const categoryPacks = categoryMap.get(cat.id) || []; if (categoryPacks.length > 0) { result.push({ category: cat, packs: categoryPacks }); } }); // Add uncategorized at the end if any if (uncategorized.length > 0) { result.push({ category: { id: 'uncategorized', name: 'More', description: null, icon: 'ellipsis-horizontal', color: null, sort_order: 999, created_at: '', is_public: true, }, packs: uncategorized, }); } return result; } export default function DiscoveryScreen() { const { user, partner, couple } = useAuthStore(); const { packs, categories, fetchPacks, getPackProgress } = usePacksStore(); const { fetchStreak } = useStreakStore(); const { subscription } = useSubscriptionStore(); const { width } = useWindowDimensions(); const [showPaywall, setShowPaywall] = useState(false); const [headerHeight, setHeaderHeight] = useState(130); // Check all sources of premium access: user flag, partner flag, or active subscription const isPremiumUser = user?.is_premium || partner?.is_premium || subscription.isProUser; const isWideScreen = width > MAX_CONTENT_WIDTH; // Refresh data when screen gains focus useFocusEffect( useCallback(() => { fetchPacks(); fetchStreak(); }, [fetchStreak]) ); // Group packs by category const packsByCategory = useMemo( () => groupPacksByCategory(packs, categories), [packs, categories] ); // Check if user has no packs enabled (empty state) const hasNoPacks = packs.length === 0; return ( setHeaderHeight(e.nativeEvent.layout.height)} > {/* Shared streak - hidden entirely for couples with nothing running */} {/* Live Draw Activity Tile */} {/* Dares */} {/* Content Rows by Category */} {packsByCategory.map((group, index) => ( setShowPaywall(true)} /> ))} {/* Empty state when no packs */} {hasNoPacks && ( No packs available Check back soon for new content )} {/* Paywall Modal */} setShowPaywall(false)} onSuccess={() => { setShowPaywall(false); fetchPacks(); // Refresh packs to update lock status }} /> ); } const styles = StyleSheet.create({ container: { flex: 1, }, contentContainer: { flexGrow: 1, paddingBottom: Platform.OS === 'ios' ? 100 : 80, }, contentContainerWide: { alignItems: 'center', }, innerContainer: { flex: 1, width: '100%', }, innerContainerWide: { maxWidth: MAX_CONTENT_WIDTH, }, emptyState: { flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: spacing.xl, paddingVertical: spacing.xxl, }, emptyIcon: { width: 80, height: 80, borderRadius: 40, backgroundColor: 'rgba(233, 69, 96, 0.1)', justifyContent: 'center', alignItems: 'center', marginBottom: spacing.lg, }, emptyTitle: { ...typography.title3, color: colors.text, marginBottom: spacing.sm, }, emptySubtitle: { ...typography.body, color: colors.textSecondary, textAlign: 'center', }, streakSlot: { paddingHorizontal: spacing.lg, }, bottomSpacer: { height: spacing.lg, }, stickyHeader: { position: 'absolute', top: 0, left: 0, right: 0, zIndex: 10, overflow: 'hidden', // Ensures blur doesn't bleed if we rounded corners (optional) }, stickyHeaderWide: { left: '50%', right: 'auto', width: MAX_CONTENT_WIDTH, transform: [{ translateX: -MAX_CONTENT_WIDTH / 2 }], }, });