import { useEffect, useRef } from "react"; import { useInfiniteQuery } from "@tanstack/react-query"; import { api, errMsg } from "../api"; import type { Entry } from "../types"; import { Discussion } from "./EntryCard"; // Das Backend deckelt die Seitenzahl bei 100 und liefert darüber wieder // Seite 100 -- ohne diese Grenze würde der Feed sie endlos wiederholen. const maxPage = 100; // Feed lädt Seiten per Infinite-Scroll: queryKey identifiziert den Feed im // Cache (zum Invalidieren nach eigenen Beiträgen), urlFor(page) baut den // API-Pfad der Seite. as ist das Container-Element:
auf der // Startseite,
innerhalb des Profils. export function Feed({ queryKey, urlFor, as: Container = "section" }: { queryKey: unknown[]; urlFor: (page: number) => string; as?: "section" | "div"; }) { const feed = useInfiniteQuery({ queryKey, queryFn: ({ pageParam }) => api.get(urlFor(pageParam)), initialPageParam: 0, getNextPageParam: (last, pages) => last.length && pages.length <= maxPage ? pages.length : undefined, }); const sentinel = useRef(null); const { hasNextPage, isFetching, isError, fetchNextPage } = feed; const canLoad = hasNextPage && !isFetching && !isError; // rootMargin lädt schon kurz vor Erreichen des Sentinels nach. Der Observer // wird nach jeder geladenen Seite neu angelegt und meldet dabei den // aktuellen Zustand: füllt die Seite den Viewport nicht, liegt der Sentinel // noch im Sichtbereich und es wird direkt weitergeladen. useEffect(() => { const el = sentinel.current; if (!el || !canLoad) return; const observer = new IntersectionObserver( (items) => { if (items.some((i) => i.isIntersecting)) fetchNextPage(); }, { rootMargin: "300px" }, ); observer.observe(el); return () => observer.disconnect(); }, [canLoad, fetchNextPage]); // Zwischen zwei Seiten kann ein Beitrag durch einen Repost nach vorn // rutschen und auf der nächsten Seite erneut auftauchen -- nur einmal zeigen. const seen = new Set(); const entries = (feed.data?.pages.flat() ?? []).filter((e) => !seen.has(e.pid) && seen.add(e.pid)); return ( <> {entries.map((e) => )} {feed.isError &&

{errMsg(feed.error, "Feed konnte nicht geladen werden.")}

} {feed.isSuccess && !hasNextPage &&

YOU REACHED THE END!

}
); }