diff --git a/android/src/screens/ChatScreen.tsx b/android/src/screens/ChatScreen.tsx index d99639a..a119532 100644 --- a/android/src/screens/ChatScreen.tsx +++ b/android/src/screens/ChatScreen.tsx @@ -1381,18 +1381,21 @@ const ChatScreen: React.FC = () => { ); const invertedMessages = useMemo(() => [...chatVisibleMessages].reverse(), [chatVisibleMessages]); - // Such-Treffer: alle Message-IDs die zur Query passen, in chronologischer - // Reihenfolge (aelteste zuerst). Bei Query-Change resetten wir den Index. + // Such-Treffer: alle Message-IDs die zur Query passen. NEUESTE ZUERST — + // analog zu WhatsApp/Telegram: User ist visuell unten im Chat, der erste + // Treffer ist meist schon im Viewport (kein weiter Pre-Scroll, kein + // Cold-Start-Sprung-Fail). „Naechster" geht in die Vergangenheit. // WICHTIG: nur in chatVisibleMessages suchen — Spezial-Bubbles (Memory/ // Skill/Trigger) sind im Chat-Stream nicht sichtbar und Treffer auf die // wuerden zu „ID nicht im FlatList → findIndex=-1 → kein Scroll"-Fail - // fuehren (Cessna in einer Memory-Bubble → springt zur falschen Stelle). + // fuehren. const searchMatchIds = useMemo(() => { const q = searchQuery.trim().toLowerCase(); if (!q) return [] as string[]; return chatVisibleMessages .filter(m => (m.text || '').toLowerCase().includes(q)) - .map(m => m.id); + .map(m => m.id) + .reverse(); }, [chatVisibleMessages, searchQuery]); useEffect(() => { @@ -1470,7 +1473,9 @@ const ChatScreen: React.FC = () => { animated: false, }); } catch {} - // Nach kurzer Render-Pause praezise nachsetzen + // Nach kurzer Render-Pause praezise nachsetzen. 200 ms statt 80 ms — + // bei Cold-Start braucht FlatList laenger fuer das Item-Layout, das + // war Stefans „erst beim zweiten Versuch klappt's"-Bug. requestAnimationFrame(() => { setTimeout(() => { try { @@ -1478,7 +1483,7 @@ const ChatScreen: React.FC = () => { } catch { // onScrollToIndexFailed-Handler uebernimmt den Fallback } - }, 80); + }, 200); }); }, [searchIndex, searchMatchIds]); diff --git a/android/src/screens/SettingsScreen.tsx b/android/src/screens/SettingsScreen.tsx index 8e08796..e2d5f19 100644 --- a/android/src/screens/SettingsScreen.tsx +++ b/android/src/screens/SettingsScreen.tsx @@ -1798,7 +1798,7 @@ const SettingsScreen: React.FC = () => { ARIA Cockpit Version {require('../../package.json').version} - ARIA \u2014 Autonomous Reasoning & Intelligence Assistant.{'\n'} + ARIA {'\u2014'} Autonomous Reasoning & Intelligence Assistant.{'\n'} Stefans Kommandozentrale.{'\n'} Gebaut mit React Native + TypeScript. diff --git a/android/src/services/gpsTracking.ts b/android/src/services/gpsTracking.ts index 240aa08..ef98299 100644 --- a/android/src/services/gpsTracking.ts +++ b/android/src/services/gpsTracking.ts @@ -26,6 +26,13 @@ class GpsTrackingService { private listeners: Set = new Set(); // Defensive: nicht zu schnell oeffentlich togglen private lastChangeAt = 0; + // Letzte bekannte Position — wird vom Heartbeat-Timer alle 60s erneut + // an die Bridge gesendet, sonst veraltet near() im Brain (NEAR_MAX_AGE_SEC + // = 5 min) wenn der User stationaer ist und distanceFilter keine Updates + // mehr triggert. + private lastLat: number | null = null; + private lastLon: number | null = null; + private heartbeatTimer: ReturnType | null = null; isActive(): boolean { return this.active; @@ -84,6 +91,8 @@ class GpsTrackingService { (pos) => { const lat = pos.coords.latitude; const lon = pos.coords.longitude; + this.lastLat = lat; + this.lastLon = lon; rvs.send('location_update' as any, { lat, lon }); }, (err) => { @@ -96,6 +105,17 @@ class GpsTrackingService { fastestInterval: 10000, // (Android) max Frequenz } as any, ); + // Heartbeat: alle 60s die letzte bekannte Position erneut senden. + // Sonst bleibt der Brain-State stale wenn der User stationaer ist + // (distanceFilter blockt watchPosition-Updates) → near()-Watcher + // verwerfen die Position als veraltet (NEAR_MAX_AGE_SEC = 300s). + // Kein neuer GPS-Wakeup, nur Re-Send der letzten Werte → akkufreundlich. + if (this.heartbeatTimer) clearInterval(this.heartbeatTimer); + this.heartbeatTimer = setInterval(() => { + if (this.lastLat != null && this.lastLon != null) { + rvs.send('location_update' as any, { lat: this.lastLat, lon: this.lastLon }); + } + }, 60_000); this.active = true; this.lastChangeAt = Date.now(); this.notify(); @@ -118,6 +138,10 @@ class GpsTrackingService { try { Geolocation.clearWatch(this.watchId); } catch {} this.watchId = null; } + if (this.heartbeatTimer) { + clearInterval(this.heartbeatTimer); + this.heartbeatTimer = null; + } this.active = false; this.lastChangeAt = Date.now(); this.notify();