diff --git a/apps/rebreak-native/app/(app)/chat.tsx b/apps/rebreak-native/app/(app)/chat.tsx index 65c35d8..b003632 100644 --- a/apps/rebreak-native/app/(app)/chat.tsx +++ b/apps/rebreak-native/app/(app)/chat.tsx @@ -94,11 +94,11 @@ export default function ChatScreen() { const colors = useColors(); const styles = makeStyles(colors); const [search, setSearch] = useState(''); + const [userRefreshing, setUserRefreshing] = useState(false); const { data: convs = [], isLoading: loadingDms, - isRefetching: refetchingDms, refetch: refetchDms, } = useQuery({ queryKey: ['dm-conversations'], @@ -106,6 +106,15 @@ export default function ChatScreen() { staleTime: 30_000, }); + const handleRefresh = useCallback(async () => { + setUserRefreshing(true); + try { + await refetchDms(); + } finally { + setUserRefreshing(false); + } + }, [refetchDms]); + const filtered = search.trim() ? convs.filter((c) => c.partnerName.toLowerCase().includes(search.toLowerCase()) || @@ -152,8 +161,8 @@ export default function ChatScreen() { keyExtractor={(item) => item.partnerId} refreshControl={ } diff --git a/apps/rebreak-native/app/dm.tsx b/apps/rebreak-native/app/dm.tsx index 6406012..978e9ac 100644 --- a/apps/rebreak-native/app/dm.tsx +++ b/apps/rebreak-native/app/dm.tsx @@ -93,6 +93,7 @@ export default function DmScreen() { setPartner(null); partnerRef.current = null; setReplyTo(null); + isInitialLoad.current = true; }, [userId]); // Lade DM-History — staleTime:0 erzwingt immer frischen Fetch (kein Cache-Hit-Bug) @@ -174,12 +175,20 @@ 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) { - requestAnimationFrame(() => flatRef.current?.scrollToEnd({ animated: true })); + const animated = !isInitialLoad.current; + isInitialLoad.current = false; + scrollToBottom(animated); } - }, [messages.length]); + }, [messages.length, scrollToBottom]); async function handleSend(payload: SendPayload) { if (sending) return; @@ -310,12 +319,12 @@ export default function DmScreen() { onReply={startReply} onLike={toggleLike} onOpenImage={() => {}} + onImageLoad={index === messages.length - 1 ? () => scrollToBottom(false) : undefined} /> )} keyExtractor={(m) => m.id} contentContainerStyle={{ paddingTop: 12, paddingBottom: 8 }} showsVerticalScrollIndicator={false} - onContentSizeChange={() => flatRef.current?.scrollToEnd({ animated: false })} /> )} diff --git a/apps/rebreak-native/components/chat/ChatBubble.tsx b/apps/rebreak-native/components/chat/ChatBubble.tsx index 5227264..a218b08 100644 --- a/apps/rebreak-native/components/chat/ChatBubble.tsx +++ b/apps/rebreak-native/components/chat/ChatBubble.tsx @@ -47,6 +47,7 @@ type Props = { onReply: (msg: ChatMsg) => void; onLike: (msg: ChatMsg) => void; onOpenImage: (url: string) => void; + onImageLoad?: () => void; }; function formatTime(ts: string) { @@ -75,6 +76,7 @@ export function ChatBubble({ onReply, onLike, onOpenImage, + onImageLoad, }: Props) { const { t } = useTranslation(); const colors = useColors(); @@ -201,6 +203,7 @@ export function ChatBubble({ contentFit="cover" cachePolicy="memory-disk" transition={200} + onLoad={onImageLoad ? () => onImageLoad() : undefined} /> {isImageOnly && (