fix(chat): Such-Sprung beim ersten Versuch nach App-Start
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) <noreply@anthropic.com>
This commit is contained in:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user