import { Ionicons } from "@expo/vector-icons"; import { BlurView } from "expo-blur"; import { useAtomValue } from "jotai"; import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { Animated, Easing, Pressable, ScrollView, TVFocusGuideView, } from "react-native"; import { Text } from "@/components/common/Text"; import { useScaledTVTypography } from "@/constants/TVTypography"; import useRouter from "@/hooks/useAppRouter"; import { tvAccountActionModalAtom } from "@/utils/atoms/tvAccountActionModal"; import { store } from "@/utils/store"; // Action card component const TVAccountActionCard: React.FC<{ label: string; icon: keyof typeof Ionicons.glyphMap; variant?: "default" | "destructive"; hasTVPreferredFocus?: boolean; onPress: () => void; }> = ({ label, icon, variant = "default", hasTVPreferredFocus, onPress }) => { const [focused, setFocused] = useState(false); const scale = useRef(new Animated.Value(1)).current; const typography = useScaledTVTypography(); const animateTo = (v: number) => Animated.timing(scale, { toValue: v, duration: 150, easing: Easing.out(Easing.quad), useNativeDriver: true, }).start(); const isDestructive = variant === "destructive"; return ( { setFocused(true); animateTo(1.05); }} onBlur={() => { setFocused(false); animateTo(1); }} hasTVPreferredFocus={hasTVPreferredFocus} > {label} ); }; export default function TVAccountActionModalPage() { const typography = useScaledTVTypography(); const router = useRouter(); const modalState = useAtomValue(tvAccountActionModalAtom); const { t } = useTranslation(); const [isReady, setIsReady] = useState(false); const overlayOpacity = useRef(new Animated.Value(0)).current; const sheetTranslateY = useRef(new Animated.Value(200)).current; // Animate in on mount useEffect(() => { overlayOpacity.setValue(0); sheetTranslateY.setValue(200); Animated.parallel([ Animated.timing(overlayOpacity, { toValue: 1, duration: 250, easing: Easing.out(Easing.quad), useNativeDriver: true, }), Animated.timing(sheetTranslateY, { toValue: 0, duration: 300, easing: Easing.out(Easing.cubic), useNativeDriver: true, }), ]).start(); const timer = setTimeout(() => setIsReady(true), 100); return () => { clearTimeout(timer); store.set(tvAccountActionModalAtom, null); }; }, [overlayOpacity, sheetTranslateY]); const handleLogin = () => { modalState?.onLogin(); router.back(); }; const handleDelete = () => { modalState?.onDelete(); router.back(); }; if (!modalState) { return null; } return ( {/* Account username as title */} {modalState.account.username} {/* Server name as subtitle */} {modalState.server.name || modalState.server.address} {/* Horizontal options */} {isReady && ( )} ); }