mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-16 10:19:40 +02:00
- Restructure chat as apps/mobile, apps/web, apps/landing, backend - Add NestJS backend for secure Azure OpenAI API calls - Remove exposed API key from mobile app (security fix) - Add shared chat-types package - Create SvelteKit web app scaffold - Create Astro landing page scaffold - Update pnpm workspace configuration - Add project-level CLAUDE.md documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
103 lines
No EOL
2.6 KiB
TypeScript
103 lines
No EOL
2.6 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { View, Text, StyleSheet, Animated, Easing } from 'react-native';
|
|
import { useTheme } from '@react-navigation/native';
|
|
|
|
type TypingIndicatorProps = {
|
|
dotCount?: number;
|
|
dotSize?: number;
|
|
dotColor?: string;
|
|
style?: any;
|
|
};
|
|
|
|
export default function TypingIndicator({
|
|
dotCount = 3,
|
|
dotSize = 8,
|
|
dotColor,
|
|
style,
|
|
}: TypingIndicatorProps) {
|
|
const { colors } = useTheme();
|
|
const [animations] = useState(() =>
|
|
Array.from({ length: dotCount }).map(() => new Animated.Value(0))
|
|
);
|
|
|
|
// Dotfarbe wird entweder von Prop oder vom Theme übernommen
|
|
const actualDotColor = dotColor || colors.text;
|
|
|
|
useEffect(() => {
|
|
// Animiere jeden Punkt mit einer Verzögerung
|
|
const animateDots = () => {
|
|
const animationSequence = animations.map((anim, i) =>
|
|
Animated.sequence([
|
|
// Verzögerung für jeden Punkt
|
|
Animated.delay(i * 150),
|
|
// Animation nach oben
|
|
Animated.timing(anim, {
|
|
toValue: 1,
|
|
duration: 400,
|
|
easing: Easing.inOut(Easing.ease),
|
|
useNativeDriver: true,
|
|
}),
|
|
// Animation zurück nach unten
|
|
Animated.timing(anim, {
|
|
toValue: 0,
|
|
duration: 400,
|
|
easing: Easing.inOut(Easing.ease),
|
|
useNativeDriver: true,
|
|
}),
|
|
// Verzögerung am Ende
|
|
Animated.delay((dotCount - i - 1) * 150),
|
|
])
|
|
);
|
|
|
|
// Starte alle Animationen parallel und in einer Schleife
|
|
Animated.loop(Animated.parallel(animationSequence)).start();
|
|
};
|
|
|
|
animateDots();
|
|
|
|
// Cleanup beim Unmount
|
|
return () => {
|
|
animations.forEach(anim => anim.stopAnimation());
|
|
};
|
|
}, [animations, dotCount]);
|
|
|
|
return (
|
|
<View style={[styles.container, style]}>
|
|
{animations.map((anim, index) => (
|
|
<Animated.View
|
|
key={index}
|
|
style={[
|
|
styles.dot,
|
|
{
|
|
width: dotSize,
|
|
height: dotSize,
|
|
backgroundColor: actualDotColor,
|
|
borderRadius: dotSize / 2,
|
|
marginHorizontal: dotSize / 3,
|
|
transform: [
|
|
{
|
|
translateY: anim.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [0, -dotSize],
|
|
}),
|
|
},
|
|
],
|
|
},
|
|
]}
|
|
/>
|
|
))}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
padding: 10,
|
|
},
|
|
dot: {
|
|
opacity: 0.6,
|
|
},
|
|
}); |