diff --git a/android/src/screens/ChatScreen.tsx b/android/src/screens/ChatScreen.tsx index 8533e94..412988c 100644 --- a/android/src/screens/ChatScreen.tsx +++ b/android/src/screens/ChatScreen.tsx @@ -1425,6 +1425,11 @@ const ChatScreen: React.FC = () => { // Den aktuellen Snapshot von invertedMessages holen wir via Ref. const invertedMessagesRef = useRef(invertedMessages); invertedMessagesRef.current = invertedMessages; + // Schaetzwert fuer durchschnittliche Bubble-Hoehe. Wird fuer den + // ersten Pre-Scroll genutzt wenn FlatList noch keine Layouts hat. + // 150 px ist Mittel zwischen kurzen Voice-Bubbles (~70) und langen + // ARIA-Antworten (~250). + const AVG_BUBBLE_HEIGHT = 150; useEffect(() => { if (!searchMatchIds.length) { lastSearchScrollKey.current = ''; @@ -1442,12 +1447,27 @@ const ChatScreen: React.FC = () => { clearPendingScrollRetry(); const idx = invertedMessagesRef.current.findIndex(m => m.id === id); if (idx < 0 || !flatListRef.current) return; + // Pre-Scroll: erst grob mit konstanter Hoehe in die Naehe springen. + // Damit hat FlatList ueberhaupt die Chance die Layouts der Bubbles in + // der Naehe zu rendern — sonst basiert info.averageItemLength im + // Failed-Handler nur auf den ersten 10 Items (Default initialNumToRender) + // und liefert beim ersten Such-Versuch nach App-Start einen voellig + // falschen Sprung. + try { + flatListRef.current?.scrollToOffset({ + offset: idx * AVG_BUBBLE_HEIGHT, + animated: false, + }); + } catch {} + // Nach kurzer Render-Pause praezise nachsetzen requestAnimationFrame(() => { - try { - flatListRef.current?.scrollToIndex({ index: idx, animated: true, viewPosition: 0 }); - } catch { - // onScrollToIndexFailed-Handler uebernimmt den Fallback - } + setTimeout(() => { + try { + flatListRef.current?.scrollToIndex({ index: idx, animated: true, viewPosition: 0 }); + } catch { + // onScrollToIndexFailed-Handler uebernimmt den Fallback + } + }, 80); }); }, [searchIndex, searchMatchIds]); @@ -2159,6 +2179,13 @@ const ChatScreen: React.FC = () => { ref={flatListRef} inverted data={invertedMessages} + // Mehr Items beim Mount messen → bessere averageItemLength fuer + // Such-Sprung gleich nach App-Start. Default sind 10 Items, das + // ist bei 300+ Bubbles im Backup viel zu wenig. + initialNumToRender={30} + // Mehr Items im Speicher halten (Default 21 = 10 oben + 10 unten). + // Macht scroll-to-far-away weniger anfaellig fuer Layout-Holes. + windowSize={41} onScroll={(e) => { // Bei inverted FlatList: contentOffset.y > 0 = weg von "unten" // (= aelter scrollen). Wir zeigen den Jump-Down-Button ab ~250px.