import { useEffect, useRef, useState } from 'react'; import { Animated, Easing, Text, TouchableOpacity, View } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import i18n from '../../lib/i18n'; import { useTranslation } from 'react-i18next'; import { RiveAvatar, type Emotion } from '../RiveAvatar'; import { useColors } from '../../lib/theme'; import { playLyraSpeech, stopLyraSpeech, type LyraSpeechStatus, } from '../../lib/lyraSpeech'; /** * Lyra-Mascot (animiertes Rive-Avatar) links + Speech-Bubble rechts. * * Plus: kleiner Audio-Button rechts oben in der Bubble — User kann den Lyra- * Text via TTS hören lassen. DiGA-Accessibility-relevant (Screen-Reader- * Alternative + Lese-Hürden-Mitigation). * * Fade+slide-in beim Mount und bei text-change (key-prop verwenden für Re-Animate). */ export function LyraBubble({ text, emotion = 'idle', }: { text: string; emotion?: Emotion; }) { const { t } = useTranslation(); const colors = useColors(); const opacity = useRef(new Animated.Value(0)).current; const translateX = useRef(new Animated.Value(-12)).current; const [speech, setSpeech] = useState('idle'); useEffect(() => { opacity.setValue(0); translateX.setValue(-12); Animated.parallel([ Animated.timing(opacity, { toValue: 1, duration: 400, useNativeDriver: true, easing: Easing.out(Easing.cubic), }), Animated.spring(translateX, { toValue: 0, useNativeDriver: true, friction: 7, tension: 80, }), ]).start(); }, [text, opacity, translateX]); // Cleanup beim Unmount — sonst spielt der Sound weiter wenn User Slide wechselt useEffect(() => { return () => { void stopLyraSpeech(); }; }, []); // Stoppt auch wenn der Bubble-Text wechselt (= neuer Slide angezeigt) useEffect(() => { setSpeech('idle'); void stopLyraSpeech(); }, [text]); async function togglePlayback() { if (speech === 'playing' || speech === 'loading') { await stopLyraSpeech(); setSpeech('idle'); return; } await playLyraSpeech(text, i18n.language || 'de', setSpeech); } const a11yLabel = speech === 'playing' ? t('onboarding.lyra.audio_stop') : speech === 'loading' ? t('onboarding.lyra.audio_loading') : t('onboarding.lyra.audio_play'); return ( {text} {/* Audio-Button rechts oben — kompakt, dezent */} ); }