import { View, Text, StyleSheet, SectionList, RefreshControl, TouchableOpacity, ActivityIndicator, Platform, useWindowDimensions, } from "react-native"; import { useResponsesStore, groupResponses, useAuthStore } from "../../src/store"; import type { ResponseWithQuestion } from "../../src/store"; import { useCallback, useState, useEffect } from "react"; import { useFocusEffect, useRouter, useLocalSearchParams } from "expo-router"; import { Ionicons } from "@expo/vector-icons"; import { LinearGradient } from "expo-linear-gradient"; import Animated, { FadeIn, FadeInUp, useSharedValue, useAnimatedScrollHandler, useAnimatedStyle, interpolate, Extrapolation, } from "react-native-reanimated"; import { BlurView } from "expo-blur"; import { GradientBackground, GlassButton, DecorativeSeparator } from "../../src/components/ui"; import { useAmbientOrbAnimation } from "../../src/hooks"; import { colors, spacing, typography, radius } from "../../src/theme"; import { ResponseCard } from "../../src/components/responses/ResponseCard"; import { EditResponseSheet } from "../../src/components/responses/EditResponseSheet"; // Premium color palette const ACCENT = colors.premium.gold; const ACCENT_RGBA = "rgba(212, 175, 55, "; const ROSE = colors.premium.rose; const ROSE_RGBA = "rgba(232, 164, 174, "; const MAX_CONTENT_WIDTH = 500; const NAV_BAR_HEIGHT = 44; const STATUS_BAR_HEIGHT = 60; const HEADER_SCROLL_DISTANCE = 100; const AnimatedSectionList = Animated.createAnimatedComponent(SectionList); type GroupByOption = "pack" | "date" | "answer"; export default function MyAnswersScreen() { const { responses, isLoading, isRefreshing, isLoadingMore, hasMore, groupBy, dateSortOrder, fetchResponses, ensureResponsesLoaded, setGroupBy, toggleDateSortOrder, totalCount } = useResponsesStore(); const { user, partner } = useAuthStore(); const router = useRouter(); const params = useLocalSearchParams(); const { width } = useWindowDimensions(); const isWideScreen = width > MAX_CONTENT_WIDTH; const handleBack = () => { const returnTo = (params.returnTo as string) || "/(app)/swipe"; router.push(returnTo as any); }; // State for edit sheet const [selectedResponse, setSelectedResponse] = useState(null); const [isEditSheetVisible, setIsEditSheetVisible] = useState(false); // Scroll animation const scrollY = useSharedValue(0); const scrollHandler = useAnimatedScrollHandler({ onScroll: (event) => { scrollY.value = event.contentOffset.y; }, }); 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 }; }); // Ambient orb breathing animations const { orbStyle1, orbStyle2 } = useAmbientOrbAnimation(); // Load once, then keep showing what is cached. Answering or editing a response // invalidates it, and pull-to-refresh is always available. useFocusEffect( useCallback(() => { void ensureResponsesLoaded(); }, [ensureResponsesLoaded]) ); const handleLoadMore = useCallback(() => { if (!isLoading && !isLoadingMore && hasMore) { fetchResponses(false); } }, [isLoading, isLoadingMore, hasMore]); const sections = groupResponses(responses, groupBy, dateSortOrder); const handleEditPress = (response: ResponseWithQuestion) => { setSelectedResponse(response); setIsEditSheetVisible(true); }; const handleEditClose = () => { setIsEditSheetVisible(false); setSelectedResponse(null); }; const handleEditSuccess = () => { // Refetch responses to get updated data fetchResponses(true); }; const renderSectionHeader = ({ section }: { section: any }) => ( {section.title} ); const renderItem = ({ item, index }: { item: any; index: number }) => ( handleEditPress(item)} onChatPress={item.match_id ? () => router.push(`/chat/${item.match_id}`) : undefined} viewerId={user?.id} viewerName={user?.name} partnerName={partner?.name} /> ); const renderFooter = () => { if (!isLoadingMore) return null; return ( ); }; const GroupByButton = ({ option, label }: { option: GroupByOption; label: string }) => { const isActive = groupBy === option; const isDateButton = option === "date"; const showChevron = isDateButton && isActive; const handlePress = () => { if (isDateButton && isActive) { // Toggle sort order when date is already selected toggleDateSortOrder(); } else { setGroupBy(option); } }; return ( {label} {showChevron && ( )} ); }; if (!user) { return ( ); } return ( {/* Ambient Orbs - Commented out for flat look */} {/* Fixed Nav Bar */} My Answers {isLoading && responses.length === 0 ? ( ) : ( item.id} contentContainerStyle={[styles.list, isWideScreen && styles.listWide]} showsVerticalScrollIndicator={false} stickySectionHeadersEnabled={false} onScroll={scrollHandler} scrollEventThrottle={16} refreshControl={ fetchResponses(true)} tintColor={colors.primary} colors={[colors.primary]} progressViewOffset={STATUS_BAR_HEIGHT + NAV_BAR_HEIGHT} /> } onEndReached={handleLoadMore} onEndReachedThreshold={2} ListFooterComponent={renderFooter} ListHeaderComponent={ REVIEW My Answers {/* Count badge */} {totalCount ?? responses.length} {(totalCount ?? responses.length) === 1 ? "ANSWER" : "ANSWERS"} {/* Group by buttons */} } ListEmptyComponent={ No Answers Yet Start swiping on questions to build your answer history. You can come back here to review and change your answers anytime. router.push("/")} style={{ marginTop: spacing.lg }} > Start Swiping } /> )} {/* Edit Response Sheet */} ); } const styles = StyleSheet.create({ container: { flex: 1, }, loadingContainer: { flex: 1, justifyContent: "center", alignItems: "center", }, // Ambient orbs ambientOrb: { position: "absolute", width: 300, height: 300, borderRadius: 150, }, orbTopRight: { top: 60, right: -40, }, orbBottomLeft: { bottom: 180, left: -40, }, orbGradient: { width: "100%", height: "100%", borderRadius: 150, }, // Fixed Nav Bar navBar: { position: "absolute", top: 0, left: 0, right: 0, height: STATUS_BAR_HEIGHT + NAV_BAR_HEIGHT, flexDirection: "row", alignItems: "center", justifyContent: "center", paddingTop: STATUS_BAR_HEIGHT - 10, paddingHorizontal: spacing.md, zIndex: 100, }, navBarBackground: { ...StyleSheet.absoluteFill, borderBottomWidth: 1, borderBottomColor: colors.border, backgroundColor: colors.background, overflow: "hidden", }, navBarBackButton: { position: "absolute", top: STATUS_BAR_HEIGHT - 5, left: spacing.md, zIndex: 10, width: 36, height: 36, borderRadius: 18, backgroundColor: colors.backgroundLight, justifyContent: "center", alignItems: "center", borderWidth: 1, borderColor: colors.border, }, // ... countBadgePremium: { flexDirection: "row", alignItems: "center", backgroundColor: colors.backgroundLight, paddingHorizontal: spacing.md, paddingVertical: spacing.sm, borderRadius: radius.full, borderWidth: 1, borderColor: colors.border, gap: spacing.xs, }, // ... groupByButton: { flexDirection: "row", alignItems: "center", paddingHorizontal: spacing.md, paddingVertical: spacing.sm, borderRadius: radius.full, backgroundColor: colors.backgroundLight, borderWidth: 1, borderColor: colors.border, }, groupByButtonActive: { backgroundColor: colors.background, borderColor: colors.primary, }, // ... emptyIcon: { width: 80, height: 80, borderRadius: 40, backgroundColor: colors.backgroundLight, justifyContent: "center", alignItems: "center", marginBottom: spacing.lg, borderWidth: 1, borderColor: colors.border, }, navBarTitle: { ...typography.headline, color: colors.text, textAlign: "center", }, // Header (Hero) header: { paddingTop: STATUS_BAR_HEIGHT + spacing.md, paddingHorizontal: spacing.lg, paddingBottom: spacing.md, alignItems: 'center', }, headerWide: { alignSelf: "center", width: "100%", maxWidth: MAX_CONTENT_WIDTH, }, headerContent: { alignItems: "center", }, headerLabel: { ...typography.caption2, fontWeight: "600", letterSpacing: 3, color: ACCENT, marginBottom: spacing.xs, }, headerTitle: { ...typography.largeTitle, color: colors.text, textAlign: "center", }, countTextPremium: { ...typography.caption2, fontWeight: "600", letterSpacing: 2, color: ACCENT, }, // Group by buttons groupByContainer: { flexDirection: "row", justifyContent: "center", gap: spacing.sm, marginTop: spacing.md, }, groupByText: { ...typography.caption1, color: colors.textSecondary, fontWeight: "500", }, groupByTextActive: { color: ACCENT, fontWeight: "600", }, sortChevron: { marginLeft: spacing.xs, }, // Section header sectionHeader: { paddingVertical: spacing.sm, paddingHorizontal: spacing.xs, marginTop: spacing.md, marginBottom: spacing.xs, }, sectionTitle: { ...typography.title3, color: colors.text, }, // List list: { padding: spacing.lg, paddingTop: 0, // Header handles top spacing paddingBottom: Platform.OS === "ios" ? 120 : 100, }, listWide: { alignSelf: "center", width: "100%", maxWidth: MAX_CONTENT_WIDTH, }, // Empty state emptyContent: { width: "100%", alignItems: "center", paddingHorizontal: spacing.md, paddingVertical: spacing.xxl, }, emptyTitle: { ...typography.title2, color: colors.text, marginBottom: spacing.sm, }, emptyDescription: { ...typography.body, color: colors.textSecondary, textAlign: "center", paddingHorizontal: spacing.md, }, footerLoader: { paddingVertical: spacing.md, alignItems: "center", justifyContent: "center", }, });