Matthias Nott
2026-03-15 e25bdba29f49b1b55a8a8cccdc4583aea3c101ed
app/chat.tsx
....@@ -30,7 +30,7 @@
3030 const [isTextMode, setIsTextMode] = useState(false);
3131 const [showSessions, setShowSessions] = useState(false);
3232 const [audioPlaying, setAudioPlaying] = useState(false);
33
- const [stagedImage, setStagedImage] = useState<StagedImage | null>(null);
33
+ const [stagedImages, setStagedImages] = useState<StagedImage[]>([]);
3434
3535 useEffect(() => {
3636 return onPlayingChange((uri) => setAudioPlaying(uri !== null));
....@@ -59,17 +59,19 @@
5959 clearMessages();
6060 }, [clearMessages]);
6161
62
- // Resolve a picked asset into a StagedImage
63
- const stageAsset = useCallback(async (asset: { base64?: string | null; uri: string; mimeType?: string | null }) => {
64
- const mimeType = asset.mimeType ?? (asset.uri.endsWith(".png") ? "image/png" : "image/jpeg");
65
- let base64 = asset.base64 ?? "";
66
- if (!base64 && asset.uri) {
67
- const { readAsStringAsync } = await import("expo-file-system/legacy");
68
- base64 = await readAsStringAsync(asset.uri, { encoding: "base64" });
62
+ // Resolve picked assets into StagedImage array
63
+ const stageAssets = useCallback(async (assets: Array<{ base64?: string | null; uri: string; mimeType?: string | null }>) => {
64
+ const staged: StagedImage[] = [];
65
+ for (const asset of assets) {
66
+ const mimeType = asset.mimeType ?? (asset.uri.endsWith(".png") ? "image/png" : "image/jpeg");
67
+ let base64 = asset.base64 ?? "";
68
+ if (!base64 && asset.uri) {
69
+ const { readAsStringAsync } = await import("expo-file-system/legacy");
70
+ base64 = await readAsStringAsync(asset.uri, { encoding: "base64" });
71
+ }
72
+ if (base64) staged.push({ base64, uri: asset.uri, mimeType });
6973 }
70
- if (base64) {
71
- setStagedImage({ base64, uri: asset.uri, mimeType });
72
- }
74
+ if (staged.length > 0) setStagedImages(staged);
7375 }, []);
7476
7577 const pickFromLibrary = useCallback(async () => {
....@@ -82,15 +84,17 @@
8284 }
8385 const result = await ImagePicker.launchImageLibraryAsync({
8486 mediaTypes: ["images"],
87
+ allowsMultipleSelection: true,
88
+ selectionLimit: 10,
8589 quality: 0.7,
8690 base64: true,
8791 });
88
- if (result.canceled || !result.assets?.[0]) return;
89
- await stageAsset(result.assets[0]);
92
+ if (result.canceled || !result.assets?.length) return;
93
+ await stageAssets(result.assets);
9094 } catch (err: any) {
9195 Alert.alert("Image Error", err?.message ?? String(err));
9296 }
93
- }, [stageAsset]);
97
+ }, [stageAssets]);
9498
9599 const pickFromCamera = useCallback(async () => {
96100 try {
....@@ -105,11 +109,11 @@
105109 base64: true,
106110 });
107111 if (result.canceled || !result.assets?.[0]) return;
108
- await stageAsset(result.assets[0]);
112
+ await stageAssets([result.assets[0]]);
109113 } catch (err: any) {
110114 Alert.alert("Camera Error", err?.message ?? String(err));
111115 }
112
- }, [stageAsset]);
116
+ }, [stageAssets]);
113117
114118 const handlePickImage = useCallback(() => {
115119 if (Platform.OS === "ios") {
....@@ -131,11 +135,14 @@
131135
132136 const handleImageSend = useCallback(
133137 (caption: string) => {
134
- if (!stagedImage) return;
135
- sendImageMessage(stagedImage.base64, caption, stagedImage.mimeType);
136
- setStagedImage(null);
138
+ if (stagedImages.length === 0) return;
139
+ // Send each image as a separate message; caption on the first only
140
+ stagedImages.forEach((img, i) => {
141
+ sendImageMessage(img.base64, i === 0 ? caption : "", img.mimeType);
142
+ });
143
+ setStagedImages([]);
137144 },
138
- [stagedImage, sendImageMessage],
145
+ [stagedImages, sendImageMessage],
139146 );
140147
141148 const handleReplay = useCallback(async () => {
....@@ -340,10 +347,10 @@
340347
341348 {/* Image caption modal — WhatsApp-style full-screen preview */}
342349 <ImageCaptionModal
343
- visible={!!stagedImage}
344
- imageUri={stagedImage ? `data:${stagedImage.mimeType};base64,${stagedImage.base64}` : ""}
350
+ visible={stagedImages.length > 0}
351
+ images={stagedImages.map((img) => ({ uri: `data:${img.mimeType};base64,${img.base64}` }))}
345352 onSend={handleImageSend}
346
- onCancel={() => setStagedImage(null)}
353
+ onCancel={() => setStagedImages([])}
347354 />
348355
349356 {/* Session drawer — absolute overlay outside KAV */}