From 61cf8e3bcc7c5f95eba57a23132a1ed28317f04d Mon Sep 17 00:00:00 2001 From: duffyduck Date: Fri, 15 May 2026 12:10:13 +0200 Subject: [PATCH] fix(chat): Such-Sprung beim ersten Versuch nach App-Start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Symptom: Suchbegriff direkt nach App-Start eingegeben → springt an falsche Stelle. Erst beim zweiten Versuch funktioniert es. Ursache: FlatList rendert per Default nur 10 Items initial. info.averageItemLength im onScrollToIndexFailed basiert nur auf diesen 10 — bei einem Suchtreffer auf Bubble 150 ist die Schaetzung katastrophal falsch. Beim zweiten Versuch ist die FlatList „warm gelaufen" und mehr Items sind gemessen → Schaetzung passt besser. Drei kombinierte Fixes: 1) Pre-Scroll: vor dem scrollToIndex erst grob mit AVG_BUBBLE_HEIGHT=150 per scrollToOffset(idx*150) in die Naehe springen. FlatList rendert die Bubbles in der Naehe, dann praezise nachsetzen nach 80ms. 2) initialNumToRender=30 (Default 10) — mehr Items beim Mount gemessen. 3) windowSize=41 (Default 21) — mehr Items im Speicher gehalten, weniger Layout-Holes beim Weit-Scroll. Kosten: minimal hoehere Mount-Zeit. Bei 300+ Bubbles im Backup macht sich der UX-Gewinn lohnt. Co-Authored-By: Claude Opus 4.7 (1M context) --- android/src/screens/ChatScreen.tsx | 37 ++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) 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.