From 48a8bbc4af73cfca702f2f310740b188ce3b1685 Mon Sep 17 00:00:00 2001 From: chahinebrini Date: Fri, 22 May 2026 20:33:34 +0200 Subject: [PATCH] =?UTF-8?q?fix(chat):=20Conversation=20auf=20inverted=20Fl?= =?UTF-8?q?atList=20=E2=80=94=20Scroll-to-bottom=20bulletproof?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der setTimeout(80)+onImageLoad-Ansatz war ein Timing-Hack gegen ein strukturelles Problem (lazy Item-Measurement unter Fabric -> scrollToEnd landet zu kurz). Stattdessen jetzt inverted FlatList: Index 0 sitzt permanent am Bildschirmrand, neueste Nachricht immer sichtbar. - dm.tsx: inverted + reversedMessages, Gruppen-Logik gespiegelt, manuellen Auto-Scroll + keyboardHeight-State entfernt - ChatBubble.tsx: onImageLoad-Prop entfernt (obsolet) Co-Authored-By: Claude Opus 4.7 --- apps/rebreak-native/app/dm.tsx | 45 ++++--------------- .../components/chat/ChatBubble.tsx | 3 -- 2 files changed, 8 insertions(+), 40 deletions(-) diff --git a/apps/rebreak-native/app/dm.tsx b/apps/rebreak-native/app/dm.tsx index 978e9ac..d6cf09c 100644 --- a/apps/rebreak-native/app/dm.tsx +++ b/apps/rebreak-native/app/dm.tsx @@ -1,4 +1,4 @@ -import { useState, useRef, useEffect, useCallback } from 'react'; +import { useState, useRef, useEffect, useCallback, useMemo } from 'react'; import { View, Text, @@ -7,7 +7,6 @@ import { Platform, ActivityIndicator, StyleSheet, - Keyboard, KeyboardAvoidingView, } from 'react-native'; import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -59,7 +58,6 @@ export default function DmScreen() { const colors = useColors(); const styles = makeStyles(colors); const queryClient = useQueryClient(); - const flatRef = useRef(null); const myUserId = useAuthStore((s) => s.user?.id); const colorScheme = useThemeStore((s) => s.colorScheme); @@ -67,7 +65,6 @@ export default function DmScreen() { const { userId } = useLocalSearchParams<{ userId: string }>(); - const [keyboardHeight, setKeyboardHeight] = useState(0); const [messages, setMessages] = useState([]); const [partner, setPartner] = useState(null); const partnerRef = useRef(null); @@ -76,24 +73,12 @@ export default function DmScreen() { ); const [sending, setSending] = useState(false); - useEffect(() => { - const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow'; - const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide'; - const show = Keyboard.addListener(showEvent, (e) => setKeyboardHeight(e.endCoordinates.height)); - const hide = Keyboard.addListener(hideEvent, () => setKeyboardHeight(0)); - return () => { - show.remove(); - hide.remove(); - }; - }, []); - // Reset aller conversation-spezifischen States wenn userId wechselt (Stack-Reuse) useEffect(() => { setMessages([]); setPartner(null); partnerRef.current = null; setReplyTo(null); - isInitialLoad.current = true; }, [userId]); // Lade DM-History — staleTime:0 erzwingt immer frischen Fetch (kein Cache-Hit-Bug) @@ -175,20 +160,7 @@ export default function DmScreen() { ); useDmRealtime(userId, onDmInsert, !!myUserId); - const isInitialLoad = useRef(true); - - const scrollToBottom = useCallback((animated: boolean) => { - setTimeout(() => flatRef.current?.scrollToEnd({ animated }), 80); - }, []); - - // Auto-Scroll bei neuen Messages - useEffect(() => { - if (messages.length > 0) { - const animated = !isInitialLoad.current; - isInitialLoad.current = false; - scrollToBottom(animated); - } - }, [messages.length, scrollToBottom]); + const reversedMessages = useMemo(() => [...messages].reverse(), [messages]); async function handleSend(payload: SendPayload) { if (sending) return; @@ -308,28 +280,27 @@ export default function DmScreen() { ) : ( ( {}} - onImageLoad={index === messages.length - 1 ? () => scrollToBottom(false) : undefined} /> )} keyExtractor={(m) => m.id} - contentContainerStyle={{ paddingTop: 12, paddingBottom: 8 }} + contentContainerStyle={{ paddingBottom: 12, paddingTop: 8 }} showsVerticalScrollIndicator={false} /> )} - 0 ? 8 : Math.max(12, insets.bottom), backgroundColor: colors.bg }}> + void; onLike: (msg: ChatMsg) => void; onOpenImage: (url: string) => void; - onImageLoad?: () => void; }; function formatTime(ts: string) { @@ -76,7 +75,6 @@ export function ChatBubble({ onReply, onLike, onOpenImage, - onImageLoad, }: Props) { const { t } = useTranslation(); const colors = useColors(); @@ -203,7 +201,6 @@ export function ChatBubble({ contentFit="cover" cachePolicy="memory-disk" transition={200} - onLoad={onImageLoad ? () => onImageLoad() : undefined} /> {isImageOnly && (