Fix premium access check and tab bar styling

This commit is contained in:
jjbrunton committed 2026-01-25 19:56:47 +00:00
1 parent 1e4974e2a0
commit a531f43d91
4 files changed
+114 -113

No files matched your search

+4 -4
View File
@@ -16,10 +16,10 @@ build/
.env.local .env.local
.env.development.local .env.development.local
.env.test.local .env.test.local
.env.production.local .env.production.local
apps/mobile/.env.prod apps/mobile/.env.*
.secrets .secrets
# Supabase # Supabase
.supabase/ .supabase/
+9 -6
View File
@@ -43,11 +43,14 @@ function TabBarBackground() {
// The solid rgba background in tabBarStyle provides the visual effect on Android // The solid rgba background in tabBarStyle provides the visual effect on Android
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
return ( return (
<BlurView <>
intensity={100} <BlurView
tint="dark" intensity={80}
style={StyleSheet.absoluteFill} tint="dark"
/> style={StyleSheet.absoluteFill}
/>
<View style={[StyleSheet.absoluteFill, { backgroundColor: 'rgba(14, 14, 17, 0.85)' }]} />
</>
); );
} }
return null; return null;
@@ -435,7 +438,7 @@ export default function AppLayout() {
tabBarBackground: () => <TabBarBackground />, tabBarBackground: () => <TabBarBackground />,
tabBarStyle: shouldHideTabBar ? { display: 'none' } : { tabBarStyle: shouldHideTabBar ? { display: 'none' } : {
position: 'absolute', 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 borderTopColor: 'rgba(233, 69, 96, 0.2)', // primary brand color
borderTopWidth: 1, borderTopWidth: 1,
paddingTop: 8, paddingTop: 8,
+99 -97
View File
@@ -4,7 +4,7 @@ import { useFocusEffect } from 'expo-router';
import Animated, { FadeInDown } from 'react-native-reanimated'; import Animated, { FadeInDown } from 'react-native-reanimated';
import { Ionicons } from '@expo/vector-icons'; import { Ionicons } from '@expo/vector-icons';
import { BlurView } from 'expo-blur'; 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 { GradientBackground } from '../../src/components/ui';
import { Paywall } from '../../src/components/paywall'; import { Paywall } from '../../src/components/paywall';
import { CompactHeader, ContentRow } from '../../src/components/discovery'; import { CompactHeader, ContentRow } from '../../src/components/discovery';
@@ -18,61 +18,63 @@ function groupPacksByCategory(
packs: QuestionPack[], packs: QuestionPack[],
categories: Category[] categories: Category[]
): { category: Category; packs: QuestionPack[] }[] { ): { category: Category; packs: QuestionPack[] }[] {
const categoryMap = new Map<string, QuestionPack[]>(); const categoryMap = new Map<string, QuestionPack[]>();
const uncategorized: QuestionPack[] = []; const uncategorized: QuestionPack[] = [];
// Initialize map with empty arrays for each category // Initialize map with empty arrays for each category
categories.forEach((cat) => { categories.forEach((cat) => {
categoryMap.set(cat.id, []); categoryMap.set(cat.id, []);
}); });
// Group packs // Group packs
packs.forEach((pack) => { packs.forEach((pack) => {
if (pack.category_id && categoryMap.has(pack.category_id)) { if (pack.category_id && categoryMap.has(pack.category_id)) {
categoryMap.get(pack.category_id)!.push(pack); categoryMap.get(pack.category_id)!.push(pack);
} else { } else {
uncategorized.push(pack); uncategorized.push(pack);
} }
}); });
// Build result array maintaining category sort order // Build result array maintaining category sort order
const result: { category: Category; packs: QuestionPack[] }[] = []; const result: { category: Category; packs: QuestionPack[] }[] = [];
categories.forEach((cat) => { categories.forEach((cat) => {
const categoryPacks = categoryMap.get(cat.id) || []; const categoryPacks = categoryMap.get(cat.id) || [];
if (categoryPacks.length > 0) { if (categoryPacks.length > 0) {
result.push({ category: cat, packs: categoryPacks }); result.push({ category: cat, packs: categoryPacks });
} }
}); });
// Add uncategorized at the end if any // Add uncategorized at the end if any
if (uncategorized.length > 0) { if (uncategorized.length > 0) {
result.push({ result.push({
category: { category: {
id: 'uncategorized', id: 'uncategorized',
name: 'More', name: 'More',
description: null, description: null,
icon: 'ellipsis-horizontal', icon: 'ellipsis-horizontal',
color: null, color: null,
sort_order: 999, sort_order: 999,
created_at: '', created_at: '',
is_public: true, is_public: true,
}, },
packs: uncategorized, packs: uncategorized,
}); });
} }
return result; return result;
} }
export default function DiscoveryScreen() { export default function DiscoveryScreen() {
const { user, partner, couple } = useAuthStore(); const { user, partner, couple } = useAuthStore();
const { packs, categories, fetchPacks, getPackProgress } = usePacksStore(); const { packs, categories, fetchPacks, getPackProgress } = usePacksStore();
const { subscription } = useSubscriptionStore();
const { width } = useWindowDimensions(); const { width } = useWindowDimensions();
const [showPaywall, setShowPaywall] = useState(false); const [showPaywall, setShowPaywall] = useState(false);
const [headerHeight, setHeaderHeight] = useState(130); 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; const isWideScreen = width > MAX_CONTENT_WIDTH;
// Refresh data when screen gains focus // Refresh data when screen gains focus
@@ -159,51 +161,51 @@ export default function DiscoveryScreen() {
); );
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
}, },
contentContainer: { contentContainer: {
flexGrow: 1, flexGrow: 1,
paddingBottom: Platform.OS === 'ios' ? 100 : 80, paddingBottom: Platform.OS === 'ios' ? 100 : 80,
}, },
contentContainerWide: { contentContainerWide: {
alignItems: 'center', alignItems: 'center',
}, },
innerContainer: { innerContainer: {
flex: 1, flex: 1,
width: '100%', width: '100%',
}, },
innerContainerWide: { innerContainerWide: {
maxWidth: MAX_CONTENT_WIDTH, maxWidth: MAX_CONTENT_WIDTH,
}, },
emptyState: { emptyState: {
flex: 1, flex: 1,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
paddingHorizontal: spacing.xl, paddingHorizontal: spacing.xl,
paddingVertical: spacing.xxl, paddingVertical: spacing.xxl,
}, },
emptyIcon: { emptyIcon: {
width: 80, width: 80,
height: 80, height: 80,
borderRadius: 40, borderRadius: 40,
backgroundColor: 'rgba(233, 69, 96, 0.1)', backgroundColor: 'rgba(233, 69, 96, 0.1)',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
marginBottom: spacing.lg, marginBottom: spacing.lg,
}, },
emptyTitle: { emptyTitle: {
...typography.title3, ...typography.title3,
color: colors.text, color: colors.text,
marginBottom: spacing.sm, marginBottom: spacing.sm,
}, },
emptySubtitle: { emptySubtitle: {
...typography.body, ...typography.body,
color: colors.textSecondary, color: colors.textSecondary,
textAlign: 'center', textAlign: 'center',
}, },
bottomSpacer: { bottomSpacer: {
height: spacing.lg, height: spacing.lg,
}, },
+2 -6
View File
@@ -1267,8 +1267,6 @@ export default function SwipeScreen() {
</Animated.View> </Animated.View>
</View> </View>
</View> </View>
{/* Spacer to balance header when back button is shown */}
{(packId || mode === 'pending') && <View style={styles.headerSpacer} />}
</View> </View>
</Animated.View> </Animated.View>
@@ -1370,11 +1368,9 @@ const styles = StyleSheet.create({
justifyContent: 'center', justifyContent: 'center',
}, },
backButton: { backButton: {
position: 'absolute',
left: 0,
padding: spacing.sm, padding: spacing.sm,
marginRight: spacing.sm,
},
headerSpacer: {
width: 40, // Match back button width for centering
}, },
progressContainer: { progressContainer: {
alignItems: "center", alignItems: "center",