Compare commits

..

2 Commits

Author SHA1 Message Date
duffyduck 22adc91c1e release: bump version to 0.1.5.1 2026-05-15 12:12:17 +02:00
duffyduck 61cf8e3bcc 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>
2026-05-15 12:10:13 +02:00
3 changed files with 35 additions and 8 deletions
+2 -2
View File
@@ -79,8 +79,8 @@ android {
applicationId "com.ariacockpit" applicationId "com.ariacockpit"
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 10500 versionCode 10501
versionName "0.1.5.0" versionName "0.1.5.1"
// Fallback fuer Libraries mit Product Flavors // Fallback fuer Libraries mit Product Flavors
missingDimensionStrategy 'react-native-camera', 'general' missingDimensionStrategy 'react-native-camera', 'general'
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "aria-cockpit", "name": "aria-cockpit",
"version": "0.1.5.0", "version": "0.1.5.1",
"private": true, "private": true,
"scripts": { "scripts": {
"android": "react-native run-android", "android": "react-native run-android",
+32 -5
View File
@@ -1425,6 +1425,11 @@ const ChatScreen: React.FC = () => {
// Den aktuellen Snapshot von invertedMessages holen wir via Ref. // Den aktuellen Snapshot von invertedMessages holen wir via Ref.
const invertedMessagesRef = useRef(invertedMessages); const invertedMessagesRef = useRef(invertedMessages);
invertedMessagesRef.current = 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(() => { useEffect(() => {
if (!searchMatchIds.length) { if (!searchMatchIds.length) {
lastSearchScrollKey.current = ''; lastSearchScrollKey.current = '';
@@ -1442,12 +1447,27 @@ const ChatScreen: React.FC = () => {
clearPendingScrollRetry(); clearPendingScrollRetry();
const idx = invertedMessagesRef.current.findIndex(m => m.id === id); const idx = invertedMessagesRef.current.findIndex(m => m.id === id);
if (idx < 0 || !flatListRef.current) return; 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(() => { requestAnimationFrame(() => {
try { setTimeout(() => {
flatListRef.current?.scrollToIndex({ index: idx, animated: true, viewPosition: 0 }); try {
} catch { flatListRef.current?.scrollToIndex({ index: idx, animated: true, viewPosition: 0 });
// onScrollToIndexFailed-Handler uebernimmt den Fallback } catch {
} // onScrollToIndexFailed-Handler uebernimmt den Fallback
}
}, 80);
}); });
}, [searchIndex, searchMatchIds]); }, [searchIndex, searchMatchIds]);
@@ -2159,6 +2179,13 @@ const ChatScreen: React.FC = () => {
ref={flatListRef} ref={flatListRef}
inverted inverted
data={invertedMessages} 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) => { onScroll={(e) => {
// Bei inverted FlatList: contentOffset.y > 0 = weg von "unten" // Bei inverted FlatList: contentOffset.y > 0 = weg von "unten"
// (= aelter scrollen). Wir zeigen den Jump-Down-Button ab ~250px. // (= aelter scrollen). Wir zeigen den Jump-Down-Button ab ~250px.