import { useEffect, useRef } from 'react'; import { Animated, Easing, Text, View } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import type { ColorScheme } from '../lib/theme'; /** * Custom-Spotlight für den interaktiven Onboarding-Flow (statt react-native-copilot). * Zwei wiederverwendbare Bausteine: * * - * Orange Bubble + Arrow-Down (zeigt auf das nächste UI-Element). * Fade-in + Spring-Bounce beim Mount. * * - {children} * Pulsierender Border-Glow um das Target. Wenn active=false → Children * werden ohne Extra-View durchgereicht (kein Layout-Shift). * * Genutzt in: * - app/profile/edit.tsx (Stage 2: Nickname) * - app/(app)/blocker.tsx (Stage 3: Schutz aktivieren) */ export function OnboardingTooltip({ text, colors, arrowOffset = 22, }: { text: string; colors: ColorScheme; /** X-Offset des Pfeils unten in px (Default 22 = links-aligned mit etwas Indent). */ arrowOffset?: number; }) { const opacity = useRef(new Animated.Value(0)).current; const translateY = useRef(new Animated.Value(-6)).current; useEffect(() => { Animated.parallel([ Animated.timing(opacity, { toValue: 1, duration: 350, useNativeDriver: true, easing: Easing.out(Easing.cubic), }), Animated.spring(translateY, { toValue: 0, useNativeDriver: true, friction: 6, tension: 80, }), ]).start(); }, []); return ( {text} {/* Arrow-Down */} ); } export function OnboardingGlow({ active, colors, children, radius = 14, }: { active: boolean; colors: ColorScheme; children: React.ReactNode; /** Border-Radius. Default 14. Anpassen wenn das Target eckiger/runder ist. */ radius?: number; }) { const pulse = useRef(new Animated.Value(0)).current; useEffect(() => { if (!active) return; const loop = Animated.loop( Animated.sequence([ Animated.timing(pulse, { toValue: 1, duration: 1200, useNativeDriver: false, easing: Easing.inOut(Easing.cubic), }), Animated.timing(pulse, { toValue: 0, duration: 1200, useNativeDriver: false, easing: Easing.inOut(Easing.cubic), }), ]), ); loop.start(); return () => loop.stop(); }, [active]); if (!active) return <>{children}; const borderColor = pulse.interpolate({ inputRange: [0, 1], outputRange: ['rgba(0,122,255,0.35)', 'rgba(0,122,255,0.95)'], }) as unknown as string; const shadowOpacity = pulse.interpolate({ inputRange: [0, 1], outputRange: [0.15, 0.45], }) as unknown as number; return ( {children} ); }