import React, { useEffect } from "react"; import { View, Dimensions, Pressable } from "react-native"; import { Text } from "@/components/ui/text"; import { useToast, ToastType } from "@/lib/toast-store"; import { CheckCircle2, AlertCircle, AlertTriangle, Lightbulb, X, } from "@/lib/icons"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import Animated, { useSharedValue, useAnimatedStyle, withSpring, withTiming, runOnJS, } from "react-native-reanimated"; import { Gesture, GestureDetector } from "react-native-gesture-handler"; const { width: SCREEN_WIDTH } = Dimensions.get("window"); const SWIPE_THRESHOLD = SCREEN_WIDTH * 0.35; const TOAST_VARIANTS: Record< ToastType, { accent: string; iconBg: string; icon: React.ReactNode; } > = { success: { accent: "#16a34a", iconBg: "#16a34a15", icon: , }, info: { accent: "#E46212", iconBg: "#E4621215", icon: , }, warning: { accent: "#d97706", iconBg: "#d9770615", icon: , }, error: { accent: "#dc2626", iconBg: "#dc262615", icon: , }, }; export function Toast() { const { visible, type, title, message, hide, duration } = useToast(); const insets = useSafeAreaInsets(); const opacity = useSharedValue(0); const scale = useSharedValue(0.85); const translateY = useSharedValue(-60); const translateX = useSharedValue(0); useEffect(() => { if (visible) { opacity.value = withTiming(1, { duration: 200 }); scale.value = withSpring(1, { damping: 14, stiffness: 160 }); translateY.value = withSpring(0, { damping: 16, stiffness: 140 }); translateX.value = 0; const timer = setTimeout(handleHide, duration); return () => clearTimeout(timer); } }, [visible]); const handleHide = () => { opacity.value = withTiming(0, { duration: 180 }); scale.value = withTiming(0.92, { duration: 180 }); translateY.value = withTiming(-40, { duration: 180 }, () => { runOnJS(hide)(); }); }; const swipeGesture = Gesture.Pan() .onUpdate((event) => { translateX.value = event.translationX; }) .onEnd((event) => { if (Math.abs(event.translationX) > SWIPE_THRESHOLD) { translateX.value = withTiming( event.translationX > 0 ? SCREEN_WIDTH : -SCREEN_WIDTH, { duration: 200 }, () => runOnJS(handleHide)(), ); } else { translateX.value = withSpring(0); } }); const animatedStyle = useAnimatedStyle(() => ({ opacity: opacity.value, transform: [ { translateY: translateY.value }, { translateX: translateX.value }, { scale: scale.value }, ], })); if (!visible) return null; const variant = TOAST_VARIANTS[type]; return ( {variant.icon} {title} {message ? ( {message} ) : null} ); }