diff --git a/android/src/components/MessageText.tsx b/android/src/components/MessageText.tsx index cba5842..dd4b261 100644 --- a/android/src/components/MessageText.tsx +++ b/android/src/components/MessageText.tsx @@ -1,25 +1,76 @@ /** - * MessageText — selektierbarer Chat-Text mit Android-Auto-Linkifizierung. + * MessageText — selektierbarer Chat-Text mit Android-Auto-Linkifizierung, + * plus Inline-Image-Rendering wenn der Text Bild-URLs enthaelt. * - * Wir nutzen Androids dataDetectorType="all" (System macht Phone/URL/Email - * automatisch klickbar) und ein einzelnes ohne nested - * mit eigenem onPress. Nested Text mit onPress fingen die Long-Press- - * Geste ab, damit war Markieren+Kopieren defekt. + * - Markdown-Syntax `![alt](url)` und plain `https://...image.png` werden + * erkannt — die URL bleibt im Text sichtbar (klickbar via Linkify), + * zusaetzlich wird das Bild als drunter gerendert. + * - Wir nutzen Androids dataDetectorType="all" (System macht Phone/URL/Email + * automatisch klickbar) und ein einzelnes ohne nested + * mit eigenem onPress — Nested Text mit onPress fing die Long-Press- + * Geste ab, damit war Markieren+Kopieren defekt. + * - SVGs werden uebersprungen (React Native Image kann SVG nicht ohne Lib). */ -import React from 'react'; -import { Text, TextStyle, StyleProp } from 'react-native'; +import React, { useEffect, useState } from 'react'; +import { View, Text, Image, TextStyle, StyleProp } from 'react-native'; interface Props { text: string; style?: StyleProp; } -const MessageText: React.FC = ({ text, style }) => { +// Bild-URL-Pattern: http(s)://... endend auf jpg/png/gif/webp/bmp/ico (kein +// SVG — das kann RN Image ohne externe Lib nicht). +const IMG_URL_RE = /https?:\/\/[^\s)<"']+\.(?:jpe?g|png|gif|webp|bmp|ico)(?:\?[^\s)<"']*)?/gi; + +function extractImageUrls(text: string): string[] { + const urls = new Set(); + const matches = text.match(IMG_URL_RE); + if (matches) matches.forEach(u => urls.add(u)); + return Array.from(urls); +} + +/** Image mit dynamischer Aspect-Ratio aus echten Bilddimensionen. */ +const InlineImage: React.FC<{ uri: string }> = ({ uri }) => { + const [aspectRatio, setAspectRatio] = useState(16 / 9); + const [failed, setFailed] = useState(false); + useEffect(() => { + let cancelled = false; + Image.getSize( + uri, + (w, h) => { if (!cancelled && w > 0 && h > 0) setAspectRatio(Math.max(0.5, Math.min(2.5, w / h))); }, + () => { if (!cancelled) setFailed(true); }, + ); + return () => { cancelled = true; }; + }, [uri]); + if (failed) return null; return ( - - {text} - + setFailed(true)} + /> + ); +}; + +const MessageText: React.FC = ({ text, style }) => { + const imageUrls = extractImageUrls(text || ''); + if (imageUrls.length === 0) { + return ( + + {text} + + ); + } + return ( + + + {text} + + {imageUrls.map(u => )} + ); };