diff --git a/.gitignore b/.gitignore index f0d6434..663039f 100755 --- a/.gitignore +++ b/.gitignore @@ -16,10 +16,10 @@ build/ .env.local .env.development.local .env.test.local -.env.production.local -apps/mobile/.env.prod -.secrets - +.env.production.local +apps/mobile/.env.* +.secrets + # Supabase .supabase/ diff --git a/apps/mobile/app/(app)/_layout.tsx b/apps/mobile/app/(app)/_layout.tsx index 03174ec..d4fe79f 100755 --- a/apps/mobile/app/(app)/_layout.tsx +++ b/apps/mobile/app/(app)/_layout.tsx @@ -43,11 +43,14 @@ function TabBarBackground() { // The solid rgba background in tabBarStyle provides the visual effect on Android if (Platform.OS === 'ios') { return ( - + <> + + + ); } return null; @@ -435,7 +438,7 @@ export default function AppLayout() { tabBarBackground: () => , tabBarStyle: shouldHideTabBar ? { display: 'none' } : { position: 'absolute', - backgroundColor: 'rgba(13, 13, 26, 0.92)', + backgroundColor: 'rgba(14, 14, 17, 0.85)', borderTopColor: 'rgba(233, 69, 96, 0.2)', // primary brand color borderTopWidth: 1, paddingTop: 8, diff --git a/apps/mobile/app/(app)/index.tsx b/apps/mobile/app/(app)/index.tsx index ff3693e..db51095 100755 --- a/apps/mobile/app/(app)/index.tsx +++ b/apps/mobile/app/(app)/index.tsx @@ -4,7 +4,7 @@ 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 } from '../../src/store'; +import { useAuthStore, usePacksStore, useSubscriptionStore } from '../../src/store'; import { GradientBackground } from '../../src/components/ui'; import { Paywall } from '../../src/components/paywall'; import { CompactHeader, ContentRow } from '../../src/components/discovery'; @@ -18,61 +18,63 @@ 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 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 { subscription } = useSubscriptionStore(); const { width } = useWindowDimensions(); const [showPaywall, setShowPaywall] = useState(false); const [headerHeight, setHeaderHeight] = useState(130); - const isPremiumUser = user?.is_premium ?? false; + // 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 @@ -159,51 +161,51 @@ export default function DiscoveryScreen() { ); } - -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', - }, + +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', + }, bottomSpacer: { height: spacing.lg, }, diff --git a/apps/mobile/app/(app)/swipe.tsx b/apps/mobile/app/(app)/swipe.tsx index 3d18a7c..b6e7743 100755 --- a/apps/mobile/app/(app)/swipe.tsx +++ b/apps/mobile/app/(app)/swipe.tsx @@ -1267,8 +1267,6 @@ export default function SwipeScreen() { - {/* Spacer to balance header when back button is shown */} - {(packId || mode === 'pending') && } @@ -1370,11 +1368,9 @@ const styles = StyleSheet.create({ justifyContent: 'center', }, backButton: { + position: 'absolute', + left: 0, padding: spacing.sm, - marginRight: spacing.sm, - }, - headerSpacer: { - width: 40, // Match back button width for centering }, progressContainer: { alignItems: "center",