1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
| | import React, { useEffect, useRef } from "react";
| | import { Animated, View } from "react-native";
| | import { useTheme } from "../../contexts/ThemeContext";
| |
| | export function TypingIndicator() {
| | const { colors, isDark } = useTheme();
| | const dot1 = useRef(new Animated.Value(0)).current;
| | const dot2 = useRef(new Animated.Value(0)).current;
| | const dot3 = useRef(new Animated.Value(0)).current;
| |
| | useEffect(() => {
| | const animate = (dot: Animated.Value, delay: number) =>
| | Animated.loop(
| | Animated.sequence([
| | Animated.delay(delay),
| | Animated.timing(dot, { toValue: 1, duration: 300, useNativeDriver: true }),
| | Animated.timing(dot, { toValue: 0, duration: 300, useNativeDriver: true }),
| | ])
| | );
| |
| | const a1 = animate(dot1, 0);
| | const a2 = animate(dot2, 200);
| | const a3 = animate(dot3, 400);
| | a1.start();
| | a2.start();
| | a3.start();
| |
| | return () => { a1.stop(); a2.stop(); a3.stop(); };
| | }, [dot1, dot2, dot3]);
| |
| | const bubbleBg = isDark ? "#252538" : colors.bgSecondary;
| | const dotColor = colors.textMuted;
| |
| | return (
| | <View
| | style={{
| | flexDirection: "row",
| | marginVertical: 4,
| | paddingHorizontal: 12,
| | justifyContent: "flex-start",
| | }}
| | >
| | <View
| | style={{
| | borderRadius: 16,
| | borderTopLeftRadius: 4,
| | paddingHorizontal: 16,
| | paddingVertical: 14,
| | backgroundColor: bubbleBg,
| | flexDirection: "row",
| | gap: 4,
| | alignItems: "center",
| | }}
| | >
| | {[dot1, dot2, dot3].map((dot, i) => (
| | <Animated.View
| | key={i}
| | style={{
| | width: 8,
| | height: 8,
| | borderRadius: 4,
| | backgroundColor: dotColor,
| | opacity: dot.interpolate({ inputRange: [0, 1], outputRange: [0.3, 1] }),
| | transform: [
| | {
| | translateY: dot.interpolate({
| | inputRange: [0, 1],
| | outputRange: [0, -4],
| | }),
| | },
| | ],
| | }}
| | />
| | ))}
| | </View>
| | </View>
| | );
| | }
|
|