diff --git a/android/src/screens/ChatScreen.tsx b/android/src/screens/ChatScreen.tsx index be480cd..b254f64 100644 --- a/android/src/screens/ChatScreen.tsx +++ b/android/src/screens/ChatScreen.tsx @@ -201,6 +201,7 @@ const ChatScreen: React.FC = () => { const [fullscreenImage, setFullscreenImage] = useState(null); const [searchQuery, setSearchQuery] = useState(''); const [searchVisible, setSearchVisible] = useState(false); + const [searchIndex, setSearchIndex] = useState(0); // welcher Treffer aktiv ist const [pendingAttachments, setPendingAttachments] = useState<{file: any, isPhoto: boolean}[]>([]); const [agentActivity, setAgentActivity] = useState<{activity: string, tool: string}>({activity: 'idle', tool: ''}); // Service-Status (Gamebox: F5-TTS / Whisper Lade-Status) + Banner-Sichtbarkeit @@ -892,6 +893,43 @@ const ChatScreen: React.FC = () => { // Inverted FlatList: neueste Nachrichten unten, kein manuelles Scrollen noetig const invertedMessages = useMemo(() => [...messages].reverse(), [messages]); + // Such-Treffer: alle Message-IDs die zur Query passen, in chronologischer + // Reihenfolge (aelteste zuerst). Bei Query-Change resetten wir den Index. + const searchMatchIds = useMemo(() => { + const q = searchQuery.trim().toLowerCase(); + if (!q) return [] as string[]; + return messages + .filter(m => (m.text || '').toLowerCase().includes(q)) + .map(m => m.id); + }, [messages, searchQuery]); + + useEffect(() => { + setSearchIndex(0); + }, [searchQuery]); + + // Bei Index-Wechsel zu der entsprechenden Bubble scrollen + useEffect(() => { + if (!searchMatchIds.length) return; + const id = searchMatchIds[searchIndex]; + if (!id) return; + // invertedMessages → index in der angezeigten Liste finden + const idx = invertedMessages.findIndex(m => m.id === id); + if (idx < 0 || !flatListRef.current) return; + try { + flatListRef.current.scrollToIndex({ index: idx, animated: true, viewPosition: 0.4 }); + } catch {} + }, [searchIndex, searchMatchIds, invertedMessages]); + + const activeSearchId = searchMatchIds[searchIndex] || ''; + const gotoSearchPrev = () => { + if (!searchMatchIds.length) return; + setSearchIndex(i => (i - 1 + searchMatchIds.length) % searchMatchIds.length); + }; + const gotoSearchNext = () => { + if (!searchMatchIds.length) return; + setSearchIndex(i => (i + 1) % searchMatchIds.length); + }; + // GPS-Position holen (optional) const getCurrentLocation = useCallback((): Promise<{ lat: number; lon: number } | null> => { if (!gpsEnabled) { @@ -1143,12 +1181,16 @@ const ChatScreen: React.FC = () => { hour: '2-digit', minute: '2-digit', }); + const isSearchHit = activeSearchId === item.id; + const searchHighlightStyle = isSearchHit + ? { borderWidth: 2, borderColor: '#FFD60A' } + : null; // Spezial-Bubble: ARIA hat einen Skill erstellt if (item.skillCreated) { const s = item.skillCreated; return ( - + {'🛠 ARIA hat einen neuen Skill erstellt'} @@ -1168,7 +1210,7 @@ const ChatScreen: React.FC = () => { } return ( - + {/* Anhang-Vorschau */} {item.attachments?.map((att, idx) => ( @@ -1342,7 +1384,7 @@ const ChatScreen: React.FC = () => { ); })()} - {/* Suchleiste */} + {/* Suchleiste mit Treffer-Navigation */} {searchVisible && ( { placeholderTextColor="#555570" autoFocus /> + {searchQuery ? ( + + {searchMatchIds.length ? `${searchIndex + 1}/${searchMatchIds.length}` : '0/0'} + + ) : null} + + {'▲'} + + + {'▼'} + { setSearchVisible(false); setSearchQuery(''); }}> X )} - {/* Nachrichtenliste */} + {/* Nachrichtenliste — Suche FILTERT NICHT mehr, sondern hebt aktiven + Treffer hervor (siehe renderMessage: activeSearchId-Border). */} m.text.toLowerCase().includes(searchQuery.toLowerCase())).reverse() : invertedMessages} + data={invertedMessages} + onScrollToIndexFailed={(info) => { + // Bei zu schnellem Aufruf vor Layout: einmal nachfassen + setTimeout(() => { + try { flatListRef.current?.scrollToIndex({ index: info.index, animated: true, viewPosition: 0.4 }); } catch {} + }, 200); + }} keyExtractor={item => item.id} renderItem={renderMessage} contentContainerStyle={styles.messageList} diff --git a/diagnostic/index.html b/diagnostic/index.html index 55f31a2..16bfc57 100644 --- a/diagnostic/index.html +++ b/diagnostic/index.html @@ -470,14 +470,9 @@
-
-

Sprachausgabe

-
- - - -
-
+

Sprachausgabe

+ +
@@ -546,9 +541,17 @@
- +
+ + + +