Matthias Nott
2026-03-07 d97d98a524d2b2dd56c718a806eecab0b3270c88
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>
  );
}