'use client'; import { useCallback, useEffect, useRef, useState } from 'react'; import type { PlazaPost } from '@/types/plaza'; import { PostGrid } from '@/components/feed/PostGrid'; async function loadFeedPage(url: string) { const response = await fetch(url); const payload = await response.json(); if (!response.ok) throw new Error(payload?.error?.message ?? '加载失败'); return payload.data as { posts: PlazaPost[]; next_cursor: string | null; has_more: boolean; }; } export function FeedLoadMore({ initialPosts, initialCursor, initialHasMore, sort, categorySlug, }: { initialPosts: PlazaPost[]; initialCursor: string | null; initialHasMore: boolean; sort: 'hot' | 'new'; categorySlug?: string; }) { const [posts, setPosts] = useState(initialPosts); const [cursor, setCursor] = useState(initialCursor); const [hasMore, setHasMore] = useState(initialHasMore); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const sentinelRef = useRef(null); const loadingRef = useRef(false); const loadMore = useCallback(async () => { if (!hasMore || loadingRef.current || !cursor) return; loadingRef.current = true; setLoading(true); setError(null); try { const query = new URLSearchParams({ sort, limit: '20', cursor }); if (categorySlug) query.set('category', categorySlug); const data = await loadFeedPage(`/api/plaza/v1/feed?${query.toString()}`); setPosts((prev) => [...prev, ...data.posts]); setCursor(data.next_cursor); setHasMore(data.has_more); } catch (err) { setError(err instanceof Error ? err.message : '加载失败'); } finally { loadingRef.current = false; setLoading(false); } }, [categorySlug, cursor, hasMore, sort]); useEffect(() => { if (!hasMore || !cursor) return; const node = sentinelRef.current; if (!node) return; const observer = new IntersectionObserver( (entries) => { if (entries.some((entry) => entry.isIntersecting)) { void loadMore(); } }, { rootMargin: '240px' }, ); observer.observe(node); return () => observer.disconnect(); }, [cursor, hasMore, loadMore]); return (
{error ?

{error}

: null} {hasMore ? (
) : null}
); }