Matthias Nott
2026-03-08 d832f656599b153be8826bc43e1832209b2a1bf6
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
79
80
81
82
83
84
85
86
87
88
import React, { useEffect, useRef } from "react";
import { Animated, Pressable, Text, View } from "react-native";
import { useTheme } from "../../contexts/ThemeContext";
interface IncomingToastProps {
  sessionName: string;
  preview: string;
  onTap: () => void;
  onDismiss: () => void;
}
const DISPLAY_MS = 4000;
export function IncomingToast({ sessionName, preview, onTap, onDismiss }: IncomingToastProps) {
  const { colors } = useTheme();
  const opacity = useRef(new Animated.Value(0)).current;
  const translateY = useRef(new Animated.Value(-40)).current;
  useEffect(() => {
    // Slide in
    Animated.parallel([
      Animated.timing(opacity, { toValue: 1, duration: 200, useNativeDriver: true }),
      Animated.timing(translateY, { toValue: 0, duration: 200, useNativeDriver: true }),
    ]).start();
    // Auto-dismiss
    const timer = setTimeout(() => {
      Animated.parallel([
        Animated.timing(opacity, { toValue: 0, duration: 200, useNativeDriver: true }),
        Animated.timing(translateY, { toValue: -40, duration: 200, useNativeDriver: true }),
      ]).start(() => onDismiss());
    }, DISPLAY_MS);
    return () => clearTimeout(timer);
  }, []);
  return (
    <Animated.View
      style={{
        position: "absolute",
        top: 0,
        left: 12,
        right: 12,
        zIndex: 100,
        opacity,
        transform: [{ translateY }],
      }}
    >
      <Pressable
        onPress={onTap}
        style={({ pressed }) => ({
          flexDirection: "row",
          alignItems: "center",
          gap: 10,
          paddingHorizontal: 14,
          paddingVertical: 10,
          borderRadius: 12,
          backgroundColor: pressed ? colors.bgTertiary : colors.bgSecondary,
          borderWidth: 1,
          borderColor: colors.border,
          shadowColor: "#000",
          shadowOffset: { width: 0, height: 2 },
          shadowOpacity: 0.25,
          shadowRadius: 4,
          elevation: 5,
        })}
      >
        <View
          style={{
            width: 8,
            height: 8,
            borderRadius: 4,
            backgroundColor: "#3b82f6",
          }}
        />
        <View style={{ flex: 1 }}>
          <Text style={{ color: colors.text, fontSize: 13, fontWeight: "700" }} numberOfLines={1}>
            {sessionName}
          </Text>
          <Text style={{ color: colors.textMuted, fontSize: 12, marginTop: 1 }} numberOfLines={1}>
            {preview}
          </Text>
        </View>
        <Text style={{ color: colors.textMuted, fontSize: 11 }}>tap to switch</Text>
      </Pressable>
    </Animated.View>
  );
}