mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-10-31 18:19:43 +00:00
Bump expo, react, react-native, and all expo-* / react-native-* peers to the SDK 56 line via expo install --fix, adding expo-splash-screen and expo-system-ui for config plugins the SDK split out of core. Bump @bacons/apple-targets, react-native-view-shot, and other native modules to versions compatible with React Native 0.85 and the new architecture. Remove @react-navigation/* direct dependencies and route their hooks through expo-router, which is no longer compatible with react-navigation as of SDK 56. Fix RN 0.85's removed StyleSheet.absoluteFillObject (use absoluteFill) and react-native-view-shot 5.x's ViewShotRef type across call sites. Downgrade eslint-plugin-react-hooks v7's new React Compiler era rules to warnings pending a dedicated refactor, and repair Jest fallout from the jest-expo/reanimated/worklets/expo-media-library bumps. Regenerate the committed ios/ and android/ native projects with `expo prebuild --clean` (required for @bacons/apple-targets 5.x) against the production release channel, and document that requirement. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
779 lines
27 KiB
TypeScript
779 lines
27 KiB
TypeScript
import { View, Text, StyleSheet, FlatList, TouchableOpacity, ActivityIndicator, Modal, Pressable, Platform } from "react-native";
|
|
import { useLocalSearchParams, useRouter } from "expo-router";
|
|
import { useEffect, useState } from "react";
|
|
import { LinearGradient } from "expo-linear-gradient";
|
|
import { BlurView } from "expo-blur";
|
|
import { appDataApi } from "../../src/lib/appDataApi";
|
|
import { usePacksStore, useSubscriptionStore, useAuthStore } from "../../src/store";
|
|
import { Ionicons } from "@expo/vector-icons";
|
|
import Animated, {
|
|
FadeInDown,
|
|
useSharedValue,
|
|
useAnimatedScrollHandler,
|
|
useAnimatedStyle,
|
|
interpolate,
|
|
Extrapolation,
|
|
} from "react-native-reanimated";
|
|
import { GradientBackground, GlassCard } from "../../src/components/ui";
|
|
import { colors, gradients, spacing, radius, typography } from "../../src/theme";
|
|
import { getPackIconName } from "../../src/lib/packIcons";
|
|
import { Question } from "../../src/types";
|
|
|
|
type TooltipType = 'initiator' | 'coupleType' | null;
|
|
|
|
const TOOLTIP_CONTENT = {
|
|
initiator: {
|
|
title: 'Initiator Filter',
|
|
description: 'The "initiator" is the first person in the couple to see and answer this question. This icon shows which genders can be the initiator. Their partner will always see the question afterward, regardless of this setting.',
|
|
},
|
|
coupleType: {
|
|
title: 'Couple Type Filter',
|
|
description: 'This shows which couple compositions can see this question. For example, a male+female icon means the question is only shown to couples where one partner is male and one is female.',
|
|
},
|
|
};
|
|
|
|
const ACCENT_COLOR = colors.primary;
|
|
|
|
const NAV_BAR_HEIGHT = 44;
|
|
const STATUS_BAR_HEIGHT = 60;
|
|
const HERO_HEIGHT = 260;
|
|
const HEADER_SCROLL_DISTANCE = 120;
|
|
|
|
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList<Question>);
|
|
|
|
export default function PackDetailsScreen() {
|
|
const { id } = useLocalSearchParams();
|
|
const router = useRouter();
|
|
const { packs, ensurePacksLoaded } = usePacksStore();
|
|
const { subscription } = useSubscriptionStore();
|
|
const { user, partner } = useAuthStore();
|
|
const [questions, setQuestions] = useState<Question[]>([]);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [activeTooltip, setActiveTooltip] = useState<TooltipType>(null);
|
|
|
|
const scrollY = useSharedValue(0);
|
|
|
|
const pack = packs.find((p) => p.id === id);
|
|
const hasPremiumAccess = user?.is_premium || partner?.is_premium || subscription.isProUser;
|
|
const isPremium = pack?.is_premium && hasPremiumAccess;
|
|
|
|
const { categories } = usePacksStore();
|
|
const category = categories.find(c => c.id === pack?.category_id);
|
|
|
|
useEffect(() => {
|
|
void ensurePacksLoaded();
|
|
fetchPackDetails();
|
|
}, [id]);
|
|
|
|
const fetchPackDetails = async () => {
|
|
try {
|
|
const { questions } = await appDataApi.packQuestions(String(id));
|
|
setQuestions(questions);
|
|
} catch (error) {
|
|
console.error("Error fetching questions:", error);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
const scrollHandler = useAnimatedScrollHandler({
|
|
onScroll: (event) => {
|
|
scrollY.value = event.contentOffset.y;
|
|
},
|
|
});
|
|
|
|
// Animated styles for collapsing header
|
|
const heroStyle = useAnimatedStyle(() => {
|
|
const opacity = interpolate(
|
|
scrollY.value,
|
|
[0, HEADER_SCROLL_DISTANCE * 0.7],
|
|
[1, 0],
|
|
Extrapolation.CLAMP
|
|
);
|
|
const scale = interpolate(
|
|
scrollY.value,
|
|
[0, HEADER_SCROLL_DISTANCE],
|
|
[1, 0.95],
|
|
Extrapolation.CLAMP
|
|
);
|
|
return { opacity, transform: [{ scale }] };
|
|
});
|
|
|
|
const compactHeaderStyle = useAnimatedStyle(() => {
|
|
const opacity = interpolate(
|
|
scrollY.value,
|
|
[HEADER_SCROLL_DISTANCE * 0.5, HEADER_SCROLL_DISTANCE],
|
|
[0, 1],
|
|
Extrapolation.CLAMP
|
|
);
|
|
return { opacity };
|
|
});
|
|
|
|
const navBarBackgroundStyle = useAnimatedStyle(() => {
|
|
const opacity = interpolate(
|
|
scrollY.value,
|
|
[0, HEADER_SCROLL_DISTANCE * 0.8],
|
|
[0, 1],
|
|
Extrapolation.CLAMP
|
|
);
|
|
return { opacity };
|
|
});
|
|
|
|
const useBlur = Platform.OS === 'ios';
|
|
|
|
const renderItem = ({ item, index }: { item: Question; index: number }) => {
|
|
return (
|
|
<Animated.View entering={FadeInDown.delay(index * 50).duration(300)}>
|
|
<View style={styles.questionCard}>
|
|
<View style={styles.questionContent}>
|
|
<Text style={styles.questionNumber}>#{index + 1}</Text>
|
|
<Text style={styles.questionText}>{item.text}</Text>
|
|
|
|
{item.partner_text && (
|
|
<View style={styles.partnerTextContainer}>
|
|
<Ionicons name="swap-horizontal" size={14} color={colors.premium.rose} />
|
|
<Text style={styles.partnerText}>{item.partner_text}</Text>
|
|
</View>
|
|
)}
|
|
|
|
<View style={styles.footerContainer}>
|
|
<View style={styles.intensityContainer}>
|
|
{[...Array(5)].map((_, i) => (
|
|
<Ionicons
|
|
key={i}
|
|
name="flame"
|
|
size={12}
|
|
color={i < item.intensity ? ACCENT_COLOR : 'rgba(255, 255, 255, 0.15)'}
|
|
style={{ marginRight: 2 }}
|
|
/>
|
|
))}
|
|
</View>
|
|
|
|
<View style={styles.badgesContainer}>
|
|
{item.target_user_genders && item.target_user_genders.length > 0 && (
|
|
<Pressable
|
|
style={styles.targetBadge}
|
|
onPress={() => setActiveTooltip('initiator')}
|
|
>
|
|
<Ionicons name="arrow-forward" size={10} color={colors.textTertiary} style={{ marginRight: 2 }} />
|
|
{item.target_user_genders.map((g, i) => (
|
|
<Ionicons
|
|
key={g}
|
|
name={g === 'male' ? 'male' : g === 'female' ? 'female' : 'person'}
|
|
size={12}
|
|
color={colors.textTertiary}
|
|
style={i > 0 ? { marginLeft: 2 } : {}}
|
|
/>
|
|
))}
|
|
</Pressable>
|
|
)}
|
|
|
|
<Pressable
|
|
style={styles.targetBadge}
|
|
onPress={() => setActiveTooltip('coupleType')}
|
|
>
|
|
{item.allowed_couple_genders && item.allowed_couple_genders.length > 0 ? (
|
|
item.allowed_couple_genders.map((g) => {
|
|
const icons: ('male' | 'female')[] = [];
|
|
if (g === 'male+male') {
|
|
icons.push('male', 'male');
|
|
} else if (g === 'female+female') {
|
|
icons.push('female', 'female');
|
|
} else {
|
|
icons.push('male', 'female');
|
|
}
|
|
|
|
return (
|
|
<View key={g} style={styles.coupleIconGroup}>
|
|
{icons.map((icon, i) => (
|
|
<Ionicons
|
|
key={i}
|
|
name={icon}
|
|
size={12}
|
|
color={colors.textTertiary}
|
|
style={i > 0 ? { marginLeft: 2 } : {}}
|
|
/>
|
|
))}
|
|
</View>
|
|
);
|
|
})
|
|
) : (
|
|
<Ionicons name="people" size={12} color={colors.textTertiary} />
|
|
)}
|
|
</Pressable>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</Animated.View>
|
|
);
|
|
};
|
|
|
|
const heroContent = (
|
|
<View style={styles.heroInner}>
|
|
{/* Premium Banner */}
|
|
{pack?.is_premium && (
|
|
<View style={styles.premiumBanner}>
|
|
<LinearGradient
|
|
colors={[colors.premium.gold, colors.premium.goldDark]}
|
|
start={{ x: 0, y: 0 }}
|
|
end={{ x: 1, y: 0 }}
|
|
style={StyleSheet.absoluteFill}
|
|
/>
|
|
<View style={styles.premiumBannerContent}>
|
|
<Ionicons name="diamond" size={16} color={colors.background} />
|
|
<Text style={styles.premiumBannerText}>PREMIUM COLLECTION</Text>
|
|
<Ionicons name="diamond" size={16} color={colors.background} />
|
|
</View>
|
|
</View>
|
|
)}
|
|
|
|
<View style={styles.heroContent}>
|
|
{/* Category label */}
|
|
<Text style={[styles.label, pack?.is_premium && styles.labelPremium]}>
|
|
{category ? category.name.toUpperCase() : 'PREVIEW'}
|
|
</Text>
|
|
|
|
{/* Icon with enhanced glow for premium */}
|
|
<View style={styles.iconWrapper}>
|
|
{pack?.is_premium && (
|
|
<>
|
|
{/* Glow effects removed for flat design */}
|
|
</>
|
|
)}
|
|
<View style={[
|
|
styles.iconContainer,
|
|
pack?.is_premium && styles.iconContainerPremium
|
|
]}>
|
|
{pack?.is_premium && (
|
|
<LinearGradient
|
|
colors={['rgba(212, 175, 55, 0.2)', 'rgba(184, 134, 11, 0.1)']}
|
|
style={StyleSheet.absoluteFill}
|
|
/>
|
|
)}
|
|
<Ionicons
|
|
name={getPackIconName(pack?.icon)}
|
|
size={32}
|
|
color={pack?.is_premium ? colors.premium.gold : colors.textSecondary}
|
|
/>
|
|
</View>
|
|
{/* Crown for premium */}
|
|
{pack?.is_premium && (
|
|
<View style={styles.crownBadge}>
|
|
<Ionicons name="star" size={14} color={colors.premium.gold} />
|
|
</View>
|
|
)}
|
|
</View>
|
|
|
|
{/* Title */}
|
|
<Text style={[styles.title, pack?.is_premium && styles.titlePremium]}>
|
|
{pack?.name || "Pack Details"}
|
|
</Text>
|
|
|
|
{/* Premium badge - larger and more prominent */}
|
|
{pack?.is_premium && (
|
|
<View style={styles.proBadge}>
|
|
<LinearGradient
|
|
colors={[colors.premium.gold, colors.premium.goldDark]}
|
|
start={{ x: 0, y: 0 }}
|
|
end={{ x: 1, y: 0 }}
|
|
style={[StyleSheet.absoluteFill, { borderRadius: radius.full }]}
|
|
/>
|
|
<Ionicons name="star" size={12} color={colors.background} />
|
|
<Text style={styles.proBadgeText}>PREMIUM</Text>
|
|
<Ionicons name="star" size={12} color={colors.background} />
|
|
</View>
|
|
)}
|
|
|
|
{/* Decorative separator */}
|
|
<View style={styles.separator}>
|
|
<View style={[styles.separatorLine, pack?.is_premium && styles.separatorLinePremium]} />
|
|
<View style={[styles.separatorDiamond, pack?.is_premium && styles.separatorDiamondPremium]} />
|
|
<View style={[styles.separatorLine, pack?.is_premium && styles.separatorLinePremium]} />
|
|
</View>
|
|
|
|
{/* Description */}
|
|
<Text style={styles.description}>{pack?.description}</Text>
|
|
|
|
{/* Stats badge */}
|
|
<View style={[styles.statsBadge, pack?.is_premium && styles.statsBadgePremium]}>
|
|
<Ionicons name="help-circle-outline" size={14} color={pack?.is_premium ? colors.premium.gold : ACCENT_COLOR} />
|
|
<Text style={[styles.statsText, pack?.is_premium && styles.statsTextPremium]}>{questions.length} questions</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
);
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<GradientBackground>
|
|
<View style={styles.loadingContainer}>
|
|
<ActivityIndicator size="large" color={colors.primary} />
|
|
</View>
|
|
</GradientBackground>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<GradientBackground>
|
|
<View style={styles.container}>
|
|
{/* Fixed Nav Bar */}
|
|
<View style={styles.navBar}>
|
|
<Animated.View style={[styles.navBarBackground, navBarBackgroundStyle]}>
|
|
<View style={[StyleSheet.absoluteFill, { backgroundColor: colors.background }]} />
|
|
</Animated.View>
|
|
|
|
<TouchableOpacity onPress={() => router.back()} style={styles.backButton}>
|
|
<View style={styles.backButtonInner}>
|
|
<Ionicons name="chevron-back" size={22} color={colors.text} />
|
|
</View>
|
|
</TouchableOpacity>
|
|
|
|
<Animated.Text style={[styles.navBarTitle, compactHeaderStyle]} numberOfLines={1}>
|
|
{pack?.name || "Pack Details"}
|
|
</Animated.Text>
|
|
|
|
<View style={styles.navBarSpacer} />
|
|
</View>
|
|
|
|
{/* Questions List with Hero Header */}
|
|
<AnimatedFlatList
|
|
data={questions}
|
|
renderItem={renderItem}
|
|
keyExtractor={(item) => item.id}
|
|
showsVerticalScrollIndicator={false}
|
|
onScroll={scrollHandler}
|
|
scrollEventThrottle={16}
|
|
contentContainerStyle={styles.list}
|
|
ListHeaderComponent={
|
|
<Animated.View style={[styles.heroContainer, heroStyle]}>
|
|
{heroContent}
|
|
</Animated.View>
|
|
}
|
|
ListEmptyComponent={
|
|
<Animated.View entering={FadeInDown.delay(100).duration(300)} style={styles.emptyContainer}>
|
|
<Ionicons name="help-circle-outline" size={48} color={colors.textTertiary} />
|
|
<Text style={styles.emptyText}>No questions in this pack yet</Text>
|
|
</Animated.View>
|
|
}
|
|
/>
|
|
|
|
{/* Tooltip Modal */}
|
|
<Modal
|
|
visible={activeTooltip !== null}
|
|
transparent
|
|
animationType="fade"
|
|
onRequestClose={() => setActiveTooltip(null)}
|
|
>
|
|
<Pressable
|
|
style={styles.modalOverlay}
|
|
onPress={() => setActiveTooltip(null)}
|
|
>
|
|
<GlassCard style={styles.tooltipContainer}>
|
|
<LinearGradient
|
|
colors={gradients.laceTint as [string, string]}
|
|
style={StyleSheet.absoluteFill}
|
|
/>
|
|
<Text style={styles.tooltipTitle}>
|
|
{activeTooltip && TOOLTIP_CONTENT[activeTooltip].title}
|
|
</Text>
|
|
<Text style={styles.tooltipDescription}>
|
|
{activeTooltip && TOOLTIP_CONTENT[activeTooltip].description}
|
|
</Text>
|
|
<TouchableOpacity
|
|
style={styles.tooltipButton}
|
|
onPress={() => setActiveTooltip(null)}
|
|
>
|
|
<LinearGradient
|
|
colors={gradients.primary as [string, string]}
|
|
start={{ x: 0, y: 0 }}
|
|
end={{ x: 1, y: 0 }}
|
|
style={styles.tooltipButtonGradient}
|
|
>
|
|
<Text style={styles.tooltipButtonText}>Got it</Text>
|
|
</LinearGradient>
|
|
</TouchableOpacity>
|
|
</GlassCard>
|
|
</Pressable>
|
|
</Modal>
|
|
</View>
|
|
</GradientBackground>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
},
|
|
loadingContainer: {
|
|
flex: 1,
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
},
|
|
|
|
// Fixed Nav Bar
|
|
navBar: {
|
|
position: "absolute",
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
height: STATUS_BAR_HEIGHT + NAV_BAR_HEIGHT,
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
paddingTop: STATUS_BAR_HEIGHT - 10,
|
|
paddingHorizontal: spacing.md,
|
|
zIndex: 100,
|
|
},
|
|
navBarBackground: {
|
|
...StyleSheet.absoluteFill,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: colors.border,
|
|
overflow: "hidden",
|
|
backgroundColor: colors.background, // Flat
|
|
},
|
|
// ...
|
|
|
|
// ...
|
|
premiumBanner: {
|
|
marginHorizontal: spacing.lg,
|
|
marginBottom: spacing.lg,
|
|
borderRadius: radius.md,
|
|
overflow: 'hidden',
|
|
// Removed heavy shadow
|
|
borderWidth: 1,
|
|
borderColor: colors.premium.gold,
|
|
},
|
|
// ...
|
|
iconWrapper: {
|
|
position: 'relative',
|
|
marginBottom: spacing.md,
|
|
// Removed glowing background elements
|
|
},
|
|
// ...
|
|
iconContainer: {
|
|
width: 72,
|
|
height: 72,
|
|
borderRadius: 36,
|
|
backgroundColor: colors.backgroundLight,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
borderWidth: 1,
|
|
borderColor: colors.border,
|
|
overflow: 'hidden',
|
|
},
|
|
iconContainerPremium: {
|
|
borderColor: colors.premium.gold,
|
|
borderWidth: 2,
|
|
backgroundColor: colors.backgroundLight,
|
|
// Removed heavy shadows
|
|
},
|
|
// ...
|
|
statsBadge: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: spacing.xs,
|
|
backgroundColor: colors.backgroundLight,
|
|
paddingHorizontal: spacing.lg,
|
|
paddingVertical: spacing.sm,
|
|
borderRadius: radius.full,
|
|
borderWidth: 1,
|
|
borderColor: colors.border,
|
|
},
|
|
// ...
|
|
questionCard: {
|
|
backgroundColor: colors.backgroundLight, // Flat
|
|
borderRadius: radius.md,
|
|
borderWidth: 1,
|
|
borderColor: colors.border,
|
|
marginBottom: spacing.sm,
|
|
},
|
|
// ...
|
|
partnerTextContainer: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: spacing.xs,
|
|
marginBottom: spacing.sm,
|
|
paddingVertical: spacing.xs,
|
|
paddingHorizontal: spacing.sm,
|
|
backgroundColor: colors.background, // Nested flat container
|
|
borderRadius: radius.sm,
|
|
alignSelf: 'flex-start',
|
|
borderWidth: 1,
|
|
borderColor: colors.border,
|
|
},
|
|
// ...
|
|
tooltipContainer: {
|
|
maxWidth: 320,
|
|
overflow: 'hidden',
|
|
backgroundColor: colors.backgroundLight, // Flat
|
|
borderWidth: 1,
|
|
borderColor: colors.border,
|
|
},
|
|
backButton: {
|
|
zIndex: 10,
|
|
},
|
|
backButtonInner: {
|
|
width: 36,
|
|
height: 36,
|
|
borderRadius: 18,
|
|
backgroundColor: 'rgba(255, 255, 255, 0.08)',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
borderWidth: 1,
|
|
borderColor: 'rgba(255, 255, 255, 0.1)',
|
|
},
|
|
navBarTitle: {
|
|
flex: 1,
|
|
...typography.headline,
|
|
color: colors.text,
|
|
textAlign: "center",
|
|
marginRight: 36,
|
|
},
|
|
navBarSpacer: {
|
|
width: 0,
|
|
},
|
|
|
|
// Hero Section
|
|
heroContainer: {
|
|
marginBottom: spacing.lg,
|
|
},
|
|
heroInner: {
|
|
paddingTop: STATUS_BAR_HEIGHT + spacing.md,
|
|
paddingBottom: spacing.lg,
|
|
minHeight: HERO_HEIGHT,
|
|
},
|
|
heroContent: {
|
|
alignItems: 'center',
|
|
paddingHorizontal: spacing.lg,
|
|
},
|
|
|
|
// Premium Banner
|
|
|
|
premiumBannerContent: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
paddingVertical: spacing.sm,
|
|
paddingHorizontal: spacing.md,
|
|
gap: spacing.sm,
|
|
},
|
|
premiumBannerText: {
|
|
...typography.caption1,
|
|
fontWeight: '800',
|
|
letterSpacing: 2,
|
|
color: colors.background,
|
|
},
|
|
|
|
label: {
|
|
...typography.caption1,
|
|
fontWeight: '600',
|
|
letterSpacing: 3,
|
|
color: ACCENT_COLOR,
|
|
textAlign: 'center',
|
|
marginBottom: spacing.md,
|
|
},
|
|
labelPremium: {
|
|
color: colors.premium.gold,
|
|
},
|
|
// Removed glow styles
|
|
iconGlowOuter: {
|
|
display: 'none',
|
|
},
|
|
iconGlow: {
|
|
display: 'none',
|
|
},
|
|
|
|
|
|
crownBadge: {
|
|
position: 'absolute',
|
|
top: -4,
|
|
right: -4,
|
|
width: 24,
|
|
height: 24,
|
|
borderRadius: 12,
|
|
backgroundColor: colors.background,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
borderWidth: 2,
|
|
borderColor: colors.premium.gold,
|
|
shadowColor: colors.premium.gold,
|
|
shadowOffset: { width: 0, height: 2 },
|
|
shadowOpacity: 0.4,
|
|
shadowRadius: 4,
|
|
elevation: 4,
|
|
},
|
|
title: {
|
|
...typography.largeTitle,
|
|
color: colors.text,
|
|
textAlign: 'center',
|
|
marginBottom: spacing.sm,
|
|
},
|
|
titlePremium: {
|
|
textShadowColor: 'rgba(212, 175, 55, 0.3)',
|
|
textShadowOffset: { width: 0, height: 0 },
|
|
textShadowRadius: 10,
|
|
},
|
|
proBadge: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
paddingHorizontal: spacing.md,
|
|
paddingVertical: spacing.xs,
|
|
borderRadius: radius.full,
|
|
gap: spacing.xs,
|
|
marginBottom: spacing.sm,
|
|
overflow: 'hidden',
|
|
shadowColor: colors.premium.gold,
|
|
shadowOffset: { width: 0, height: 2 },
|
|
shadowOpacity: 0.4,
|
|
shadowRadius: 6,
|
|
elevation: 6,
|
|
},
|
|
proBadgeText: {
|
|
...typography.caption1,
|
|
color: colors.background,
|
|
fontWeight: '800',
|
|
letterSpacing: 1.5,
|
|
},
|
|
separator: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
marginVertical: spacing.md,
|
|
width: 140,
|
|
},
|
|
separatorLine: {
|
|
flex: 1,
|
|
height: 1,
|
|
backgroundColor: 'rgba(233, 69, 96, 0.25)',
|
|
},
|
|
separatorLinePremium: {
|
|
backgroundColor: 'rgba(212, 175, 55, 0.4)',
|
|
},
|
|
separatorDiamond: {
|
|
width: 6,
|
|
height: 6,
|
|
backgroundColor: ACCENT_COLOR,
|
|
transform: [{ rotate: '45deg' }],
|
|
marginHorizontal: spacing.md,
|
|
opacity: 0.6,
|
|
},
|
|
separatorDiamondPremium: {
|
|
backgroundColor: colors.premium.gold,
|
|
opacity: 1,
|
|
},
|
|
description: {
|
|
...typography.body,
|
|
color: colors.textSecondary,
|
|
textAlign: 'center',
|
|
lineHeight: 24,
|
|
paddingHorizontal: spacing.md,
|
|
marginBottom: spacing.md,
|
|
},
|
|
|
|
statsBadgePremium: {
|
|
backgroundColor: 'rgba(212, 175, 55, 0.1)',
|
|
borderColor: 'rgba(212, 175, 55, 0.3)',
|
|
},
|
|
statsText: {
|
|
...typography.subhead,
|
|
color: colors.textSecondary,
|
|
fontWeight: '500',
|
|
},
|
|
statsTextPremium: {
|
|
color: colors.premium.champagne,
|
|
},
|
|
|
|
// List
|
|
list: {
|
|
paddingHorizontal: spacing.md,
|
|
paddingBottom: Platform.OS === 'ios' ? 100 : 80,
|
|
},
|
|
|
|
questionContent: {
|
|
padding: spacing.md,
|
|
},
|
|
questionNumber: {
|
|
...typography.caption2,
|
|
color: colors.textTertiary,
|
|
marginBottom: spacing.xs,
|
|
},
|
|
questionText: {
|
|
...typography.body,
|
|
color: colors.text,
|
|
marginBottom: spacing.sm,
|
|
lineHeight: 22,
|
|
},
|
|
|
|
partnerText: {
|
|
...typography.caption1,
|
|
color: colors.premium.rose,
|
|
fontStyle: 'italic',
|
|
},
|
|
footerContainer: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
marginTop: spacing.xs,
|
|
paddingTop: spacing.xs,
|
|
borderTopWidth: 1,
|
|
borderTopColor: 'rgba(255, 255, 255, 0.04)',
|
|
},
|
|
intensityContainer: {
|
|
flexDirection: 'row',
|
|
},
|
|
badgesContainer: {
|
|
flexDirection: 'row',
|
|
gap: spacing.xs,
|
|
},
|
|
targetBadge: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
paddingHorizontal: spacing.xs,
|
|
paddingVertical: 2,
|
|
},
|
|
coupleIconGroup: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
},
|
|
emptyContainer: {
|
|
alignItems: 'center',
|
|
paddingVertical: spacing.xxl,
|
|
gap: spacing.md,
|
|
},
|
|
emptyText: {
|
|
...typography.body,
|
|
color: colors.textSecondary,
|
|
},
|
|
modalOverlay: {
|
|
flex: 1,
|
|
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
padding: spacing.lg,
|
|
},
|
|
|
|
tooltipTitle: {
|
|
...typography.title3,
|
|
color: colors.text,
|
|
marginBottom: spacing.sm,
|
|
},
|
|
tooltipDescription: {
|
|
...typography.body,
|
|
color: colors.textSecondary,
|
|
lineHeight: 24,
|
|
marginBottom: spacing.lg,
|
|
},
|
|
tooltipButton: {
|
|
borderRadius: radius.md,
|
|
overflow: 'hidden',
|
|
},
|
|
tooltipButtonGradient: {
|
|
paddingVertical: spacing.sm,
|
|
alignItems: 'center',
|
|
},
|
|
tooltipButtonText: {
|
|
...typography.headline,
|
|
color: colors.text,
|
|
},
|
|
});
|