Matthias Nott
2026-03-15 e25bdba29f49b1b55a8a8cccdc4583aea3c101ed
components/chat/ImageCaptionModal.tsx
....@@ -1,6 +1,7 @@
11 import React, { useEffect, useRef, useState } from "react";
22 import {
33 Dimensions,
4
+ FlatList,
45 Image,
56 KeyboardAvoidingView,
67 Modal,
....@@ -12,22 +13,28 @@
1213 } from "react-native";
1314 import { useTheme } from "../../contexts/ThemeContext";
1415
16
+interface ImageItem {
17
+ uri: string;
18
+}
19
+
1520 interface ImageCaptionModalProps {
1621 visible: boolean;
17
- imageUri: string;
22
+ images: ImageItem[];
1823 onSend: (caption: string) => void;
1924 onCancel: () => void;
2025 }
2126
22
-export function ImageCaptionModal({ visible, imageUri, onSend, onCancel }: ImageCaptionModalProps) {
27
+export function ImageCaptionModal({ visible, images, onSend, onCancel }: ImageCaptionModalProps) {
2328 const { colors } = useTheme();
2429 const [caption, setCaption] = useState("");
30
+ const [selectedIndex, setSelectedIndex] = useState(0);
2531 const inputRef = useRef<TextInput>(null);
2632 const { width, height } = Dimensions.get("window");
2733
2834 useEffect(() => {
2935 if (visible) {
3036 setCaption("");
37
+ setSelectedIndex(0);
3138 setTimeout(() => inputRef.current?.focus(), 300);
3239 }
3340 }, [visible]);
....@@ -37,6 +44,9 @@
3744 setCaption("");
3845 };
3946
47
+ const currentImage = images[selectedIndex]?.uri ?? "";
48
+ const isMultiple = images.length > 1;
49
+
4050 return (
4151 <Modal visible={visible} animationType="slide" transparent={false} onRequestClose={onCancel}>
4252 <View style={{ flex: 1, backgroundColor: "#000" }}>
....@@ -45,7 +55,7 @@
4555 behavior={Platform.OS === "ios" ? "padding" : undefined}
4656 keyboardVerticalOffset={0}
4757 >
48
- {/* Top bar with cancel */}
58
+ {/* Top bar with cancel + count */}
4959 <View
5060 style={{
5161 paddingTop: 54,
....@@ -68,17 +78,48 @@
6878 >
6979 <Text style={{ color: "#fff", fontSize: 16, fontWeight: "600" }}>Cancel</Text>
7080 </Pressable>
81
+ {isMultiple && (
82
+ <Text style={{ color: "rgba(255,255,255,0.6)", fontSize: 14 }}>
83
+ {selectedIndex + 1} / {images.length}
84
+ </Text>
85
+ )}
7186 </View>
7287
7388 {/* Image preview */}
7489 <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
7590 <Image
76
- source={{ uri: imageUri }}
77
- style={{ width, height: height * 0.55 }}
91
+ source={{ uri: currentImage }}
92
+ style={{ width, height: isMultiple ? height * 0.45 : height * 0.55 }}
7893 resizeMode="contain"
7994 />
8095 </View>
8196
97
+ {/* Thumbnail strip — only for multiple images */}
98
+ {isMultiple && (
99
+ <FlatList
100
+ data={images}
101
+ horizontal
102
+ showsHorizontalScrollIndicator={false}
103
+ keyExtractor={(_, i) => String(i)}
104
+ contentContainerStyle={{ paddingHorizontal: 12, paddingVertical: 8, gap: 8 }}
105
+ renderItem={({ item, index }) => (
106
+ <Pressable onPress={() => setSelectedIndex(index)}>
107
+ <Image
108
+ source={{ uri: item.uri }}
109
+ style={{
110
+ width: 56,
111
+ height: 56,
112
+ borderRadius: 8,
113
+ borderWidth: index === selectedIndex ? 2 : 0,
114
+ borderColor: colors.accent,
115
+ }}
116
+ resizeMode="cover"
117
+ />
118
+ </Pressable>
119
+ )}
120
+ />
121
+ )}
122
+
82123 {/* Caption input + send */}
83124 <View
84125 style={{