作者主页

This commit is contained in:
2025-11-29 21:56:38 +08:00
parent 63ed49e762
commit 674c202264
11 changed files with 279 additions and 37 deletions
+31 -14
View File
@@ -8,9 +8,10 @@ import type { FeedItem, FeedResponse } from '@/app/types/feed';
type Props = {
initialItems: FeedItem[];
initialCursor: string | null;
fetchUrl?: string;
};
export default function FeedMasonry({ initialItems, initialCursor }: Props) {
export default function FeedMasonry({ initialItems, initialCursor, fetchUrl = '/api/feed' }: Props) {
// 哨兵与容器
const [cursor, setCursor] = useState<string | null>(initialCursor);
const [loading, setLoading] = useState(false);
@@ -91,7 +92,8 @@ export default function FeedMasonry({ initialItems, initialCursor }: Props) {
const params = new URLSearchParams();
if (cursor) params.set('before', cursor);
params.set('limit', '24');
const res = await fetch(`/api/feed?${params.toString()}`, { cache: 'no-store' });
const url = fetchUrl.includes('?') ? `${fetchUrl}&${params.toString()}` : `${fetchUrl}?${params.toString()}`;
const res = await fetch(url, { cache: 'no-store' });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data: FeedResponse = await res.json();
// 将新数据按最短列分配
@@ -137,8 +139,8 @@ export default function FeedMasonry({ initialItems, initialCursor }: Props) {
}, [fetchMore]);
const renderCard = useCallback((item: FeedItem) => (
<Link key={item.aweme_id} href={`/aweme/${item.aweme_id}`} target="_blank" className="mb-4 block group">
<article className="relative overflow-hidden rounded-2xl shadow-sm ring-1 ring-black/5 bg-white/80 dark:bg-zinc-900/60 backdrop-blur-sm transition-transform duration-300 group-hover:-translate-y-1">
<article key={item.aweme_id} className="mb-4 group relative overflow-hidden rounded-2xl shadow-sm ring-1 ring-black/5 bg-white/80 dark:bg-zinc-900/60 backdrop-blur-sm transition-transform duration-300 hover:-translate-y-1">
<Link href={`/aweme/${item.aweme_id}`} target="_blank" className="block relative w-full">
<div
className="relative w-full"
style={{ aspectRatio: `${(item.width && item.height) ? `${item.width}/${item.height}` : ''}` as any }}
@@ -167,18 +169,33 @@ export default function FeedMasonry({ initialItems, initialCursor }: Props) {
</span>
</div>
</div>
</Link>
<div className="flex items-center gap-2 p-3">
<div className="size-6 rounded-full overflow-hidden bg-zinc-200">
{item.author.avatar_url ? (
<img src={item.author.avatar_url} alt="avatar" className="w-full h-full object-cover" />
) : null}
<div className="flex items-center gap-2 p-3">
{item.author.sec_uid ? (
<Link href={`/author/${item.author.sec_uid}`} className="flex items-center gap-2 min-w-0 flex-1 hover:opacity-80 transition-opacity">
<div className="size-6 rounded-full overflow-hidden bg-zinc-200 shrink-0">
{item.author.avatar_url ? (
<img src={item.author.avatar_url} alt="avatar" className="w-full h-full object-cover" />
) : null}
</div>
<span className="text-sm text-zinc-700 dark:text-zinc-300 truncate">{item.author.nickname}</span>
</Link>
) : (
<div className="flex items-center gap-2 min-w-0 flex-1">
<div className="size-6 rounded-full overflow-hidden bg-zinc-200 shrink-0">
{item.author.avatar_url ? (
<img src={item.author.avatar_url} alt="avatar" className="w-full h-full object-cover" />
) : null}
</div>
<span className="text-sm text-zinc-700 dark:text-zinc-300 truncate">{item.author.nickname}</span>
</div>
<span className="text-sm text-zinc-700 dark:text-zinc-300 truncate">{item.author.nickname}</span>
<span className="ml-auto text-sm text-zinc-700 dark:text-zinc-300">{item.likes} </span><ThumbsUp size={16} style={{ color: 'var(--color-zinc-700)' }} />
</div>
</article>
</Link>
)}
<span className="ml-auto text-sm text-zinc-700 dark:text-zinc-300 flex items-center gap-1">
{item.likes} <ThumbsUp size={16} style={{ color: 'var(--color-zinc-700)' }} />
</span>
</div>
</article>
), []);
return (