59 lines
2.5 KiB
TypeScript
59 lines
2.5 KiB
TypeScript
'use client';
|
|
|
|
import { Rss, ExternalLink } from 'lucide-react';
|
|
import { useState, useEffect } from 'react';
|
|
import { formatDistanceToNow } from 'date-fns';
|
|
|
|
export function NewsFeed() {
|
|
const [news, setNews] = useState<any[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
async function fetchNews() {
|
|
try {
|
|
const res = await fetch('/api/rss');
|
|
const data = await res.json();
|
|
if (Array.isArray(data)) {
|
|
setNews(data);
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
fetchNews();
|
|
}, []);
|
|
|
|
return (
|
|
<div className="col-span-1 md:col-span-2 lg:col-span-2 row-span-2 bg-neutral-900 border border-neutral-800 rounded-xl p-6 flex flex-col hover:border-neutral-700 transition-colors">
|
|
<div className="flex items-center gap-2 text-neutral-400 mb-4">
|
|
<Rss size={18} />
|
|
<span className="text-sm font-medium">Hacker News</span>
|
|
</div>
|
|
|
|
<div className="flex-1 overflow-y-auto pr-2 space-y-4 scrollbar-thin scrollbar-thumb-neutral-800 max-h-[300px]">
|
|
{loading ? (
|
|
[1, 2, 3].map(i => (
|
|
<div key={i} className="space-y-2 animate-pulse">
|
|
<div className="h-4 bg-neutral-800 rounded w-3/4"></div>
|
|
<div className="h-3 bg-neutral-800 rounded w-1/2"></div>
|
|
</div>
|
|
))
|
|
) : (
|
|
news.map((item) => (
|
|
<a key={item.link} href={item.link} target="_blank" rel="noopener noreferrer" className="block group cursor-pointer border-b border-neutral-800/50 pb-3 last:border-0 last:pb-0">
|
|
<h3 className="text-sm text-neutral-300 font-medium group-hover:text-amber-400 transition-colors line-clamp-2">
|
|
{item.title}
|
|
</h3>
|
|
<div className="flex gap-3 mt-1 text-xs text-neutral-500">
|
|
<span className="font-mono">{item.pubDate ? formatDistanceToNow(new Date(item.pubDate), { addSuffix: true }) : ''}</span>
|
|
</div>
|
|
</a>
|
|
))
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|